Skip to content

Commit 8fbe40a

Browse files
committed
Merge #593: Add support for Taproot and tr() descriptors
20d36c7 Update CHANGELOG.md for Taproot (Alekos Filini) ef08fbd Update to the newest release of rust-bitcoin (Alekos Filini) 5320c83 taproot-tests: validate `tap_tree` in psbt outputs (Alekos Filini) c1bfaf9 Add blockchain tests for parsing, signing, finalizing taproot core psbts (Steve Myers) 0643f76 taproot-tests: Add tests for the policy module (Alekos Filini) 89cb425 taproot-tests: Add test coverage for tx signing (Alekos Filini) 461397e taproot-tests: Test taproot key and script spend in the blockchain tests (Alekos Filini) c67116f policy: Consider `tap_key_origins` when looking for sigs in PSBTs (Alekos Filini) 572c3ee policy: Build `SatisfiableItem::*Signature` based on the context (Alekos Filini) ff1abc6 policy: Refactor `PkOrF` into an enum (Alekos Filini) 3087089 Fix type inference for the `tr()` descriptor, add basic tests (Alekos Filini) fe1877f Support `tr()` descriptors in dsl (Alekos Filini) cdc7057 Add `tr()` descriptors to the `descriptor!()` macro (Alekos Filini) c121dd0 Use `tap_key_origins` in PSBTs to derive descriptors (Alekos Filini) 8553821 Populate more taproot fields in PSBTs (Alekos Filini) 8a5a87b Populate `tap_key_origin` in PSBT inputs and outputs (Alekos Filini) 1312184 Attach a context to our software signers (Alekos Filini) 906598a Refactor signer traits, add support for taproot signatures (Alekos Filini) Pull request description: ### Description This is a work-in-progress PR to update BDK to rust-bitcoin `0.28` which introduces taproot support and a few other improvements. While updating we also introduce taproot support in BDK. High level list of subtasks for this PR: - [x] Update rust-bitcoin and rust-miniscript - [x] Stop using deprecated structs - [x] Add taproot metadata to psbts - [x] Produce schnorr signatures - [x] Finalize taproot txs - [x] Support `tr()` descriptors in the `descriptor!()` macro - [x] Write a lot of tests - [x] Interoperability with other wallets (Core + ?) - [x] Signing/finalizing a psbt made by core - [x] Producing a psbt that core can sign and finalize - [x] Creating psbts - [x] Verify the metadata are correct - [x] Verify sighashes are applied correctly - [x] Create a tx with a foreign taproot and non-taproot utxo - [x] Signing psbts - [x] Signing for a key spend - [x] Signing for a script spend - [x] Signing with a single (wif) key - [x] Signing with an xprv (with and without knowing the utxo being spent in the db) - [x] Signing with weird sighashes - [x] Policy module - [x] Simple key spend - [x] More complex tap tree with a few keys - [x] Verify both `contribution` and `satisfaction` of a PSBT input - [x] Wallet module - [x] Generate addresses Fixes #63 ### Notes to the reviewers #### Milestone I'm adding this to the `0.19` milestone because now that rust-bitcoin and rust-miniscript have been released we should not waiting too long to release a version of BDK that supports the new libraries. #### API Breaks Since this is an API-break because of the new version of rust-bitcoin and rust-miniscript, I'm also taking the chance to update a few things in our lib that I had been thinking about for a while. One example is the signer interface, which had that weird `sign_whole_tx()` method. This has now been removed, and the `Signer` trait replaced with `TransactionSigner` and `InputSigner`. I'm also starting to think that the signer should not only look at the psbt to figure out what to do, but ideally it should also receive some information about the descriptor (for example, the type) to simplify the code. One option is to add an extra parameter, but that would probably only be used by our internal signers and not much else (for example, if you ask an hardware wallet to sign, it will probably already know what kind of wallet you have). Another option is to wrap `PrivateKey` and `DescriptorXKey<ExtendedPrivKey>` which are the two internal signers we support with a struct that contains metadata about the descriptor, and then implement the signer traits on that struct. We could construct this in `Wallet::new()`, after miniscript parses the descriptor. #### MSRV Bump Due to the update of `rust-electrum-client`, which in turn depends on an updated `webpki`, we will have to bump our MSRV beacuse 1.46 is not supported by the new `webpki` version. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [x] I've added docs for the new feature * [ ] I've updated `CHANGELOG.md` Top commit has no ACKs. Tree-SHA512: 44ec6c4e7fe0bc87862bb76581ddae7905c8796a4a130c90b48b73b4b7d01ea1a20043b8a0ff449fc2db2f7aecc490def8daee2d420809ded1ece7f085a48f55
2 parents fbd98b4 + 20d36c7 commit 8fbe40a

