From 07dc014154bd9804a473d5ab29cb721c7dacbb50 Mon Sep 17 00:00:00 2001 From: Austin Mackillop Date: Thu, 16 Oct 2025 11:35:29 -0400 Subject: [PATCH 1/2] Use MonitorUpdatingPersister for ChainMonitor Replace direct KVStore usage with MonitorUpdatingPersister to enable incremental channel monitor updates instead of rewriting the entire monitor on each update. This improves I/O efficiency by writing only differential updates (typically hundreds of bytes) rather than rewriting the full monitor (potentially tens of megabytes) for each state change. The existing behaviour tends to make channel updates very slow after a few thousand updates. The persister is configured with a maximum of 100 pending updates before consolidating to a full monitor write. This balances I/O bandwidth savings against storage complexity. Note: MonitorUpdatingPersister format is not backward-compatible with the standard persister due to the sentinel byte sequence prepended to monitors. --- src/builder.rs | 21 +++++++++++++++------ src/types.rs | 11 +++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index bd53f8a957..4b9ae87a5d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -26,12 +26,10 @@ use lightning::ln::msgs::{RoutingMessageHandler, SocketAddress}; use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler}; use lightning::routing::gossip::NodeAlias; use lightning::routing::router::DefaultRouter; -use lightning::routing::scoring::{ - ProbabilisticScorer, ProbabilisticScoringDecayParameters, ProbabilisticScoringFeeParameters, -}; +use lightning::routing::scoring::ProbabilisticScorer; use lightning::sign::{EntropySource, NodeSigner}; use lightning::util::persist::{ - read_channel_monitors, KVStoreSync, CHANNEL_MANAGER_PERSISTENCE_KEY, + read_channel_monitors, KVStoreSync, MonitorUpdatingPersister, CHANNEL_MANAGER_PERSISTENCE_KEY, CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, }; use lightning::util::ser::ReadableArgs; @@ -76,6 +74,7 @@ use crate::{Node, NodeMetrics}; const VSS_HARDENED_CHILD_INDEX: u32 = 877; const VSS_LNURL_AUTH_HARDENED_CHILD_INDEX: u32 = 138; const LSPS_HARDENED_CHILD_INDEX: u32 = 577; +const MAXIMUM_PENDING_CHANNEL_UPDATES: u64 = 100; #[derive(Debug, Clone)] enum ChainDataSourceConfig { @@ -1327,13 +1326,23 @@ fn build_with_store_internal( let peer_storage_key = keys_manager.get_peer_storage_key(); - // Initialize the ChainMonitor + // Initialize the ChainMonitor with MonitorUpdatingPersister + let persister = Arc::new(MonitorUpdatingPersister::new( + Arc::clone(&kv_store), + Arc::clone(&logger), + MAXIMUM_PENDING_CHANNEL_UPDATES, + Arc::clone(&keys_manager), + Arc::clone(&keys_manager), + Arc::clone(&tx_broadcaster), + Arc::clone(&fee_estimator), + )); + let chain_monitor: Arc = Arc::new(chainmonitor::ChainMonitor::new( Some(Arc::clone(&chain_source)), Arc::clone(&tx_broadcaster), Arc::clone(&logger), Arc::clone(&fee_estimator), - Arc::clone(&kv_store), + persister, Arc::clone(&keys_manager), peer_storage_key, )); diff --git a/src/types.rs b/src/types.rs index ddd5879852..d6e01f334f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -19,7 +19,7 @@ use lightning::routing::gossip; use lightning::routing::router::DefaultRouter; use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; use lightning::sign::InMemorySigner; -use lightning::util::persist::{KVStore, KVStoreSync}; +use lightning::util::persist::{KVStore, KVStoreSync, MonitorUpdatingPersister}; use lightning::util::ser::{Readable, Writeable, Writer}; use lightning::util::sweep::OutputSweeper; use lightning_block_sync::gossip::{GossipVerifier, UtxoSource}; @@ -55,7 +55,14 @@ pub(crate) type ChainMonitor = chainmonitor::ChainMonitor< Arc, Arc, Arc, - Arc, + Arc, + Arc, + Arc, + Arc, + Arc, + Arc, + >>, Arc, >; From 5ccbce717bf54bfb8aec8605d77394077847148a Mon Sep 17 00:00:00 2001 From: Austin Mackillop Date: Thu, 16 Oct 2025 11:58:51 -0400 Subject: [PATCH 2/2] Fix channel monitor reading with MonitorUpdatingPersister Use MonitorUpdatingPersister's read_all_channel_monitors_with_updates() method instead of the standard read_channel_monitors() function. The MonitorUpdatingPersister writes monitors with a special sentinel byte sequence (MONITOR_UPDATING_PERSISTER_PREPEND_SENTINEL) that makes them incompatible with the standard reading function. Only the persister's own reading method can properly deserialize monitors that may have unapplied updates. This fixes the "Failed to read ChannelMonitor" InvalidData error that occurs when attempting to load existing channel state. --- src/builder.rs | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 4b9ae87a5d..8d6303c99d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -29,7 +29,7 @@ use lightning::routing::router::DefaultRouter; use lightning::routing::scoring::ProbabilisticScorer; use lightning::sign::{EntropySource, NodeSigner}; use lightning::util::persist::{ - read_channel_monitors, KVStoreSync, MonitorUpdatingPersister, CHANNEL_MANAGER_PERSISTENCE_KEY, + KVStoreSync, MonitorUpdatingPersister, CHANNEL_MANAGER_PERSISTENCE_KEY, CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, }; use lightning::util::ser::ReadableArgs; @@ -1326,7 +1326,7 @@ fn build_with_store_internal( let peer_storage_key = keys_manager.get_peer_storage_key(); - // Initialize the ChainMonitor with MonitorUpdatingPersister + // Initialize the MonitorUpdatingPersister let persister = Arc::new(MonitorUpdatingPersister::new( Arc::clone(&kv_store), Arc::clone(&logger), @@ -1337,6 +1337,20 @@ fn build_with_store_internal( Arc::clone(&fee_estimator), )); + // Read ChannelMonitor state from store using the persister + let channel_monitors = match persister.read_all_channel_monitors_with_updates() { + Ok(monitors) => monitors, + Err(e) => { + if e.kind() == lightning::io::ErrorKind::NotFound { + Vec::new() + } else { + log_error!(logger, "Failed to read channel monitors: {}", e.to_string()); + return Err(BuildError::ReadFailed); + } + }, + }; + + // Initialize the ChainMonitor let chain_monitor: Arc = Arc::new(chainmonitor::ChainMonitor::new( Some(Arc::clone(&chain_source)), Arc::clone(&tx_broadcaster), @@ -1389,23 +1403,6 @@ fn build_with_store_internal( scoring_fee_params, )); - // Read ChannelMonitor state from store - let channel_monitors = match read_channel_monitors( - Arc::clone(&kv_store), - Arc::clone(&keys_manager), - Arc::clone(&keys_manager), - ) { - Ok(monitors) => monitors, - Err(e) => { - if e.kind() == lightning::io::ErrorKind::NotFound { - Vec::new() - } else { - log_error!(logger, "Failed to read channel monitors: {}", e.to_string()); - return Err(BuildError::ReadFailed); - } - }, - }; - let mut user_config = default_user_config(&config); if liquidity_source_config.and_then(|lsc| lsc.lsps2_service.as_ref()).is_some() {