Skip to content

Commit 61626d4

Browse files
committed
fix(wallet): Don't fail in build_fee_bump for missing parent txid
Clean up `test_add_foreign_utxo_bump_fee`. fix #325
1 parent 4ad1183 commit 61626d4

File tree

2 files changed

+81
-102
lines changed

2 files changed

+81
-102
lines changed

src/wallet/mod.rs

Lines changed: 52 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,15 +1711,15 @@ impl Wallet {
17111711
&mut self,
17121712
txid: Txid,
17131713
) -> Result<TxBuilder<'_, DefaultCoinSelectionAlgorithm>, BuildFeeBumpError> {
1714-
let graph = self.indexed_graph.graph();
1714+
let tx_graph = self.indexed_graph.graph();
17151715
let txout_index = &self.indexed_graph.index;
17161716
let chain_tip = self.chain.tip().block_id();
1717-
let chain_positions = graph
1717+
let chain_positions: HashMap<Txid, ChainPosition<_>> = tx_graph
17181718
.list_canonical_txs(&self.chain, chain_tip, CanonicalizationParams::default())
17191719
.map(|canon_tx| (canon_tx.tx_node.txid, canon_tx.chain_position))
1720-
.collect::<HashMap<Txid, _>>();
1720+
.collect();
17211721

1722-
let mut tx = graph
1722+
let mut tx = tx_graph
17231723
.get_tx(txid)
17241724
.ok_or(BuildFeeBumpError::TransactionNotFound(txid))?
17251725
.as_ref()
@@ -1751,68 +1751,60 @@ impl Wallet {
17511751
.map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
17521752

17531753
// Remove the inputs from the tx and process them.
1754-
let utxos = tx
1754+
let utxos: Vec<WeightedUtxo> = tx
17551755
.input
17561756
.drain(..)
17571757
.map(|txin| -> Result<_, BuildFeeBumpError> {
1758-
graph
1759-
// Get previous transaction.
1760-
.get_tx(txin.previous_output.txid)
1761-
.ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))
1762-
// Get chain position.
1763-
.and_then(|prev_tx| {
1758+
let outpoint = txin.previous_output;
1759+
let prev_txout = tx_graph.get_txout(outpoint).cloned();
1760+
match prev_txout.as_ref().and_then(|prev_txout| {
1761+
txout_index.index_of_spk(prev_txout.script_pubkey.clone())
1762+
}) {
1763+
// This is a local utxo.
1764+
Some(&(keychain, derivation_index)) => {
1765+
let txout = prev_txout.ok_or(BuildFeeBumpError::UnknownUtxo(outpoint))?;
17641766
let chain_position = chain_positions
1765-
.get(&txin.previous_output.txid)
1767+
.get(&outpoint.txid)
17661768
.cloned()
1767-
.ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?;
1768-
let prev_txout = prev_tx
1769-
.output
1770-
.get(txin.previous_output.vout as usize)
1771-
.ok_or(BuildFeeBumpError::InvalidOutputIndex(txin.previous_output))
1772-
.cloned()?;
1773-
Ok((prev_tx, prev_txout, chain_position))
1774-
})
1775-
.map(|(prev_tx, prev_txout, chain_position)| {
1776-
match txout_index.index_of_spk(prev_txout.script_pubkey.clone()) {
1777-
Some(&(keychain, derivation_index)) => WeightedUtxo {
1778-
satisfaction_weight: self
1779-
.public_descriptor(keychain)
1780-
.max_weight_to_satisfy()
1781-
.unwrap(),
1782-
utxo: Utxo::Local(LocalOutput {
1783-
outpoint: txin.previous_output,
1784-
txout: prev_txout.clone(),
1785-
keychain,
1786-
is_spent: true,
1787-
derivation_index,
1788-
chain_position,
1789-
}),
1790-
},
1791-
None => {
1792-
let satisfaction_weight = Weight::from_wu_usize(
1793-
serialize(&txin.script_sig).len() * 4
1794-
+ serialize(&txin.witness).len(),
1795-
);
1796-
WeightedUtxo {
1797-
utxo: Utxo::Foreign {
1798-
outpoint: txin.previous_output,
1799-
sequence: txin.sequence,
1800-
psbt_input: Box::new(psbt::Input {
1801-
witness_utxo: prev_txout
1802-
.script_pubkey
1803-
.witness_version()
1804-
.map(|_| prev_txout.clone()),
1805-
non_witness_utxo: Some(prev_tx.as_ref().clone()),
1806-
..Default::default()
1807-
}),
1808-
},
1809-
satisfaction_weight,
1810-
}
1811-
}
1812-
}
1813-
})
1769+
.ok_or(BuildFeeBumpError::TransactionNotFound(outpoint.txid))?;
1770+
Ok(WeightedUtxo {
1771+
satisfaction_weight: self
1772+
.public_descriptor(keychain)
1773+
.max_weight_to_satisfy()
1774+
.expect("descriptor should be satisfiable"),
1775+
utxo: Utxo::Local(LocalOutput {
1776+
outpoint,
1777+
txout,
1778+
keychain,
1779+
is_spent: true,
1780+
derivation_index,
1781+
chain_position,
1782+
}),
1783+
})
1784+
}
1785+
// This is a foreign utxo.
1786+
None => Ok(WeightedUtxo {
1787+
satisfaction_weight: Weight::from_wu_usize(
1788+
serialize(&txin.script_sig).len() * 4 + serialize(&txin.witness).len(),
1789+
),
1790+
utxo: Utxo::Foreign {
1791+
outpoint,
1792+
sequence: txin.sequence,
1793+
psbt_input: Box::new(psbt::Input {
1794+
witness_utxo: prev_txout
1795+
.as_ref()
1796+
.and_then(|txout| txout.script_pubkey.witness_version())
1797+
.and(prev_txout),
1798+
non_witness_utxo: tx_graph
1799+
.get_tx(outpoint.txid)
1800+
.map(|tx| tx.as_ref().clone()),
1801+
..Default::default()
1802+
}),
1803+
},
1804+
}),
1805+
}
18141806
})
1815-
.collect::<Result<Vec<WeightedUtxo>, BuildFeeBumpError>>()?;
1807+
.collect::<Result<_, _>>()?;
18161808

