Skip to content

Commit f15582d

Browse files
committed
fix(wallet): Don't fail in build_fee_bump for missing parent txid
This fixes an issue that made using `build_fee_bump` impossible if the original transaction was created using `add_foreign_utxo`. Note that it is still required for the previous txouts to exist in the TxGraph in order to calculate the fee / feerate of the original transaction, and to populate the witness utxo, etc. In the future this process could be improved by changing `add_foreign_utxo` to automatically insert the foreign txout into the wallet, but to avoid scope creep that change is left out of this patch.
1 parent 992a08f commit f15582d

File tree

1 file changed

+52
-64
lines changed

1 file changed

+52
-64
lines changed

src/wallet/mod.rs

Lines changed: 52 additions & 64 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()
@@ -1746,73 +1746,62 @@ impl Wallet {
17461746
let fee = self
17471747
.calculate_fee(&tx)
17481748
.map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
1749-
let fee_rate = self
1750-
.calculate_fee_rate(&tx)
1751-
.map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
1749+
let fee_rate = fee / tx.weight();
17521750

17531751
// Remove the inputs from the tx and process them.
1754-
let utxos = tx
1752+
let utxos: Vec<WeightedUtxo> = tx
17551753
.input
17561754
.drain(..)
17571755
.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| {
1756+
let outpoint = txin.previous_output;
1757+
let prev_txout = tx_graph
1758+
.get_txout(outpoint)
1759+
.cloned()
1760+
.ok_or(BuildFeeBumpError::UnknownUtxo(outpoint))?;
1761+
match txout_index.index_of_spk(prev_txout.script_pubkey.clone()) {
1762+
Some(&(keychain, derivation_index)) => {
1763+
let txout = prev_txout;
17641764
let chain_position = chain_positions
1765-
.get(&txin.previous_output.txid)
1765+
.get(&outpoint.txid)
17661766
.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-
})
1767+
.ok_or(BuildFeeBumpError::TransactionNotFound(outpoint.txid))?;
1768+
Ok(WeightedUtxo {
1769+
satisfaction_weight: self
1770+
.public_descriptor(keychain)
1771+
.max_weight_to_satisfy()
1772+
.expect("descriptor should be satisfiable"),
1773+
utxo: Utxo::Local(LocalOutput {
1774+
outpoint,
1775+
txout,
1776+
keychain,
1777+
is_spent: true,
1778+
derivation_index,
1779+
chain_position,
1780+
}),
1781+
})
1782+
}
1783+
None => Ok(WeightedUtxo {
1784+
satisfaction_weight: Weight::from_wu_usize(
1785+
serialize(&txin.script_sig).len() * 4 + serialize(&txin.witness).len(),
1786+
),
1787+
utxo: Utxo::Foreign {
1788+
outpoint,
1789+
sequence: txin.sequence,
1790+
psbt_input: Box::new(psbt::Input {
1791+
witness_utxo: prev_txout
1792+
.script_pubkey
1793+
.witness_version()
1794+
.map(|_| prev_txout),
1795+
non_witness_utxo: tx_graph
1796+
.get_tx(outpoint.txid)
1797+
.map(|tx| tx.as_ref().clone()),
1798+
..Default::default()
1799+
}),
1800+
},
1801+
}),
1802+
}
18141803
})
1815-
.collect::<Result<Vec<WeightedUtxo>, BuildFeeBumpError>>()?;
1804+
.collect::<Result<_, _>>()?;
18161805

18171806
if tx.output.len() > 1 {
18181807
let mut change_index = None;
@@ -1832,7 +1821,6 @@ impl Wallet {
18321821
}
18331822

18341823
let params = TxParams {
1835-
// TODO: figure out what rbf option should be?
18361824
version: Some(tx.version),
18371825
recipients: tx
18381826
.output

0 commit comments

Comments
 (0)