File tree

10 files changed

+1751
-352
lines changed

10 files changed

+1751
-352
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Add traits to reuse `Blockchain`s across multiple wallets (`BlockchainFactory` and `StatelessBlockchain`).
1313
- Upgrade to rust-bitcoin `0.28`
1414
- If using the `sqlite-db` feature all cached wallet data is deleted due to a possible UTXO inconsistency, a wallet.sync will recreate it
15+
- Update `PkOrF` in the policy module to become an enum
16+
- Add experimental support for Taproot, including:
17+
- Support for `tr()` descriptors with complex tapscript trees
18+
- Creation of Taproot PSBTs (BIP-371)
19+
- Signing Taproot PSBTs (key spend and script spend)
20+
- Support for `tr()` descriptors in the `descriptor!()` macro
1521

1622
## [v0.18.0] - [v0.17.0]
1723

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0"
1515
bdk-macros = "^0.6"
1616
log = "^0.4"
1717
miniscript = { version = "7.0", features = ["use-serde"] }
18-
bitcoin = { version = "0.28", features = ["use-serde", "base64"] }
18+
bitcoin = { version = "0.28.1", features = ["use-serde", "base64", "rand"] }
1919
serde = { version = "^1.0", features = ["derive"] }
2020
serde_json = { version = "^1.0" }
2121
rand = "^0.7"

src/descriptor/dsl.rs

+167-11
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,48 @@ macro_rules! impl_top_level_pk {
7373
}};
7474
}
7575

