Skip to content

Commit be0a040

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 fdaa759 commit be0a040

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

src/builder.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,22 @@ impl NodeBuilder {
738738
kv_store,
739739
)
740740
}
741+
742+
/// Sets whether to automatically rebroadcast unconfirmed transactions (e.g., funding or sweep transactions).
743+
///
744+
/// If enabled (default), the node will periodically rebroadcast unconfirmed transactions to increase
745+
/// the likelihood of confirmation. This helps recover from mempool drops or poor initial propagation.
746+
///
747+
/// Disabling this may be desired for privacy or bandwidth-constrained environments, but could result
748+
/// in slower or failed confirmations if transactions are not re-announced.
749+
///
750+
/// Default: `true`
751+
pub fn set_auto_rebroadcast_unconfirmed(
752+
&mut self, enable: bool,
753+
) -> Result<&mut Self, BuildError> {
754+
self.config.auto_rebroadcast_unconfirmed_tx = Some(enable);
755+
Ok(self)
756+
}
741757
}
742758

743759
/// A builder for an [`Node`] instance, allowing to set some configuration and module choices from

src/config.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ pub(crate) const RGS_SYNC_TIMEOUT_SECS: u64 = 5;
9494
/// The length in bytes of our wallets' keys seed.
9595
pub const WALLET_KEYS_SEED_LEN: usize = 64;
9696

97+
// The time in-between unconfirmed transaction broadcasts.
98+
pub(crate) const UNCONFIRMED_TX_BROADCAST_INTERVAL: Duration = Duration::from_secs(300);
99+
97100
#[derive(Debug, Clone)]
98101
/// Represents the configuration of an [`Node`] instance.
99102
///
@@ -179,6 +182,16 @@ pub struct Config {
179182
/// **Note:** If unset, default parameters will be used, and you will be able to override the
180183
/// parameters on a per-payment basis in the corresponding method calls.
181184
pub sending_parameters: Option<SendingParameters>,
185+
/// This will determine whether to automatically rebroadcast unconfirmed transactions
186+
/// (e.g., channel funding or sweep transactions).
187+
///
188+
/// If enabled, the node will periodically attempt to rebroadcast any unconfirmed transactions to
189+
/// increase propagation and confirmation likelihood. This is helpful in cases where transactions
190+
/// were dropped by the mempool or not widely propagated.
191+
///
192+
/// Defaults to `true`. Disabling this may be desired for privacy-sensitive use cases or low-bandwidth
193+
/// environments, but may result in slower or failed confirmations if transactions are not re-announced.
194+
pub auto_rebroadcast_unconfirmed_tx: Option<bool>,
182195
}
183196

184197
impl Default for Config {
@@ -193,6 +206,7 @@ impl Default for Config {
193206
anchor_channels_config: Some(AnchorChannelsConfig::default()),
194207
sending_parameters: None,
195208
node_alias: None,
209+
auto_rebroadcast_unconfirmed_tx: Some(true),
196210
}
197211
}
198212
}
@@ -534,6 +548,45 @@ impl From<MaxDustHTLCExposure> for LdkMaxDustHTLCExposure {
534548
}
535549
}
536550

551+
/// Policy for controlling transaction rebroadcasting behavior.
552+
///
553+
/// Determines the strategy for resending unconfirmed transactions to the network
554+
/// to ensure they remain in mempools and eventually get confirmed.
555+
#[derive(Clone, Debug)]
556+
pub struct RebroadcastPolicy {
557+
/// Minimum time between rebroadcast attempts in seconds.
558+
///
559+
/// This prevents excessive network traffic by ensuring a minimum delay
560+
/// between consecutive rebroadcast attempts.
561+
///
562+
/// **Recommended values**: 60-600 seconds (1-10 minutes)
563+
pub min_rebroadcast_interval: u64,
564+
/// Maximum number of broadcast attempts before giving up.
565+
///
566+
/// After reaching this limit, the transaction will no longer be rebroadcast
567+
/// automatically. Manual intervention may be required.
568+
///
569+
/// **Recommended values**: 12-48 attempts
570+
pub max_broadcast_attempts: u32,
571+
/// Exponential backoff factor for increasing intervals between attempts.
572+
///
573+
/// Each subsequent rebroadcast wait time is multiplied by this factor,
574+
/// creating an exponential backoff pattern.
575+
///
576+
/// - `1.0`: No backoff (constant interval)
577+
/// - `1.5`: 50% increase each attempt
578+
/// - `2.0`: 100% increase (doubling) each attempt
579+
///
580+
/// **Recommended values**: 1.2-2.0
581+
pub backoff_factor: f32,
582+
}
583+
584+
impl Default for RebroadcastPolicy {
585+
fn default() -> Self {
586+
Self { min_rebroadcast_interval: 300, max_broadcast_attempts: 24, backoff_factor: 1.5 }
587+
}
588+
}
589+
537590
#[cfg(test)]
538591
mod tests {
539592
use std::str::FromStr;

src/lib.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub use builder::NodeBuilder as Builder;
129129
use chain::ChainSource;
130130
use config::{
131131
default_user_config, may_announce_channel, ChannelConfig, Config, NODE_ANN_BCAST_INTERVAL,
132-
PEER_RECONNECTION_INTERVAL, RGS_SYNC_INTERVAL,
132+
PEER_RECONNECTION_INTERVAL, RGS_SYNC_INTERVAL, UNCONFIRMED_TX_BROADCAST_INTERVAL,
133133
};
134134
use connection::ConnectionManager;
135135
use event::{EventHandler, EventQueue};
@@ -402,6 +402,33 @@ impl Node {
402402
}
403403
});
404404

405+
// Regularly rebroadcast unconfirmed transactions.
406+
let rebroadcast_wallet = Arc::clone(&self.wallet);
407+
let rebroadcast_logger = Arc::clone(&self.logger);
408+
let mut stop_rebroadcast = self.stop_sender.subscribe();
409+
if let Some(true) = &self.config.auto_rebroadcast_unconfirmed_tx {
410+
self.runtime.spawn_cancellable_background_task(async move {
411+
let mut interval = tokio::time::interval(UNCONFIRMED_TX_BROADCAST_INTERVAL);
412+
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
413+
loop {
414+
tokio::select! {
415+
_ = stop_rebroadcast.changed() => {
416+
log_debug!(
417+
rebroadcast_logger,
418+
"Stopping rebroadcasting unconfirmed transactions."
419+
);
420+
return;
421+
}
422+
_ = interval.tick() => {
423+
if let Err(e) = rebroadcast_wallet.rebroadcast_unconfirmed_transactions() {
424+
log_error!(rebroadcast_logger, "Background rebroadcast failed: {}", e);
425+
}
426+
}
427+
}
428+
}
429+
});
430+
}
431+
405432
// Regularly broadcast node announcements.
406433
let bcast_cm = Arc::clone(&self.channel_manager);
407434
let bcast_pm = Arc::clone(&self.peer_manager);

0 commit comments

Comments
 (0)