From 41e8fb0863e5c09f1ad96acaf7d74df9cc504c1f Mon Sep 17 00:00:00 2001 From: yu Date: Thu, 1 Feb 2024 15:37:26 +0800 Subject: [PATCH] Support signing taproot in psbt --- bitcoin/examples/ecdsa-psbt.rs | 2 +- bitcoin/src/psbt/mod.rs | 211 +++++++++++++++-- bitcoin/tests/{psbt.rs => bip_174.rs} | 0 bitcoin/tests/psbt-sign-taproot.rs | 316 ++++++++++++++++++++++++++ 4 files changed, 512 insertions(+), 17 deletions(-) rename bitcoin/tests/{psbt.rs => bip_174.rs} (100%) create mode 100644 bitcoin/tests/psbt-sign-taproot.rs diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs index fa5b068e..b5984c8e 100644 --- a/bitcoin/examples/ecdsa-psbt.rs +++ b/bitcoin/examples/ecdsa-psbt.rs @@ -133,7 +133,7 @@ impl ColdStorage { fn master_fingerprint(&self) -> Fingerprint { self.master_xpub.fingerprint() } /// Signs `psbt` with this signer. - fn sign_psbt(&self, secp: &Secp256k1, mut psbt: Psbt) -> Result { + fn sign_psbt(&self, secp: &Secp256k1, mut psbt: Psbt) -> Result { match psbt.sign(&self.master_xpriv, secp) { Ok(keys) => assert_eq!(keys.len(), 1), Err((_, e)) => { diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index 29a99f2c..8034946c 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -20,15 +20,17 @@ use std::collections::{HashMap, HashSet}; use hashes::Hash; use internals::write_err; -use secp256k1::{Message, Secp256k1, Signing}; +use secp256k1::{Keypair, Message, Secp256k1, Signing, Verification}; use crate::bip32::{self, KeySource, Xpriv, Xpub}; use crate::blockdata::transaction::{self, Transaction, TxOut}; -use crate::crypto::ecdsa; +use crate::crypto::{ecdsa, taproot}; use crate::crypto::key::{PrivateKey, PublicKey}; use crate::prelude::*; -use crate::sighash::{self, EcdsaSighashType, SighashCache}; -use crate::{Amount, FeeRate}; +use crate::sighash::{self, EcdsaSighashType, Prevouts, SighashCache}; +use crate::{Amount, FeeRate, TapSighashType}; +use crate::key::TapTweak; +use crate::TapLeafHash; #[rustfmt::skip] // Keep public re-exports separate. #[doc(inline)] @@ -303,7 +305,7 @@ impl Psbt { secp: &Secp256k1, ) -> Result where - C: Signing, + C: Signing + Verification, K: GetKey, { let tx = self.unsigned_tx.clone(); // clone because we need to mutably borrow when signing. @@ -313,16 +315,31 @@ impl Psbt { let mut errors = BTreeMap::new(); for i in 0..self.inputs.len() { - if let Ok(SigningAlgorithm::Ecdsa) = self.signing_algorithm(i) { - match self.bip32_sign_ecdsa(k, i, &mut cache, secp) { - Ok(v) => { - used.insert(i, v); - } - Err(e) => { - errors.insert(i, e); + match self.signing_algorithm(i) { + Ok(SigningAlgorithm::Ecdsa) => { + match self.bip32_sign_ecdsa(k, i, &mut cache, secp) { + Ok(v) => { + used.insert(i, v); + } + Err(e) => { + errors.insert(i, e); + } } } - }; + Ok(SigningAlgorithm::Schnorr) => { + match self.bip32_sign_schnorr(k, i, &mut cache, secp) { + Ok(v) => { + used.insert(i, v); + } + Err(e) => { + errors.insert(i, e); + } + } + } + Err(e) => { + errors.insert(i, e); + } + } } if errors.is_empty() { Ok(used) @@ -385,6 +402,102 @@ impl Psbt { Ok(used) } + /// Attempts to create all signatures required by this PSBT's `tap_key_origins` field, adding + /// them to `tap_key_sig` or `tap_script_sigs`. + /// + /// # Returns + /// + /// - Ok: A list of the public keys used in signing. + /// - Err: Error encountered trying to calculate the sighash AND we had the signing key. Also panics + /// if input_index is out of bounds. + fn bip32_sign_schnorr( + &mut self, + k: &K, + input_index: usize, + cache: &mut SighashCache, + secp: &Secp256k1, + ) -> Result, SignError> + where + C: Signing + Verification, + T: Borrow, + K: GetKey, + { + + let mut input = self.inputs[input_index].clone(); + + let mut used = vec![]; // List of pubkeys used to sign the input. + + for (&xonly, (leaf_hashes, key_source)) in input.tap_key_origins.iter() { + let sk = if let Ok(Some(secret_key)) = k.get_key(KeyRequest::Bip32(key_source.clone()), secp) { + secret_key + } else { + continue; + }; + + // Considering the responsibility of the PSBT's finalizer to extract valid signatures, + // the goal of this algorithm is to provide signatures to the best of our ability: + // 1) If the conditions for key path spend are met, proceed to provide the signature for key path spend + // 2) If the conditions for script path spend are met, proceed to provide the signature for script path spend + + // key path spend + if let Some(internal_key) = input.tap_internal_key { + + // BIP 371: The internal key does not have leaf hashes, so can be indicated with a hashes len of 0. + + // Based on input.tap_internal_key.is_some() alone, it is not sufficient to determine whether it is a key path spend. + // According to BIP 371, we also need to consider the condition leaf_hashes.is_empty() for a more accurate determination. + if internal_key == xonly && leaf_hashes.is_empty() && input.tap_key_sig.is_none() { + let (msg, sighash_type) = self.sighash_taproot(input_index, cache, None)?; + let key_pair = Keypair::from_secret_key(secp, &sk.inner) + .tap_tweak(secp, input.tap_merkle_root) + .to_inner(); + + #[cfg(feature = "rand-std")] + let signature = secp.sign_schnorr(&msg, &key_pair); + #[cfg(not(feature = "rand-std"))] + let signature = secp.sign_schnorr_no_aux_rand(&msg, &key_pair); + + let signature = taproot::Signature { signature, sighash_type }; + input.tap_key_sig = Some(signature); + + used.push(sk.public_key(secp)); + } + } + + // script path spend + if let Some((leaf_hashes, _)) = input.tap_key_origins.get(&xonly) { + let leaf_hashes = leaf_hashes + .iter() + .filter(|lh| !input.tap_script_sigs.contains_key(&(xonly, **lh))) + .cloned() + .collect::>(); + + if !leaf_hashes.is_empty() { + + let key_pair = Keypair::from_secret_key(secp, &sk.inner); + + for lh in leaf_hashes { + let (msg, sighash_type) = self.sighash_taproot(input_index, cache, Some(lh))?; + + #[cfg(feature = "rand-std")] + let signature = secp.sign_schnorr(&msg, &key_pair); + #[cfg(not(feature = "rand-std"))] + let signature = secp.sign_schnorr_no_aux_rand(&msg, &key_pair); + + let signature = taproot::Signature { signature, sighash_type }; + input.tap_script_sigs.insert((xonly, lh), signature); + } + + used.push(sk.public_key(secp)); + } + } + } + + self.inputs[input_index] = input; + + Ok(used) + } + /// Returns the sighash message to sign an ECDSA input along with the sighash type. /// /// Uses the [`EcdsaSighashType`] from this input if one is specified. If no sighash type is @@ -446,6 +559,64 @@ impl Psbt { } } + /// Returns the sighash message to sign an SCHNORR input along with the sighash type. + /// + /// Uses the [`TapSighashType`] from this input if one is specified. If no sighash type is + /// specified uses [`TapSighashType::Default`]. + fn sighash_taproot>( + &self, + input_index: usize, + cache: &mut SighashCache, + leaf_hash: Option + ) -> Result<(Message, TapSighashType), SignError> { + use OutputType::*; + + if self.signing_algorithm(input_index)? != SigningAlgorithm::Schnorr { + return Err(SignError::WrongSigningAlgorithm); + } + + let input = self.checked_input(input_index)?; + + match self.output_type(input_index)? { + Tr => { + let hash_ty = input + .sighash_type + .unwrap_or_else(|| TapSighashType::Default.into()) + .taproot_hash_ty() + .map_err(|_| SignError::InvalidSighashType)?; + + let spend_utxos = (0..self.inputs.len()) + .map(|i| self.spend_utxo(i).ok()) + .collect::>(); + let all_spend_utxos; + + let is_anyone_can_pay = PsbtSighashType::from(hash_ty).to_u32() & 0x80 != 0; + + let prev_outs = if is_anyone_can_pay { + Prevouts::One( + input_index, + spend_utxos[input_index].ok_or(SignError::MissingSpendUtxo)?, + ) + } else if spend_utxos.iter().all(Option::is_some) { + all_spend_utxos = spend_utxos.iter().filter_map(|x| *x).collect::>(); + Prevouts::All(&all_spend_utxos) + } else { + return Err(SignError::MissingSpendUtxo); + }; + + let sighash = if let Some(leaf_hash) = leaf_hash { + cache.taproot_script_spend_signature_hash(input_index, &prev_outs, leaf_hash, hash_ty)? + } else { + cache.taproot_key_spend_signature_hash(input_index, &prev_outs, hash_ty)? + }; + Ok((Message::from(sighash), hash_ty)) + } + _ => { + Err(SignError::Unsupported) + } + } + } + /// Returns the spending utxo for this PSBT's input at `input_index`. pub fn spend_utxo(&self, input_index: usize) -> Result<&TxOut, SignError> { let input = self.checked_input(input_index)?; @@ -774,6 +945,8 @@ pub enum SignError { SegwitV0Sighash(transaction::InputsIndexError), /// Sighash computation error (p2wpkh input). P2wpkhSighash(sighash::P2wpkhError), + /// Sighash computation error (taproot input). + TaprootError(sighash::TaprootError), /// Unable to determine the output type. UnknownOutputType, /// Unable to find key. @@ -800,6 +973,7 @@ impl fmt::Display for SignError { NotWpkh => write!(f, "the scriptPubkey is not a P2WPKH script"), SegwitV0Sighash(ref e) => write_err!(f, "segwit v0 sighash"; e), P2wpkhSighash(ref e) => write_err!(f, "p2wpkh sighash"; e), + TaprootError(ref e) => write_err!(f, "taproot sighash"; e), UnknownOutputType => write!(f, "unable to determine the output type"), KeyNotFound => write!(f, "unable to find key"), WrongSigningAlgorithm => @@ -817,6 +991,7 @@ impl std::error::Error for SignError { match *self { SegwitV0Sighash(ref e) => Some(e), P2wpkhSighash(ref e) => Some(e), + TaprootError(ref e) => Some(e), IndexOutOfBounds(ref e) => Some(e), InvalidSighashType | MissingInputUtxo @@ -842,6 +1017,10 @@ impl From for SignError { fn from(e: IndexOutOfBoundsError) -> Self { SignError::IndexOutOfBounds(e) } } +impl From for SignError { + fn from(e: sighash::TaprootError) -> Self { SignError::TaprootError(e) } +} + /// This error is returned when extracting a [`Transaction`] from a [`Psbt`]. #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] @@ -2091,9 +2270,9 @@ mod tests { }; psbt.inputs[1].witness_utxo = Some(txout_unknown_future); - let sigs = psbt.sign(&key_map, &secp).unwrap(); + let (signing_keys, _) = psbt.sign(&key_map, &secp).unwrap_err(); - assert!(sigs.len() == 1); - assert!(sigs[&0] == vec![pk]); + assert_eq!(signing_keys.len(), 1); + assert_eq!(signing_keys[&0], vec![pk]); } } diff --git a/bitcoin/tests/psbt.rs b/bitcoin/tests/bip_174.rs similarity index 100% rename from bitcoin/tests/psbt.rs rename to bitcoin/tests/bip_174.rs diff --git a/bitcoin/tests/psbt-sign-taproot.rs b/bitcoin/tests/psbt-sign-taproot.rs new file mode 100644 index 00000000..e1aad558 --- /dev/null +++ b/bitcoin/tests/psbt-sign-taproot.rs @@ -0,0 +1,316 @@ +#![cfg(not(feature = "rand-std"))] + +use std::collections::BTreeMap; +use std::str::FromStr; +use secp256k1::{Keypair, Signing, Secp256k1, XOnlyPublicKey}; +use bitcoin::{absolute, Address, Network, OutPoint, PrivateKey, Psbt, script, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Witness}; +use bitcoin::bip32::{DerivationPath, Fingerprint}; +use bitcoin::consensus::encode::serialize_hex; +use bitcoin::opcodes::all::OP_CHECKSIG; +use bitcoin::psbt::{GetKey, Input, KeyRequest, PsbtSighashType, SignError}; +use bitcoin::taproot::{LeafVersion, TaprootBuilder, TaprootSpendInfo}; +use bitcoin::transaction::Version; +use units::Amount; + + +#[test] +fn psbt_sign_taproot() { + + struct Keystore { + sk: PrivateKey, + mfp: Fingerprint, + } + + impl GetKey for Keystore { + type Error = SignError; + fn get_key(&self, key_request: KeyRequest, _secp: &Secp256k1) -> Result, Self::Error> { + match key_request { + KeyRequest::Bip32((mfp, _)) => { + if mfp == self.mfp { + Ok(Some(self.sk)) + } else { + Err(SignError::KeyNotFound) + } + } + _ => Err(SignError::KeyNotFound) + } + } + } + + let secp = &Secp256k1::new(); + + let sk_path = [ + ("dff1c8c2c016a572914b4c5adb8791d62b4768ae9d0a61be8ab94cf5038d7d90", "86'/1'/0'/0/0"), + ("1ede31b0e7e47c2afc65ffd158b1b1b9d3b752bba8fd117dc8b9e944a390e8d9", "86'/1'/0'/0/1"), + ("1fb777f1a6fb9b76724551f8bc8ad91b77f33b8c456d65d746035391d724922a", "86'/1'/0'/0/2"), + ]; + let mfp = "73c5da0a"; + + // + // Step 0: Create P2TR address. + // + + // Create three basic scripts to test script path spend. + let script1 = create_basic_single_sig_script(secp, sk_path[0].0); // m/86'/1'/0'/0/0 + let script2 = create_basic_single_sig_script(secp, sk_path[1].0); // m/86'/1'/0'/0/1 + let script3 = create_basic_single_sig_script(secp, sk_path[2].0); // m/86'/1'/0'/0/2 + + // Just use one of the secret keys for the key path spend. + let kp = Keypair::from_seckey_str(secp, &sk_path[2].0).expect("failed to create keypair"); + + let internal_key = kp.x_only_public_key().0; // Ignore the parity. + + let tree = create_taproot_tree(secp, script1.clone(), script2.clone(), script3.clone(), internal_key); + + let address = create_p2tr_address(tree.clone()); + assert_eq!("tb1pytee2mxz0f4fkrsqqws2lsgnkp8nrw2atjkjy2n9gahggsphr0gszaxxmv", address.to_string()); + + // m/86'/1'/0'/0/7 + let to_address = "tb1pyfv094rr0vk28lf8v9yx3veaacdzg26ztqk4ga84zucqqhafnn5q9my9rz"; + let to_address = Address::from_str(to_address).unwrap().assume_checked(); + + // key path spend + { + // + // Step 1: create psbt for key path spend. + // + let mut psbt_key_path_spend = create_psbt_for_taproot_key_path_spend(address.clone(), to_address.clone(), tree.clone()); + + // + // Step 2: sign psbt. + // + let keystore = Keystore { + mfp: Fingerprint::from_str(mfp).unwrap(), + sk: PrivateKey::new(kp.secret_key(), Network::Testnet), + }; + let _ = psbt_key_path_spend.sign(&keystore, secp); + + let sig = "92864dc9e56b6260ecbd54ec16b94bb597a2e6be7cca0de89d75e17921e0e1528cba32dd04217175c237e1835b5db1c8b384401718514f9443dce933c6ba9c87"; + assert_eq!(sig, psbt_key_path_spend.inputs[0].tap_key_sig.unwrap().signature.to_string()); + + // + // Step 3: finalize psbt. + // + let final_psbt = finalize_psbt_for_key_path_spend(psbt_key_path_spend); + let tx = final_psbt.extract_tx().unwrap(); + + let tx_id = "5306516f2032d9f34c9f2f6d2b1b8ad2486ef1ba196d8d8d780e59773e48ad6d"; + assert_eq!(tx_id, tx.compute_txid().to_string()); + + let tx_bytes = "020000000001013aee4d6b51da574900e56d173041115bd1e1d01d4697a845784cf716a10c98060000000000ffffffff0100190000000000002251202258f2d4637b2ca3fd27614868b33dee1a242b42582d5474f51730005fa99ce8014092864dc9e56b6260ecbd54ec16b94bb597a2e6be7cca0de89d75e17921e0e1528cba32dd04217175c237e1835b5db1c8b384401718514f9443dce933c6ba9c8700000000"; + let tx_hex = serialize_hex(&tx); + assert_eq!(tx_bytes, tx_hex); + } + + // script path spend + { + // use private key of path "m/86'/1'/0'/0/1" as signing key + let kp = Keypair::from_seckey_str(secp, &sk_path[1].0).expect("failed to create keypair"); + let x_only_pubkey = kp.x_only_public_key().0; + let signing_key_path = sk_path[1].1; + + let keystore = Keystore { + mfp: Fingerprint::from_str(mfp).unwrap(), + sk: PrivateKey::new(kp.secret_key(), Network::Testnet), + }; + + // + // Step 1: create psbt for script path spend. + // + let mut psbt_script_path_spend = create_psbt_for_taproot_script_path_spend(address.clone(), to_address.clone(), tree.clone(), x_only_pubkey, signing_key_path, script2.clone()); + + // + // Step 2: sign psbt. + // + let _ = psbt_script_path_spend.sign(&keystore, secp); + + let sig = "9c1466e1631a58c55fcb8642ce5f7896314f4b565d92c5c80b17aa9abf56d22e0b5e5dcbcfe836bbd7d409491f58aa9e1f68a491ef8f05eef62fb50ffac85727"; + assert_eq!(sig, psbt_script_path_spend.inputs[0].tap_script_sigs.get(&(x_only_pubkey, script2.clone().tapscript_leaf_hash())).unwrap().signature.to_string()); + + // + // Step 3: finalize psbt. + // + let final_psbt = finalize_psbt_for_script_path_spend(psbt_script_path_spend); + let tx = final_psbt.extract_tx().unwrap(); + + let tx_id = "a51f723beffc810248809355ba9c9e4b39c6e55c08429f0aeaa79b73f18bc2a6"; + assert_eq!(tx_id, tx.compute_txid().to_string()); + + let tx_hex = serialize_hex(&tx); + let tx_bytes = "0200000000010176a3c94a6b21d742e8ca192130ad10fdfc4c83510cb6baba8572a5fc70677c9d0000000000ffffffff0170170000000000002251202258f2d4637b2ca3fd27614868b33dee1a242b42582d5474f51730005fa99ce803419c1466e1631a58c55fcb8642ce5f7896314f4b565d92c5c80b17aa9abf56d22e0b5e5dcbcfe836bbd7d409491f58aa9e1f68a491ef8f05eef62fb50ffac857270122203058679f6d60b87ef921d98a2a9a1f1e0779dae27bedbd1cdb2f147a07835ac9ac61c1b68df382cad577d8304d5a8e640c3cb42d77c10016ab754caa4d6e68b6cb296d9b9d92a717ebeba858f75182936f0da5a7aecc434b0eebb2dc8a6af5409422ccf87f124e735a592a8ff390a68f6f05469ba8422e246dc78b0b57cd1576ffa98c00000000"; + assert_eq!(tx_bytes, tx_hex); + } +} + +fn create_basic_single_sig_script(secp: &Secp256k1::, sk: &str) -> ScriptBuf { + let kp = Keypair::from_seckey_str(secp, sk).expect("failed to create keypair"); + let x_only_pubkey = kp.x_only_public_key().0; + script::Builder::new() + .push_slice(x_only_pubkey.serialize()) + .push_opcode(OP_CHECKSIG) + .into_script() +} + +fn create_taproot_tree(secp: &Secp256k1::, script1: ScriptBuf, script2: ScriptBuf, script3: ScriptBuf, internal_key: XOnlyPublicKey) -> TaprootSpendInfo { + let builder = TaprootBuilder::new(); + let builder = builder.add_leaf(2, script1).unwrap(); + let builder = builder.add_leaf(2, script2).unwrap(); + let builder = builder.add_leaf(1, script3).unwrap(); + builder.finalize(secp, internal_key).unwrap() +} + +fn create_p2tr_address(tree: TaprootSpendInfo) -> Address { + let output_key = tree.output_key(); + Address::p2tr_tweaked(output_key, Network::Testnet) +} + +fn create_psbt_for_taproot_key_path_spend(from_address: Address, to_address: Address, tree: TaprootSpendInfo) -> Psbt { + + let send_value = 6400; + let out_puts = vec![ + TxOut { value: Amount::from_sat(send_value), script_pubkey: to_address.script_pubkey() }, + ]; + let prev_tx_id = "06980ca116f74c7845a897461dd0e1d15b114130176de5004957da516b4dee3a"; + + let transaction = Transaction { + version: Version(2), + lock_time: absolute::LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint { txid: prev_tx_id.parse().unwrap(), vout: 0 }, + script_sig: ScriptBuf::new(), + sequence: Sequence(0xFFFFFFFF), // Ignore nSequence. + witness: Witness::default(), + }], + output: out_puts, + }; + + let mut psbt = Psbt::from_unsigned_tx(transaction).unwrap(); + + + let mfp = "73c5da0a"; + let internal_key_path = "86'/1'/0'/0/2"; + + let mut origins = BTreeMap::new(); + origins.insert( + tree.internal_key(), + ( + vec![], + ( + Fingerprint::from_str(mfp).unwrap(), + DerivationPath::from_str(internal_key_path).unwrap(), + ), + ), + ); + + let utxo_value = 6588; + let mut input = Input { + witness_utxo: { + let script_pubkey = from_address.script_pubkey(); + Some(TxOut { value: Amount::from_sat(utxo_value), script_pubkey }) + }, + tap_key_origins: origins, + ..Default::default() + }; + let ty = PsbtSighashType::from_str("SIGHASH_DEFAULT").unwrap(); + input.sighash_type = Some(ty); + input.tap_internal_key = Some(tree.internal_key()); + input.tap_merkle_root = tree.merkle_root(); + psbt.inputs = vec![input]; + psbt +} + +fn finalize_psbt_for_key_path_spend(mut psbt: Psbt) -> Psbt { + psbt.inputs.iter_mut().for_each(|input| { + let mut script_witness: Witness = Witness::new(); + script_witness.push(input.tap_key_sig.unwrap().to_vec()); + input.final_script_witness = Some(script_witness); + input.partial_sigs = BTreeMap::new(); + input.sighash_type = None; + input.redeem_script = None; + input.witness_script = None; + input.bip32_derivation = BTreeMap::new(); + }); + psbt +} + +fn create_psbt_for_taproot_script_path_spend(from_address: Address, to_address: Address, tree: TaprootSpendInfo, x_only_pubkey_of_signing_key: XOnlyPublicKey, signing_key_path: &str, use_script: ScriptBuf) -> Psbt { + let utxo_value = 6280; + let send_value = 6000; + let mfp = "73c5da0a"; + + let out_puts = vec![ + TxOut { value: Amount::from_sat(send_value), script_pubkey: to_address.script_pubkey() }, + ]; + let prev_tx_id = "9d7c6770fca57285babab60c51834cfcfd10ad302119cae842d7216b4ac9a376"; + let transaction = Transaction { + version: Version(2), + lock_time: absolute::LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint { txid: prev_tx_id.parse().unwrap(), vout: 0 }, + script_sig: ScriptBuf::new(), + sequence: Sequence(0xFFFFFFFF), // Ignore nSequence. + witness: Witness::default(), + }], + output: out_puts, + }; + + let mut psbt = Psbt::from_unsigned_tx(transaction).unwrap(); + + let mut origins = BTreeMap::new(); + origins.insert( + x_only_pubkey_of_signing_key, + ( + vec![use_script.tapscript_leaf_hash()], + ( + Fingerprint::from_str(mfp).unwrap(), + DerivationPath::from_str(signing_key_path).unwrap(), + ), + ), + ); + + let mut tap_scripts = BTreeMap::new(); + tap_scripts.insert( + tree.control_block(&(use_script.clone(), LeafVersion::TapScript)).unwrap(), + (use_script.clone(), LeafVersion::TapScript), + ); + + let mut input = Input { + witness_utxo: { + let script_pubkey= from_address.script_pubkey(); + Some(TxOut { value: Amount::from_sat(utxo_value), script_pubkey }) + }, + tap_key_origins: origins, + tap_scripts, + ..Default::default() + }; + let ty = PsbtSighashType::from_str("SIGHASH_ALL").unwrap(); + input.sighash_type = Some(ty); + input.tap_internal_key = Some(tree.internal_key()); + input.tap_merkle_root = tree.merkle_root(); + psbt.inputs = vec![input]; + psbt +} + + +fn finalize_psbt_for_script_path_spend(mut psbt: Psbt) -> Psbt { + psbt.inputs.iter_mut().for_each(|input| { + let mut script_witness: Witness = Witness::new(); + for (_, signature) in input.tap_script_sigs.iter() { + script_witness.push(signature.to_vec()); + } + for (control_block, (script, _)) in input.tap_scripts.iter() { + script_witness.push(script.to_bytes()); + script_witness.push(control_block.serialize()); + } + input.final_script_witness = Some(script_witness); + input.partial_sigs = BTreeMap::new(); + input.sighash_type = None; + input.redeem_script = None; + input.witness_script = None; + input.bip32_derivation = BTreeMap::new(); + input.tap_script_sigs = BTreeMap::new(); + input.tap_scripts = BTreeMap::new(); + input.tap_key_sig = None; + }); + psbt +}