Support signing taproot in psbt

This commit is contained in:
yu 2024-02-01 15:37:26 +08:00
parent 975ada3570
commit 41e8fb0863
4 changed files with 512 additions and 17 deletions

View File

@ -133,7 +133,7 @@ impl ColdStorage {
fn master_fingerprint(&self) -> Fingerprint { self.master_xpub.fingerprint() } fn master_fingerprint(&self) -> Fingerprint { self.master_xpub.fingerprint() }
/// Signs `psbt` with this signer. /// Signs `psbt` with this signer.
fn sign_psbt<C: Signing>(&self, secp: &Secp256k1<C>, mut psbt: Psbt) -> Result<Psbt> { fn sign_psbt<C: Signing + Verification>(&self, secp: &Secp256k1<C>, mut psbt: Psbt) -> Result<Psbt> {
match psbt.sign(&self.master_xpriv, secp) { match psbt.sign(&self.master_xpriv, secp) {
Ok(keys) => assert_eq!(keys.len(), 1), Ok(keys) => assert_eq!(keys.len(), 1),
Err((_, e)) => { Err((_, e)) => {

View File

@ -20,15 +20,17 @@ use std::collections::{HashMap, HashSet};
use hashes::Hash; use hashes::Hash;
use internals::write_err; 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::bip32::{self, KeySource, Xpriv, Xpub};
use crate::blockdata::transaction::{self, Transaction, TxOut}; use crate::blockdata::transaction::{self, Transaction, TxOut};
use crate::crypto::ecdsa; use crate::crypto::{ecdsa, taproot};
use crate::crypto::key::{PrivateKey, PublicKey}; use crate::crypto::key::{PrivateKey, PublicKey};
use crate::prelude::*; use crate::prelude::*;
use crate::sighash::{self, EcdsaSighashType, SighashCache}; use crate::sighash::{self, EcdsaSighashType, Prevouts, SighashCache};
use crate::{Amount, FeeRate}; use crate::{Amount, FeeRate, TapSighashType};
use crate::key::TapTweak;
use crate::TapLeafHash;
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)] #[doc(inline)]
@ -303,7 +305,7 @@ impl Psbt {
secp: &Secp256k1<C>, secp: &Secp256k1<C>,
) -> Result<SigningKeys, (SigningKeys, SigningErrors)> ) -> Result<SigningKeys, (SigningKeys, SigningErrors)>
where where
C: Signing, C: Signing + Verification,
K: GetKey, K: GetKey,
{ {
let tx = self.unsigned_tx.clone(); // clone because we need to mutably borrow when signing. let tx = self.unsigned_tx.clone(); // clone because we need to mutably borrow when signing.
@ -313,7 +315,8 @@ impl Psbt {
let mut errors = BTreeMap::new(); let mut errors = BTreeMap::new();
for i in 0..self.inputs.len() { for i in 0..self.inputs.len() {
if let Ok(SigningAlgorithm::Ecdsa) = self.signing_algorithm(i) { match self.signing_algorithm(i) {
Ok(SigningAlgorithm::Ecdsa) => {
match self.bip32_sign_ecdsa(k, i, &mut cache, secp) { match self.bip32_sign_ecdsa(k, i, &mut cache, secp) {
Ok(v) => { Ok(v) => {
used.insert(i, v); used.insert(i, v);
@ -322,7 +325,21 @@ impl Psbt {
errors.insert(i, 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() { if errors.is_empty() {
Ok(used) Ok(used)
@ -385,6 +402,102 @@ impl Psbt {
Ok(used) 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<C, K, T>(
&mut self,
k: &K,
input_index: usize,
cache: &mut SighashCache<T>,
secp: &Secp256k1<C>,
) -> Result<Vec<PublicKey>, SignError>
where
C: Signing + Verification,
T: Borrow<Transaction>,
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::<Vec<_>>();
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. /// 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 /// 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<T: Borrow<Transaction>>(
&self,
input_index: usize,
cache: &mut SighashCache<T>,
leaf_hash: Option<TapLeafHash>
) -> 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::<Vec<_>>();
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::<Vec<_>>();
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`. /// Returns the spending utxo for this PSBT's input at `input_index`.
pub fn spend_utxo(&self, input_index: usize) -> Result<&TxOut, SignError> { pub fn spend_utxo(&self, input_index: usize) -> Result<&TxOut, SignError> {
let input = self.checked_input(input_index)?; let input = self.checked_input(input_index)?;
@ -774,6 +945,8 @@ pub enum SignError {
SegwitV0Sighash(transaction::InputsIndexError), SegwitV0Sighash(transaction::InputsIndexError),
/// Sighash computation error (p2wpkh input). /// Sighash computation error (p2wpkh input).
P2wpkhSighash(sighash::P2wpkhError), P2wpkhSighash(sighash::P2wpkhError),
/// Sighash computation error (taproot input).
TaprootError(sighash::TaprootError),
/// Unable to determine the output type. /// Unable to determine the output type.
UnknownOutputType, UnknownOutputType,
/// Unable to find key. /// Unable to find key.
@ -800,6 +973,7 @@ impl fmt::Display for SignError {
NotWpkh => write!(f, "the scriptPubkey is not a P2WPKH script"), NotWpkh => write!(f, "the scriptPubkey is not a P2WPKH script"),
SegwitV0Sighash(ref e) => write_err!(f, "segwit v0 sighash"; e), SegwitV0Sighash(ref e) => write_err!(f, "segwit v0 sighash"; e),
P2wpkhSighash(ref e) => write_err!(f, "p2wpkh 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"), UnknownOutputType => write!(f, "unable to determine the output type"),
KeyNotFound => write!(f, "unable to find key"), KeyNotFound => write!(f, "unable to find key"),
WrongSigningAlgorithm => WrongSigningAlgorithm =>
@ -817,6 +991,7 @@ impl std::error::Error for SignError {
match *self { match *self {
SegwitV0Sighash(ref e) => Some(e), SegwitV0Sighash(ref e) => Some(e),
P2wpkhSighash(ref e) => Some(e), P2wpkhSighash(ref e) => Some(e),
TaprootError(ref e) => Some(e),
IndexOutOfBounds(ref e) => Some(e), IndexOutOfBounds(ref e) => Some(e),
InvalidSighashType InvalidSighashType
| MissingInputUtxo | MissingInputUtxo
@ -842,6 +1017,10 @@ impl From<IndexOutOfBoundsError> for SignError {
fn from(e: IndexOutOfBoundsError) -> Self { SignError::IndexOutOfBounds(e) } fn from(e: IndexOutOfBoundsError) -> Self { SignError::IndexOutOfBounds(e) }
} }
impl From<sighash::TaprootError> for SignError {
fn from(e: sighash::TaprootError) -> Self { SignError::TaprootError(e) }
}
/// This error is returned when extracting a [`Transaction`] from a [`Psbt`]. /// This error is returned when extracting a [`Transaction`] from a [`Psbt`].
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive] #[non_exhaustive]
@ -2091,9 +2270,9 @@ mod tests {
}; };
psbt.inputs[1].witness_utxo = Some(txout_unknown_future); 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_eq!(signing_keys.len(), 1);
assert!(sigs[&0] == vec![pk]); assert_eq!(signing_keys[&0], vec![pk]);
} }
} }

View File

@ -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<C: Signing>(&self, key_request: KeyRequest, _secp: &Secp256k1<C>) -> Result<Option<PrivateKey>, 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::<secp256k1::All>, 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::<secp256k1::All>, 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
}