Skip mixed-mode assertion for replayed monitor updates
What changed, and why it matters
This commit fixes a bug where LDK would incorrectly crash (panic) when restarting after switching a setting from asynchronous to synchronous persistence. The crash happened because replayed background tasks looked like they completed out of order. The fix tells LDK to skip that order-check for replayed tasks, since they are expected to look out of order during startup replay. It is a reliability/stability fix, not an exploitable security vulnerability.
Treat as a stability/bug-fix patch. Upgrade nodes that may restart after switching from async to sync persistence to avoid an unnecessary panic. No active exploit mitigation is required.
Security signals we found
Assertion/panic in async persistence contract enforcement
Crash-on-restart scenario triggered by configuration change (async to sync persistence)
Replayed background events interact with in-flight update ordering check
Evidence from the diff
In ChannelManager::update_channel_monitor, monitor updates that were in-flight at shutdown are replayed as background events after restart. When a node switches from async to sync persistence, the sync Watch returns Completed immediately for a replayed update, while older in-flight updates are still queued as background events. The existing assertion panicked because it interpreted this as an out-of-order completion. The patch tracks whether the update is a replay (already present in in_flight_monitor_updates) and skips the assertion for replays. The assertion remains active for normal runtime updates.
Changed components
lightning/src/ln/channelmanager.rsChannelManager monitor update replay logicWatch::update_channel completion assertionInspect captured patch +14 / −6
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 30eb7f8..d042a69 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -10365,11 +10365,12 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
// During startup, we push monitor updates as background events through to here in
// order to replay updates that were in-flight when we shut down. Thus, we have to
// filter for uniqueness here.
- let update_idx =
- in_flight_updates.iter().position(|upd| upd == &new_update).unwrap_or_else(|| {
- in_flight_updates.push(new_update);
- in_flight_updates.len() - 1
- });
+ let existing_idx = in_flight_updates.iter().position(|upd| upd == &new_update);
+ let is_replay = existing_idx.is_some();
+ let update_idx = existing_idx.unwrap_or_else(|| {
+ in_flight_updates.push(new_update);
+ in_flight_updates.len() - 1
+ });
if self.background_events_processed_since_startup.load(Ordering::Acquire) {
let update_res =
@@ -10382,11 +10383,18 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
}
// A Watch implementation must not return Completed while prior updates are
// still InProgress, as this would violate the async persistence contract.
+ // We skip this check for replayed updates (startup background events)
+ // because during startup replay, the remaining in-flight updates may not
+ // have been submitted to the Watch yet and will be processed by subsequent
+ // background events. This is specifically necessary when switching from
+ // async to sync persistence across a restart: the replayed update
+ // returns Completed from the now-sync Watch while earlier in-flight
+ // updates are still queued as background events.
#[cfg(test)]
let skip_check = self.skip_monitor_update_assertion.load(Ordering::Relaxed);
#[cfg(not(test))]
let skip_check = false;
- if !skip_check && update_completed && !in_flight_updates.is_empty() {
+ if !skip_check && !is_replay && update_completed && !in_flight_updates.is_empty() {
panic!("Watch::update_channel returned Completed while prior updates are still InProgress");
}
(update_completed, update_completed && in_flight_updates.is_empty())
Why this scored 29/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.