Merge rust-bitcoin/rust-bitcoin#2458: Support signing taproot in psbt

41e8fb0863 Support signing taproot in psbt (yu)

Pull request description:

  Hi team, I'm from Keystone Wallet team. currently rust-bitcoin does not support signing taproot transactions in psbt.
  We think this founction should be included in the psbt module, we submit this PR. Some context and discussion about this PR can be found here: #2418.

  For this PR, mostly two new functions are introduced:

  - `bip32_sign_schnorr`:  sign a taproot input.
  - `sighash_taproot`: calculate the sighash message to sign a taproot input along with the sighash type.

  Looking forward to your feedback.

ACKs for top commit:
  tcharding:
    ACK 41e8fb0863
  sanket1729:
    ACK 41e8fb0863.

Tree-SHA512: 2eb14a3204e6ed848515483778dd7986662aacb332783d187da72d29e207b78a2d427939f2b958135a32de5459221385e6f1f5bae89f491b58d8bc79f202b724
This commit is contained in:
Andrew Poelstra 2024-03-15 07:48:09 +00:00
commit bf4783db47
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
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,16 +315,31 @@ 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) {
match self.bip32_sign_ecdsa(k, i, &mut cache, secp) { Ok(SigningAlgorithm::Ecdsa) => {
Ok(v) => { match self.bip32_sign_ecdsa(k, i, &mut cache, secp) {
used.insert(i, v); Ok(v) => {
} used.insert(i, v);
Err(e) => { }
errors.insert(i, e); 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() { 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)?;
@ -776,6 +947,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.
@ -804,6 +977,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 =>
@ -821,6 +995,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
@ -846,6 +1021,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]
@ -2101,9 +2280,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
}