Compare commits
16 Commits
release/0.
...
release/0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6cf423721 | ||
|
|
c7d0803000 | ||
|
|
1a4b9b440d | ||
|
|
1f914c2b4d | ||
|
|
fadb316451 | ||
|
|
3fefd3c1fb | ||
|
|
2cbb314d0b | ||
|
|
535fc70433 | ||
|
|
10fa276bec | ||
|
|
1cc9afaeb3 | ||
|
|
6f5e621561 | ||
|
|
1dd6f2d9f8 | ||
|
|
f92b45db6a | ||
|
|
c971d54aea | ||
|
|
2abccafb8f | ||
|
|
75d0415bec |
101
.github/ISSUE_TEMPLATE/minor_release.md
vendored
Normal file
101
.github/ISSUE_TEMPLATE/minor_release.md
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
name: Minor Release
|
||||||
|
about: Create a new minor release [for release managers only]
|
||||||
|
title: 'Release MAJOR.MINOR+1.0'
|
||||||
|
labels: 'release'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create a new minor release
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
<--release summary to be used in announcements-->
|
||||||
|
|
||||||
|
### Commit
|
||||||
|
|
||||||
|
<--latest commit ID to include in this release-->
|
||||||
|
|
||||||
|
### Changelog
|
||||||
|
|
||||||
|
<--add notices from PRs merged since the prior release, see ["keep a changelog"]-->
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
Release numbering must follow [Semantic Versioning]. These steps assume the current `master`
|
||||||
|
branch **development** version is *MAJOR.MINOR.0*.
|
||||||
|
|
||||||
|
#### On the day of the feature freeze
|
||||||
|
|
||||||
|
Change the `master` branch to the next MINOR+1 version:
|
||||||
|
|
||||||
|
- [ ] Switch to the `master` branch.
|
||||||
|
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR+1`, eg. `bump_dev_0_22`.
|
||||||
|
- [ ] Bump the `bump_dev_MAJOR_MINOR+1` branch to the next development MINOR+1 version.
|
||||||
|
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
|
||||||
|
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
|
||||||
|
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR+1` branch to `master`.
|
||||||
|
- Title PR "Bump version to MAJOR.MINOR+1.0".
|
||||||
|
|
||||||
|
Create a new release branch:
|
||||||
|
|
||||||
|
- [ ] Double check that your local `master` is up-to-date with the upstream repo.
|
||||||
|
- [ ] Create a new branch called `release/MAJOR.MINOR+1` from `master`.
|
||||||
|
|
||||||
|
Add a release candidate tag, this is optional and only needed for major `bdk-ffi` changes that
|
||||||
|
require a longer testing cycle:
|
||||||
|
|
||||||
|
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.1` version.
|
||||||
|
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.1`.
|
||||||
|
- The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.1".
|
||||||
|
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
|
||||||
|
- The tag name should be `vMAJOR.MINOR+1.0-rc.1`
|
||||||
|
- Use message "Release MAJOR.MINOR+1.0 rc.1".
|
||||||
|
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
|
||||||
|
- [ ] Push the `release/MAJOR.MINOR` branch and new tag to the `bitcoindevkit/bdk` repo.
|
||||||
|
- Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.1` tag.
|
||||||
|
|
||||||
|
If any issues need to be fixed before the *MAJOR.MINOR+1.0* version is released:
|
||||||
|
|
||||||
|
- [ ] Merge fix PRs to the `master` branch.
|
||||||
|
- [ ] Git cherry-pick fix commits to the `release/MAJOR.MINOR+1` branch.
|
||||||
|
- [ ] Verify fixes in `release/MAJOR.MINOR+1` branch.
|
||||||
|
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.x+1` version.
|
||||||
|
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.x+1`.
|
||||||
|
- The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.x+1".
|
||||||
|
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
|
||||||
|
- The tag name should be `vMAJOR.MINOR+1.0-rc.x+1`, where x is the current release candidate number.
|
||||||
|
- Use tag message "Release MAJOR.MINOR+1.0 rc.x+1".
|
||||||
|
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
|
||||||
|
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
|
||||||
|
- Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.x+1` tag.
|
||||||
|
|
||||||
|
#### On the day of the release
|
||||||
|
|
||||||
|
Tag and publish new release:
|
||||||
|
|
||||||
|
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0` version.
|
||||||
|
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
|
||||||
|
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
|
||||||
|
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
|
||||||
|
- The tag name should be `vMAJOR.MINOR+1.0`
|
||||||
|
- The first line of the tag message should be "Release MAJOR.MINOR+1.0".
|
||||||
|
- In the body of the tag message put a copy of the **Summary** and **Changelog** for the release.
|
||||||
|
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
|
||||||
|
- [ ] Wait for the CI to finish one last time.
|
||||||
|
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
|
||||||
|
- [ ] Create the release on GitHub.
|
||||||
|
- Go to "tags", click on the dots on the right and select "Create Release".
|
||||||
|
- Set the title to `Release MAJOR.MINOR+1.0`.
|
||||||
|
- In the release notes body put the **Summary** and **Changelog**.
|
||||||
|
- Use the "+ Auto-generate release notes" button to add details from included PRs.
|
||||||
|
- Until we reach a `1.0.0` release check the "Pre-release" box.
|
||||||
|
- [ ] After downstream language repos are also updated announce the release, using the **Summary**,
|
||||||
|
on Discord, Twitter and Mastodon.
|
||||||
|
- [ ] Celebrate 🎉
|
||||||
|
|
||||||
|
[Semantic Versioning]: https://semver.org/
|
||||||
|
[crates.io]: https://crates.io/crates/bdk
|
||||||
|
[docs.rs]: https://docs.rs/bdk/latest/bdk
|
||||||
|
["keep a changelog"]: https://keepachangelog.com/en/1.0.0/
|
||||||
69
.github/ISSUE_TEMPLATE/patch_release.md
vendored
Normal file
69
.github/ISSUE_TEMPLATE/patch_release.md
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
name: Patch Release
|
||||||
|
about: Create a new patch release [for release managers only]
|
||||||
|
title: 'Release MAJOR.MINOR.PATCH+1'
|
||||||
|
labels: 'release'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create a new patch release
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
<--release summary to be used in announcements-->
|
||||||
|
|
||||||
|
### Commit
|
||||||
|
|
||||||
|
<--latest commit ID to include in this release-->
|
||||||
|
|
||||||
|
### Changelog
|
||||||
|
|
||||||
|
<--add notices from PRs merged since the prior release, see ["keep a changelog"]-->
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
Release numbering must follow [Semantic Versioning]. These steps assume the current `master`
|
||||||
|
branch **development** version is *MAJOR.MINOR.PATCH*.
|
||||||
|
|
||||||
|
### On the day of the patch release
|
||||||
|
|
||||||
|
Change the `master` branch to the new PATCH+1 version:
|
||||||
|
|
||||||
|
- [ ] Switch to the `master` branch.
|
||||||
|
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR_PATCH+1`, eg. `bump_dev_0_22_1`.
|
||||||
|
- [ ] Bump the `bump_dev_MAJOR_MINOR` branch to the next development PATCH+1 version.
|
||||||
|
- Change the `Cargo.toml` version value to `MAJOR.MINOR.PATCH+1`.
|
||||||
|
- The commit message should be "Bump version to MAJOR.MINOR.PATCH+1".
|
||||||
|
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR_PATCH+1` branch to `master`.
|
||||||
|
- Title PR "Bump version to MAJOR.MINOR.PATCH+1".
|
||||||
|
|
||||||
|
Cherry-pick, tag and publish new PATCH+1 release:
|
||||||
|
|
||||||
|
- [ ] Merge fix PRs to the `master` branch.
|
||||||
|
- [ ] Git cherry-pick fix commits to the `release/MAJOR.MINOR` branch to be patched.
|
||||||
|
- [ ] Verify fixes in `release/MAJOR.MINOR` branch.
|
||||||
|
- [ ] Bump the `release/MAJOR.MINOR.PATCH+1` branch to `MAJOR.MINOR.PATCH+1` version.
|
||||||
|
- Change the `Cargo.toml` version value to `MAJOR.MINOR.MINOR.PATCH+1`.
|
||||||
|
- The commit message should be "Bump version to MAJOR.MINOR.PATCH+1".
|
||||||
|
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR` branch.
|
||||||
|
- The tag name should be `vMAJOR.MINOR.PATCH+1`
|
||||||
|
- The first line of the tag message should be "Release MAJOR.MINOR.PATCH+1".
|
||||||
|
- In the body of the tag message put a copy of the **Summary** and **Changelog** for the release.
|
||||||
|
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
|
||||||
|
- [ ] Wait for the CI to finish one last time.
|
||||||
|
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
|
||||||
|
- [ ] Create the release on GitHub.
|
||||||
|
- Go to "tags", click on the dots on the right and select "Create Release".
|
||||||
|
- Set the title to `Release MAJOR.MINOR.PATCH+1`.
|
||||||
|
- In the release notes body put the **Summary** and **Changelog**.
|
||||||
|
- Use the "+ Auto-generate release notes" button to add details from included PRs.
|
||||||
|
- Until we reach a `1.0.0` release check the "Pre-release" box.
|
||||||
|
- [ ] After downstream language repos are also updated announce the release, using the **Summary**,
|
||||||
|
on Discord, Twitter and Mastodon.
|
||||||
|
- [ ] Celebrate 🎉
|
||||||
|
|
||||||
|
[Semantic Versioning]: https://semver.org/
|
||||||
|
[crates.io]: https://crates.io/crates/bdk
|
||||||
|
[docs.rs]: https://docs.rs/bdk/latest/bdk
|
||||||
|
["keep a changelog"]: https://keepachangelog.com/en/1.0.0/
|
||||||
6
.github/pull_request_template.md
vendored
6
.github/pull_request_template.md
vendored
@@ -9,6 +9,11 @@
|
|||||||
<!-- In this section you can include notes directed to the reviewers, like explaining why some parts
|
<!-- In this section you can include notes directed to the reviewers, like explaining why some parts
|
||||||
of the PR were done in a specific way -->
|
of the PR were done in a specific way -->
|
||||||
|
|
||||||
|
### Changelog notice
|
||||||
|
|
||||||
|
<!-- Notice the release manager should include in the release tag message changelog -->
|
||||||
|
<!-- See https://keepachangelog.com/en/1.0.0/ for examples -->
|
||||||
|
|
||||||
### Checklists
|
### Checklists
|
||||||
|
|
||||||
#### All Submissions:
|
#### All Submissions:
|
||||||
@@ -21,7 +26,6 @@ of the PR were done in a specific way -->
|
|||||||
|
|
||||||
* [ ] I've added tests for the new feature
|
* [ ] I've added tests for the new feature
|
||||||
* [ ] I've added docs for the new feature
|
* [ ] I've added docs for the new feature
|
||||||
* [ ] I've updated `CHANGELOG.md`
|
|
||||||
|
|
||||||
#### Bugfixes:
|
#### Bugfixes:
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project prior to release **0.9.0** are documented in this file. Future
|
||||||
|
changelog information can be found in each release's git tag and can be viewed with `git tag -ln100 "v*"`.
|
||||||
|
Changelog info is also documented on the [GitHub releases](https://github.com/bitcoindevkit/bdk-ffi/releases)
|
||||||
|
page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|||||||
10
Cargo.toml
10
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bdk-ffi"
|
name = "bdk-ffi"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@@ -14,10 +14,10 @@ crate-type = ["staticlib", "cdylib"]
|
|||||||
name = "bdkffi"
|
name = "bdkffi"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bdk = { version = "0.22", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
|
bdk = { version = "0.23", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
|
||||||
|
|
||||||
uniffi_macros = { version = "0.19.3", features = ["builtin-bindgen"] }
|
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||||
uniffi = { version = "0.19.3", features = ["builtin-bindgen"] }
|
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
uniffi_build = { version = "0.19.3", features = ["builtin-bindgen"] }
|
uniffi_build = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||||
|
|||||||
35
DEVELOPMENT_CYCLE.md
Normal file
35
DEVELOPMENT_CYCLE.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Development Cycle
|
||||||
|
|
||||||
|
This project follows a regular releasing schedule similar to the one [used by the Rust language]
|
||||||
|
except releases always follow the latest [`bdk`] release by one to two weeks. In short, this means
|
||||||
|
that a new release is made at a regular cadence, with all the feature/bugfixes that made it to
|
||||||
|
`master` in time. This ensures that we don't keep delaying releases waiting for
|
||||||
|
"just one more little thing".
|
||||||
|
|
||||||
|
After making a new `bdk-ffi` release tag all downstream language bindings should also be updated.
|
||||||
|
|
||||||
|
This project uses [Semantic Versioning], but is currently at MAJOR version zero (0.y.z) meaning it
|
||||||
|
is still in initial development. Anything MAY change at any time. The public API SHOULD NOT be
|
||||||
|
considered stable. Until we reach version `1.0.0` we will do our best to document any breaking API
|
||||||
|
changes in the changelog info attached to each release tag.
|
||||||
|
|
||||||
|
We decided to maintain a faster release cycle while the library is still in "beta", i.e. before
|
||||||
|
release `1.0.0`: since we are constantly adding new features and, even more importantly, fixing
|
||||||
|
issues, we want developers to have access to those updates as fast as possible. For this reason we
|
||||||
|
will make a release **every 4 weeks**.
|
||||||
|
|
||||||
|
Once the project reaches a more mature state (>= `1.0.0`), we will very likely switch to longer
|
||||||
|
release cycles of **6 weeks**.
|
||||||
|
|
||||||
|
The "feature freeze" will happen when [`bdk`] releases a release candidate. This project will then
|
||||||
|
be updated and tested with [`bdk`] release candidates until a final release is published. This
|
||||||
|
means a new branch will be created originating from the `master` tip at that time, and in that
|
||||||
|
branch we will stop adding new features and only focus on ensuring the ones we've added are working
|
||||||
|
properly.
|
||||||
|
|
||||||
|
To create a new release a release manager will create a new issue using a `Release` template and
|
||||||
|
follow the template instructions.
|
||||||
|
|
||||||
|
[used by the Rust language]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html
|
||||||
|
[Semantic Versioning]: https://semver.org/
|
||||||
|
[`bdk`]: https://github.com/bitcoindevkit/bdk
|
||||||
@@ -4,7 +4,7 @@ version = "0.2.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "=1.0.45" # remove after upgrading to next version of uniffi
|
anyhow = "1.0.45" # remove after upgrading to next version of uniffi
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
uniffi_bindgen = "0.19.5"
|
uniffi_bindgen = "0.21.0"
|
||||||
camino = "1.0.9"
|
camino = "1.0.9"
|
||||||
|
|||||||
33
src/bdk.udl
33
src/bdk.udl
@@ -173,8 +173,8 @@ dictionary LocalUtxo {
|
|||||||
boolean is_spent;
|
boolean is_spent;
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary AddressAmount {
|
dictionary ScriptAmount {
|
||||||
string address;
|
Script script;
|
||||||
u64 amount;
|
u64 amount;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -210,12 +210,22 @@ interface PartiallySignedBitcoinTransaction {
|
|||||||
string serialize();
|
string serialize();
|
||||||
|
|
||||||
string txid();
|
string txid();
|
||||||
|
|
||||||
|
sequence<u8> extract_tx();
|
||||||
|
|
||||||
|
[Throws=BdkError]
|
||||||
|
PartiallySignedBitcoinTransaction combine(PartiallySignedBitcoinTransaction other);
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary TxBuilderResult {
|
||||||
|
PartiallySignedBitcoinTransaction psbt;
|
||||||
|
TransactionDetails transaction_details;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TxBuilder {
|
interface TxBuilder {
|
||||||
constructor();
|
constructor();
|
||||||
|
|
||||||
TxBuilder add_recipient(string address, u64 amount);
|
TxBuilder add_recipient(Script script, u64 amount);
|
||||||
|
|
||||||
TxBuilder add_unspendable(OutPoint unspendable);
|
TxBuilder add_unspendable(OutPoint unspendable);
|
||||||
|
|
||||||
@@ -245,10 +255,10 @@ interface TxBuilder {
|
|||||||
|
|
||||||
TxBuilder add_data(sequence<u8> data);
|
TxBuilder add_data(sequence<u8> data);
|
||||||
|
|
||||||
TxBuilder set_recipients(sequence<AddressAmount> recipients);
|
TxBuilder set_recipients(sequence<ScriptAmount> recipients);
|
||||||
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
PartiallySignedBitcoinTransaction finish([ByRef] Wallet wallet);
|
TxBuilderResult finish([ByRef] Wallet wallet);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface BumpFeeTxBuilder {
|
interface BumpFeeTxBuilder {
|
||||||
@@ -280,6 +290,8 @@ interface DescriptorSecretKey {
|
|||||||
|
|
||||||
DescriptorPublicKey as_public();
|
DescriptorPublicKey as_public();
|
||||||
|
|
||||||
|
sequence<u8> secret_bytes();
|
||||||
|
|
||||||
string as_string();
|
string as_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -291,3 +303,14 @@ interface DescriptorPublicKey {
|
|||||||
|
|
||||||
string as_string();
|
string as_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface Address {
|
||||||
|
[Throws=BdkError]
|
||||||
|
constructor(string address);
|
||||||
|
|
||||||
|
Script script_pubkey();
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Script {
|
||||||
|
constructor(sequence<u8> raw_output_script);
|
||||||
|
};
|
||||||
|
|||||||
187
src/lib.rs
187
src/lib.rs
@@ -1,8 +1,10 @@
|
|||||||
|
use bdk::bitcoin::blockdata::script::Script as BdkScript;
|
||||||
use bdk::bitcoin::hashes::hex::ToHex;
|
use bdk::bitcoin::hashes::hex::ToHex;
|
||||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
use bdk::bitcoin::secp256k1::Secp256k1;
|
||||||
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
|
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
|
||||||
|
use bdk::bitcoin::util::psbt::serialize::Serialize;
|
||||||
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
|
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
|
||||||
use bdk::bitcoin::{Address, Network, OutPoint as BdkOutPoint, Script, Txid};
|
use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid};
|
||||||
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
|
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
|
||||||
use bdk::blockchain::GetBlockHash;
|
use bdk::blockchain::GetBlockHash;
|
||||||
use bdk::blockchain::GetHeight;
|
use bdk::blockchain::GetHeight;
|
||||||
@@ -23,7 +25,7 @@ use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
|||||||
use bdk::wallet::AddressIndex as BdkAddressIndex;
|
use bdk::wallet::AddressIndex as BdkAddressIndex;
|
||||||
use bdk::wallet::AddressInfo as BdkAddressInfo;
|
use bdk::wallet::AddressInfo as BdkAddressInfo;
|
||||||
use bdk::{
|
use bdk::{
|
||||||
Balance as BdkBalance, BlockTime, Error, FeeRate, KeychainKind, SignOptions,
|
Balance as BdkBalance, BlockTime, Error as BdkError, FeeRate, KeychainKind, SignOptions,
|
||||||
SyncOptions as BdkSyncOptions, Wallet as BdkWallet,
|
SyncOptions as BdkSyncOptions, Wallet as BdkWallet,
|
||||||
};
|
};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@@ -35,10 +37,9 @@ use std::sync::{Arc, Mutex, MutexGuard};
|
|||||||
|
|
||||||
uniffi_macros::include_scaffolding!("bdk");
|
uniffi_macros::include_scaffolding!("bdk");
|
||||||
|
|
||||||
type BdkError = Error;
|
/// A output script and an amount of satoshis.
|
||||||
|
pub struct ScriptAmount {
|
||||||
pub struct AddressAmount {
|
pub script: Arc<Script>,
|
||||||
pub address: String,
|
|
||||||
pub amount: u64,
|
pub amount: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,16 +209,16 @@ impl Blockchain {
|
|||||||
self.blockchain_mutex.lock().expect("blockchain")
|
self.blockchain_mutex.lock().expect("blockchain")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn broadcast(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<(), Error> {
|
fn broadcast(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<(), BdkError> {
|
||||||
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
|
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
|
||||||
self.get_blockchain().broadcast(&tx)
|
self.get_blockchain().broadcast(&tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
fn get_height(&self) -> Result<u32, BdkError> {
|
||||||
self.get_blockchain().get_height()
|
self.get_blockchain().get_height()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block_hash(&self, height: u32) -> Result<String, Error> {
|
fn get_block_hash(&self, height: u32) -> Result<String, BdkError> {
|
||||||
self.get_blockchain()
|
self.get_blockchain()
|
||||||
.get_block_hash(u64::from(height))
|
.get_block_hash(u64::from(height))
|
||||||
.map(|hash| hash.to_string())
|
.map(|hash| hash.to_string())
|
||||||
@@ -304,12 +305,9 @@ impl NetworkLocalUtxo for LocalUtxo {
|
|||||||
},
|
},
|
||||||
txout: TxOut {
|
txout: TxOut {
|
||||||
value: x.txout.value,
|
value: x.txout.value,
|
||||||
address: bdk::bitcoin::util::address::Address::from_script(
|
address: BdkAddress::from_script(&x.txout.script_pubkey, network)
|
||||||
&x.txout.script_pubkey,
|
.unwrap()
|
||||||
network,
|
.to_string(),
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
},
|
},
|
||||||
keychain: x.keychain,
|
keychain: x.keychain,
|
||||||
is_spent: x.is_spent,
|
is_spent: x.is_spent,
|
||||||
@@ -329,7 +327,7 @@ struct ProgressHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BdkProgress for ProgressHolder {
|
impl BdkProgress for ProgressHolder {
|
||||||
fn update(&self, progress: f32, message: Option<String>) -> Result<(), Error> {
|
fn update(&self, progress: f32, message: Option<String>) -> Result<(), BdkError> {
|
||||||
self.progress.update(progress, message);
|
self.progress.update(progress, message);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -342,12 +340,12 @@ impl fmt::Debug for ProgressHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PartiallySignedBitcoinTransaction {
|
pub struct PartiallySignedBitcoinTransaction {
|
||||||
internal: Mutex<PartiallySignedTransaction>,
|
internal: Mutex<PartiallySignedTransaction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartiallySignedBitcoinTransaction {
|
impl PartiallySignedBitcoinTransaction {
|
||||||
fn new(psbt_base64: String) -> Result<Self, Error> {
|
fn new(psbt_base64: String) -> Result<Self, BdkError> {
|
||||||
let psbt: PartiallySignedTransaction = PartiallySignedTransaction::from_str(&psbt_base64)?;
|
let psbt: PartiallySignedTransaction = PartiallySignedTransaction::from_str(&psbt_base64)?;
|
||||||
Ok(PartiallySignedBitcoinTransaction {
|
Ok(PartiallySignedBitcoinTransaction {
|
||||||
internal: Mutex::new(psbt),
|
internal: Mutex::new(psbt),
|
||||||
@@ -364,6 +362,32 @@ impl PartiallySignedBitcoinTransaction {
|
|||||||
let txid = tx.txid();
|
let txid = tx.txid();
|
||||||
txid.to_hex()
|
txid.to_hex()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the transaction as bytes.
|
||||||
|
fn extract_tx(&self) -> Vec<u8> {
|
||||||
|
self.internal
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.extract_tx()
|
||||||
|
.serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
|
||||||
|
///
|
||||||
|
/// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
||||||
|
fn combine(
|
||||||
|
&self,
|
||||||
|
other: Arc<PartiallySignedBitcoinTransaction>,
|
||||||
|
) -> Result<Arc<PartiallySignedBitcoinTransaction>, BdkError> {
|
||||||
|
let other_psbt = other.internal.lock().unwrap().clone();
|
||||||
|
let mut original_psbt = self.internal.lock().unwrap().clone();
|
||||||
|
|
||||||
|
original_psbt.combine(other_psbt)?;
|
||||||
|
Ok(Arc::new(PartiallySignedBitcoinTransaction {
|
||||||
|
internal: Mutex::new(original_psbt),
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Bitcoin wallet.
|
/// A Bitcoin wallet.
|
||||||
@@ -430,18 +454,18 @@ impl Wallet {
|
|||||||
|
|
||||||
/// Return the balance, meaning the sum of this wallet’s unspent outputs’ values. Note that this method only operates
|
/// Return the balance, meaning the sum of this wallet’s unspent outputs’ values. Note that this method only operates
|
||||||
/// on the internal database, which first needs to be Wallet.sync manually.
|
/// on the internal database, which first needs to be Wallet.sync manually.
|
||||||
fn get_balance(&self) -> Result<Balance, Error> {
|
fn get_balance(&self) -> Result<Balance, BdkError> {
|
||||||
self.get_wallet().get_balance().map(|b| b.into())
|
self.get_wallet().get_balance().map(|b| b.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign a transaction with all the wallet’s signers.
|
/// Sign a transaction with all the wallet’s signers.
|
||||||
fn sign(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<bool, Error> {
|
fn sign(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<bool, BdkError> {
|
||||||
let mut psbt = psbt.internal.lock().unwrap();
|
let mut psbt = psbt.internal.lock().unwrap();
|
||||||
self.get_wallet().sign(&mut psbt, SignOptions::default())
|
self.get_wallet().sign(&mut psbt, SignOptions::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually.
|
/// Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually.
|
||||||
fn list_transactions(&self) -> Result<Vec<TransactionDetails>, Error> {
|
fn list_transactions(&self) -> Result<Vec<TransactionDetails>, BdkError> {
|
||||||
let transaction_details = self.get_wallet().list_transactions(true)?;
|
let transaction_details = self.get_wallet().list_transactions(true)?;
|
||||||
Ok(transaction_details
|
Ok(transaction_details
|
||||||
.iter()
|
.iter()
|
||||||
@@ -451,7 +475,7 @@ impl Wallet {
|
|||||||
|
|
||||||
/// Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database,
|
/// Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database,
|
||||||
/// which first needs to be Wallet.sync manually.
|
/// which first needs to be Wallet.sync manually.
|
||||||
fn list_unspent(&self) -> Result<Vec<LocalUtxo>, Error> {
|
fn list_unspent(&self) -> Result<Vec<LocalUtxo>, BdkError> {
|
||||||
let unspents = self.get_wallet().list_unspent()?;
|
let unspents = self.get_wallet().list_unspent()?;
|
||||||
Ok(unspents
|
Ok(unspents
|
||||||
.iter()
|
.iter()
|
||||||
@@ -460,24 +484,63 @@ impl Wallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_script_pubkey(address: &str) -> Result<Script, BdkError> {
|
fn to_script_pubkey(address: &str) -> Result<BdkScript, BdkError> {
|
||||||
Address::from_str(address)
|
BdkAddress::from_str(address)
|
||||||
.map(|x| x.script_pubkey())
|
.map(|x| x.script_pubkey())
|
||||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Bitcoin address.
|
||||||
|
struct Address {
|
||||||
|
address: BdkAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Address {
|
||||||
|
fn new(address: String) -> Result<Self, BdkError> {
|
||||||
|
BdkAddress::from_str(address.as_str())
|
||||||
|
.map(|a| Address { address: a })
|
||||||
|
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn script_pubkey(&self) -> Arc<Script> {
|
||||||
|
Arc::new(Script {
|
||||||
|
script: self.address.script_pubkey(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Bitcoin script.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Script {
|
||||||
|
script: BdkScript,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Script {
|
||||||
|
fn new(raw_output_script: Vec<u8>) -> Self {
|
||||||
|
let script: BdkScript = BdkScript::from(raw_output_script);
|
||||||
|
Script { script }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum RbfValue {
|
enum RbfValue {
|
||||||
Default,
|
Default,
|
||||||
Value(u32),
|
Value(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
|
||||||
|
/// transaction details.
|
||||||
|
pub struct TxBuilderResult {
|
||||||
|
pub psbt: Arc<PartiallySignedBitcoinTransaction>,
|
||||||
|
pub transaction_details: TransactionDetails,
|
||||||
|
}
|
||||||
|
|
||||||
/// A transaction builder.
|
/// A transaction builder.
|
||||||
/// After creating the TxBuilder, you set options on it until finally calling finish to consume the builder and generate the transaction.
|
/// After creating the TxBuilder, you set options on it until finally calling finish to consume the builder and generate the transaction.
|
||||||
/// Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
|
/// Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct TxBuilder {
|
struct TxBuilder {
|
||||||
recipients: Vec<(String, u64)>,
|
recipients: Vec<(BdkScript, u64)>,
|
||||||
utxos: Vec<OutPoint>,
|
utxos: Vec<OutPoint>,
|
||||||
unspendable: HashSet<OutPoint>,
|
unspendable: HashSet<OutPoint>,
|
||||||
change_policy: ChangeSpendPolicy,
|
change_policy: ChangeSpendPolicy,
|
||||||
@@ -508,19 +571,19 @@ impl TxBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add a recipient to the internal list.
|
/// Add a recipient to the internal list.
|
||||||
fn add_recipient(&self, recipient: String, amount: u64) -> Arc<Self> {
|
fn add_recipient(&self, script: Arc<Script>, amount: u64) -> Arc<Self> {
|
||||||
let mut recipients = self.recipients.to_vec();
|
let mut recipients: Vec<(BdkScript, u64)> = self.recipients.clone();
|
||||||
recipients.append(&mut vec![(recipient, amount)]);
|
recipients.append(&mut vec![(script.script.clone(), amount)]);
|
||||||
Arc::new(TxBuilder {
|
Arc::new(TxBuilder {
|
||||||
recipients,
|
recipients,
|
||||||
..self.clone()
|
..self.clone()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_recipients(&self, recipients: Vec<AddressAmount>) -> Arc<Self> {
|
fn set_recipients(&self, recipients: Vec<ScriptAmount>) -> Arc<Self> {
|
||||||
let recipients = recipients
|
let recipients = recipients
|
||||||
.iter()
|
.iter()
|
||||||
.map(|address_amount| (address_amount.address.clone(), address_amount.amount))
|
.map(|script_amount| (script_amount.script.script.clone(), script_amount.amount))
|
||||||
.collect();
|
.collect();
|
||||||
Arc::new(TxBuilder {
|
Arc::new(TxBuilder {
|
||||||
recipients,
|
recipients,
|
||||||
@@ -657,11 +720,11 @@ impl TxBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finish building the transaction. Returns the BIP174 PSBT.
|
/// Finish building the transaction. Returns the BIP174 PSBT.
|
||||||
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedBitcoinTransaction>, Error> {
|
fn finish(&self, wallet: &Wallet) -> Result<TxBuilderResult, BdkError> {
|
||||||
let wallet = wallet.get_wallet();
|
let wallet = wallet.get_wallet();
|
||||||
let mut tx_builder = wallet.build_tx();
|
let mut tx_builder = wallet.build_tx();
|
||||||
for (address, amount) in &self.recipients {
|
for (script, amount) in &self.recipients {
|
||||||
tx_builder.add_recipient(to_script_pubkey(address)?, *amount);
|
tx_builder.add_recipient(script.clone(), *amount);
|
||||||
}
|
}
|
||||||
tx_builder.change_policy(self.change_policy);
|
tx_builder.change_policy(self.change_policy);
|
||||||
if !self.utxos.is_empty() {
|
if !self.utxos.is_empty() {
|
||||||
@@ -705,10 +768,12 @@ impl TxBuilder {
|
|||||||
|
|
||||||
tx_builder
|
tx_builder
|
||||||
.finish()
|
.finish()
|
||||||
.map(|(psbt, _)| PartiallySignedBitcoinTransaction {
|
.map(|(psbt, tx_details)| TxBuilderResult {
|
||||||
internal: Mutex::new(psbt),
|
psbt: Arc::new(PartiallySignedBitcoinTransaction {
|
||||||
|
internal: Mutex::new(psbt),
|
||||||
|
}),
|
||||||
|
transaction_details: TransactionDetails::from(&tx_details),
|
||||||
})
|
})
|
||||||
.map(Arc::new)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -762,14 +827,14 @@ impl BumpFeeTxBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finish building the transaction. Returns the BIP174 PSBT.
|
/// Finish building the transaction. Returns the BIP174 PSBT.
|
||||||
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedBitcoinTransaction>, Error> {
|
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedBitcoinTransaction>, BdkError> {
|
||||||
let wallet = wallet.get_wallet();
|
let wallet = wallet.get_wallet();
|
||||||
let txid = Txid::from_str(self.txid.as_str())?;
|
let txid = Txid::from_str(self.txid.as_str())?;
|
||||||
let mut tx_builder = wallet.build_fee_bump(txid)?;
|
let mut tx_builder = wallet.build_fee_bump(txid)?;
|
||||||
tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate));
|
tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate));
|
||||||
if let Some(allow_shrinking) = &self.allow_shrinking {
|
if let Some(allow_shrinking) = &self.allow_shrinking {
|
||||||
let address =
|
let address = BdkAddress::from_str(allow_shrinking)
|
||||||
Address::from_str(allow_shrinking).map_err(|e| Error::Generic(e.to_string()))?;
|
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||||
let script = address.script_pubkey();
|
let script = address.script_pubkey();
|
||||||
tx_builder.allow_shrinking(script)?;
|
tx_builder.allow_shrinking(script)?;
|
||||||
}
|
}
|
||||||
@@ -894,6 +959,21 @@ impl DescriptorSecretKey {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the private key as bytes.
|
||||||
|
fn secret_bytes(&self) -> Vec<u8> {
|
||||||
|
let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap();
|
||||||
|
let secret_bytes: Vec<u8> = match descriptor_secret_key.deref() {
|
||||||
|
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||||
|
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
|
||||||
|
}
|
||||||
|
BdkDescriptorSecretKey::SinglePriv(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
secret_bytes
|
||||||
|
}
|
||||||
|
|
||||||
fn as_string(&self) -> String {
|
fn as_string(&self) -> String {
|
||||||
self.descriptor_secret_key_mutex.lock().unwrap().to_string()
|
self.descriptor_secret_key_mutex.lock().unwrap().to_string()
|
||||||
}
|
}
|
||||||
@@ -984,12 +1064,12 @@ mod test {
|
|||||||
let tx_builder = TxBuilder::new()
|
let tx_builder = TxBuilder::new()
|
||||||
.drain_wallet()
|
.drain_wallet()
|
||||||
.drain_to(drain_to_address.clone());
|
.drain_to(drain_to_address.clone());
|
||||||
//dbg!(&tx_builder);
|
|
||||||
assert!(tx_builder.drain_wallet);
|
assert!(tx_builder.drain_wallet);
|
||||||
assert_eq!(tx_builder.drain_to, Some(drain_to_address));
|
assert_eq!(tx_builder.drain_to, Some(drain_to_address));
|
||||||
|
|
||||||
let psbt = tx_builder.finish(&test_wallet).unwrap();
|
let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
|
||||||
let psbt = psbt.internal.lock().unwrap().clone();
|
let psbt = tx_builder_result.psbt.internal.lock().unwrap().clone();
|
||||||
|
let tx_details = tx_builder_result.transaction_details;
|
||||||
|
|
||||||
// confirm one input with 50,000 sats
|
// confirm one input with 50,000 sats
|
||||||
assert_eq!(psbt.inputs.len(), 1);
|
assert_eq!(psbt.inputs.len(), 1);
|
||||||
@@ -1025,12 +1105,22 @@ mod test {
|
|||||||
);
|
);
|
||||||
let output_value = psbt.unsigned_tx.output.get(0).cloned().unwrap().value;
|
let output_value = psbt.unsigned_tx.output.get(0).cloned().unwrap().value;
|
||||||
assert_eq!(output_value, 49_890_u64); // input - fee
|
assert_eq!(output_value, 49_890_u64); // input - fee
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tx_details.txid,
|
||||||
|
"312f1733badab22dc26b8dcbc83ba5629fb7b493af802e8abe07d865e49629c5"
|
||||||
|
);
|
||||||
|
assert_eq!(tx_details.received, 0);
|
||||||
|
assert_eq!(tx_details.sent, 50000);
|
||||||
|
assert!(tx_details.fee.is_some());
|
||||||
|
assert_eq!(tx_details.fee.unwrap(), 110);
|
||||||
|
assert!(tx_details.confirmation_time.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_descriptor_secret_key() -> DescriptorSecretKey {
|
fn get_descriptor_secret_key() -> DescriptorSecretKey {
|
||||||
let mnemonic =
|
let mnemonic =
|
||||||
"chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string();
|
"chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string();
|
||||||
DescriptorSecretKey::new(Network::Testnet, mnemonic, None).unwrap()
|
DescriptorSecretKey::new(Testnet, mnemonic, None).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_dsk(
|
fn derive_dsk(
|
||||||
@@ -1102,7 +1192,6 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_derive_and_extend_descriptor_secret_key() {
|
fn test_derive_and_extend_descriptor_secret_key() {
|
||||||
let master_dsk = get_descriptor_secret_key();
|
let master_dsk = get_descriptor_secret_key();
|
||||||
|
|
||||||
// derive DescriptorSecretKey with path "m/0" from master
|
// derive DescriptorSecretKey with path "m/0" from master
|
||||||
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap();
|
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap();
|
||||||
assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*");
|
assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*");
|
||||||
@@ -1118,4 +1207,14 @@ mod test {
|
|||||||
let derived_dpk = &derive_dpk(&master_dpk, "m/84h/1h/0h");
|
let derived_dpk = &derive_dpk(&master_dpk, "m/84h/1h/0h");
|
||||||
assert!(derived_dpk.is_err());
|
assert!(derived_dpk.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_retrieve_master_secret_key() {
|
||||||
|
let master_dpk = get_descriptor_secret_key();
|
||||||
|
let master_private_key = master_dpk.secret_bytes().to_hex();
|
||||||
|
assert_eq!(
|
||||||
|
master_private_key,
|
||||||
|
"e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user