Skip to content

Commit e23c662

Browse files
committed
Implement background job for transaction rebroadcasting
Introduces a `RebroadcastPolicy` to manage the automatic rebroadcasting of unconfirmed transactions with exponential backoff. This prevents excessive network spam while systematically retrying stuck transactions. The feature is enabled by default but can be disabled via the builder: `builder.set_auto_rebroadcast_unconfirmed(false)`. Configuration options: - min_rebroadcast_interval: Base delay between attempts (seconds) - max_broadcast_attempts: Total attempts before abandonment - backoff_factor: Multiplier for exponential delay increase Sensible defaults are provided (300s, 24 attempts, 1.5x backoff).
1 parent a6a54a4 commit e23c662

1 file changed

Lines changed: 43 additions & 19 deletions

File tree

src/wallet/mod.rs

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use bitcoin::{
2929
Address, Amount, FeeRate, OutPoint, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, Weight,
3030
WitnessProgram, WitnessVersion,
3131
};
32+
3233
use lightning::chain::chaininterface::BroadcasterInterface;
3334
use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
3435
use lightning::chain::{BestBlock, Listen};
@@ -244,31 +245,54 @@ impl Wallet {
244245
self.pending_payment_store.insert_or_update(pending_payment)?;
245246
},
246247
WalletEvent::ChainTipChanged { new_tip, .. } => {
247-
// Get all payments that are Pending with Confirmed status
248+
// Get all on-chain payments that are Pending
248249
let pending_payments: Vec<PendingPaymentDetails> =
249250
self.pending_payment_store.list_filter(|p| {
250251
p.details.status == PaymentStatus::Pending
251-
&& matches!(
252-
p.details.kind,
253-
PaymentKind::Onchain {
254-
status: ConfirmationStatus::Confirmed { .. },
255-
..
256-
}
257-
)
252+
&& matches!(p.details.kind, PaymentKind::Onchain { .. })
258253
});
259254

255+
let mut unconfirmed_outbound_txids: Vec<Txid> = Vec::new();
256+
260257
for mut payment in pending_payments {
261-
if let PaymentKind::Onchain {
262-
status: ConfirmationStatus::Confirmed { height, .. },
263-
..
264-
} = payment.details.kind
265-
{
266-
let payment_id = payment.details.id;
267-
if new_tip.height >= height + ANTI_REORG_DELAY - 1 {
268-
payment.details.status = PaymentStatus::Succeeded;
269-
self.payment_store.insert_or_update(payment.details)?;
270-
self.pending_payment_store.remove(&payment_id)?;
271-
}
258+
match payment.details.kind {
259+
PaymentKind::Onchain {
260+
status: ConfirmationStatus::Confirmed { height, .. },
261+
..
262+
} => {
263+
let payment_id = payment.details.id;
264+
if new_tip.height >= height + ANTI_REORG_DELAY - 1 {
265+
payment.details.status = PaymentStatus::Succeeded;
266+
self.payment_store.insert_or_update(payment.details)?;
267+
self.pending_payment_store.remove(&payment_id)?;
268+
}
269+
},
270+
PaymentKind::Onchain {
271+
txid,
272+
status: ConfirmationStatus::Unconfirmed,
273+
} if payment.details.direction == PaymentDirection::Outbound => {
274+
unconfirmed_outbound_txids.push(txid);
275+
},
276+
_ => {},
277+
}
278+
}
279+
280+
if !unconfirmed_outbound_txids.is_empty() {
281+
let txs_to_broadcast: Vec<Transaction> = unconfirmed_outbound_txids
282+
.iter()
283+
.filter_map(|txid| {
284+
locked_wallet.tx_details(*txid).map(|d| (*d.tx).clone())
285+
})
286+
.collect();
287+
288+
if !txs_to_broadcast.is_empty() {
289+
let tx_refs: Vec<&Transaction> = txs_to_broadcast.iter().collect();
290+
self.broadcaster.broadcast_transactions(&tx_refs);
291+
log_info!(
292+
self.logger,
293+
"Rebroadcast {} unconfirmed transactions on chain tip change",
294+
txs_to_broadcast.len()
295+
);
272296
}
273297
}
274298
},

0 commit comments

Comments
 (0)