76+
#[doc(hidden)]
77+
#[macro_export]
78+
macro_rules! impl_top_level_tr {
79+
( $internal_key:expr, $tap_tree:expr ) => {{
80+
use $crate::miniscript::descriptor::{
81+
Descriptor, DescriptorPublicKey, KeyMap, TapTree, Tr,
82+
};
83+
use $crate::miniscript::Tap;
84+
85+
#[allow(unused_imports)]
86+
use $crate::keys::{DescriptorKey, IntoDescriptorKey, ValidNetworks};
87+
88+
let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
89+
90+
$internal_key
91+
.into_descriptor_key()
92+
.and_then(|key: DescriptorKey<Tap>| key.extract(&secp))
93+
.map_err($crate::descriptor::DescriptorError::Key)
94+
.and_then(|(pk, mut key_map, mut valid_networks)| {
95+
let tap_tree = $tap_tree.map(
96+
|(tap_tree, tree_keymap, tree_networks): (
97+
TapTree<DescriptorPublicKey>,
98+
KeyMap,
99+
ValidNetworks,
100+
)| {
101+
key_map.extend(tree_keymap.into_iter());
102+
valid_networks =
103+
$crate::keys::merge_networks(&valid_networks, &tree_networks);
104+
105+
tap_tree
106+
},
107+
);
108+
109+
Ok((
110+
Descriptor::<DescriptorPublicKey>::Tr(Tr::new(pk, tap_tree)?),
111+
key_map,
112+
valid_networks,
113+
))
114+
})
115+
}};
116+
}
117+
76118
#[doc(hidden)]
77119
#[macro_export]
78120
macro_rules! impl_leaf_opcode {
@@ -228,6 +270,62 @@ macro_rules! impl_sortedmulti {
228270

229271
}
230272

273+
#[doc(hidden)]
274+
#[macro_export]
275+
macro_rules! parse_tap_tree {
276+
( @merge $tree_a:expr, $tree_b:expr) => {{
277+
use std::sync::Arc;
278+
use $crate::miniscript::descriptor::TapTree;
279+
280+
$tree_a
281+
.and_then(|tree_a| Ok((tree_a, $tree_b?)))
282+
.and_then(|((a_tree, mut a_keymap, a_networks), (b_tree, b_keymap, b_networks))| {
283+
a_keymap.extend(b_keymap.into_iter());
284+
Ok((TapTree::Tree(Arc::new(a_tree), Arc::new(b_tree)), a_keymap, $crate::keys::merge_networks(&a_networks, &b_networks)))
285+
})
286+
287+
}};
288+
289+
// Two sub-trees
290+
( { { $( $tree_a:tt )* }, { $( $tree_b:tt )* } } ) => {{
291+
let tree_a = $crate::parse_tap_tree!( { $( $tree_a )* } );
292+
let tree_b = $crate::parse_tap_tree!( { $( $tree_b )* } );
293+
294+
$crate::parse_tap_tree!(@merge tree_a, tree_b)
295+
}};
296+
297+
// One leaf and a sub-tree
298+
( { $op_a:ident ( $( $minisc_a:tt )* ), { $( $tree_b:tt )* } } ) => {{
299+
let tree_a = $crate::parse_tap_tree!( $op_a ( $( $minisc_a )* ) );
300+
let tree_b = $crate::parse_tap_tree!( { $( $tree_b )* } );
301+
302+
$crate::parse_tap_tree!(@merge tree_a, tree_b)
303+
}};
304+
( { { $( $tree_a:tt )* }, $op_b:ident ( $( $minisc_b:tt )* ) } ) => {{
305+
let tree_a = $crate::parse_tap_tree!( { $( $tree_a )* } );
306+
let tree_b = $crate::parse_tap_tree!( $op_b ( $( $minisc_b )* ) );
307+
308+
$crate::parse_tap_tree!(@merge tree_a, tree_b)
309+
}};
310+
311+
// Two leaves
312+
( { $op_a:ident ( $( $minisc_a:tt )* ), $op_b:ident ( $( $minisc_b:tt )* ) } ) => {{
313+
let tree_a = $crate::parse_tap_tree!( $op_a ( $( $minisc_a )* ) );
314+
let tree_b = $crate::parse_tap_tree!( $op_b ( $( $minisc_b )* ) );
315+
316+
$crate::parse_tap_tree!(@merge tree_a, tree_b)
317+
}};
318+
319+
// Single leaf
320+
( $op:ident ( $( $minisc:tt )* ) ) => {{
321+
use std::sync::Arc;
322+
use $crate::miniscript::descriptor::TapTree;
323+
324+
$crate::fragment!( $op ( $( $minisc )* ) )
325+
.map(|(a_minisc, a_keymap, a_networks)| (TapTree::Leaf(Arc::new(a_minisc)), a_keymap, a_networks))
326+
}};
327+
}
328+
231329
#[doc(hidden)]
232330
#[macro_export]
233331
macro_rules! apply_modifier {
@@ -441,6 +539,15 @@ macro_rules! descriptor {
441539
( wsh ( $( $minisc:tt )* ) ) => ({
442540
$crate::impl_top_level_sh!(Wsh, new, new_sortedmulti, Segwitv0, $( $minisc )*)
443541
});
542+
543+
( tr ( $internal_key:expr ) ) => ({
544+
$crate::impl_top_level_tr!($internal_key, None)
545+
});
546+
( tr ( $internal_key:expr, $( $taptree:tt )* ) ) => ({
547+
let tap_tree = $crate::parse_tap_tree!( $( $taptree )* );
548+
tap_tree
549+
.and_then(|tap_tree| $crate::impl_top_level_tr!($internal_key, Some(tap_tree)))
550+
});
444551
}
445552

446553
#[doc(hidden)]
@@ -480,6 +587,23 @@ impl<A, B, C> From<(A, (B, (C, ())))> for TupleThree<A, B, C> {
480587
}
481588
}
482589

590+
#[doc(hidden)]
591+
#[macro_export]
592+
macro_rules! group_multi_keys {
593+
( $( $key:expr ),+ ) => {{
594+
use $crate::keys::IntoDescriptorKey;
595+
596+
let keys = vec![
597+
$(
598+
$key.into_descriptor_key(),
599+
)*
600+
];
601+
602+
keys.into_iter().collect::<Result<Vec<_>, _>>()
603+
.map_err($crate::descriptor::DescriptorError::Key)
604+
}};
605+
}
606+
483607
#[doc(hidden)]
484608
#[macro_export]
485609
macro_rules! fragment_internal {
@@ -640,21 +764,22 @@ macro_rules! fragment {
640764
.and_then(|items| $crate::fragment!(thresh_vec($thresh, items)))
641765
});
642766
( multi_vec ( $thresh:expr, $keys:expr ) ) => ({
643-
$crate::keys::make_multi($thresh, $keys)
767+
let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
768+
769+
$crate::keys::make_multi($thresh, $crate::miniscript::Terminal::Multi, $keys, &secp)
644770
});
645771
( multi ( $thresh:expr $(, $key:expr )+ ) ) => ({
646-
use $crate::keys::IntoDescriptorKey;
772+
$crate::group_multi_keys!( $( $key ),* )
773+
.and_then(|keys| $crate::fragment!( multi_vec ( $thresh, keys ) ))
774+
});
775+
( multi_a_vec ( $thresh:expr, $keys:expr ) ) => ({
647776
let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
648777

649-
let keys = vec![
650-
$(
651-
$key.into_descriptor_key(),
652-
)*
653-
];
654-
655-
keys.into_iter().collect::<Result<Vec<_>, _>>()
656-
.map_err($crate::descriptor::DescriptorError::Key)
657-
.and_then(|keys| $crate::keys::make_multi($thresh, keys, &secp))
778+
$crate::keys::make_multi($thresh, $crate::miniscript::Terminal::MultiA, $keys, &secp)
779+
});
780+
( multi_a ( $thresh:expr $(, $key:expr )+ ) ) => ({
781+
$crate::group_multi_keys!( $( $key ),* )
782+
.and_then(|keys| $crate::fragment!( multi_a_vec ( $thresh, keys ) ))
658783
});
659784

660785
// `sortedmulti()` is handled separately
@@ -1064,4 +1189,35 @@ mod test {
10641189

10651190
descriptor!(wsh(v: pk(uncompressed_pk))).unwrap();
10661191
}
1192+
1193+
#[test]
1194+
fn test_dsl_tr_only_key() {
1195+
let private_key =
1196+
PrivateKey::from_wif("cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR").unwrap();
1197+
let (descriptor, _, _) = descriptor!(tr(private_key)).unwrap();
1198+
1199+
assert_eq!(
1200+
descriptor.to_string(),
1201+
"tr(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c)#heq9m95v"
1202+
)
1203+
}
1204+
1205+
#[test]
1206+
fn test_dsl_tr_simple_tree() {
1207+
let private_key =
1208+
PrivateKey::from_wif("cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR").unwrap();
1209+
let (descriptor, _, _) =
1210+
descriptor!(tr(private_key, { pk(private_key), pk(private_key) })).unwrap();
1211+
1212+
assert_eq!(descriptor.to_string(), "tr(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c,{pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c),pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c)})#xy5fjw6d")
1213+
}
1214+
1215+
#[test]
1216+
fn test_dsl_tr_single_leaf() {
1217+
let private_key =
1218+
PrivateKey::from_wif("cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR").unwrap();
1219+
let (descriptor, _, _) = descriptor!(tr(private_key, pk(private_key))).unwrap();
1220+
1221+
assert_eq!(descriptor.to_string(), "tr(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c,pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c))#lzl2vmc7")
1222+
}
10671223
}

0 commit comments

Comments
 (0)