bitcoindevkit / bitcoindevkit/bdk_wallet

`build_fee_bump` panics on legacy foreign input when previous tx is not in the tx graph

Open
#556 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Rust
Stars
59
Forks
105
Avg merge
10d 9h
Merged PRs (30d)
1

Description

**Describe the bug**

`Wallet::build_fee_bump` reconstructs the original transaction's inputs as `WeightedUtxo`s. For inputs the wallet does not own, it builds a `Utxo::Foreign` whose `psbt_input` gets:

- `witness_utxo` only if the prevout script has a witness version, and
- `non_witness_utxo` only if the full previous transaction is in the tx graph.

If the foreign input is legacy (e.g. p2pkh) and the wallet only knows the prevout via `insert_txout` (which is what the `add_foreign_utxo` docs suggest for fee calculation), both fields end up `None`. `build_fee_bump` itself succeeds, but `TxBuilder::finish` then panics in `Utxo::txout` (`src/types.rs`, `expect("Foreign UTXOs should have one of witness_utxo, non_witness_utxo set")`).

Related: #474 discusses the panic in `Utxo::txout` itself; this issue is about `build_fee_bump` constructing such a value internally.

This issue was found by AI.

**To Reproduce**

```rust
let (mut wallet1, _) = get_funded_wallet_wpkh();
let (wallet2, _) = get_funded_wallet_single("pkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
let utxo = wallet2.list_unspent().next().unwrap();
let prev_tx = wallet2.get_tx(utxo.outpoint.txid).unwrap().tx_node.tx.as_ref().clone();
let weight = wallet2.public_descriptor(KeychainKind::External).max_weight_to_satisfy().unwrap();

let addr = wallet1.next_unused_address(KeychainKind::External);
let mut builder = wallet1.build_tx();
builder
.add_recipient(addr.script_pubkey(), Amount::from_sat(60_000))
.add_foreign_utxo(
utxo.outpoint,
psbt::Input { non_witness_utxo: Some(prev_tx), ..Default::default() },
weight,
)
.unwrap();
let tx = builder.finish().unwrap().extract_tx().unwrap();
let txid = tx.compute_txid();

// wallet learns the prevout but not the full previous transaction
wallet1.insert_txout(utxo.outpoint, utxo.txout);
insert_tx(&mut wallet1, tx);

let mut builder = wallet1.build_fee_bump(txid).unwrap();
builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
builder.finish(); // panics at src/types.rs:132
```

**Expected behavior**

`build_fee_bump` / `finish` should return an error for inputs it cannot reconstruct enough information for, rather than panic.

**Build environment**
- BDK tag/commit: `6fc68462` (master)

**Which backend(s) are relevant (if any)?**
- [x] None / not backend-related (e.g. `bdk_chain`, `bdk_core`)

**Is this blocking production use?**
- [x] No

Contributor guide

Open the contributing guide

Research direction

Start in src/types.rs at Utxo::txout, then trace Wallet::build_fee_bump through TxBuilder::finish using the reproduction in the issue. Done means the legacy foreign-input case returns an error from build_fee_bump or finish instead of panicking when only the prevout is known.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
blockchain
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.