From f9d34673976e2115e86166245c8bb9255e1a1cc1 Mon Sep 17 00:00:00 2001 From: Justin Moon Date: Mon, 16 Nov 2020 16:25:16 -0600 Subject: [PATCH] [wallet] Add witness and redeem scripts to PSBT outputs --- src/wallet/mod.rs | 23 +++++++++++++++++++++++ src/wallet/tx_builder.rs | 12 ++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 4759b5a0..8d7530d5 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -1220,6 +1220,11 @@ where { let (desc, _) = self.get_descriptor_for_script_type(script_type); psbt_output.hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?; + if builder.include_output_redeem_witness_script { + let derived_descriptor = desc.derive(ChildNumber::from_normal_idx(child)?); + psbt_output.witness_script = derived_descriptor.psbt_witness_script(&self.secp); + psbt_output.redeem_script = derived_descriptor.psbt_redeem_script(&self.secp); + }; } } @@ -3231,4 +3236,22 @@ mod test { let extracted = signed_psbt.extract_tx(); assert_eq!(extracted.input[0].witness.len(), 2); } + + #[test] + fn test_include_output_redeem_witness_script() { + let (wallet, _, _) = get_funded_wallet("sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))"); + let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); + let (psbt, _) = wallet + .create_tx( + TxBuilder::with_recipients(vec![(addr.script_pubkey(), 45_000)]) + .include_output_redeem_witness_script(), + ) + .unwrap(); + + // p2sh-p2wsh transaction should contain both witness and redeem scripts + assert!(psbt + .outputs + .iter() + .any(|output| output.redeem_script.is_some() && output.witness_script.is_some())); + } } diff --git a/src/wallet/tx_builder.rs b/src/wallet/tx_builder.rs index 672602f7..393797e5 100644 --- a/src/wallet/tx_builder.rs +++ b/src/wallet/tx_builder.rs @@ -90,6 +90,7 @@ pub struct TxBuilder, Ctx: TxBuilderC pub(crate) change_policy: ChangeSpendPolicy, pub(crate) force_non_witness_utxo: bool, pub(crate) coin_selection: Cs, + pub(crate) include_output_redeem_witness_script: bool, phantom: PhantomData<(D, Ctx)>, } @@ -131,6 +132,7 @@ where change_policy: Default::default(), force_non_witness_utxo: Default::default(), coin_selection: Default::default(), + include_output_redeem_witness_script: Default::default(), phantom: PhantomData, } @@ -374,10 +376,20 @@ impl, Ctx: TxBuilderContext> TxBuilde change_policy: self.change_policy, force_non_witness_utxo: self.force_non_witness_utxo, coin_selection, + include_output_redeem_witness_script: self.include_output_redeem_witness_script, phantom: PhantomData, } } + + /// Fill-in the [`psbt::Output::redeem_script`](bitcoin::util::psbt::Output::redeem_script) and + /// [`psbt::Output::witness_script`](bitcoin::util::psbt::Output::witness_script) fields. + /// + /// This is useful for signers which always require it, like ColdCard hardware wallets. + pub fn include_output_redeem_witness_script(mut self) -> Self { + self.include_output_redeem_witness_script = true; + self + } } // methods supported only by create_tx, and only for `DefaultCoinSelectionAlgorithm`