Attach a context to our software signers

This allows the signer to know the signing context precisely without
relying on heuristics on the psbt fields.

Due to the context being static, we still have to look at the PSBT when
producing taproot signatures to determine the set of leaf hashes that
the key can sign for.
This commit is contained in:
Alekos Filini 2022-05-12 17:28:41 +02:00
parent 906598ad92
commit 1312184ed7
No known key found for this signature in database
GPG Key ID: 431401E4A4530061
3 changed files with 150 additions and 94 deletions

View File

@ -21,6 +21,7 @@
//! ``` //! ```
//! # use std::sync::Arc; //! # use std::sync::Arc;
//! # use bdk::descriptor::*; //! # use bdk::descriptor::*;
//! # use bdk::wallet::signer::*;
//! # use bdk::bitcoin::secp256k1::Secp256k1; //! # use bdk::bitcoin::secp256k1::Secp256k1;
//! use bdk::descriptor::policy::BuildSatisfaction; //! use bdk::descriptor::policy::BuildSatisfaction;
//! let secp = Secp256k1::new(); //! let secp = Secp256k1::new();
@ -29,7 +30,7 @@
//! let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(&secp, desc)?; //! let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(&secp, desc)?;
//! println!("{:?}", extended_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)?; //! let policy = extended_desc.extract_policy(&signers, BuildSatisfaction::None, &secp)?;
//! println!("policy: {}", serde_json::to_string(&policy)?); //! println!("policy: {}", serde_json::to_string(&policy)?);
//! # Ok::<(), bdk::Error>(()) //! # Ok::<(), bdk::Error>(())
@ -1109,7 +1110,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1124,7 +1125,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1148,7 +1149,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1180,7 +1181,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1212,7 +1213,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1244,7 +1245,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1277,7 +1278,7 @@ mod test {
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let single_key = wallet_desc.derive(0); 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 let policy = single_key
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1293,7 +1294,7 @@ mod test {
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let single_key = wallet_desc.derive(0); 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 let policy = single_key
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1320,7 +1321,7 @@ mod test {
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let single_key = wallet_desc.derive(0); 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 let policy = single_key
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1363,7 +1364,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1402,7 +1403,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1427,7 +1428,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1445,7 +1446,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
@ -1467,10 +1468,10 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers = keymap.into(); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let policy = wallet_desc let policy = wallet_desc
.extract_policy(&signers, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
@ -1533,7 +1534,7 @@ mod test {
addr.to_string() 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(); let psbt = Psbt::from_str(ALICE_SIGNED_PSBT).unwrap();
@ -1594,7 +1595,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap)); let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
let addr = wallet_desc let addr = wallet_desc
.as_derived(0, &secp) .as_derived(0, &secp)
@ -1682,7 +1683,7 @@ mod test {
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .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); let policy = wallet_desc.extract_policy(&signers_container, BuildSatisfaction::None, &secp);
assert!(policy.is_ok()); assert!(policy.is_ok());

View File

@ -197,7 +197,7 @@ where
KeychainKind::External, KeychainKind::External,
get_checksum(&descriptor.to_string())?.as_bytes(), 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 { let (change_descriptor, change_signers) = match change_descriptor {
Some(desc) => { Some(desc) => {
let (change_descriptor, change_keymap) = let (change_descriptor, change_keymap) =
@ -207,7 +207,11 @@ where
get_checksum(&change_descriptor.to_string())?.as_bytes(), 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()) { // if !parsed.same_structure(descriptor.as_ref()) {
// return Err(Error::DifferentDescriptorStructure); // return Err(Error::DifferentDescriptorStructure);
// } // }

View File

@ -82,23 +82,26 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use std::ops::Bound::Included; use std::ops::{Bound::Included, Deref};
use std::sync::Arc; use std::sync::Arc;
use bitcoin::blockdata::opcodes; use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::script::Builder as ScriptBuilder; use bitcoin::blockdata::script::Builder as ScriptBuilder;
use bitcoin::hashes::{hash160, Hash}; 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::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint};
use bitcoin::util::{ecdsa, psbt, schnorr, sighash, taproot}; use bitcoin::util::{ecdsa, psbt, schnorr, sighash, taproot};
use bitcoin::{secp256k1, XOnlyPublicKey}; use bitcoin::{secp256k1, XOnlyPublicKey};
use bitcoin::{EcdsaSighashType, PrivateKey, PublicKey, SchnorrSighashType, Script}; 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 miniscript::{Legacy, MiniscriptKey, Segwitv0, Tap};
use super::utils::SecpCtx; 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 /// Identifier of a signer in the `SignersContainers`. Used as a key to find the right signer among
/// multiple of them /// multiple of them
@ -171,6 +174,44 @@ impl fmt::Display for SignerError {
impl std::error::Error 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<S: Sized + fmt::Debug + Clone> {
signer: S,
ctx: SignerContext,
}
impl<S: Sized + fmt::Debug + Clone> SignerWrapper<S> {
/// Create a wrapped signer from a signer and a context
pub fn new(signer: S, ctx: SignerContext) -> Self {
SignerWrapper { signer, ctx }
}
}
impl<S: Sized + fmt::Debug + Clone> Deref for SignerWrapper<S> {
type Target = S;
fn deref(&self) -> &Self::Target {
&self.signer
}
}
/// Common signer methods /// Common signer methods
pub trait SignerCommon: fmt::Debug + Send + Sync { pub trait SignerCommon: fmt::Debug + Send + Sync {
/// Return the [`SignerId`] for this signer /// Return the [`SignerId`] for this signer
@ -231,17 +272,17 @@ impl<T: InputSigner> TransactionSigner for T {
} }
} }
impl SignerCommon for DescriptorXKey<ExtendedPrivKey> { impl SignerCommon for SignerWrapper<DescriptorXKey<ExtendedPrivKey>> {
fn id(&self, secp: &SecpCtx) -> SignerId { fn id(&self, secp: &SecpCtx) -> SignerId {
SignerId::from(self.root_fingerprint(secp)) SignerId::from(self.root_fingerprint(secp))
} }
fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> { fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> {
Some(DescriptorSecretKey::XPrv(self.clone())) Some(DescriptorSecretKey::XPrv(self.signer.clone()))
} }
} }
impl InputSigner for DescriptorXKey<ExtendedPrivKey> { impl InputSigner for SignerWrapper<DescriptorXKey<ExtendedPrivKey>> {
fn sign_input( fn sign_input(
&self, &self,
psbt: &mut psbt::PartiallySignedTransaction, psbt: &mut psbt::PartiallySignedTransaction,
@ -289,30 +330,31 @@ impl InputSigner for DescriptorXKey<ExtendedPrivKey> {
Err(SignerError::InvalidKey) Err(SignerError::InvalidKey)
} else { } else {
// HD wallets imply compressed keys // HD wallets imply compressed keys
PrivateKey { let priv_key = PrivateKey {
compressed: true, compressed: true,
network: self.xkey.network, network: self.xkey.network,
inner: derived_key.private_key, 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<PrivateKey> {
fn id(&self, secp: &SecpCtx) -> SignerId { fn id(&self, secp: &SecpCtx) -> SignerId {
SignerId::from(self.public_key(secp).to_pubkeyhash()) SignerId::from(self.public_key(secp).to_pubkeyhash())
} }
fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> { fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> {
Some(DescriptorSecretKey::SinglePriv(DescriptorSinglePriv { Some(DescriptorSecretKey::SinglePriv(DescriptorSinglePriv {
key: *self, key: self.signer,
origin: None, origin: None,
})) }))
} }
} }
impl InputSigner for PrivateKey { impl InputSigner for SignerWrapper<PrivateKey> {
fn sign_input( fn sign_input(
&self, &self,
psbt: &mut psbt::PartiallySignedTransaction, psbt: &mut psbt::PartiallySignedTransaction,
@ -331,11 +373,9 @@ impl InputSigner for PrivateKey {
let pubkey = PublicKey::from_private_key(secp, self); let pubkey = PublicKey::from_private_key(secp, self);
let x_only_pubkey = XOnlyPublicKey::from(pubkey.inner); 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 { if let SignerContext::Tap { is_internal_key } = self.ctx {
Some(k) if k == x_only_pubkey && psbt.inputs[input_index].tap_key_sig.is_none() => { if is_internal_key && psbt.inputs[input_index].tap_key_sig.is_none() {
let (hash, hash_ty) = Tap::sighash(psbt, input_index, None)?; let (hash, hash_ty) = Tap::sighash(psbt, input_index, None)?;
sign_psbt_schnorr( sign_psbt_schnorr(
&self.inner, &self.inner,
@ -347,56 +387,54 @@ impl InputSigner for PrivateKey {
secp, 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::<Vec<_>>();
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 let Some((leaf_hashes, _)) =
if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) { psbt.inputs[input_index].tap_key_origins.get(&x_only_pubkey)
return Ok(()); {
let leaf_hashes = leaf_hashes
.iter()
.filter(|lh| {
!psbt.inputs[input_index]
.tap_script_sigs
.contains_key(&(x_only_pubkey, **lh))
})
.cloned()
.collect::<Vec<_>>();
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 return Ok(());
// 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<Ctx: ScriptContex>() 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,
);
} }
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(()) Ok(())
} }
} }
@ -496,24 +534,37 @@ impl SignersContainer {
.filter_map(|secret| secret.as_public(secp).ok().map(|public| (public, secret))) .filter_map(|secret| secret.as_public(secp).ok().map(|public| (public, secret)))
.collect() .collect()
} }
}
impl From<KeyMap> for SignersContainer { /// Build a new signer container from a [`KeyMap`]
fn from(keymap: KeyMap) -> SignersContainer { ///
let secp = Secp256k1::new(); /// Also looks at the corresponding descriptor to determine the [`SignerContext`] to attach to
/// the signers
pub fn build(
keymap: KeyMap,
descriptor: &Descriptor<DescriptorPublicKey>,
secp: &SecpCtx,
) -> SignersContainer {
let mut container = SignersContainer::new(); 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 { match secret {
DescriptorSecretKey::SinglePriv(private_key) => container.add_external( 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(), SignerOrdering::default(),
Arc::new(private_key.key), Arc::new(SignerWrapper::new(private_key.key, ctx)),
), ),
DescriptorSecretKey::XPrv(xprv) => container.add_external( DescriptorSecretKey::XPrv(xprv) => container.add_external(
SignerId::from(xprv.root_fingerprint(&secp)), SignerId::from(xprv.root_fingerprint(secp)),
SignerOrdering::default(), 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 (prvkey1, _, _) = setup_keys(TPRV0_STR);
let (prvkey2, _, _) = setup_keys(TPRV1_STR); let (prvkey2, _, _) = setup_keys(TPRV1_STR);
let desc = descriptor!(sh(multi(2, prvkey1, prvkey2))).unwrap(); let desc = descriptor!(sh(multi(2, prvkey1, prvkey2))).unwrap();
let (_, keymap) = desc let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet) .into_wallet_descriptor(&secp, Network::Testnet)
.unwrap(); .unwrap();
let signers = SignersContainer::from(keymap); let signers = SignersContainer::build(keymap, &wallet_desc, &secp);
assert_eq!(signers.ids().len(), 2); assert_eq!(signers.ids().len(), 2);
let signers = signers.signers(); let signers = signers.signers();