From 18dff831b983f55122796938804d9b1eeef459fa Mon Sep 17 00:00:00 2001 From: Leo Nash Date: Thu, 20 Aug 2026 13:36:36 +0000 Subject: [PATCH] Add `disable_peer_networking` flag The motivating use case is a recovery from a stale channel backup: after the peer has force-closed the channel, and the commitment transaction has confirmed, the peer should update to the latest blockheight with the stale database without reconnecting to the peer. Note that this only recovers funds in the `to_remote` output of the commitment transaction; it does not recover any HTLC funds. Co-Authored-By: HAL 9000 --- src/builder.rs | 1 + src/config.rs | 13 +++++++++++++ src/connection.rs | 19 ++++++++++++++++--- src/lib.rs | 38 +++++++++++++++++++++----------------- 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index f0f38783f..5a6aae272 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2238,6 +2238,7 @@ fn build_with_store_internal( let connection_manager = Arc::new(ConnectionManager::new( Arc::clone(&peer_manager), + config.disable_peer_networking, config.tor_config.clone(), Arc::clone(&keys_manager), Arc::clone(&logger), diff --git a/src/config.rs b/src/config.rs index a409b9e48..7c0c9fd16 100644 --- a/src/config.rs +++ b/src/config.rs @@ -179,6 +179,7 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_ /// |----------------------------------------|--------------------------------------| /// | `storage_dir_path` | /tmp/ldk_node/ | /// | `network` | Bitcoin | +/// | `disable_peer_networking` | false | /// | `listening_addresses` | None | /// | `announcement_addresses` | None | /// | `node_alias` | None | @@ -199,6 +200,12 @@ pub struct Config { pub storage_dir_path: String, /// The used Bitcoin network. pub network: Network, + /// Whether Lightning peer networking is disabled. + /// + /// If enabled, the node won't listen for incoming peer connections, reconnect to persisted + /// peers, or initiate new peer connections. Chain synchronization and on-chain transaction + /// broadcasting remain enabled. + pub disable_peer_networking: bool, /// The addresses on which the node will listen for incoming connections. /// /// **Note**: We will only allow opening and accepting public channels if the `node_alias` and the @@ -271,6 +278,7 @@ impl Default for Config { Self { storage_dir_path: DEFAULT_STORAGE_DIR_PATH.to_string(), network: DEFAULT_NETWORK, + disable_peer_networking: false, listening_addresses: None, announcement_addresses: None, trusted_peers_0conf: Vec::new(), @@ -886,6 +894,11 @@ mod tests { assert_eq!(ElectrumSyncConfig::default().full_scan_stop_gap, DEFAULT_FULL_SCAN_STOP_GAP); } + #[test] + fn peer_networking_is_enabled_by_default() { + assert!(!Config::default().disable_peer_networking); + } + #[test] fn full_scan_stop_gap_is_clamped_to_valid_range() { assert_eq!(clamp_full_scan_stop_gap(MIN_FULL_SCAN_STOP_GAP), MIN_FULL_SCAN_STOP_GAP); diff --git a/src/connection.rs b/src/connection.rs index ccb6f9846..c353bf87f 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -57,6 +57,7 @@ where { pending_connections: PendingConnections, peer_manager: Arc, + disable_peer_networking: bool, tor_proxy_config: Option, keys_manager: Arc, logger: L, @@ -67,12 +68,19 @@ where L::Target: LdkLogger, { pub(crate) fn new( - peer_manager: Arc, tor_proxy_config: Option, - keys_manager: Arc, logger: L, + peer_manager: Arc, disable_peer_networking: bool, + tor_proxy_config: Option, keys_manager: Arc, logger: L, ) -> Self { let pending_connections = Mutex::new(HashMap::new()); - Self { pending_connections, peer_manager, tor_proxy_config, keys_manager, logger } + Self { + pending_connections, + peer_manager, + disable_peer_networking, + tor_proxy_config, + keys_manager, + logger, + } } pub(crate) async fn connect_peer_if_necessary( @@ -92,6 +100,11 @@ where pub(crate) async fn do_connect_peer( &self, node_id: PublicKey, addr: SocketAddress, ) -> Result<(), Error> { + if self.disable_peer_networking { + log_debug!(self.logger, "Peer networking is disabled."); + return Err(Error::ConnectionFailed); + } + // If another task is already connecting, subscribe to its result instead of starting a // duplicate attempt. if let Some(pending_connection_ready_receiver) = diff --git a/src/lib.rs b/src/lib.rs index 18152b3a7..f7cea26f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,9 +27,9 @@ //! # { //! use std::str::FromStr; //! +//! use ldk_node::bip39::Mnemonic; //! use ldk_node::bitcoin::secp256k1::PublicKey; //! use ldk_node::bitcoin::Network; -//! use ldk_node::bip39::Mnemonic; //! use ldk_node::entropy::NodeEntropy; //! use ldk_node::lightning::ln::msgs::SocketAddress; //! use ldk_node::lightning_invoice::Bolt11Invoice; @@ -426,7 +426,9 @@ impl Node { ); } - if let Some(listening_addresses) = &self.config.listening_addresses { + if self.config.disable_peer_networking { + log_info!(self.logger, "Lightning peer networking is disabled."); + } else if let Some(listening_addresses) = &self.config.listening_addresses { // Setup networking let peer_manager_connection_handler = Arc::clone(&self.peer_manager); let listening_logger = Arc::clone(&self.logger); @@ -520,17 +522,18 @@ impl Node { } } - // Regularly reconnect to persisted peers. - let connect_cm = Arc::clone(&self.connection_manager); - let connect_pm = Arc::clone(&self.peer_manager); - let connect_logger = Arc::clone(&self.logger); - let connect_peer_store = Arc::clone(&self.peer_store); - let mut stop_connect = self.stop_sender.subscribe(); - self.runtime.spawn_cancellable_background_task(async move { - let mut interval = tokio::time::interval(PEER_RECONNECTION_INTERVAL); - interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); - loop { - tokio::select! { + if !self.config.disable_peer_networking { + // Regularly reconnect to persisted peers. + let connect_cm = Arc::clone(&self.connection_manager); + let connect_pm = Arc::clone(&self.peer_manager); + let connect_logger = Arc::clone(&self.logger); + let connect_peer_store = Arc::clone(&self.peer_store); + let mut stop_connect = self.stop_sender.subscribe(); + self.runtime.spawn_cancellable_background_task(async move { + let mut interval = tokio::time::interval(PEER_RECONNECTION_INTERVAL); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + loop { + tokio::select! { _ = stop_connect.changed() => { log_debug!( connect_logger, @@ -549,12 +552,13 @@ impl Node { let _ = connect_cm.do_connect_peer( peer_info.node_id, peer_info.address.clone(), - ).await; + ).await; } } + } } - } - }); + }); + } // Regularly broadcast node announcements. let bcast_cm = Arc::clone(&self.channel_manager); @@ -565,7 +569,7 @@ impl Node { let bcast_node_metrics = Arc::clone(&self.node_metrics); let mut stop_bcast = self.stop_sender.subscribe(); let node_alias = self.config.node_alias.clone(); - if may_announce_channel(&self.config).is_ok() { + if !self.config.disable_peer_networking && may_announce_channel(&self.config).is_ok() { self.runtime.spawn_cancellable_background_task(async move { // We check every 30 secs whether our last broadcast is NODE_ANN_BCAST_INTERVAL away. #[cfg(not(test))]