From de73ddcc0e753d7629a553f35e139bf208b82af9 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Tue, 11 Jun 2024 11:26:01 +0200 Subject: [PATCH 1/4] Periodically archive fully-resolved channel monitors Previously, LDK was very conservative and kept channel monitors around ~forever or until the user manually decided to prune them. Recently it introduced the `ChainMonitor::archive_fully_resolved_monitors` method, which we now call periodically: every time a wallet sync succeeds, we check whether the latest archival height is 6 blocks in the past and call `archive_fully_resolved_monitors`. As this is not permanently persisted, we will always try to archive any pruned monitors when the first background sync after fresh initialization succeeds, ensuring we call it regularly also on short-lived sessions, e.g, on mobile. --- src/builder.rs | 2 ++ src/config.rs | 3 +++ src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 2a361396d..ef4295669 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -979,6 +979,7 @@ fn build_with_store_internal( let latest_fee_rate_cache_update_timestamp = Arc::new(RwLock::new(None)); let latest_rgs_snapshot_timestamp = Arc::new(RwLock::new(None)); let latest_node_announcement_broadcast_timestamp = Arc::new(RwLock::new(None)); + let latest_channel_monitor_archival_height = Arc::new(RwLock::new(None)); Ok(Node { runtime, @@ -1010,6 +1011,7 @@ fn build_with_store_internal( latest_fee_rate_cache_update_timestamp, latest_rgs_snapshot_timestamp, latest_node_announcement_broadcast_timestamp, + latest_channel_monitor_archival_height, }) } diff --git a/src/config.rs b/src/config.rs index aaa205f24..1a8d5d2f0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -30,6 +30,9 @@ pub(crate) const DEFAULT_ESPLORA_SERVER_URL: &str = "https://blockstream.info/ap // The timeout after which we abandon retrying failed payments. pub(crate) const LDK_PAYMENT_RETRY_TIMEOUT: Duration = Duration::from_secs(10); +// The interval (in block height) after which we retry archiving fully resolved channel monitors. +pub(crate) const LDK_CHANNEL_MONITOR_ARCHIVAL_INTERVAL: u32 = 6; + // The time in-between peer reconnection attempts. pub(crate) const PEER_RECONNECTION_INTERVAL: Duration = Duration::from_secs(10); diff --git a/src/lib.rs b/src/lib.rs index 9c3c12342..5f09b1a61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,8 +122,8 @@ pub use builder::BuildError; pub use builder::NodeBuilder as Builder; use config::{ - NODE_ANN_BCAST_INTERVAL, PEER_RECONNECTION_INTERVAL, RGS_SYNC_INTERVAL, - WALLET_SYNC_INTERVAL_MINIMUM_SECS, + LDK_CHANNEL_MONITOR_ARCHIVAL_INTERVAL, NODE_ANN_BCAST_INTERVAL, PEER_RECONNECTION_INTERVAL, + RGS_SYNC_INTERVAL, WALLET_SYNC_INTERVAL_MINIMUM_SECS, }; use connection::ConnectionManager; use event::{EventHandler, EventQueue}; @@ -198,6 +198,7 @@ pub struct Node { latest_fee_rate_cache_update_timestamp: Arc>>, latest_rgs_snapshot_timestamp: Arc>>, latest_node_announcement_broadcast_timestamp: Arc>>, + latest_channel_monitor_archival_height: Arc>>, } impl Node { @@ -343,10 +344,13 @@ impl Node { let tx_sync = Arc::clone(&self.tx_sync); let sync_cman = Arc::clone(&self.channel_manager); + let archive_cman = Arc::clone(&self.channel_manager); let sync_cmon = Arc::clone(&self.chain_monitor); + let archive_cmon = Arc::clone(&self.chain_monitor); let sync_sweeper = Arc::clone(&self.output_sweeper); let sync_logger = Arc::clone(&self.logger); let sync_wallet_timestamp = Arc::clone(&self.latest_wallet_sync_timestamp); + let sync_monitor_archival_height = Arc::clone(&self.latest_channel_monitor_archival_height); let mut stop_sync = self.stop_sender.subscribe(); let wallet_sync_interval_secs = self.config.wallet_sync_interval_secs.max(WALLET_SYNC_INTERVAL_MINIMUM_SECS); @@ -376,6 +380,12 @@ impl Node { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); *sync_wallet_timestamp.write().unwrap() = unix_time_secs_opt; + + periodically_archive_fully_resolved_monitors( + Arc::clone(&archive_cman), + Arc::clone(&archive_cmon), + Arc::clone(&sync_monitor_archival_height) + ); } Err(e) => { log_error!(sync_logger, "Background sync of Lightning wallet failed: {}", e) @@ -1128,7 +1138,9 @@ impl Node { let wallet = Arc::clone(&self.wallet); let tx_sync = Arc::clone(&self.tx_sync); let sync_cman = Arc::clone(&self.channel_manager); + let archive_cman = Arc::clone(&self.channel_manager); let sync_cmon = Arc::clone(&self.chain_monitor); + let archive_cmon = Arc::clone(&self.chain_monitor); let sync_sweeper = Arc::clone(&self.output_sweeper); let sync_logger = Arc::clone(&self.logger); let confirmables = vec![ @@ -1136,6 +1148,7 @@ impl Node { &*sync_cmon as &(dyn Confirm + Sync + Send), &*sync_sweeper as &(dyn Confirm + Sync + Send), ]; + let sync_monitor_archival_height = Arc::clone(&self.latest_channel_monitor_archival_height); tokio::task::block_in_place(move || { tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on( @@ -1163,6 +1176,12 @@ impl Node { "Sync of Lightning wallet finished in {}ms.", now.elapsed().as_millis() ); + + periodically_archive_fully_resolved_monitors( + archive_cman, + archive_cmon, + sync_monitor_archival_height, + ); Ok(()) }, Err(e) => { @@ -1486,3 +1505,19 @@ pub(crate) fn total_anchor_channels_reserve_sats( * anchor_channels_config.per_channel_reserve_sats }) } + +fn periodically_archive_fully_resolved_monitors( + channel_manager: Arc, chain_monitor: Arc, + latest_channel_monitor_archival_height: Arc>>, +) { + let mut latest_archival_height_lock = latest_channel_monitor_archival_height.write().unwrap(); + let cur_height = channel_manager.current_best_block().height; + let should_archive = latest_archival_height_lock + .as_ref() + .map_or(true, |h| cur_height >= h + LDK_CHANNEL_MONITOR_ARCHIVAL_INTERVAL); + + if should_archive { + chain_monitor.archive_fully_resolved_channel_monitors(); + *latest_archival_height_lock = Some(cur_height); + } +} From a503eb55d8f6fea8547f9c379a1969d0b99672c5 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Tue, 11 Jun 2024 13:02:12 +0200 Subject: [PATCH 2/4] f Rename the const to `RESOLVED_CHANNEL_MONITOR_ARCHIVAL_INTERVAL` --- src/config.rs | 2 +- src/lib.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/config.rs b/src/config.rs index 1a8d5d2f0..37e37bd63 100644 --- a/src/config.rs +++ b/src/config.rs @@ -31,7 +31,7 @@ pub(crate) const DEFAULT_ESPLORA_SERVER_URL: &str = "https://blockstream.info/ap pub(crate) const LDK_PAYMENT_RETRY_TIMEOUT: Duration = Duration::from_secs(10); // The interval (in block height) after which we retry archiving fully resolved channel monitors. -pub(crate) const LDK_CHANNEL_MONITOR_ARCHIVAL_INTERVAL: u32 = 6; +pub(crate) const RESOLVED_CHANNEL_MONITOR_ARCHIVAL_INTERVAL: u32 = 6; // The time in-between peer reconnection attempts. pub(crate) const PEER_RECONNECTION_INTERVAL: Duration = Duration::from_secs(10); diff --git a/src/lib.rs b/src/lib.rs index 5f09b1a61..e336540ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,8 +122,9 @@ pub use builder::BuildError; pub use builder::NodeBuilder as Builder; use config::{ - LDK_CHANNEL_MONITOR_ARCHIVAL_INTERVAL, NODE_ANN_BCAST_INTERVAL, PEER_RECONNECTION_INTERVAL, - RGS_SYNC_INTERVAL, WALLET_SYNC_INTERVAL_MINIMUM_SECS, + NODE_ANN_BCAST_INTERVAL, PEER_RECONNECTION_INTERVAL, + RESOLVED_CHANNEL_MONITOR_ARCHIVAL_INTERVAL, RGS_SYNC_INTERVAL, + WALLET_SYNC_INTERVAL_MINIMUM_SECS, }; use connection::ConnectionManager; use event::{EventHandler, EventQueue}; @@ -1514,7 +1515,7 @@ fn periodically_archive_fully_resolved_monitors( let cur_height = channel_manager.current_best_block().height; let should_archive = latest_archival_height_lock .as_ref() - .map_or(true, |h| cur_height >= h + LDK_CHANNEL_MONITOR_ARCHIVAL_INTERVAL); + .map_or(true, |h| cur_height >= h + RESOLVED_CHANNEL_MONITOR_ARCHIVAL_INTERVAL); if should_archive { chain_monitor.archive_fully_resolved_channel_monitors(); From 9dba3ac79089e183351c944baf3702196ccc087d Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Tue, 11 Jun 2024 11:31:50 +0200 Subject: [PATCH 3/4] Also update `latest_sync_` timestamps in `sync_wallets` ... which we previously omitted. --- src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e336540ac..abc4f11f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1149,6 +1149,8 @@ impl Node { &*sync_cmon as &(dyn Confirm + Sync + Send), &*sync_sweeper as &(dyn Confirm + Sync + Send), ]; + let sync_wallet_timestamp = Arc::clone(&self.latest_wallet_sync_timestamp); + let sync_onchain_wallet_timestamp = Arc::clone(&self.latest_onchain_wallet_sync_timestamp); let sync_monitor_archival_height = Arc::clone(&self.latest_channel_monitor_archival_height); tokio::task::block_in_place(move || { @@ -1162,6 +1164,11 @@ impl Node { "Sync of on-chain wallet finished in {}ms.", now.elapsed().as_millis() ); + let unix_time_secs_opt = SystemTime::now() + .duration_since(UNIX_EPOCH) + .ok() + .map(|d| d.as_secs()); + *sync_onchain_wallet_timestamp.write().unwrap() = unix_time_secs_opt; }, Err(e) => { log_error!(sync_logger, "Sync of on-chain wallet failed: {}", e); @@ -1178,6 +1185,12 @@ impl Node { now.elapsed().as_millis() ); + let unix_time_secs_opt = SystemTime::now() + .duration_since(UNIX_EPOCH) + .ok() + .map(|d| d.as_secs()); + *sync_wallet_timestamp.write().unwrap() = unix_time_secs_opt; + periodically_archive_fully_resolved_monitors( archive_cman, archive_cmon, From accd3c8060e1af629a002d93b30b78b443417a93 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Tue, 11 Jun 2024 11:36:28 +0200 Subject: [PATCH 4/4] Also update the fee rate cache in `sync_wallets` .. which we previously omitted. --- src/lib.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index abc4f11f9..2368e5a5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1124,7 +1124,8 @@ impl Node { } } - /// Manually sync the LDK and BDK wallets with the current chain state. + /// Manually sync the LDK and BDK wallets with the current chain state and update the fee rate + /// cache. /// /// **Note:** The wallets are regularly synced in the background, which is configurable via /// [`Config::onchain_wallet_sync_interval_secs`] and [`Config::wallet_sync_interval_secs`]. @@ -1142,6 +1143,7 @@ impl Node { let archive_cman = Arc::clone(&self.channel_manager); let sync_cmon = Arc::clone(&self.chain_monitor); let archive_cmon = Arc::clone(&self.chain_monitor); + let fee_estimator = Arc::clone(&self.fee_estimator); let sync_sweeper = Arc::clone(&self.output_sweeper); let sync_logger = Arc::clone(&self.logger); let confirmables = vec![ @@ -1150,6 +1152,8 @@ impl Node { &*sync_sweeper as &(dyn Confirm + Sync + Send), ]; let sync_wallet_timestamp = Arc::clone(&self.latest_wallet_sync_timestamp); + let sync_fee_rate_update_timestamp = + Arc::clone(&self.latest_fee_rate_cache_update_timestamp); let sync_onchain_wallet_timestamp = Arc::clone(&self.latest_onchain_wallet_sync_timestamp); let sync_monitor_archival_height = Arc::clone(&self.latest_channel_monitor_archival_height); @@ -1176,6 +1180,26 @@ impl Node { }, }; + let now = Instant::now(); + match fee_estimator.update_fee_estimates().await { + Ok(()) => { + log_info!( + sync_logger, + "Fee rate cache update finished in {}ms.", + now.elapsed().as_millis() + ); + let unix_time_secs_opt = SystemTime::now() + .duration_since(UNIX_EPOCH) + .ok() + .map(|d| d.as_secs()); + *sync_fee_rate_update_timestamp.write().unwrap() = unix_time_secs_opt; + }, + Err(e) => { + log_error!(sync_logger, "Fee rate cache update failed: {}", e,); + return Err(e); + }, + } + let now = Instant::now(); match tx_sync.sync(confirmables).await { Ok(()) => {