Skip to content

Commit 8537db0

Browse files
committed
Minor adjustment to fee rate handling in RBF fee bumping
Use user-provided fee rate for bumping transaction and return an error if it is too low, otherwise use system estimated fee rate and retry mechanism. Also switches the RBF test to use `random_chain_source`, and removes unnecessary sleep-based waits.
1 parent 2f5a966 commit 8537db0

3 files changed

Lines changed: 57 additions & 14 deletions

File tree

src/wallet/mod.rs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ impl Wallet {
338338
WalletEvent::TxUnconfirmed { txid, tx, old_block_time: None } => {
339339
let payment_id = self
340340
.find_payment_by_txid(txid)
341+
.or_else(|| self.find_payment_by_tx_conflicts(locked_wallet, &tx))
341342
.unwrap_or_else(|| PaymentId(txid.to_byte_array()));
342343

343344
let payment = self.create_payment_from_tx(
@@ -378,8 +379,8 @@ impl Wallet {
378379
);
379380
let payment =
380381
self.payment_store.get(&payment_id).ok_or(Error::InvalidPaymentId)?;
381-
let pending_payment_details = self
382-
.create_pending_payment_from_tx(payment.clone(), conflict_txids.clone());
382+
let pending_payment_details =
383+
self.create_pending_payment_from_tx(payment, conflict_txids.clone());
383384

384385
self.pending_payment_store.insert_or_update(pending_payment_details)?;
385386
},
@@ -1042,6 +1043,16 @@ impl Wallet {
10421043
return Some(direct_payment_id);
10431044
}
10441045

1046+
if let Some(replaced_details) = self
1047+
.pending_payment_store
1048+
.list_filter(
1049+
|p| matches!(p.details.kind, PaymentKind::Onchain { txid, .. } if txid == target_txid),
1050+
)
1051+
.first()
1052+
{
1053+
return Some(replaced_details.details.id);
1054+
}
1055+
10451056
if let Some(replaced_details) = self
10461057
.pending_payment_store
10471058
.list_filter(|p| p.conflicting_txids.contains(&target_txid))
@@ -1053,6 +1064,15 @@ impl Wallet {
10531064
None
10541065
}
10551066

1067+
fn find_payment_by_tx_conflicts(
1068+
&self, locked_wallet: &PersistedWallet<KVStoreWalletPersister>, tx: &Transaction,
1069+
) -> Option<PaymentId> {
1070+
locked_wallet
1071+
.tx_graph()
1072+
.direct_conflicts(tx)
1073+
.find_map(|(_, conflict_txid)| self.find_payment_by_txid(conflict_txid))
1074+
}
1075+
10561076
#[allow(deprecated)]
10571077
pub(crate) fn bump_fee_rbf(
10581078
&self, payment_id: PaymentId, fee_rate: Option<FeeRate>,
@@ -1125,13 +1145,13 @@ impl Wallet {
11251145
old_fee_rate.to_sat_per_kwu() + INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT as u64;
11261146

11271147
let confirmation_target = ConfirmationTarget::OnchainPayment;
1128-
let estimated_fee_rate =
1129-
fee_rate.unwrap_or_else(|| self.fee_estimator.estimate_fee_rate(confirmation_target));
1148+
let estimated_fee_rate = self.fee_estimator.estimate_fee_rate(confirmation_target);
11301149

11311150
// Use the higher of minimum RBF requirement or current network estimate
11321151
let final_fee_rate_sat_per_kwu =
11331152
min_required_fee_rate_sat_per_kwu.max(estimated_fee_rate.to_sat_per_kwu());
1134-
let final_fee_rate = FeeRate::from_sat_per_kwu(final_fee_rate_sat_per_kwu);
1153+
let final_fee_rate =
1154+
fee_rate.unwrap_or_else(|| FeeRate::from_sat_per_kwu(final_fee_rate_sat_per_kwu));
11351155

11361156
let mut psbt = {
11371157
let mut builder = locked_wallet.build_fee_bump(txid).map_err(|e| {
@@ -1156,6 +1176,17 @@ impl Wallet {
11561176
match builder.finish() {
11571177
Ok(psbt) => Ok(psbt),
11581178
Err(CreateTxError::FeeRateTooLow { required: required_fee_rate }) => {
1179+
if fee_rate.is_some() {
1180+
log_error!(
1181+
self.logger,
1182+
"Provided fee rate {} is too low for RBF fee bump of txid {}, required minimum fee rate: {}",
1183+
fee_rate.unwrap(),
1184+
txid,
1185+
required_fee_rate
1186+
);
1187+
return Err(Error::InvalidFeeRate);
1188+
}
1189+
11591190
log_info!(self.logger, "BDK requires higher fee rate: {}", required_fee_rate);
11601191

11611192
// BDK may require a higher fee rate than our estimate due to

tests/common/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,21 @@ pub(crate) async fn wait_for_tx<E: ElectrumApi>(electrs: &E, txid: Txid) {
538538
.await;
539539
}
540540

541+
pub(crate) async fn wait_for_tx_in_script_history<E: ElectrumApi>(
542+
electrs: &E, script: &bitcoin::Script, txid: Txid,
543+
) {
544+
if electrs.script_get_history(script).unwrap().iter().any(|h| h.tx_hash == txid) {
545+
return;
546+
}
547+
548+
exponential_backoff_poll(|| {
549+
electrs.ping().unwrap();
550+
let found = electrs.script_get_history(script).unwrap().iter().any(|h| h.tx_hash == txid);
551+
found.then_some(())
552+
})
553+
.await;
554+
}
555+
541556
pub(crate) async fn wait_for_outpoint_spend<E: ElectrumApi>(electrs: &E, outpoint: OutPoint) {
542557
let tx = electrs.transaction_get(&outpoint.txid).unwrap();
543558
let txout_script = tx.output.get(outpoint.vout as usize).unwrap().clone().script_pubkey;

tests/integration_tests_rust.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ use lightning_invoice::{Bolt11InvoiceDescription, Description};
4040
use lightning_types::payment::{PaymentHash, PaymentPreimage};
4141
use log::LevelFilter;
4242

43+
use crate::common::wait_for_tx_in_script_history;
44+
4345
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
4446
async fn channel_full_cycle() {
4547
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
@@ -2466,11 +2468,12 @@ async fn persistence_backwards_compatibility() {
24662468
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
24672469
async fn onchain_fee_bump_rbf() {
24682470
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
2469-
let chain_source = TestChainSource::Esplora(&electrsd);
2471+
let chain_source = random_chain_source(&bitcoind, &electrsd);
24702472
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false);
24712473

24722474
// Fund both nodes
24732475
let addr_a = node_a.onchain_payment().new_address().unwrap();
2476+
let addr_a_script = addr_a.script_pubkey();
24742477
let addr_b = node_b.onchain_payment().new_address().unwrap();
24752478

24762479
let premine_amount_sat = 500_000;
@@ -2514,10 +2517,7 @@ async fn onchain_fee_bump_rbf() {
25142517

25152518
// Successful fee bump
25162519
let new_txid = node_b.onchain_payment().bump_fee_rbf(payment_id, None).unwrap();
2517-
wait_for_tx(&electrsd.client, new_txid).await;
2518-
2519-
// Sleep to allow for transaction propagation
2520-
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
2520+
wait_for_tx_in_script_history(&electrsd.client, &addr_a_script, new_txid).await;
25212521

25222522
node_a.sync_wallets().unwrap();
25232523
node_b.sync_wallets().unwrap();
@@ -2555,10 +2555,7 @@ async fn onchain_fee_bump_rbf() {
25552555

25562556
// Multiple consecutive bumps
25572557
let second_bump_txid = node_b.onchain_payment().bump_fee_rbf(payment_id, None).unwrap();
2558-
wait_for_tx(&electrsd.client, second_bump_txid).await;
2559-
2560-
// Sleep to allow for transaction propagation
2561-
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
2558+
wait_for_tx_in_script_history(&electrsd.client, &addr_a_script, second_bump_txid).await;
25622559

25632560
node_a.sync_wallets().unwrap();
25642561
node_b.sync_wallets().unwrap();

0 commit comments

Comments
 (0)