Skip to content

Commit 1c94108

Browse files
committed
Merge #648: test: BDK won't add unconf inputs when fee bumping
5d00f82 test that BDK won't add unconf inputs when fee bumping (Daniela Brozzoni) 9874890 test: fix populate_test_db conf calculation (Daniela Brozzoni) 1d9fdd0 Remove wrong TODO comment in build_fee_bump (Daniela Brozzoni) Pull request description: ### Description Closes #144 ### Notes to reviewers #144 is describing a bug that doesn't seem to happen in BDK master anymore (BDK not respecting BIP125 rule 2). This PR just adds a test to check that the bug is fixed. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### Bugfixes: * [ ] This pull request breaks the existing API * [x] I've added tests to reproduce the issue which are now passing * [x] I'm linking the issue being fixed by this PR ACKs for top commit: afilini: ACK 5d00f82 Tree-SHA512: 95833f3566f9716762884d65f3f656346482e45525a3e92efa86710b9f574fdd9af7d235f1f425e4298d6ff380db9af60d1d2008ccde2588d971757db2d136b8
2 parents dd832cb + 5d00f82 commit 1c94108

File tree

2 files changed

+101
-8
lines changed

2 files changed

+101
-8
lines changed

src/database/memory.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -512,10 +512,13 @@ macro_rules! populate_test_db {
512512
};
513513

514514
let txid = tx.txid();
515-
let confirmation_time = tx_meta.min_confirmations.map(|conf| $crate::BlockTime {
516-
height: current_height.unwrap().checked_sub(conf as u32).unwrap(),
517-
timestamp: 0,
518-
});
515+
let confirmation_time = tx_meta
516+
.min_confirmations
517+
.and_then(|v| if v == 0 { None } else { Some(v) })
518+
.map(|conf| $crate::BlockTime {
519+
height: current_height.unwrap().checked_sub(conf as u32).unwrap() + 1,
520+
timestamp: 0,
521+
});
519522

520523
let tx_details = $crate::TransactionDetails {
521524
transaction: Some(tx.clone()),

src/wallet/mod.rs

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -899,8 +899,6 @@ where
899899
/// # Ok::<(), bdk::Error>(())
900900
/// ```
901901
// TODO: support for merging multiple transactions while bumping the fees
902-
// TODO: option to force addition of an extra output? seems bad for privacy to update the
903-
// change
904902
pub fn build_fee_bump(
905903
&self,
906904
txid: Txid,
@@ -2264,7 +2262,6 @@ pub(crate) mod test {
22642262
.drain_to(drain_addr.script_pubkey())
22652263
.drain_wallet();
22662264
let (psbt, details) = builder.finish().unwrap();
2267-
dbg!(&psbt);
22682265
let outputs = psbt.unsigned_tx.output;
22692266

22702267
assert_eq!(outputs.len(), 2);
@@ -3861,6 +3858,99 @@ pub(crate) mod test {
38613858
assert_eq!(details.fee.unwrap_or(0), 250);
38623859
}
38633860

3861+
#[test]
3862+
#[should_panic(expected = "InsufficientFunds")]
3863+
fn test_bump_fee_unconfirmed_inputs_only() {
3864+
// We try to bump the fee, but:
3865+
// - We can't reduce the change, as we have no change
3866+
// - All our UTXOs are unconfirmed
3867+
// So, we fail with "InsufficientFunds", as per RBF rule 2:
3868+
// The replacement transaction may only include an unconfirmed input
3869+
// if that input was included in one of the original transactions.
3870+
let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
3871+
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3872+
let mut builder = wallet.build_tx();
3873+
builder
3874+
.drain_wallet()
3875+
.drain_to(addr.script_pubkey())
3876+
.enable_rbf();
3877+
let (psbt, mut original_details) = builder.finish().unwrap();
3878+
// Now we receive one transaction with 0 confirmations. We won't be able to use that for
3879+
// fee bumping, as it's still unconfirmed!
3880+
crate::populate_test_db!(
3881+
wallet.database.borrow_mut(),
3882+
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 0)),
3883+
Some(100),
3884+
);
3885+
let mut tx = psbt.extract_tx();
3886+
let txid = tx.txid();
3887+
for txin in &mut tx.input {
3888+
txin.witness.push([0x00; 108]); // fake signature
3889+
wallet
3890+
.database
3891+
.borrow_mut()
3892+
.del_utxo(&txin.previous_output)
3893+
.unwrap();
3894+
}
3895+
original_details.transaction = Some(tx);
3896+
wallet
3897+
.database
3898+
.borrow_mut()
3899+
.set_tx(&original_details)
3900+
.unwrap();
3901+
3902+
let mut builder = wallet.build_fee_bump(txid).unwrap();
3903+
builder.fee_rate(FeeRate::from_sat_per_vb(25.0));
3904+
builder.finish().unwrap();
3905+
}
3906+
3907+
#[test]
3908+
fn test_bump_fee_unconfirmed_input() {
3909+
// We create a tx draining the wallet and spending one confirmed
3910+
// and one unconfirmed UTXO. We check that we can fee bump normally
3911+
// (BIP125 rule 2 only apply to newly added unconfirmed input, you can
3912+
// always fee bump with an unconfirmed input if it was included in the
3913+
// original transaction)
3914+
let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
3915+
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
3916+
// We receive a tx with 0 confirmations, which will be used as an input
3917+
// in the drain tx.
3918+
crate::populate_test_db!(
3919+
wallet.database.borrow_mut(),
3920+
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 0)),
3921+
Some(100),
3922+
);
3923+
let mut builder = wallet.build_tx();
3924+
builder
3925+
.drain_wallet()
3926+
.drain_to(addr.script_pubkey())
3927+
.enable_rbf();
3928+
let (psbt, mut original_details) = builder.finish().unwrap();
3929+
let mut tx = psbt.extract_tx();
3930+
let txid = tx.txid();
3931+
for txin in &mut tx.input {
3932+
txin.witness.push([0x00; 108]); // fake signature
3933+
wallet
3934+
.database
3935+
.borrow_mut()
3936+
.del_utxo(&txin.previous_output)
3937+
.unwrap();
3938+
}
3939+
original_details.transaction = Some(tx);
3940+
wallet
3941+
.database
3942+
.borrow_mut()
3943+
.set_tx(&original_details)
3944+
.unwrap();
3945+
3946+
let mut builder = wallet.build_fee_bump(txid).unwrap();
3947+
builder
3948+
.fee_rate(FeeRate::from_sat_per_vb(15.0))
3949+
.allow_shrinking(addr.script_pubkey())
3950+
.unwrap();
3951+
builder.finish().unwrap();
3952+
}
3953+
38643954
#[test]
38653955
fn test_sign_single_xprv() {
38663956
let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
@@ -4725,7 +4815,7 @@ pub(crate) mod test {
47254815

47264816
crate::populate_test_db!(
47274817
wallet.database.borrow_mut(),
4728-
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 0)),
4818+
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
47294819
Some(confirmation_time),
47304820
(@coinbase true)
47314821
);

0 commit comments

Comments
 (0)