use bdk_chain::{bitcoin, collections::*, miniscript}; use core::ops::Deref; use bitcoin::{ bip32, hashes::{hash160, ripemd160, sha256}, key::XOnlyPublicKey, psbt::Prevouts, secp256k1::{KeyPair, Message, PublicKey, Signing, Verification}, sighash, sighash::{EcdsaSighashType, SighashCache, TapSighashType}, taproot, Transaction, TxOut, }; use super::*; use miniscript::{ descriptor::{DescriptorSecretKey, KeyMap}, hash256, }; #[derive(Clone, Debug)] /// Signatures and hash pre-images that must be provided to complete the plan. pub struct Requirements { /// required signatures pub signatures: RequiredSignatures, /// required sha256 pre-images pub sha256_images: HashSet, /// required hash160 pre-images pub hash160_images: HashSet, /// required hash256 pre-images pub hash256_images: HashSet, /// required ripemd160 pre-images pub ripemd160_images: HashSet, } impl Default for RequiredSignatures { fn default() -> Self { RequiredSignatures::Legacy { keys: Default::default(), } } } impl Default for Requirements { fn default() -> Self { Self { signatures: Default::default(), sha256_images: Default::default(), hash160_images: Default::default(), hash256_images: Default::default(), ripemd160_images: Default::default(), } } } impl Requirements { /// Whether any hash pre-images are required in the plan pub fn requires_hash_preimages(&self) -> bool { !(self.sha256_images.is_empty() && self.hash160_images.is_empty() && self.hash256_images.is_empty() && self.ripemd160_images.is_empty()) } } /// The signatures required to complete the plan #[derive(Clone, Debug)] pub enum RequiredSignatures { /// Legacy ECDSA signatures are required Legacy { keys: Vec> }, /// Segwitv0 ECDSA signatures are required Segwitv0 { keys: Vec> }, /// A Taproot key spend signature is required TapKey { /// the internal key plan_key: PlanKey, /// The merkle root of the taproot output merkle_root: Option, }, /// Taproot script path signatures are required TapScript { /// The leaf hash of the script being used leaf_hash: TapLeafHash, /// The keys in the script that require signatures plan_keys: Vec>, }, } #[derive(Clone, Debug)] pub enum SigningError { SigHashError(sighash::Error), DerivationError(bip32::Error), } impl From for SigningError { fn from(e: sighash::Error) -> Self { Self::SigHashError(e) } } impl core::fmt::Display for SigningError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { SigningError::SigHashError(e) => e.fmt(f), SigningError::DerivationError(e) => e.fmt(f), } } } impl From for SigningError { fn from(e: bip32::Error) -> Self { Self::DerivationError(e) } } #[cfg(feature = "std")] impl std::error::Error for SigningError {} impl RequiredSignatures { pub fn sign_with_keymap>( &self, input_index: usize, keymap: &KeyMap, prevouts: &Prevouts<'_, impl core::borrow::Borrow>, schnorr_sighashty: Option, _ecdsa_sighashty: Option, sighash_cache: &mut SighashCache, auth_data: &mut SatisfactionMaterial, secp: &Secp256k1, ) -> Result { match self { RequiredSignatures::Legacy { .. } | RequiredSignatures::Segwitv0 { .. } => todo!(), RequiredSignatures::TapKey { plan_key, merkle_root, } => { let schnorr_sighashty = schnorr_sighashty.unwrap_or(TapSighashType::Default); let sighash = sighash_cache.taproot_key_spend_signature_hash( input_index, prevouts, schnorr_sighashty, )?; let secret_key = match keymap.get(&plan_key.asset_key) { Some(secret_key) => secret_key, None => return Ok(false), }; let secret_key = match secret_key { DescriptorSecretKey::Single(single) => single.key.inner, DescriptorSecretKey::XPrv(xprv) => { xprv.xkey .derive_priv(&secp, &plan_key.derivation_hint)? .private_key } DescriptorSecretKey::MultiXPrv(_) => { // This crate will be replaced by // https://github.com/rust-bitcoin/rust-miniscript/pull/481 anyways todo!(); } }; let pubkey = PublicKey::from_secret_key(&secp, &secret_key); let x_only_pubkey = XOnlyPublicKey::from(pubkey); let tweak = taproot::TapTweakHash::from_key_and_tweak(x_only_pubkey, merkle_root.clone()); let keypair = KeyPair::from_secret_key(&secp, &secret_key.clone()) .add_xonly_tweak(&secp, &tweak.to_scalar()) .unwrap(); let msg = Message::from_slice(sighash.as_ref()).expect("Sighashes are 32 bytes"); let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair); let bitcoin_sig = taproot::Signature { sig, hash_ty: schnorr_sighashty, }; auth_data .schnorr_sigs .insert(plan_key.descriptor_key.clone(), bitcoin_sig); Ok(true) } RequiredSignatures::TapScript { leaf_hash, plan_keys, } => { let sighash_type = schnorr_sighashty.unwrap_or(TapSighashType::Default); let sighash = sighash_cache.taproot_script_spend_signature_hash( input_index, prevouts, *leaf_hash, sighash_type, )?; let mut modified = false; for plan_key in plan_keys { if let Some(secret_key) = keymap.get(&plan_key.asset_key) { let secret_key = match secret_key { DescriptorSecretKey::Single(single) => single.key.inner, DescriptorSecretKey::XPrv(xprv) => { xprv.xkey .derive_priv(&secp, &plan_key.derivation_hint)? .private_key } DescriptorSecretKey::MultiXPrv(_) => { // This crate will be replaced by // https://github.com/rust-bitcoin/rust-miniscript/pull/481 anyways todo!(); } }; let keypair = KeyPair::from_secret_key(&secp, &secret_key.clone()); let msg = Message::from_slice(sighash.as_ref()).expect("Sighashes are 32 bytes"); let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair); let bitcoin_sig = taproot::Signature { sig, hash_ty: sighash_type, }; auth_data .schnorr_sigs .insert(plan_key.descriptor_key.clone(), bitcoin_sig); modified = true; } } Ok(modified) } } } }