Skip to content

Commit 890eb05

Browse files
committed
Merge #258: feat: Add exclude_unconfirmed and exclude_below_confirmations
5a65422 feat: Add `exclude_unconfirmed` and `exclude_below_confirmations` (志宇) Pull request description: Fixes #143 ### Description These are convenience methods on `TxBuilder` that can be used to filter out outpoints which do not meet a given confirmation threshold. Previously, I advocated a feature freeze on `TxBuilder`, but I believe we can make an exception for these helper methods, as they do not broaden the `TxBuilder` interface’s error surface. Going forward, all other development should be directed to the [`bdk-tx`](https://github.com/bitcoindevkit/bdk-tx) repository and crate. cc. @tnull ### Changelog notice ```md Added: - `TxBuilder::exclude_unconfirmed` - `TxBuilder::exclude_below_confirmations` ``` ### 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 +nightly fmt` and `cargo clippy` before committing #### New Features: * [x] I've added tests for the new feature * [x] I've added docs for the new feature ACKs for top commit: ValuedMammal: ACK 5a65422 oleonardolima: utACK 5a65422 notmandatory: ACK 5a65422 Tree-SHA512: f458ce62cf377c847bf19e6ae458d9ff028d6a28b9ee8df14c4a0dfeb7004249ff522c3462dfb41f391ba3cf92cdd7445c28e58ea614676c9fcf2a09dce7ef67
2 parents b925402 + 5a65422 commit 890eb05

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

wallet/src/wallet/tx_builder.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,45 @@ impl<'a, Cs> TxBuilder<'a, Cs> {
486486
self
487487
}
488488

489+
/// Excludes any outpoints whose enclosing transaction has fewer than `min_confirms`
490+
/// confirmations.
491+
///
492+
/// `min_confirms` is the minimum number of confirmations a transaction must have in order for
493+
/// its outpoints to remain spendable.
494+
/// - Passing `0` will include all transactions (no filtering).
495+
/// - Passing `1` will exclude all unconfirmed transactions (equivalent to
496+
/// `exclude_unconfirmed`).
497+
/// - Passing `6` will only allow outpoints from transactions with at least 6 confirmations.
498+
///
499+
/// If you chain this with other filtering methods, the final set of unspendable outpoints will
500+
/// be the union of all filters.
501+
pub fn exclude_below_confirmations(&mut self, min_confirms: u32) -> &mut Self {
502+
let tip_height = self.wallet.latest_checkpoint().height();
503+
let to_exclude = self
504+
.wallet
505+
.list_unspent()
506+
.filter(|utxo| {
507+
utxo.chain_position
508+
.confirmation_height_upper_bound()
509+
.map_or(0, |h| tip_height.saturating_add(1).saturating_sub(h))
510+
< min_confirms
511+
})
512+
.map(|utxo| utxo.outpoint);
513+
for op in to_exclude {
514+
self.params.unspendable.insert(op);
515+
}
516+
self
517+
}
518+
519+
/// Exclude outpoints whose enclosing transaction is unconfirmed.
520+
///
521+
/// This is a shorthand for [`exclude_below_confirmations(1)`].
522+
///
523+
/// [`exclude_below_confirmations(1)`]: Self::exclude_below_confirmations
524+
pub fn exclude_unconfirmed(&mut self) -> &mut Self {
525+
self.exclude_below_confirmations(1)
526+
}
527+
489528
/// Sign with a specific sig hash
490529
///
491530
/// **Use this option very carefully**
@@ -1126,6 +1165,96 @@ mod test {
11261165
assert_eq!(filtered[0].keychain, KeychainKind::Internal);
11271166
}
11281167

1168+
#[test]
1169+
fn test_exclude_unconfirmed() {
1170+
use crate::test_utils::*;
1171+
use bdk_chain::BlockId;
1172+
use bitcoin::{hashes::Hash, BlockHash, Network};
1173+
1174+
let mut wallet = Wallet::create_single(get_test_tr_single_sig())
1175+
.network(Network::Regtest)
1176+
.create_wallet_no_persist()
1177+
.unwrap();
1178+
let recipient = wallet.next_unused_address(KeychainKind::External).address;
1179+
1180+
insert_checkpoint(
1181+
&mut wallet,
1182+
BlockId {
1183+
height: 1,
1184+
hash: BlockHash::all_zeros(),
1185+
},
1186+
);
1187+
insert_checkpoint(
1188+
&mut wallet,
1189+
BlockId {
1190+
height: 2,
1191+
hash: BlockHash::all_zeros(),
1192+
},
1193+
);
1194+
receive_output(
1195+
&mut wallet,
1196+
Amount::ONE_BTC,
1197+
ReceiveTo::Block(chain::ConfirmationBlockTime {
1198+
block_id: BlockId {
1199+
height: 1,
1200+
hash: BlockHash::all_zeros(),
1201+
},
1202+
confirmation_time: 1,
1203+
}),
1204+
);
1205+
receive_output(
1206+
&mut wallet,
1207+
Amount::ONE_BTC * 2,
1208+
ReceiveTo::Block(chain::ConfirmationBlockTime {
1209+
block_id: BlockId {
1210+
height: 2,
1211+
hash: BlockHash::all_zeros(),
1212+
},
1213+
confirmation_time: 2,
1214+
}),
1215+
);
1216+
receive_output(&mut wallet, Amount::ONE_BTC * 3, ReceiveTo::Mempool(100));
1217+
1218+
// Exclude nothing.
1219+
{
1220+
let mut builder = wallet.build_tx();
1221+
builder
1222+
.fee_rate(FeeRate::ZERO)
1223+
.exclude_below_confirmations(0)
1224+
.drain_wallet()
1225+
.drain_to(recipient.script_pubkey());
1226+
let tx = builder.finish().unwrap();
1227+
let output = tx.unsigned_tx.output.first().expect("must have one output");
1228+
assert_eq!(output.value, Amount::ONE_BTC * 6);
1229+
}
1230+
1231+
// Exclude < 1 conf (a.k.a exclude unconfirmed).
1232+
{
1233+
let mut builder = wallet.build_tx();
1234+
builder
1235+
.fee_rate(FeeRate::ZERO)
1236+
.exclude_below_confirmations(1)
1237+
.drain_wallet()
1238+
.drain_to(recipient.script_pubkey());
1239+
let tx = builder.finish().unwrap();
1240+
let output = tx.unsigned_tx.output.first().expect("must have one output");
1241+
assert_eq!(output.value, Amount::ONE_BTC * 3);
1242+
}
1243+
1244+
// Exclude < 2 conf (a.k.a need at least 2 conf)
1245+
{
1246+
let mut builder = wallet.build_tx();
1247+
builder
1248+
.fee_rate(FeeRate::ZERO)
1249+
.exclude_below_confirmations(2)
1250+
.drain_wallet()
1251+
.drain_to(recipient.script_pubkey());
1252+
let tx = builder.finish().unwrap();
1253+
let output = tx.unsigned_tx.output.first().expect("must have one output");
1254+
assert_eq!(output.value, Amount::ONE_BTC);
1255+
}
1256+
}
1257+
11291258
#[test]
11301259
fn test_build_fee_bump_remove_change_output_single_desc() {
11311260
use crate::test_utils::*;

0 commit comments

Comments
 (0)