From b500cfe4e59dd16de9d69ad706e231642207a757 Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Wed, 15 Sep 2021 10:21:38 +0200 Subject: [PATCH 1/4] [descriptor] Fix `extract_policy()` for descriptors with `pk_h()` --- src/descriptor/policy.rs | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/src/descriptor/policy.rs b/src/descriptor/policy.rs index 4a6e93b4..6184365c 100644 --- a/src/descriptor/policy.rs +++ b/src/descriptor/policy.rs @@ -47,14 +47,12 @@ use bitcoin::util::bip32::Fingerprint; use bitcoin::PublicKey; use miniscript::descriptor::{DescriptorPublicKey, ShInner, SortedMultiVec, WshInner}; -use miniscript::{ - Descriptor, Miniscript, MiniscriptKey, Satisfier, ScriptContext, Terminal, ToPublicKey, -}; +use miniscript::{Descriptor, Miniscript, MiniscriptKey, Satisfier, ScriptContext, Terminal}; #[allow(unused_imports)] use log::{debug, error, info, trace}; -use crate::descriptor::{DerivedDescriptorKey, ExtractPolicy}; +use crate::descriptor::ExtractPolicy; use crate::wallet::signer::{SignerId, SignersContainer}; use crate::wallet::utils::{self, After, Older, SecpCtx}; @@ -88,13 +86,6 @@ impl PkOrF { }, } } - - fn from_key_hash(k: hash160::Hash) -> Self { - PkOrF { - pubkey_hash: Some(k), - ..Default::default() - } - } } /// An item that needs to be satisfied @@ -779,25 +770,6 @@ fn signature_in_psbt(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> }) } -fn signature_key( - key: &::Hash, - signers: &SignersContainer, - secp: &SecpCtx, -) -> Policy { - let key_hash = DerivedDescriptorKey::new(key.clone(), secp) - .to_public_key() - .to_pubkeyhash(); - let mut policy: Policy = SatisfiableItem::Signature(PkOrF::from_key_hash(key_hash)).into(); - - if signers.find(SignerId::PkHash(key_hash)).is_some() { - policy.contribution = Satisfaction::Complete { - condition: Default::default(), - } - } - - policy -} - impl ExtractPolicy for Miniscript { fn extract_policy( &self, @@ -809,7 +781,7 @@ impl ExtractPolicy for Miniscript // Leaves Terminal::True | Terminal::False => None, Terminal::PkK(pubkey) => Some(signature(pubkey, signers, build_sat, secp)), - Terminal::PkH(pubkey_hash) => Some(signature_key(pubkey_hash, signers, secp)), + Terminal::PkH(pubkey_hash) => Some(signature(pubkey_hash, signers, build_sat, secp)), Terminal::After(value) => { let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { value: *value }.into(); policy.contribution = Satisfaction::Complete { From e3dd7553962e6f872cf6717ab008235c07353c6e Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Wed, 15 Sep 2021 10:35:01 +0200 Subject: [PATCH 2/4] [descriptor] Fix `pk_h()` in the `descriptor!()` macro Instead of accepting just a `DescriptorPublicKey` it now accepts anything that implements `IntoDescriptorKey` like `pk_k()` does. --- src/descriptor/dsl.rs | 5 +++-- src/keys/mod.rs | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/descriptor/dsl.rs b/src/descriptor/dsl.rs index 76eb4ecd..b4fdcba7 100644 --- a/src/descriptor/dsl.rs +++ b/src/descriptor/dsl.rs @@ -571,8 +571,9 @@ macro_rules! fragment { ( pk ( $key:expr ) ) => ({ $crate::fragment!(c:pk_k ( $key )) }); - ( pk_h ( $key_hash:expr ) ) => ({ - $crate::impl_leaf_opcode_value!(PkH, $key_hash) + ( pk_h ( $key:expr ) ) => ({ + let secp = $crate::bitcoin::secp256k1::Secp256k1::new(); + $crate::keys::make_pkh($key, &secp) }); ( after ( $value:expr ) ) => ({ $crate::impl_leaf_opcode_value!(After, $value) diff --git a/src/keys/mod.rs b/src/keys/mod.rs index 1fbeada7..15b53e15 100644 --- a/src/keys/mod.rs +++ b/src/keys/mod.rs @@ -753,6 +753,20 @@ pub fn make_pk, Ctx: ScriptContext>( Ok((minisc, key_map, valid_networks)) } +// Used internally by `bdk::fragment!` to build `pk_h()` fragments +#[doc(hidden)] +pub fn make_pkh, Ctx: ScriptContext>( + descriptor_key: Pk, + secp: &SecpCtx, +) -> Result<(Miniscript, KeyMap, ValidNetworks), DescriptorError> { + let (key, key_map, valid_networks) = descriptor_key.into_descriptor_key()?.extract(secp)?; + let minisc = Miniscript::from_ast(Terminal::PkH(key))?; + + minisc.check_minsicript()?; + + Ok((minisc, key_map, valid_networks)) +} + // Used internally by `bdk::fragment!` to build `multi()` fragments #[doc(hidden)] pub fn make_multi, Ctx: ScriptContext>( From 5edf663f3db3fcf383388e36c503013125909e7d Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Wed, 15 Sep 2021 10:36:02 +0200 Subject: [PATCH 3/4] [descriptor] Add an alias for `and_or()` The descriptor syntax encodes it with `andor()`, without the underscore --- src/descriptor/dsl.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/descriptor/dsl.rs b/src/descriptor/dsl.rs index b4fdcba7..b9acce3b 100644 --- a/src/descriptor/dsl.rs +++ b/src/descriptor/dsl.rs @@ -602,6 +602,9 @@ macro_rules! fragment { ( and_or ( $( $inner:tt )* ) ) => ({ $crate::impl_node_opcode_three!(AndOr, $( $inner )*) }); + ( andor ( $( $inner:tt )* ) ) => ({ + $crate::impl_node_opcode_three!(AndOr, $( $inner )*) + }); ( or_b ( $( $inner:tt )* ) ) => ({ $crate::impl_node_opcode_two!(OrB, $( $inner )*) }); From 326b64de3ae5233fb76a14cfac38f50a20ff7a65 Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Wed, 15 Sep 2021 10:36:50 +0200 Subject: [PATCH 4/4] [descriptor] Add a test for `extract_policy()` on `pk_h()` operands --- src/descriptor/policy.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/descriptor/policy.rs b/src/descriptor/policy.rs index 6184365c..f3baa3e0 100644 --- a/src/descriptor/policy.rs +++ b/src/descriptor/policy.rs @@ -1416,6 +1416,7 @@ mod test { const ALICE_TPRV_STR:&str = "tprv8ZgxMBicQKsPf6T5X327efHnvJDr45Xnb8W4JifNWtEoqXu9MRYS4v1oYe6DFcMVETxy5w3bqpubYRqvcVTqovG1LifFcVUuJcbwJwrhYzP"; const BOB_TPRV_STR:&str = "tprv8ZgxMBicQKsPeinZ155cJAn117KYhbaN6MV3WeG6sWhxWzcvX1eg1awd4C9GpUN1ncLEM2rzEvunAg3GizdZD4QPPCkisTz99tXXB4wZArp"; + const CAROL_TPRV_STR:&str = "tprv8ZgxMBicQKsPdC3CicFifuLCEyVVdXVUNYorxUWj3iGZ6nimnLAYAY9SYB7ib8rKzRxrCKFcEytCt6szwd2GHnGPRCBLAEAoSVDefSNk4Bt"; const ALICE_BOB_PATH: &str = "m/0'"; #[test] @@ -1574,4 +1575,28 @@ mod test { ); //println!("{}", serde_json::to_string(&policy_expired_signed).unwrap()); } + + #[test] + fn test_extract_pkh() { + let secp = Secp256k1::new(); + + let (prvkey_alice, _, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp); + let (prvkey_bob, _, _) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp); + let (prvkey_carol, _, _) = setup_keys(CAROL_TPRV_STR, ALICE_BOB_PATH, &secp); + + let desc = descriptor!(wsh(c: andor( + pk(prvkey_alice), + pk_k(prvkey_bob), + pk_h(prvkey_carol), + ))) + .unwrap(); + + let (wallet_desc, keymap) = desc + .into_wallet_descriptor(&secp, Network::Testnet) + .unwrap(); + let signers_container = Arc::new(SignersContainer::from(keymap)); + + let policy = wallet_desc.extract_policy(&signers_container, BuildSatisfaction::None, &secp); + assert!(policy.is_ok()); + } }