18171809
if tx.output.len() > 1 {
18181810
let mut change_index = None;
@@ -1832,7 +1824,6 @@ impl Wallet {
18321824
}
18331825

18341826
let params = TxParams {
1835-
// TODO: figure out what rbf option should be?
18361827
version: Some(tx.version),
18371828
recipients: tx
18381829
.output

tests/add_foreign_utxo.rs

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use bdk_wallet::signer::SignOptions;
55
use bdk_wallet::test_utils::*;
66
use bdk_wallet::tx_builder::AddForeignUtxoError;
77
use bdk_wallet::KeychainKind;
8-
use bitcoin::{psbt, Address, Amount, ScriptBuf};
8+
use bitcoin::{hashes::Hash, psbt, Address, Amount, FeeRate, OutPoint, ScriptBuf, TxIn, TxOut};
99

1010
mod common;
1111

@@ -294,56 +294,44 @@ fn test_taproot_foreign_utxo() {
294294
#[test]
295295
fn test_add_foreign_utxo_bump_fee() {
296296
// Create tx spending a p2a output
297-
let (mut w, _) = get_funded_wallet_wpkh();
298-
299-
let drain_to = w.next_unused_address(KeychainKind::External);
300-
// b
301-
// .allow_dust(true)
302-
// .add_recipient(ScriptBuf::new_p2a(), Amount::ZERO);
303-
//
304-
// let mut psbt = b.finish().unwrap();
305-
// for txout in &psbt.unsigned_tx.output {
306-
// let spk = &txout.script_pubkey;
307-
// println!("{}", spk.to_asm_string()); // OP_PUSHNUM_1 OP_PUSHBYTES_2 4e73
308-
// println!("{}", spk.to_hex_string()); // 51024e73
309-
// }
310-
311-
use bitcoin::hashes::Hash;
312-
use bitcoin::OutPoint;
313-
use bitcoin::TxIn;
314-
use bitcoin::TxOut;
297+
let (mut wallet, _) = get_funded_wallet_wpkh();
298+
299+
let drain_spk = wallet
300+
.next_unused_address(KeychainKind::External)
301+
.script_pubkey();
315302
let witness_utxo = TxOut {
316303
value: Amount::ZERO,
317304
script_pubkey: ScriptBuf::new_p2a(),
318305
};
319-
let sat_wu = TxIn::default().segwit_weight();
320-
let op = OutPoint::new(Hash::hash(b"prev"), 4);
306+
let outpoint = OutPoint::new(Hash::hash(b"prev"), 1);
307+
// Remember to include this as a "floating" txout in the wallet.
308+
wallet.insert_txout(outpoint, witness_utxo.clone());
309+
let satisfaction_weight = TxIn::default().segwit_weight();
321310
let psbt_input = psbt::Input {
322-
witness_utxo: Some(witness_utxo.clone()),
311+
witness_utxo: Some(witness_utxo),
323312
..Default::default()
324313
};
325-
w.insert_txout(op, witness_utxo);
326314

327-
let mut b = w.build_tx();
328-
b.add_foreign_utxo(op, psbt_input, sat_wu)
315+
let mut tx_builder = wallet.build_tx();
316+
tx_builder
317+
.add_foreign_utxo(outpoint, psbt_input, satisfaction_weight)
329318
.unwrap()
330319
.only_witness_utxo()
331-
.drain_to(drain_to.script_pubkey());
332-
let mut psbt = b.finish().unwrap();
333-
334-
let sign_options = SignOptions {
335-
trust_witness_utxo: true,
336-
..Default::default()
337-
};
338-
let _finalized = w.sign(&mut psbt, sign_options).unwrap();
339-
// dbg!(_finalized); // false
340-
// dbg!(&psbt);
341-
342-
let tx = psbt.extract_tx().unwrap();
343-
// dbg!(&tx);
320+
.drain_to(drain_spk.clone());
321+
let psbt = tx_builder.finish().unwrap();
322+
let tx = psbt.unsigned_tx.clone();
323+
assert!(tx.input.iter().any(|txin| txin.previous_output == outpoint));
344324
let txid1 = tx.compute_txid();
345-
insert_tx(&mut w, tx);
325+
insert_tx(&mut wallet, tx);
346326

347-
// Build fee bump
348-
let _ = w.build_fee_bump(txid1).unwrap_err();
327+
// Now build fee bump
328+
let mut tx_builder = wallet.build_fee_bump(txid1).unwrap();
329+
tx_builder
330+
.set_recipients(vec![])
331+
.drain_to(drain_spk)
332+
.only_witness_utxo()
333+
.fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
334+
let psbt = tx_builder.finish().unwrap();
335+
let tx = &psbt.unsigned_tx;
336+
assert!(tx.input.iter().any(|txin| txin.previous_output == outpoint));
349337
}

0 commit comments

Comments
 (0)