From 3798b4d115f39ea418de300b3bdc401597413cf1 Mon Sep 17 00:00:00 2001 From: davemo88 Date: Mon, 15 Mar 2021 21:50:51 -0400 Subject: [PATCH 1/3] add get_psbt_input --- src/wallet/mod.rs | 132 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 91 insertions(+), 41 deletions(-) diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 7594033b..6794f919 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -24,8 +24,9 @@ use bitcoin::secp256k1::Secp256k1; use bitcoin::consensus::encode::serialize; use bitcoin::util::base58; use bitcoin::util::psbt::raw::Key as PSBTKey; +use bitcoin::util::psbt::Input; use bitcoin::util::psbt::PartiallySignedTransaction as PSBT; -use bitcoin::{Address, Network, OutPoint, Script, Transaction, TxOut, Txid}; +use bitcoin::{Address, Network, OutPoint, Script, SigHashType, Transaction, TxOut, Txid}; use miniscript::descriptor::DescriptorTrait; use miniscript::psbt::PsbtInputSatisfier; @@ -1250,41 +1251,21 @@ where None => continue, }; - // Only set it if the params has a custom one, otherwise leave blank which defaults to - // SIGHASH_ALL - if let Some(sighash_type) = params.sighash { - psbt_input.sighash_type = Some(sighash_type); - } - match utxo { Utxo::Local(utxo) => { - // Try to find the prev_script in our db to figure out if this is internal or external, - // and the derivation index - let (keychain, child) = match self - .database - .borrow() - .get_path_from_script_pubkey(&utxo.txout.script_pubkey)? - { - Some(x) => x, - None => continue, - }; - - let desc = self.get_descriptor_for_keychain(keychain); - let derived_descriptor = desc.as_derived(child, &self.secp); - psbt_input.bip32_derivation = derived_descriptor.get_hd_keypaths(&self.secp)?; - - psbt_input.redeem_script = derived_descriptor.psbt_redeem_script(); - psbt_input.witness_script = derived_descriptor.psbt_witness_script(); - - let prev_output = input.previous_output; - if let Some(prev_tx) = self.database.borrow().get_raw_tx(&prev_output.txid)? { - if desc.is_witness() { - psbt_input.witness_utxo = - Some(prev_tx.output[prev_output.vout as usize].clone()); - } - if !desc.is_witness() || params.force_non_witness_utxo { - psbt_input.non_witness_utxo = Some(prev_tx); - } + *psbt_input = match self.get_psbt_input( + utxo, + params.sighash, + params.force_non_witness_utxo, + ) { + Ok(psbt_input) => psbt_input, + Err(e) => match e { + Error::UnknownUTXO => Input { + sighash_type: params.sighash, + ..Input::default() + }, + _ => return Err(e), + }, } } Utxo::Foreign { @@ -1332,6 +1313,48 @@ where Ok(psbt) } + /// get the corresponding PSBT Input for a LocalUtxo + pub fn get_psbt_input( + &self, + utxo: LocalUtxo, + sighash_type: Option, + force_non_witness_utxo: bool, + ) -> Result { + // Try to find the prev_script in our db to figure out if this is internal or external, + // and the derivation index + let (keychain, child) = match self + .database + .borrow() + .get_path_from_script_pubkey(&utxo.txout.script_pubkey)? + { + Some(x) => x, + None => return Err(Error::UnknownUTXO), + }; + + let mut psbt_input = Input { + sighash_type, + ..Input::default() + }; + + let desc = self.get_descriptor_for_keychain(keychain); + let derived_descriptor = desc.as_derived(child, &self.secp); + psbt_input.bip32_derivation = derived_descriptor.get_hd_keypaths(&self.secp)?; + + psbt_input.redeem_script = derived_descriptor.psbt_redeem_script(); + psbt_input.witness_script = derived_descriptor.psbt_witness_script(); + + let prev_output = utxo.outpoint; + if let Some(prev_tx) = self.database.borrow().get_raw_tx(&prev_output.txid)? { + if desc.is_witness() { + psbt_input.witness_utxo = Some(prev_tx.output[prev_output.vout as usize].clone()); + } + if !desc.is_witness() || force_non_witness_utxo { + psbt_input.non_witness_utxo = Some(prev_tx); + } + } + Ok(psbt_input) + } + fn add_input_hd_keypaths(&self, psbt: &mut PSBT) -> Result<(), Error> { let mut input_utxos = Vec::with_capacity(psbt.inputs.len()); for n in 0..psbt.inputs.len() { @@ -1603,13 +1626,30 @@ mod test { ) .unwrap(); - let txid = crate::populate_test_db!( - wallet.database.borrow_mut(), - testutils! { - @tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1) - }, - Some(100) - ); + let funding_address_kix = 0; + + let tx_meta = testutils! { + @tx ( (@external descriptors, funding_address_kix) => 50_000 ) (@confirmations 1) + }; + + wallet + .database + .borrow_mut() + .set_script_pubkey( + &bitcoin::Address::from_str(&tx_meta.output.iter().next().unwrap().to_address) + .unwrap() + .script_pubkey(), + KeychainKind::External, + funding_address_kix, + ) + .unwrap(); + wallet + .database + .borrow_mut() + .set_last_index(KeychainKind::External, funding_address_kix) + .unwrap(); + + let txid = crate::populate_test_db!(wallet.database.borrow_mut(), tx_meta, Some(100)); (wallet, descriptors, txid) } @@ -2540,6 +2580,16 @@ mod test { } } + #[test] + fn test_get_psbt_input() { + // this should grab a known good utxo and set the input + let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); + for utxo in wallet.list_unspent().unwrap() { + let psbt_input = wallet.get_psbt_input(utxo, None, false).unwrap(); + assert!(psbt_input.witness_utxo.is_some() || psbt_input.non_witness_utxo.is_some()); + } + } + #[test] #[should_panic( expected = "MissingKeyOrigin(\"tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3\")" From cc17ac885923b1180f7c67f0429b2562cbf7f43f Mon Sep 17 00:00:00 2001 From: davemo88 Date: Mon, 15 Mar 2021 21:58:03 -0400 Subject: [PATCH 2/3] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5646fb1..eb33d5cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `get_address(AddressIndex::LastUnused)` which returns the last derived address if it has not been used or if used in a received transaction returns a new address - Added `get_address(AddressIndex::Peek(u32))` which returns a derived address for a specified descriptor index but does not change the current index - Added `get_address(AddressIndex::Reset(u32))` which returns a derived address for a specified descriptor index and resets current index to the given value +- Added `get_psbt_input` to create the corresponding psbt input for a local utxo. ## [v0.4.0] - [v0.3.0] From e82dfa971e308a1f44569488d065e45fd8a271e9 Mon Sep 17 00:00:00 2001 From: davemo88 Date: Tue, 16 Mar 2021 10:20:07 -0400 Subject: [PATCH 3/3] brevity --- src/wallet/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 6794f919..2b20da67 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -1322,14 +1322,11 @@ where ) -> Result { // Try to find the prev_script in our db to figure out if this is internal or external, // and the derivation index - let (keychain, child) = match self + let (keychain, child) = self .database .borrow() .get_path_from_script_pubkey(&utxo.txout.script_pubkey)? - { - Some(x) => x, - None => return Err(Error::UnknownUTXO), - }; + .ok_or(Error::UnknownUTXO)?; let mut psbt_input = Input { sighash_type,