diff --git a/src/descriptor/policy.rs b/src/descriptor/policy.rs index 98b26573..c4b94a52 100644 --- a/src/descriptor/policy.rs +++ b/src/descriptor/policy.rs @@ -21,6 +21,7 @@ //! ``` //! # use std::sync::Arc; //! # use bdk::descriptor::*; +//! # use bdk::wallet::signer::*; //! # use bdk::bitcoin::secp256k1::Secp256k1; //! use bdk::descriptor::policy::BuildSatisfaction; //! let secp = Secp256k1::new(); @@ -29,7 +30,7 @@ //! let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(&secp, desc)?; //! println!("{:?}", extended_desc); //! -//! let signers = Arc::new(key_map.into()); +//! let signers = Arc::new(SignersContainer::build(key_map, &extended_desc, &secp)); //! let policy = extended_desc.extract_policy(&signers, BuildSatisfaction::None, &secp)?; //! println!("policy: {}", serde_json::to_string(&policy)?); //! # Ok::<(), bdk::Error>(()) @@ -1109,7 +1110,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1124,7 +1125,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1148,7 +1149,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1180,7 +1181,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1212,7 +1213,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1244,7 +1245,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1277,7 +1278,7 @@ mod test { .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); let single_key = wallet_desc.derive(0); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = single_key .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1293,7 +1294,7 @@ mod test { .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); let single_key = wallet_desc.derive(0); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = single_key .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1320,7 +1321,7 @@ mod test { .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); let single_key = wallet_desc.derive(0); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = single_key .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1363,7 +1364,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1402,7 +1403,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1427,7 +1428,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1445,7 +1446,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() @@ -1467,10 +1468,10 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers = keymap.into(); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc - .extract_policy(&signers, BuildSatisfaction::None, &secp) + .extract_policy(&signers_container, BuildSatisfaction::None, &secp) .unwrap() .unwrap(); @@ -1533,7 +1534,7 @@ mod test { addr.to_string() ); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let psbt = Psbt::from_str(ALICE_SIGNED_PSBT).unwrap(); @@ -1594,7 +1595,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let addr = wallet_desc .as_derived(0, &secp) @@ -1682,7 +1683,7 @@ mod test { let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers_container = Arc::new(SignersContainer::from(keymap)); + let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp)); let policy = wallet_desc.extract_policy(&signers_container, BuildSatisfaction::None, &secp); assert!(policy.is_ok()); diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index b04921bd..ebbf5d4f 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -197,7 +197,7 @@ where KeychainKind::External, get_checksum(&descriptor.to_string())?.as_bytes(), )?; - let signers = Arc::new(SignersContainer::from(keymap)); + let signers = Arc::new(SignersContainer::build(keymap, &descriptor, &secp)); let (change_descriptor, change_signers) = match change_descriptor { Some(desc) => { let (change_descriptor, change_keymap) = @@ -207,7 +207,11 @@ where get_checksum(&change_descriptor.to_string())?.as_bytes(), )?; - let change_signers = Arc::new(SignersContainer::from(change_keymap)); + let change_signers = Arc::new(SignersContainer::build( + change_keymap, + &change_descriptor, + &secp, + )); // if !parsed.same_structure(descriptor.as_ref()) { // return Err(Error::DifferentDescriptorStructure); // } diff --git a/src/wallet/signer.rs b/src/wallet/signer.rs index b6371035..cf6c882e 100644 --- a/src/wallet/signer.rs +++ b/src/wallet/signer.rs @@ -82,23 +82,26 @@ use std::cmp::Ordering; use std::collections::BTreeMap; use std::fmt; -use std::ops::Bound::Included; +use std::ops::{Bound::Included, Deref}; use std::sync::Arc; use bitcoin::blockdata::opcodes; use bitcoin::blockdata::script::Builder as ScriptBuilder; use bitcoin::hashes::{hash160, Hash}; -use bitcoin::secp256k1::{Message, Secp256k1}; +use bitcoin::secp256k1::Message; use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint}; use bitcoin::util::{ecdsa, psbt, schnorr, sighash, taproot}; use bitcoin::{secp256k1, XOnlyPublicKey}; use bitcoin::{EcdsaSighashType, PrivateKey, PublicKey, SchnorrSighashType, Script}; -use miniscript::descriptor::{DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey, KeyMap}; +use miniscript::descriptor::{ + Descriptor, DescriptorPublicKey, DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey, + KeyMap, +}; use miniscript::{Legacy, MiniscriptKey, Segwitv0, Tap}; use super::utils::SecpCtx; -use crate::descriptor::XKeyUtils; +use crate::descriptor::{DescriptorMeta, XKeyUtils}; /// Identifier of a signer in the `SignersContainers`. Used as a key to find the right signer among /// multiple of them @@ -171,6 +174,44 @@ impl fmt::Display for SignerError { impl std::error::Error for SignerError {} +/// Signing context +/// +/// Used by our software signers to determine the type of signatures to make +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SignerContext { + /// Legacy context + Legacy, + /// Segwit v0 context (BIP 143) + Segwitv0, + /// Taproot context (BIP 340) + Tap { + /// Whether the signer can sign for the internal key or not + is_internal_key: bool, + }, +} + +/// Wrapper structure to pair a signer with its context +#[derive(Debug, Clone)] +pub struct SignerWrapper { + signer: S, + ctx: SignerContext, +} + +impl SignerWrapper { + /// Create a wrapped signer from a signer and a context + pub fn new(signer: S, ctx: SignerContext) -> Self { + SignerWrapper { signer, ctx } + } +} + +impl Deref for SignerWrapper { + type Target = S; + + fn deref(&self) -> &Self::Target { + &self.signer + } +} + /// Common signer methods pub trait SignerCommon: fmt::Debug + Send + Sync { /// Return the [`SignerId`] for this signer @@ -231,17 +272,17 @@ impl TransactionSigner for T { } } -impl SignerCommon for DescriptorXKey { +impl SignerCommon for SignerWrapper> { fn id(&self, secp: &SecpCtx) -> SignerId { SignerId::from(self.root_fingerprint(secp)) } fn descriptor_secret_key(&self) -> Option { - Some(DescriptorSecretKey::XPrv(self.clone())) + Some(DescriptorSecretKey::XPrv(self.signer.clone())) } } -impl InputSigner for DescriptorXKey { +impl InputSigner for SignerWrapper> { fn sign_input( &self, psbt: &mut psbt::PartiallySignedTransaction, @@ -289,30 +330,31 @@ impl InputSigner for DescriptorXKey { Err(SignerError::InvalidKey) } else { // HD wallets imply compressed keys - PrivateKey { + let priv_key = PrivateKey { compressed: true, network: self.xkey.network, inner: derived_key.private_key, - } - .sign_input(psbt, input_index, secp) + }; + + SignerWrapper::new(priv_key, self.ctx).sign_input(psbt, input_index, secp) } } } -impl SignerCommon for PrivateKey { +impl SignerCommon for SignerWrapper { fn id(&self, secp: &SecpCtx) -> SignerId { SignerId::from(self.public_key(secp).to_pubkeyhash()) } fn descriptor_secret_key(&self) -> Option { Some(DescriptorSecretKey::SinglePriv(DescriptorSinglePriv { - key: *self, + key: self.signer, origin: None, })) } } -impl InputSigner for PrivateKey { +impl InputSigner for SignerWrapper { fn sign_input( &self, psbt: &mut psbt::PartiallySignedTransaction, @@ -331,11 +373,9 @@ impl InputSigner for PrivateKey { let pubkey = PublicKey::from_private_key(secp, self); let x_only_pubkey = XOnlyPublicKey::from(pubkey.inner); - let is_taproot = psbt.inputs[input_index].tap_internal_key.is_some() - || psbt.inputs[input_index].tap_merkle_root.is_some(); - match psbt.inputs[input_index].tap_internal_key { - Some(k) if k == x_only_pubkey && psbt.inputs[input_index].tap_key_sig.is_none() => { + if let SignerContext::Tap { is_internal_key } = self.ctx { + if is_internal_key && psbt.inputs[input_index].tap_key_sig.is_none() { let (hash, hash_ty) = Tap::sighash(psbt, input_index, None)?; sign_psbt_schnorr( &self.inner, @@ -347,56 +387,54 @@ impl InputSigner for PrivateKey { secp, ); } - _ => {} - } - if let Some((leaf_hashes, _)) = psbt.inputs[input_index].tap_key_origins.get(&x_only_pubkey) - { - let leaf_hashes = leaf_hashes - .iter() - .filter(|lh| { - !psbt.inputs[input_index] - .tap_script_sigs - .contains_key(&(x_only_pubkey, **lh)) - }) - .cloned() - .collect::>(); - for lh in leaf_hashes { - let (hash, hash_ty) = Tap::sighash(psbt, input_index, Some(lh))?; - sign_psbt_schnorr( - &self.inner, - x_only_pubkey, - Some(lh), - &mut psbt.inputs[input_index], - hash, - hash_ty, - secp, - ); - } - } - if !is_taproot { - if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) { - return Ok(()); + if let Some((leaf_hashes, _)) = + psbt.inputs[input_index].tap_key_origins.get(&x_only_pubkey) + { + let leaf_hashes = leaf_hashes + .iter() + .filter(|lh| { + !psbt.inputs[input_index] + .tap_script_sigs + .contains_key(&(x_only_pubkey, **lh)) + }) + .cloned() + .collect::>(); + for lh in leaf_hashes { + let (hash, hash_ty) = Tap::sighash(psbt, input_index, Some(lh))?; + sign_psbt_schnorr( + &self.inner, + x_only_pubkey, + Some(lh), + &mut psbt.inputs[input_index], + hash, + hash_ty, + secp, + ); + } } - // FIXME: use the presence of `witness_utxo` as an indication that we should make a bip143 - // sig. Does this make sense? Should we add an extra argument to explicitly switch between - // these? The original idea was to declare sign() as sign() and use Ctx, - // but that violates the rules for trait-objects, so we can't do it. - let (hash, hash_ty) = match psbt.inputs[input_index].witness_utxo { - Some(_) => Segwitv0::sighash(psbt, input_index, ())?, - None => Legacy::sighash(psbt, input_index, ())?, - }; - sign_psbt_ecdsa( - &self.inner, - pubkey, - &mut psbt.inputs[input_index], - hash, - hash_ty, - secp, - ); + return Ok(()); } + if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) { + return Ok(()); + } + + let (hash, hash_ty) = match self.ctx { + SignerContext::Segwitv0 => Segwitv0::sighash(psbt, input_index, ())?, + SignerContext::Legacy => Legacy::sighash(psbt, input_index, ())?, + _ => return Ok(()), // handled above + }; + sign_psbt_ecdsa( + &self.inner, + pubkey, + &mut psbt.inputs[input_index], + hash, + hash_ty, + secp, + ); + Ok(()) } } @@ -496,24 +534,37 @@ impl SignersContainer { .filter_map(|secret| secret.as_public(secp).ok().map(|public| (public, secret))) .collect() } -} -impl From for SignersContainer { - fn from(keymap: KeyMap) -> SignersContainer { - let secp = Secp256k1::new(); + /// Build a new signer container from a [`KeyMap`] + /// + /// Also looks at the corresponding descriptor to determine the [`SignerContext`] to attach to + /// the signers + pub fn build( + keymap: KeyMap, + descriptor: &Descriptor, + secp: &SecpCtx, + ) -> SignersContainer { let mut container = SignersContainer::new(); - for (_, secret) in keymap { + for (pubkey, secret) in keymap { + let ctx = match descriptor { + Descriptor::Tr(tr) => SignerContext::Tap { + is_internal_key: tr.internal_key() == &pubkey, + }, + _ if descriptor.is_witness() => SignerContext::Segwitv0, + _ => SignerContext::Legacy, + }; + match secret { DescriptorSecretKey::SinglePriv(private_key) => container.add_external( - SignerId::from(private_key.key.public_key(&secp).to_pubkeyhash()), + SignerId::from(private_key.key.public_key(secp).to_pubkeyhash()), SignerOrdering::default(), - Arc::new(private_key.key), + Arc::new(SignerWrapper::new(private_key.key, ctx)), ), DescriptorSecretKey::XPrv(xprv) => container.add_external( - SignerId::from(xprv.root_fingerprint(&secp)), + SignerId::from(xprv.root_fingerprint(secp)), SignerOrdering::default(), - Arc::new(xprv), + Arc::new(SignerWrapper::new(xprv, ctx)), ), }; } @@ -869,11 +920,11 @@ mod signers_container_tests { let (prvkey1, _, _) = setup_keys(TPRV0_STR); let (prvkey2, _, _) = setup_keys(TPRV1_STR); let desc = descriptor!(sh(multi(2, prvkey1, prvkey2))).unwrap(); - let (_, keymap) = desc + let (wallet_desc, keymap) = desc .into_wallet_descriptor(&secp, Network::Testnet) .unwrap(); - let signers = SignersContainer::from(keymap); + let signers = SignersContainer::build(keymap, &wallet_desc, &secp); assert_eq!(signers.ids().len(), 2); let signers = signers.signers();