Compare commits
32 Commits
dependabot
...
v0.28.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7231039e81 | ||
|
|
1d840e09f8 | ||
|
|
b6fecc8bc0 | ||
|
|
d0f7543f69 | ||
|
|
7587f1603d | ||
|
|
177c96db5a | ||
|
|
07c1ce9c85 | ||
|
|
6097957650 | ||
|
|
9cffaad71f | ||
|
|
3ccdb84523 | ||
|
|
f7d0852e92 | ||
|
|
15079ee4db | ||
|
|
0f25c6ab29 | ||
|
|
0bf9a0ee2c | ||
|
|
973cd9a286 | ||
|
|
78529b6a42 | ||
|
|
0ad65c7776 | ||
|
|
cbcbdd120d | ||
|
|
f507185729 | ||
|
|
573bf52578 | ||
|
|
10608afb76 | ||
|
|
de46a51208 | ||
|
|
e8acafce8e | ||
|
|
bb2b2d6dd8 | ||
|
|
87c558c9cf | ||
|
|
a4647cfa98 | ||
|
|
b111f97c58 | ||
|
|
7a8e6609b1 | ||
|
|
4ec6f3272e | ||
|
|
553df318ff | ||
|
|
9e2e6411f2 | ||
|
|
5d48e37926 |
2
.cargo/audit.toml
Normal file
2
.cargo/audit.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[advisories]
|
||||||
|
ignore = ["RUSTSEC-2022-0046"]
|
||||||
3
.github/workflows/audit.yml
vendored
3
.github/workflows/audit.yml
vendored
@@ -2,6 +2,9 @@ name: Audit
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'release/*'
|
||||||
paths:
|
paths:
|
||||||
- '**/Cargo.toml'
|
- '**/Cargo.toml'
|
||||||
- '**/Cargo.lock'
|
- '**/Cargo.lock'
|
||||||
|
|||||||
10
.github/workflows/code_coverage.yml
vendored
10
.github/workflows/code_coverage.yml
vendored
@@ -1,4 +1,12 @@
|
|||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'release/*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'release/*'
|
||||||
|
|
||||||
name: Code Coverage
|
name: Code Coverage
|
||||||
|
|
||||||
|
|||||||
30
.github/workflows/cont_integration.yml
vendored
30
.github/workflows/cont_integration.yml
vendored
@@ -1,4 +1,12 @@
|
|||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'release/*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'release/*'
|
||||||
|
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
@@ -51,6 +59,16 @@ jobs:
|
|||||||
run: rustup component add clippy
|
run: rustup component add clippy
|
||||||
- name: Update toolchain
|
- name: Update toolchain
|
||||||
run: rustup update
|
run: rustup update
|
||||||
|
- name: Pin dependencies for MSRV
|
||||||
|
if: matrix.rust.version == '1.57.0'
|
||||||
|
run: |
|
||||||
|
cargo update -p log --precise "0.4.18"
|
||||||
|
cargo update -p tempfile --precise "3.6.0"
|
||||||
|
cargo update -p hashlink --precise "0.8.1"
|
||||||
|
cargo update -p regex --precise "1.7.3"
|
||||||
|
cargo update -p zip --precise "0.6.3"
|
||||||
|
cargo update -p base64ct --precise "1.5.3"
|
||||||
|
cargo update -p rustix --precise "0.37.23"
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --features ${{ matrix.features }} --no-default-features
|
run: cargo build --features ${{ matrix.features }} --no-default-features
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
@@ -199,5 +217,15 @@ jobs:
|
|||||||
run: rustup set profile minimal
|
run: rustup set profile minimal
|
||||||
- name: Update toolchain
|
- name: Update toolchain
|
||||||
run: rustup update
|
run: rustup update
|
||||||
|
- name: Pin dependencies for MSRV
|
||||||
|
if: matrix.rust.version == '1.57.0'
|
||||||
|
run: |
|
||||||
|
cargo update -p log --precise "0.4.18"
|
||||||
|
cargo update -p tempfile --precise "3.6.0"
|
||||||
|
cargo update -p hashlink --precise "0.8.1"
|
||||||
|
cargo update -p regex --precise "1.7.3"
|
||||||
|
cargo update -p zip --precise "0.6.3"
|
||||||
|
cargo update -p base64ct --precise "1.5.3"
|
||||||
|
cargo update -p rustix --precise "0.37.23"
|
||||||
- name: Test
|
- name: Test
|
||||||
run: cargo test --features test-hardware-signer
|
run: cargo test --features test-hardware-signer
|
||||||
|
|||||||
10
.github/workflows/nightly_docs.yml
vendored
10
.github/workflows/nightly_docs.yml
vendored
@@ -1,6 +1,14 @@
|
|||||||
name: Publish Nightly Docs
|
name: Publish Nightly Docs
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'release/*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'release/*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_docs:
|
build_docs:
|
||||||
|
|||||||
44
CHANGELOG.md
44
CHANGELOG.md
@@ -9,6 +9,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [v0.28.1]
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
This patch release backports (from the BDK 1.0 dev branch) a fix for a bug in the policy condition calculation and adds a new taproot single key descriptor template (BIP-86). The policy condition calculation bug can cause issues when a policy subtree fails due to missing info even if it's not selected when creating a new transaction, errors on unused policy paths are now ignored.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Backported #932 fix for policy condition calculation #1008
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Backported #840 taproot descriptor template (BIP-86) #1033
|
||||||
|
|
||||||
|
## [v0.28.0]
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
Disable default-features for rust-bitcoin and rust-miniscript dependencies, and for rust-esplora-client optional dependency. New default `std` feature must be enabled unless building for wasm.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Bump bip39 crate to v2.0.0 #875
|
||||||
|
- Set default-features = false for rust-bitcoin and rust-miniscript #882
|
||||||
|
- Update esplora client dependency to version 0.4 #884
|
||||||
|
- Added new `std` feature as part of default features #930
|
||||||
|
|
||||||
|
## [v0.27.1]
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
Fixes [RUSTSEC-2022-0090], this issue is only applicable if you are using the optional sqlite database feature.
|
||||||
|
|
||||||
|
[RUSTSEC-2022-0090]: https://rustsec.org/advisories/RUSTSEC-2022-0090
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Update optional sqlite dependency from 0.27.0 to 0.28.0. #867
|
||||||
|
|
||||||
## [v0.27.0]
|
## [v0.27.0]
|
||||||
|
|
||||||
### Summary
|
### Summary
|
||||||
@@ -629,4 +668,7 @@ final transaction is created by calling `finish` on the builder.
|
|||||||
[v0.25.0]: https://github.com/bitcoindevkit/bdk/compare/v0.24.0...v0.25.0
|
[v0.25.0]: https://github.com/bitcoindevkit/bdk/compare/v0.24.0...v0.25.0
|
||||||
[v0.26.0]: https://github.com/bitcoindevkit/bdk/compare/v0.25.0...v0.26.0
|
[v0.26.0]: https://github.com/bitcoindevkit/bdk/compare/v0.25.0...v0.26.0
|
||||||
[v0.27.0]: https://github.com/bitcoindevkit/bdk/compare/v0.26.0...v0.27.0
|
[v0.27.0]: https://github.com/bitcoindevkit/bdk/compare/v0.26.0...v0.27.0
|
||||||
[Unreleased]: https://github.com/bitcoindevkit/bdk/compare/v0.27.0...HEAD
|
[v0.27.1]: https://github.com/bitcoindevkit/bdk/compare/v0.27.0...v0.27.1
|
||||||
|
[v0.28.0]: https://github.com/bitcoindevkit/bdk/compare/v0.27.1...v0.28.0
|
||||||
|
[v0.28.1]: https://github.com/bitcoindevkit/bdk/compare/v0.28.0...v0.28.1
|
||||||
|
[Unreleased]: https://github.com/bitcoindevkit/bdk/compare/v0.28.1...HEAD
|
||||||
|
|||||||
29
Cargo.toml
29
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bdk"
|
name = "bdk"
|
||||||
version = "0.27.0"
|
version = "0.28.1"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Alekos Filini <alekos.filini@gmail.com>", "Riccardo Casatta <riccardo@casatta.it>"]
|
authors = ["Alekos Filini <alekos.filini@gmail.com>", "Riccardo Casatta <riccardo@casatta.it>"]
|
||||||
homepage = "https://bitcoindevkit.org"
|
homepage = "https://bitcoindevkit.org"
|
||||||
@@ -13,9 +13,9 @@ license = "MIT OR Apache-2.0"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bdk-macros = "^0.6"
|
bdk-macros = "^0.6"
|
||||||
log = "^0.4"
|
log = "0.4"
|
||||||
miniscript = { version = "9.0", features = ["serde"] }
|
miniscript = { version = "9.0", default-features = false, features = ["serde"] }
|
||||||
bitcoin = { version = "0.29.1", features = ["serde", "base64", "rand"] }
|
bitcoin = { version = "0.29.2", default-features = false, features = ["serde", "base64", "rand"] }
|
||||||
serde = { version = "^1.0", features = ["derive"] }
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
serde_json = { version = "^1.0" }
|
serde_json = { version = "^1.0" }
|
||||||
rand = "^0.8"
|
rand = "^0.8"
|
||||||
@@ -23,17 +23,17 @@ rand = "^0.8"
|
|||||||
# Optional dependencies
|
# Optional dependencies
|
||||||
sled = { version = "0.34", optional = true }
|
sled = { version = "0.34", optional = true }
|
||||||
electrum-client = { version = "0.12", optional = true }
|
electrum-client = { version = "0.12", optional = true }
|
||||||
esplora-client = { version = "0.3", default-features = false, optional = true }
|
esplora-client = { version = "0.5", default-features = false, optional = true }
|
||||||
rusqlite = { version = "0.27.0", optional = true }
|
rusqlite = { version = "0.28.0", optional = true }
|
||||||
ahash = { version = "0.7.6", optional = true }
|
ahash = { version = "0.7.6", optional = true }
|
||||||
futures = { version = "0.3", optional = true }
|
futures = { version = "0.3", optional = true }
|
||||||
async-trait = { version = "0.1", optional = true }
|
async-trait = { version = "0.1", optional = true }
|
||||||
rocksdb = { version = "0.14", default-features = false, features = ["snappy"], optional = true }
|
rocksdb = { version = "0.14", default-features = false, features = ["snappy"], optional = true }
|
||||||
cc = { version = ">=1.0.64", optional = true }
|
cc = { version = ">=1.0.64", optional = true }
|
||||||
socks = { version = "0.3", optional = true }
|
socks = { version = "0.3", optional = true }
|
||||||
hwi = { version = "0.5", optional = true, features = [ "use-miniscript"] }
|
hwi = { version = "0.5", optional = true, features = ["use-miniscript"] }
|
||||||
|
|
||||||
bip39 = { version = "1.0.1", optional = true }
|
bip39 = { version = "2.0.0", optional = true }
|
||||||
bitcoinconsensus = { version = "0.19.0-3", optional = true }
|
bitcoinconsensus = { version = "0.19.0-3", optional = true }
|
||||||
|
|
||||||
# Needed by bdk_blockchain_tests macro and the `rpc` feature
|
# Needed by bdk_blockchain_tests macro and the `rpc` feature
|
||||||
@@ -52,7 +52,10 @@ js-sys = "0.3"
|
|||||||
minimal = []
|
minimal = []
|
||||||
compiler = ["miniscript/compiler"]
|
compiler = ["miniscript/compiler"]
|
||||||
verify = ["bitcoinconsensus"]
|
verify = ["bitcoinconsensus"]
|
||||||
default = ["key-value-db", "electrum"]
|
default = ["std", "key-value-db", "electrum"]
|
||||||
|
# std feature is always required unless building for wasm32-unknown-unknown target
|
||||||
|
# if building for wasm user must add dependencies bitcoin/no-std,miniscript/no-std
|
||||||
|
std = ["bitcoin/std", "miniscript/std"]
|
||||||
sqlite = ["rusqlite", "ahash"]
|
sqlite = ["rusqlite", "ahash"]
|
||||||
sqlite-bundled = ["sqlite", "rusqlite/bundled"]
|
sqlite-bundled = ["sqlite", "rusqlite/bundled"]
|
||||||
compact_filters = ["rocksdb", "socks", "cc"]
|
compact_filters = ["rocksdb", "socks", "cc"]
|
||||||
@@ -104,14 +107,14 @@ test-hardware-signer = ["hardware-signer"]
|
|||||||
dev-getrandom-wasm = ["getrandom/js"]
|
dev-getrandom-wasm = ["getrandom/js"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
miniscript = { version = "9.0", features = ["std"] }
|
||||||
|
bitcoin = { version = "0.29.2", features = ["std"] }
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
env_logger = "0.7"
|
env_logger = { version = "0.7", default-features = false }
|
||||||
electrsd = "0.22"
|
electrsd = "0.22"
|
||||||
# Move back to importing from rust-bitcoin once https://github.com/rust-bitcoin/rust-bitcoin/pull/1342 is released
|
# Remove after upgrade to rust-bitcoin ^0.30 where base64 is re-exported
|
||||||
base64 = "^0.13"
|
base64 = "^0.13"
|
||||||
assert_matches = "1.5.0"
|
assert_matches = "1.5.0"
|
||||||
# zip versions after 0.6.3 don't work with our MSRV 1.57.0
|
|
||||||
zip = "=0.6.3"
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "compact_filters_balance"
|
name = "compact_filters_balance"
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -201,3 +201,26 @@ at your option.
|
|||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||||
dual licensed as above, without any additional terms or conditions.
|
dual licensed as above, without any additional terms or conditions.
|
||||||
|
|
||||||
|
## Minimum Supported Rust Version (MSRV)
|
||||||
|
|
||||||
|
This library should compile with any combination of features with Rust 1.57.0.
|
||||||
|
|
||||||
|
To build with the MSRV you will need to pin dependencies as follows:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# log 0.4.19 has MSRV 1.60.0
|
||||||
|
cargo update -p log --precise "0.4.18"
|
||||||
|
# tempfile 3.7.0 has MSRV 1.63.0
|
||||||
|
cargo update -p tempfile --precise "3.6.0"
|
||||||
|
# required for sqlite feature, hashlink 0.8.2 has MSRV 1.61.0
|
||||||
|
cargo update -p hashlink --precise "0.8.1"
|
||||||
|
# required for compact_filters feature, regex after 1.7.3 has MSRV 1.60.0
|
||||||
|
cargo update -p regex --precise "1.7.3"
|
||||||
|
# zip 0.6.3 has MSRV 1.59.0 but still works
|
||||||
|
cargo update -p zip --precise "0.6.3"
|
||||||
|
# base64ct 1.6.0 has MSRV 1.60.0
|
||||||
|
cargo update -p base64ct --precise "1.5.3"
|
||||||
|
# rustix 0.38.0 has MSRV 1.65.0
|
||||||
|
cargo update -p rustix --precise "0.37.23"
|
||||||
|
```
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ RUN apt-get install wget -y
|
|||||||
RUN wget "https://github.com/LedgerHQ/speculos/blob/master/apps/nanos%23btc%232.1%231c8db8da.elf?raw=true" -O /speculos/btc.elf
|
RUN wget "https://github.com/LedgerHQ/speculos/blob/master/apps/nanos%23btc%232.1%231c8db8da.elf?raw=true" -O /speculos/btc.elf
|
||||||
ADD automation.json /speculos/automation.json
|
ADD automation.json /speculos/automation.json
|
||||||
|
|
||||||
ENTRYPOINT ["python", "./speculos.py", "--automation", "file:automation.json", "--display", "headless", "--vnc-port", "41000", "btc.elf"]
|
ENTRYPOINT ["python", "./speculos.py", "--automation", "file:automation.json", "--model", "nanos", "--display", "headless", "--vnc-port", "41000", "btc.elf"]
|
||||||
|
|||||||
@@ -659,11 +659,11 @@ impl Policy {
|
|||||||
(0..*threshold).collect()
|
(0..*threshold).collect()
|
||||||
}
|
}
|
||||||
SatisfiableItem::Multisig { keys, .. } => (0..keys.len()).collect(),
|
SatisfiableItem::Multisig { keys, .. } => (0..keys.len()).collect(),
|
||||||
_ => vec![],
|
_ => HashSet::new(),
|
||||||
};
|
};
|
||||||
let selected = match path.get(&self.id) {
|
let selected: HashSet<_> = match path.get(&self.id) {
|
||||||
Some(arr) => arr,
|
Some(arr) => arr.iter().copied().collect(),
|
||||||
_ => &default,
|
_ => default,
|
||||||
};
|
};
|
||||||
|
|
||||||
match &self.item {
|
match &self.item {
|
||||||
@@ -671,14 +671,24 @@ impl Policy {
|
|||||||
let mapped_req = items
|
let mapped_req = items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| i.get_condition(path))
|
.map(|i| i.get_condition(path))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// if all the requirements are null we don't care about `selected` because there
|
// if all the requirements are null we don't care about `selected` because there
|
||||||
// are no requirements
|
// are no requirements
|
||||||
if mapped_req.iter().all(Condition::is_null) {
|
if mapped_req
|
||||||
|
.iter()
|
||||||
|
.all(|cond| matches!(cond, Ok(c) if c.is_null()))
|
||||||
|
{
|
||||||
return Ok(Condition::default());
|
return Ok(Condition::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure all the indexes in the `selected` list are within range
|
||||||
|
for index in &selected {
|
||||||
|
if *index >= items.len() {
|
||||||
|
return Err(PolicyError::IndexOutOfRange(*index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if we have something, make sure we have enough items. note that the user can set
|
// if we have something, make sure we have enough items. note that the user can set
|
||||||
// an empty value for this step in case of n-of-n, because `selected` is set to all
|
// an empty value for this step in case of n-of-n, because `selected` is set to all
|
||||||
// the elements above
|
// the elements above
|
||||||
@@ -687,23 +697,18 @@ impl Policy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check the selected items, see if there are conflicting requirements
|
// check the selected items, see if there are conflicting requirements
|
||||||
let mut requirements = Condition::default();
|
mapped_req
|
||||||
for item_index in selected {
|
.into_iter()
|
||||||
requirements = requirements.merge(
|
.enumerate()
|
||||||
mapped_req
|
.filter(|(index, _)| selected.contains(index))
|
||||||
.get(*item_index)
|
.try_fold(Condition::default(), |acc, (_, cond)| acc.merge(&cond?))
|
||||||
.ok_or(PolicyError::IndexOutOfRange(*item_index))?,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(requirements)
|
|
||||||
}
|
}
|
||||||
SatisfiableItem::Multisig { keys, threshold } => {
|
SatisfiableItem::Multisig { keys, threshold } => {
|
||||||
if selected.len() < *threshold {
|
if selected.len() < *threshold {
|
||||||
return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
|
return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
|
||||||
}
|
}
|
||||||
if let Some(item) = selected.iter().find(|i| **i >= keys.len()) {
|
if let Some(item) = selected.into_iter().find(|&i| i >= keys.len()) {
|
||||||
return Err(PolicyError::IndexOutOfRange(*item));
|
return Err(PolicyError::IndexOutOfRange(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Condition::default())
|
Ok(Condition::default())
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
use bitcoin::util::bip32;
|
use bitcoin::util::bip32;
|
||||||
use bitcoin::Network;
|
use bitcoin::Network;
|
||||||
|
|
||||||
use miniscript::{Legacy, Segwitv0};
|
use miniscript::{Legacy, Segwitv0, Tap};
|
||||||
|
|
||||||
use super::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
|
use super::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
|
||||||
use crate::descriptor::DescriptorError;
|
use crate::descriptor::DescriptorError;
|
||||||
@@ -170,6 +170,35 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// P2TR template. Expands to a descriptor `tr(key)`
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
|
/// # use bdk::Wallet;
|
||||||
|
/// # use bdk::database::MemoryDatabase;
|
||||||
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
|
/// use bdk::template::P2TR;
|
||||||
|
///
|
||||||
|
/// let key =
|
||||||
|
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||||
|
/// let mut wallet = Wallet::new(P2TR(key), None, Network::Testnet, MemoryDatabase::default())?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// wallet.get_address(New)?.to_string(),
|
||||||
|
/// "tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46"
|
||||||
|
/// );
|
||||||
|
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
|
/// ```
|
||||||
|
pub struct P2TR<K: IntoDescriptorKey<Tap>>(pub K);
|
||||||
|
|
||||||
|
impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
|
||||||
|
fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
|
||||||
|
descriptor!(tr(self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// BIP44 template. Expands to `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
|
/// BIP44 template. Expands to `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
|
||||||
///
|
///
|
||||||
/// Since there are hardened derivation steps, this template requires a private derivable key (generally a `xprv`/`tprv`).
|
/// Since there are hardened derivation steps, this template requires a private derivable key (generally a `xprv`/`tprv`).
|
||||||
@@ -407,6 +436,85 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// BIP86 template. Expands to `tr(key/86'/{0,1}'/0'/{0,1}/*)`
|
||||||
|
///
|
||||||
|
/// Since there are hardened derivation steps, this template requires a private derivable key (generally a `xprv`/`tprv`).
|
||||||
|
///
|
||||||
|
/// See [`Bip86Public`] for a template that can work with a `xpub`/`tpub`.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use std::str::FromStr;
|
||||||
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
|
/// # use bdk::{Wallet, KeychainKind};
|
||||||
|
/// # use bdk::database::MemoryDatabase;
|
||||||
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
|
/// use bdk::template::Bip86;
|
||||||
|
///
|
||||||
|
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
||||||
|
/// let mut wallet = Wallet::new(
|
||||||
|
/// Bip86(key.clone(), KeychainKind::External),
|
||||||
|
/// Some(Bip86(key, KeychainKind::Internal)),
|
||||||
|
/// Network::Testnet,
|
||||||
|
/// MemoryDatabase::default()
|
||||||
|
/// )?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(wallet.get_address(New)?.to_string(), "tb1p5unlj09djx8xsjwe97269kqtxqpwpu2epeskgqjfk4lnf69v4tnqpp35qu");
|
||||||
|
/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "tr([c55b303f/86'/1'/0']tpubDCiHofpEs47kx358bPdJmTZHmCDqQ8qw32upCSxHrSEdeeBs2T5Mq6QMB2ukeMqhNBiyhosBvJErteVhfURPGXPv3qLJPw5MVpHUewsbP2m/0/*)#dkgvr5hm");
|
||||||
|
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
|
/// ```
|
||||||
|
pub struct Bip86<K: DerivableKey<Tap>>(pub K, pub KeychainKind);
|
||||||
|
|
||||||
|
impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
|
||||||
|
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
|
||||||
|
P2TR(segwit_v1::make_bipxx_private(86, self.0, self.1, network)?).build(network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BIP86 public template. Expands to `tr(key/{0,1}/*)`
|
||||||
|
///
|
||||||
|
/// This assumes that the key used has already been derived with `m/86'/0'/0'` for Mainnet or `m/86'/1'/0'` for Testnet.
|
||||||
|
///
|
||||||
|
/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||||
|
///
|
||||||
|
/// See [`Bip86`] for a template that does the full derivation, but requires private data
|
||||||
|
/// for the key.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use std::str::FromStr;
|
||||||
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
|
/// # use bdk::{Wallet, KeychainKind};
|
||||||
|
/// # use bdk::database::MemoryDatabase;
|
||||||
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
|
/// use bdk::template::Bip86Public;
|
||||||
|
///
|
||||||
|
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
||||||
|
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
||||||
|
/// let mut wallet = Wallet::new(
|
||||||
|
/// Bip86Public(key.clone(), fingerprint, KeychainKind::External),
|
||||||
|
/// Some(Bip86Public(key, fingerprint, KeychainKind::Internal)),
|
||||||
|
/// Network::Testnet,
|
||||||
|
/// MemoryDatabase::default()
|
||||||
|
/// )?;
|
||||||
|
///
|
||||||
|
/// assert_eq!(wallet.get_address(New)?.to_string(), "tb1pwjp9f2k5n0xq73ecuu0c5njvgqr3vkh7yaylmpqvsuuaafymh0msvcmh37");
|
||||||
|
/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "tr([c55b303f/86'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#2p65srku");
|
||||||
|
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
|
/// ```
|
||||||
|
pub struct Bip86Public<K: DerivableKey<Tap>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
|
||||||
|
|
||||||
|
impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86Public<K> {
|
||||||
|
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
|
||||||
|
P2TR(segwit_v1::make_bipxx_public(
|
||||||
|
86, self.0, self.1, self.2, network,
|
||||||
|
)?)
|
||||||
|
.build(network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! expand_make_bipxx {
|
macro_rules! expand_make_bipxx {
|
||||||
( $mod_name:ident, $ctx:ty ) => {
|
( $mod_name:ident, $ctx:ty ) => {
|
||||||
mod $mod_name {
|
mod $mod_name {
|
||||||
@@ -473,6 +581,7 @@ macro_rules! expand_make_bipxx {
|
|||||||
|
|
||||||
expand_make_bipxx!(legacy, Legacy);
|
expand_make_bipxx!(legacy, Legacy);
|
||||||
expand_make_bipxx!(segwit_v0, Segwitv0);
|
expand_make_bipxx!(segwit_v0, Segwitv0);
|
||||||
|
expand_make_bipxx!(segwit_v1, Tap);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
@@ -484,7 +593,6 @@ mod test {
|
|||||||
use crate::descriptor::{DescriptorError, DescriptorMeta};
|
use crate::descriptor::{DescriptorError, DescriptorMeta};
|
||||||
use crate::keys::ValidNetworks;
|
use crate::keys::ValidNetworks;
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use bitcoin::network::constants::Network::Regtest;
|
|
||||||
use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
|
use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
|
||||||
use miniscript::Descriptor;
|
use miniscript::Descriptor;
|
||||||
|
|
||||||
@@ -526,11 +634,14 @@ mod test {
|
|||||||
fn check(
|
fn check(
|
||||||
desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), DescriptorError>,
|
desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), DescriptorError>,
|
||||||
is_witness: bool,
|
is_witness: bool,
|
||||||
|
is_taproot: bool,
|
||||||
is_fixed: bool,
|
is_fixed: bool,
|
||||||
|
network: Network,
|
||||||
expected: &[&str],
|
expected: &[&str],
|
||||||
) {
|
) {
|
||||||
let (desc, _key_map, _networks) = desc.unwrap();
|
let (desc, _key_map, _networks) = desc.unwrap();
|
||||||
assert_eq!(desc.is_witness(), is_witness);
|
assert_eq!(desc.is_witness(), is_witness);
|
||||||
|
assert_eq!(desc.is_taproot(), is_taproot);
|
||||||
assert_eq!(!desc.has_wildcard(), is_fixed);
|
assert_eq!(!desc.has_wildcard(), is_fixed);
|
||||||
for i in 0..expected.len() {
|
for i in 0..expected.len() {
|
||||||
let index = i as u32;
|
let index = i as u32;
|
||||||
@@ -539,7 +650,7 @@ mod test {
|
|||||||
} else {
|
} else {
|
||||||
desc.at_derivation_index(index)
|
desc.at_derivation_index(index)
|
||||||
};
|
};
|
||||||
let address = child_desc.address(Regtest).unwrap();
|
let address = child_desc.address(network).unwrap();
|
||||||
assert_eq!(address.to_string(), *expected.get(i).unwrap());
|
assert_eq!(address.to_string(), *expected.get(i).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -553,7 +664,9 @@ mod test {
|
|||||||
check(
|
check(
|
||||||
P2Pkh(prvkey).build(Network::Bitcoin),
|
P2Pkh(prvkey).build(Network::Bitcoin),
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
true,
|
true,
|
||||||
|
Network::Regtest,
|
||||||
&["mwJ8hxFYW19JLuc65RCTaP4v1rzVU8cVMT"],
|
&["mwJ8hxFYW19JLuc65RCTaP4v1rzVU8cVMT"],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -564,7 +677,9 @@ mod test {
|
|||||||
check(
|
check(
|
||||||
P2Pkh(pubkey).build(Network::Bitcoin),
|
P2Pkh(pubkey).build(Network::Bitcoin),
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
true,
|
true,
|
||||||
|
Network::Regtest,
|
||||||
&["muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi"],
|
&["muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -578,7 +693,9 @@ mod test {
|
|||||||
check(
|
check(
|
||||||
P2Wpkh_P2Sh(prvkey).build(Network::Bitcoin),
|
P2Wpkh_P2Sh(prvkey).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
true,
|
true,
|
||||||
|
Network::Regtest,
|
||||||
&["2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"],
|
&["2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -589,7 +706,9 @@ mod test {
|
|||||||
check(
|
check(
|
||||||
P2Wpkh_P2Sh(pubkey).build(Network::Bitcoin),
|
P2Wpkh_P2Sh(pubkey).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
true,
|
true,
|
||||||
|
Network::Regtest,
|
||||||
&["2N5LiC3CqzxDamRTPG1kiNv1FpNJQ7x28sb"],
|
&["2N5LiC3CqzxDamRTPG1kiNv1FpNJQ7x28sb"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -603,7 +722,9 @@ mod test {
|
|||||||
check(
|
check(
|
||||||
P2Wpkh(prvkey).build(Network::Bitcoin),
|
P2Wpkh(prvkey).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
true,
|
true,
|
||||||
|
Network::Regtest,
|
||||||
&["bcrt1q4525hmgw265tl3drrl8jjta7ayffu6jfcwxx9y"],
|
&["bcrt1q4525hmgw265tl3drrl8jjta7ayffu6jfcwxx9y"],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -614,11 +735,42 @@ mod test {
|
|||||||
check(
|
check(
|
||||||
P2Wpkh(pubkey).build(Network::Bitcoin),
|
P2Wpkh(pubkey).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
true,
|
true,
|
||||||
|
Network::Regtest,
|
||||||
&["bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h"],
|
&["bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// P2TR `tr(key)`
|
||||||
|
#[test]
|
||||||
|
fn test_p2tr_template() {
|
||||||
|
let prvkey =
|
||||||
|
bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
|
||||||
|
.unwrap();
|
||||||
|
check(
|
||||||
|
P2TR(prvkey).build(Network::Bitcoin),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
Network::Regtest,
|
||||||
|
&["bcrt1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xqnwtkqq"],
|
||||||
|
);
|
||||||
|
|
||||||
|
let pubkey = bitcoin::PublicKey::from_str(
|
||||||
|
"03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
check(
|
||||||
|
P2TR(pubkey).build(Network::Bitcoin),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
Network::Regtest,
|
||||||
|
&["bcrt1pw74tdcrxlzn5r8z6ku2vztr86fgq0m245s72mjktf4afwzsf8ugs4evwdf"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// BIP44 `pkh(key/44'/0'/0'/{0,1}/*)`
|
// BIP44 `pkh(key/44'/0'/0'/{0,1}/*)`
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bip44_template() {
|
fn test_bip44_template() {
|
||||||
@@ -627,6 +779,8 @@ mod test {
|
|||||||
Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"n453VtnjDHPyDt2fDstKSu7A3YCJoHZ5g5",
|
"n453VtnjDHPyDt2fDstKSu7A3YCJoHZ5g5",
|
||||||
"mvfrrumXgTtwFPWDNUecBBgzuMXhYM7KRP",
|
"mvfrrumXgTtwFPWDNUecBBgzuMXhYM7KRP",
|
||||||
@@ -637,6 +791,8 @@ mod test {
|
|||||||
Bip44(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
Bip44(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"muHF98X9KxEzdKrnFAX85KeHv96eXopaip",
|
"muHF98X9KxEzdKrnFAX85KeHv96eXopaip",
|
||||||
"n4hpyLJE5ub6B5Bymv4eqFxS5KjrewSmYR",
|
"n4hpyLJE5ub6B5Bymv4eqFxS5KjrewSmYR",
|
||||||
@@ -654,6 +810,8 @@ mod test {
|
|||||||
Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"miNG7dJTzJqNbFS19svRdTCisC65dsubtR",
|
"miNG7dJTzJqNbFS19svRdTCisC65dsubtR",
|
||||||
"n2UqaDbCjWSFJvpC84m3FjUk5UaeibCzYg",
|
"n2UqaDbCjWSFJvpC84m3FjUk5UaeibCzYg",
|
||||||
@@ -664,6 +822,8 @@ mod test {
|
|||||||
Bip44Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
Bip44Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"moDr3vJ8wpt5nNxSK55MPq797nXJb2Ru9H",
|
"moDr3vJ8wpt5nNxSK55MPq797nXJb2Ru9H",
|
||||||
"ms7A1Yt4uTezT2XkefW12AvLoko8WfNJMG",
|
"ms7A1Yt4uTezT2XkefW12AvLoko8WfNJMG",
|
||||||
@@ -680,6 +840,8 @@ mod test {
|
|||||||
Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"2N9bCAJXGm168MjVwpkBdNt6ucka3PKVoUV",
|
"2N9bCAJXGm168MjVwpkBdNt6ucka3PKVoUV",
|
||||||
"2NDckYkqrYyDMtttEav5hB3Bfw9EGAW5HtS",
|
"2NDckYkqrYyDMtttEav5hB3Bfw9EGAW5HtS",
|
||||||
@@ -690,6 +852,8 @@ mod test {
|
|||||||
Bip49(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
Bip49(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"2NB3pA8PnzJLGV8YEKNDFpbViZv3Bm1K6CG",
|
"2NB3pA8PnzJLGV8YEKNDFpbViZv3Bm1K6CG",
|
||||||
"2NBiX2Wzxngb5rPiWpUiJQ2uLVB4HBjFD4p",
|
"2NBiX2Wzxngb5rPiWpUiJQ2uLVB4HBjFD4p",
|
||||||
@@ -707,6 +871,8 @@ mod test {
|
|||||||
Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt",
|
"2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt",
|
||||||
"2NCTQfJ1sZa3wQ3pPseYRHbaNEpC3AquEfX",
|
"2NCTQfJ1sZa3wQ3pPseYRHbaNEpC3AquEfX",
|
||||||
@@ -717,6 +883,8 @@ mod test {
|
|||||||
Bip49Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
Bip49Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"2NF2vttKibwyxigxtx95Zw8K7JhDbo5zPVJ",
|
"2NF2vttKibwyxigxtx95Zw8K7JhDbo5zPVJ",
|
||||||
"2Mtmyd8taksxNVWCJ4wVvaiss7QPZGcAJuH",
|
"2Mtmyd8taksxNVWCJ4wVvaiss7QPZGcAJuH",
|
||||||
@@ -733,6 +901,8 @@ mod test {
|
|||||||
Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s",
|
"bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s",
|
||||||
"bcrt1qx0v6zgfwe50m4kqc58cqzcyem7ay2sfl3gvqhp",
|
"bcrt1qx0v6zgfwe50m4kqc58cqzcyem7ay2sfl3gvqhp",
|
||||||
@@ -743,6 +913,8 @@ mod test {
|
|||||||
Bip84(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
Bip84(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa",
|
"bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa",
|
||||||
"bcrt1qqqasfhxpkkf7zrxqnkr2sfhn74dgsrc3e3ky45",
|
"bcrt1qqqasfhxpkkf7zrxqnkr2sfhn74dgsrc3e3ky45",
|
||||||
@@ -760,6 +932,8 @@ mod test {
|
|||||||
Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"bcrt1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2prcdvd0h",
|
"bcrt1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2prcdvd0h",
|
||||||
"bcrt1q3lncdlwq3lgcaaeyruynjnlccr0ve0kakh6ana",
|
"bcrt1q3lncdlwq3lgcaaeyruynjnlccr0ve0kakh6ana",
|
||||||
@@ -770,6 +944,8 @@ mod test {
|
|||||||
Bip84Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
Bip84Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
Network::Regtest,
|
||||||
&[
|
&[
|
||||||
"bcrt1qm6wqukenh7guu792lj2njgw9n78cmwsy8xy3z2",
|
"bcrt1qm6wqukenh7guu792lj2njgw9n78cmwsy8xy3z2",
|
||||||
"bcrt1q694twxtjn4nnrvnyvra769j0a23rllj5c6cgwp",
|
"bcrt1q694twxtjn4nnrvnyvra769j0a23rllj5c6cgwp",
|
||||||
@@ -777,4 +953,67 @@ mod test {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BIP86 `tr(key/86'/0'/0'/{0,1}/*)`
|
||||||
|
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
|
||||||
|
#[test]
|
||||||
|
fn test_bip86_template() {
|
||||||
|
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap();
|
||||||
|
check(
|
||||||
|
Bip86(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
Network::Bitcoin,
|
||||||
|
&[
|
||||||
|
"bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
|
||||||
|
"bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
|
||||||
|
"bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
Bip86(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
Network::Bitcoin,
|
||||||
|
&[
|
||||||
|
"bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7",
|
||||||
|
"bc1ptdg60grjk9t3qqcqczp4tlyy3z47yrx9nhlrjsmw36q5a72lhdrs9f00nj",
|
||||||
|
"bc1pgcwgsu8naxp7xlp5p7ufzs7emtfza2las7r2e7krzjhe5qj5xz2q88kmk5",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BIP86 public `tr(key/{0,1}/*)`
|
||||||
|
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
|
||||||
|
#[test]
|
||||||
|
fn test_bip86_public_template() {
|
||||||
|
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap();
|
||||||
|
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("73c5da0a").unwrap();
|
||||||
|
check(
|
||||||
|
Bip86Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
Network::Bitcoin,
|
||||||
|
&[
|
||||||
|
"bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
|
||||||
|
"bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
|
||||||
|
"bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
Bip86Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
Network::Bitcoin,
|
||||||
|
&[
|
||||||
|
"bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7",
|
||||||
|
"bc1ptdg60grjk9t3qqcqczp4tlyy3z47yrx9nhlrjsmw36q5a72lhdrs9f00nj",
|
||||||
|
"bc1pgcwgsu8naxp7xlp5p7ufzs7emtfza2las7r2e7krzjhe5qj5xz2q88kmk5",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2936,6 +2936,25 @@ pub(crate) mod test {
|
|||||||
assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(144));
|
assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(144));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_tx_policy_path_ignored_subtree_with_csv() {
|
||||||
|
let (wallet, _, _) = get_funded_wallet("wsh(or_d(pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu),or_i(and_v(v:pkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(30)),and_v(v:pkh(cMnkdebixpXMPfkcNEjjGin7s94hiehAH4mLbYkZoh9KSiNNmqC8),older(90)))))");
|
||||||
|
|
||||||
|
let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
|
||||||
|
let root_id = external_policy.id;
|
||||||
|
// child #0 is pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)
|
||||||
|
let path = vec![(root_id, vec![0])].into_iter().collect();
|
||||||
|
|
||||||
|
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||||
|
let mut builder = wallet.build_tx();
|
||||||
|
builder
|
||||||
|
.add_recipient(addr.script_pubkey(), 30_000)
|
||||||
|
.policy_path(path, KeychainKind::External);
|
||||||
|
let (psbt, _) = builder.finish().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_tx_global_xpubs_with_origin() {
|
fn test_create_tx_global_xpubs_with_origin() {
|
||||||
use bitcoin::hashes::hex::FromHex;
|
use bitcoin::hashes::hex::FromHex;
|
||||||
|
|||||||
Reference in New Issue
Block a user