Skip to content

Commit 0634479

Browse files
committed
Fix type inference for the tr() descriptor, add basic tests
1 parent 66ea28f commit 0634479

File tree

4 files changed

+105
-27
lines changed

4 files changed

+105
-27
lines changed

src/descriptor/dsl.rs

+49-8
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,34 @@ macro_rules! impl_top_level_pk {
7777
#[macro_export]
7878
macro_rules! impl_top_level_tr {
7979
( $internal_key:expr, $tap_tree:expr ) => {{
80-
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, Tr};
80+
use $crate::miniscript::descriptor::{
81+
Descriptor, DescriptorPublicKey, KeyMap, TapTree, Tr,
82+
};
8183
use $crate::miniscript::Tap;
8284

8385
#[allow(unused_imports)]
84-
use $crate::keys::{DescriptorKey, IntoDescriptorKey};
86+
use $crate::keys::{DescriptorKey, IntoDescriptorKey, ValidNetworks};
87+
8588
let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
8689

8790
$internal_key
8891
.into_descriptor_key()
8992
.and_then(|key: DescriptorKey<Tap>| key.extract(&secp))
9093
.map_err($crate::descriptor::DescriptorError::Key)
9194
.and_then(|(pk, mut key_map, mut valid_networks)| {
92-
let tap_tree = $tap_tree.map(|(tap_tree, tree_keymap, tree_networks)| {
93-
key_map.extend(tree_keymap.into_iter());
94-
valid_networks = $crate::keys::merge_networks(&valid_networks, &tree_networks);
95-
96-
tap_tree
97-
});
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+
);
98108

99109
Ok((
100110
Descriptor::<DescriptorPublicKey>::Tr(Tr::new(pk, tap_tree)?),
@@ -1179,4 +1189,35 @@ mod test {
11791189

11801190
descriptor!(wsh(v: pk(uncompressed_pk))).unwrap();
11811191
}
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+
}
11821223
}

src/descriptor/mod.rs

+38-13
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
use std::collections::{BTreeMap, HashSet};
1818
use std::ops::Deref;
1919

20-
use bitcoin::{secp256k1, XOnlyPublicKey, PublicKey};
2120
use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource};
2221
use bitcoin::util::{psbt, taproot};
22+
use bitcoin::{secp256k1, PublicKey, XOnlyPublicKey};
2323
use bitcoin::{Network, Script, TxOut};
2424

2525
use miniscript::descriptor::{DescriptorType, InnerXKey, SinglePubKey};
@@ -327,7 +327,11 @@ pub(crate) trait DescriptorMeta {
327327
tap_key_origins: &TapKeyOrigins,
328328
secp: &'s SecpCtx,
329329
) -> Option<DerivedDescriptor<'s>>;
330-
fn derive_from_psbt_key_origins<'s>(&self, key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>, secp: &'s SecpCtx) -> Option<DerivedDescriptor<'s>>;
330+
fn derive_from_psbt_key_origins<'s>(
331+
&self,
332+
key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
333+
secp: &'s SecpCtx,
334+
) -> Option<DerivedDescriptor<'s>>;
331335
fn derive_from_psbt_input<'s>(
332336
&self,
333337
psbt_input: &psbt::Input,
@@ -396,10 +400,20 @@ impl DescriptorMeta for ExtendedDescriptor {
396400
Ok(answer)
397401
}
398402

399-
fn derive_from_psbt_key_origins<'s>(&self, key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>, secp: &'s SecpCtx) -> Option<DerivedDescriptor<'s>> {
403+
fn derive_from_psbt_key_origins<'s>(
404+
&self,
405+
key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
406+
secp: &'s SecpCtx,
407+
) -> Option<DerivedDescriptor<'s>> {
400408
// Ensure that deriving `xpub` with `path` yields `expected`
401-
let verify_key = |xpub: &DescriptorXKey<ExtendedPubKey>, path: &DerivationPath, expected: &SinglePubKey| {
402-
let derived = xpub.xkey.derive_pub(secp, path).expect("The path should never contain hardened derivation steps").public_key;
409+
let verify_key = |xpub: &DescriptorXKey<ExtendedPubKey>,
410+
path: &DerivationPath,
411+
expected: &SinglePubKey| {
412+
let derived = xpub
413+
.xkey
414+
.derive_pub(secp, path)
415+
.expect("The path should never contain hardened derivation steps")
416+
.public_key;
403417

404418
match expected {
405419
SinglePubKey::FullKey(pk) if &PublicKey::new(derived) == pk => true,
@@ -421,10 +435,12 @@ impl DescriptorMeta for ExtendedDescriptor {
421435
let derive_path = key_origins
422436
.get_key_value(&root_fingerprint)
423437
.and_then(|(fingerprint, (path, expected))| {
424-
xpub.matches(&(*fingerprint, (*path).clone()), secp).zip(Some((path, expected)))
438+
xpub.matches(&(*fingerprint, (*path).clone()), secp)
439+
.zip(Some((path, expected)))
425440
})
426441
.and_then(|(prefix, (full_path, expected))| {
427-
let derive_path = full_path.into_iter()
442+
let derive_path = full_path
443+
.into_iter()
428444
.skip(prefix.into_iter().count())
429445
.cloned()
430446
.collect::<DerivationPath>();
@@ -433,10 +449,18 @@ impl DescriptorMeta for ExtendedDescriptor {
433449
// an empty path for fixed descriptors. To verify the key we also need the normal steps
434450
// that come before the wildcard, so we take them directly from `xpub` and then append
435451
// the final index
436-
if verify_key(xpub, &xpub.derivation_path.extend(derive_path.clone()), expected) {
452+
if verify_key(
453+
xpub,
454+
&xpub.derivation_path.extend(derive_path.clone()),
455+
expected,
456+
) {
437457
Some(derive_path)
438458
} else {
439-
log::debug!("Key `{}` derived with {} yields an unexpected key", root_fingerprint, derive_path);
459+
log::debug!(
460+
"Key `{}` derived with {} yields an unexpected key",
461+
root_fingerprint,
462+
derive_path
463+
);
440464
None
441465
}
442466
});
@@ -472,7 +496,10 @@ impl DescriptorMeta for ExtendedDescriptor {
472496
let key_origins = hd_keypaths
473497
.iter()
474498
.map(|(pk, (fingerprint, path))| {
475-
(*fingerprint, (path, SinglePubKey::FullKey(PublicKey::new(*pk))))
499+
(
500+
*fingerprint,
501+
(path, SinglePubKey::FullKey(PublicKey::new(*pk))),
502+
)
476503
})
477504
.collect();
478505
self.derive_from_psbt_key_origins(key_origins, secp)
@@ -486,9 +513,7 @@ impl DescriptorMeta for ExtendedDescriptor {
486513
// "Convert" a tap_key_origins map to the format required by `derive_from_psbt_key_origins`
487514
let key_origins = tap_key_origins
488515
.iter()
489-
.map(|(pk, (_, (fingerprint, path)))| {
490-
(*fingerprint, (path, SinglePubKey::XOnly(*pk)))
491-
})
516+
.map(|(pk, (_, (fingerprint, path)))| (*fingerprint, (path, SinglePubKey::XOnly(*pk))))
492517
.collect();
493518
self.derive_from_psbt_key_origins(key_origins, secp)
494519
}

src/wallet/mod.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ use bitcoin::secp256k1::Secp256k1;
2626
use bitcoin::consensus::encode::serialize;
2727
use bitcoin::util::{psbt, taproot};
2828
use bitcoin::{
29-
Address, EcdsaSighashType, SchnorrSighashType, Network, OutPoint, Script, Transaction, TxOut, Txid, Witness,
29+
Address, EcdsaSighashType, Network, OutPoint, SchnorrSighashType, Script, Transaction, TxOut,
30+
Txid, Witness,
3031
};
3132

3233
use miniscript::descriptor::DescriptorTrait;
@@ -1026,7 +1027,10 @@ where
10261027
// is using `SIGHASH_ALL` or `SIGHASH_DEFAULT` for taproot
10271028
if !sign_options.allow_all_sighashes
10281029
&& !psbt.inputs.iter().all(|i| {
1029-
i.sighash_type.is_none() || i.sighash_type == Some(EcdsaSighashType::All.into()) || i.sighash_type == Some(SchnorrSighashType::All.into()) || i.sighash_type == Some(SchnorrSighashType::Default.into())
1030+
i.sighash_type.is_none()
1031+
|| i.sighash_type == Some(EcdsaSighashType::All.into())
1032+
|| i.sighash_type == Some(SchnorrSighashType::All.into())
1033+
|| i.sighash_type == Some(SchnorrSighashType::Default.into())
10301034
})
10311035
{
10321036
return Err(Error::Signer(signer::SignerError::NonStandardSighash));
@@ -4216,6 +4220,9 @@ pub(crate) mod test {
42164220
builder.add_recipient(addr.script_pubkey(), 25_000);
42174221
let (mut psbt, _) = builder.finish().unwrap();
42184222

4219-
assert!(wallet.sign(&mut psbt, Default::default()).unwrap(), "Unable to finalize taproot script spend");
4223+
assert!(
4224+
wallet.sign(&mut psbt, Default::default()).unwrap(),
4225+
"Unable to finalize taproot script spend"
4226+
);
42204227
}
42214228
}

src/wallet/signer.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ use bitcoin::util::{ecdsa, psbt, schnorr, sighash, taproot};
9494
use bitcoin::{secp256k1, XOnlyPublicKey};
9595
use bitcoin::{EcdsaSighashType, PrivateKey, PublicKey, SchnorrSighashType, Script};
9696

97-
use miniscript::descriptor::{DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey, SinglePubKey, KeyMap};
97+
use miniscript::descriptor::{
98+
DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey, KeyMap, SinglePubKey,
99+
};
98100
use miniscript::{Legacy, MiniscriptKey, Segwitv0, Tap};
99101

100102
use super::utils::SecpCtx;
@@ -258,12 +260,15 @@ impl InputSigner for DescriptorXKey<ExtendedPrivKey> {
258260
return Ok(());
259261
}
260262

261-
let tap_key_origins = psbt.inputs[input_index].tap_key_origins.iter().map(|(pk, (_, keysource))| (SinglePubKey::XOnly(*pk), keysource)).collect::<Vec<_>>();
263+
let tap_key_origins = psbt.inputs[input_index]
264+
.tap_key_origins
265+
.iter()
266+
.map(|(pk, (_, keysource))| (SinglePubKey::XOnly(*pk), keysource));
262267
let (public_key, full_path) = match psbt.inputs[input_index]
263268
.bip32_derivation
264269
.iter()
265270
.map(|(pk, keysource)| (SinglePubKey::FullKey(PublicKey::new(*pk)), keysource))
266-
.chain(tap_key_origins.into_iter())
271+
.chain(tap_key_origins)
267272
.filter_map(|(pk, keysource)| {
268273
if self.matches(keysource, secp).is_some() {
269274
Some((pk, keysource.1.clone()))

0 commit comments

Comments
 (0)