From 6f9300f1156d3f47c3bb91c6cc34ef033714c38c Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Wed, 4 Dec 2024 23:48:01 -0800 Subject: [PATCH] Persist unresolved ChannelMonitors on empty height change --- lightning/src/chain/chainmonitor.rs | 11 ++++++++--- lightning/src/chain/channelmonitor.rs | 17 +++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index 4e578ff9c47..5519fe91995 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -640,17 +640,22 @@ where C::Target: chain::Filter, /// data could be moved to an archive location or removed entirely. pub fn archive_fully_resolved_channel_monitors(&self) { let mut have_monitors_to_prune = false; - for (_, monitor_holder) in self.monitors.read().unwrap().iter() { + for (funding_txo, monitor_holder) in self.monitors.read().unwrap().iter() { let logger = WithChannelMonitor::from(&self.logger, &monitor_holder.monitor, None); - if monitor_holder.monitor.is_fully_resolved(&logger) { + let (is_fully_resolved, needs_persistence) = monitor_holder.monitor.check_and_update_full_resolution_status(&logger); + if is_fully_resolved { have_monitors_to_prune = true; } + if needs_persistence { + self.persister.update_persisted_channel(*funding_txo, None, &monitor_holder.monitor); + } } if have_monitors_to_prune { let mut monitors = self.monitors.write().unwrap(); monitors.retain(|funding_txo, monitor_holder| { let logger = WithChannelMonitor::from(&self.logger, &monitor_holder.monitor, None); - if monitor_holder.monitor.is_fully_resolved(&logger) { + let (is_fully_resolved, _) = monitor_holder.monitor.check_and_update_full_resolution_status(&logger); + if is_fully_resolved { log_info!(logger, "Archiving fully resolved ChannelMonitor for funding txo {}", funding_txo diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 4f279cf6548..ddfc51a68d5 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -1994,10 +1994,15 @@ impl ChannelMonitor { /// Checks if the monitor is fully resolved. Resolved monitor is one that has claimed all of /// its outputs and balances (i.e. [`Self::get_claimable_balances`] returns an empty set). + /// Additionally may update state to track when the balances set became empty. /// - /// This function returns true only if [`Self::get_claimable_balances`] has been empty for at least + /// This function returns a tuple of two booleans, the first indicating whether the monitor is + /// fully resolved, and the second whether the monitor needs persistence to ensure it is + /// reliably marked as resolved within 4032 blocks. + /// + /// The first boolean is true only if [`Self::get_claimable_balances`] has been empty for at least /// 4032 blocks as an additional protection against any bugs resulting in spuriously empty balance sets. - pub fn is_fully_resolved(&self, logger: &L) -> bool { + pub fn check_and_update_full_resolution_status(&self, logger: &L) -> (bool, bool) { let mut is_all_funds_claimed = self.get_claimable_balances().is_empty(); let current_height = self.current_best_block().height; let mut inner = self.inner.lock().unwrap(); @@ -2011,7 +2016,7 @@ impl ChannelMonitor { match (inner.balances_empty_height, is_all_funds_claimed) { (Some(balances_empty_height), true) => { // Claimed all funds, check if reached the blocks threshold. - current_height >= balances_empty_height + BLOCKS_THRESHOLD + (current_height >= balances_empty_height + BLOCKS_THRESHOLD, false) }, (Some(_), false) => { // previously assumed we claimed all funds, but we have new funds to claim. @@ -2021,7 +2026,7 @@ impl ChannelMonitor { "WARNING: LDK thought it was done claiming all the available funds in the ChannelMonitor for channel {}, but later decided it had more to claim. This is potentially an important bug in LDK, please report it at https://github.com/lightningdevkit/rust-lightning/issues/new", inner.get_funding_txo().0); inner.balances_empty_height = None; - false + (false, true) }, (None, true) => { // Claimed all funds but `balances_empty_height` is None. It is set to the @@ -2030,11 +2035,11 @@ impl ChannelMonitor { "ChannelMonitor funded at {} is now fully resolved. It will become archivable in {} blocks", inner.get_funding_txo().0, BLOCKS_THRESHOLD); inner.balances_empty_height = Some(current_height); - false + (false, true) }, (None, false) => { // Have funds to claim. - false + (false, false) }, } }