-
Notifications
You must be signed in to change notification settings - Fork 452
Skip ChannelManager persistence after monitor update completion #4424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3372,8 +3372,12 @@ macro_rules! process_events_body { | |
|
|
||
| // TODO: This behavior should be documented. It's unintuitive that we query | ||
| // ChannelMonitors when clearing other events. | ||
| if $self.process_pending_monitor_events() { | ||
| result = NotifyOption::DoPersist; | ||
| match $self.process_pending_monitor_events() { | ||
| NotifyOption::DoPersist => result = NotifyOption::DoPersist, | ||
| NotifyOption::SkipPersistHandleEvents | ||
| if result == NotifyOption::SkipPersistNoEvents => | ||
| result = NotifyOption::SkipPersistHandleEvents, | ||
| _ => {}, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -10263,6 +10267,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| pending_msg_events: &mut Vec<MessageSendEvent>, is_connected: bool, | ||
| chan: &mut FundedChannel<SP>, | ||
| ) -> PostMonitorUpdateChanResume { | ||
| debug_assert!(self.total_consistency_lock.try_write().is_err()); | ||
|
|
||
| let chan_id = chan.context.channel_id(); | ||
| let outbound_alias = chan.context.outbound_scid_alias(); | ||
| let counterparty_node_id = chan.context.get_counterparty_node_id(); | ||
|
|
@@ -10280,6 +10286,9 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
|
|
||
| if chan.blocked_monitor_updates_pending() != 0 { | ||
| log_debug!(logger, "Channel has blocked monitor updates, completing update actions but leaving channel blocked"); | ||
| if !update_actions.is_empty() { | ||
| self.needs_persist_flag.store(true, Ordering::Release); | ||
| } | ||
| PostMonitorUpdateChanResume::Blocked { update_actions } | ||
| } else { | ||
| log_debug!(logger, "Channel is open and awaiting update, resuming it"); | ||
|
|
@@ -10311,6 +10320,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| None | ||
| }; | ||
|
|
||
| // Checked before handle_channel_resumption moves these fields. | ||
| let has_state_changes = updates.funding_broadcastable.is_some() | ||
| || updates.channel_ready.is_some() | ||
| || updates.announcement_sigs.is_some(); | ||
|
|
||
| let (htlc_forwards, decode_update_add_htlcs) = self.handle_channel_resumption( | ||
| pending_msg_events, | ||
| chan, | ||
|
|
@@ -10333,6 +10347,20 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| let unbroadcasted_batch_funding_txid = | ||
| chan.context.unbroadcasted_batch_funding_txid(&chan.funding); | ||
|
|
||
| // Queuing outbound messages (commitment_update, raa) alone does | ||
| // not require ChannelManager persistence. | ||
| let needs_persist = has_state_changes | ||
| || !updates.finalized_claimed_htlcs.is_empty() | ||
| || !updates.failed_htlcs.is_empty() | ||
| || !update_actions.is_empty() | ||
| || unbroadcasted_batch_funding_txid.is_some() | ||
| || !htlc_forwards.is_empty() | ||
| || decode_update_add_htlcs.is_some(); | ||
|
|
||
| if needs_persist { | ||
| self.needs_persist_flag.store(true, Ordering::Release); | ||
| } | ||
|
|
||
| PostMonitorUpdateChanResume::Unblocked { | ||
| channel_id: chan_id, | ||
| counterparty_node_id, | ||
|
|
@@ -10620,7 +10648,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| let per_peer_state = self.per_peer_state.read().unwrap(); | ||
| let mut peer_state_lock; | ||
| let peer_state_mutex_opt = per_peer_state.get(counterparty_node_id); | ||
| if peer_state_mutex_opt.is_none() { return } | ||
| if peer_state_mutex_opt.is_none() { | ||
| // Peer is gone; conservatively request persistence. | ||
| self.needs_persist_flag.store(true, Ordering::Release); | ||
| return; | ||
| } | ||
| peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap(); | ||
| let peer_state = &mut *peer_state_lock; | ||
|
|
||
|
|
@@ -10681,6 +10713,9 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| } else { | ||
| let update_actions = peer_state.monitor_update_blocked_actions | ||
| .remove(channel_id).unwrap_or(Vec::new()); | ||
| if !update_actions.is_empty() { | ||
| self.needs_persist_flag.store(true, Ordering::Release); | ||
| } | ||
| log_trace!(logger, "Channel is closed, applying {} post-update actions", update_actions.len()); | ||
| mem::drop(peer_state_lock); | ||
| mem::drop(per_peer_state); | ||
|
|
@@ -13013,19 +13048,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| Ok(()) | ||
| } | ||
|
|
||
| /// Process pending events from the [`chain::Watch`], returning whether any events were processed. | ||
| fn process_pending_monitor_events(&self) -> bool { | ||
| /// Process pending events from the [`chain::Watch`], returning the appropriate | ||
| /// [`NotifyOption`] for persistence and event handling. | ||
| fn process_pending_monitor_events(&self) -> NotifyOption { | ||
| debug_assert!(self.total_consistency_lock.try_write().is_err()); // Caller holds read lock | ||
|
|
||
| let mut failed_channels: Vec<(Result<Infallible, _>, _)> = Vec::new(); | ||
| let mut pending_monitor_events = self.chain_monitor.release_pending_monitor_events(); | ||
| let has_pending_monitor_events = !pending_monitor_events.is_empty(); | ||
| if pending_monitor_events.is_empty() { | ||
| return NotifyOption::SkipPersistNoEvents; | ||
| } | ||
| let mut needs_persist = false; | ||
| for (funding_outpoint, channel_id, mut monitor_events, counterparty_node_id) in | ||
| pending_monitor_events.drain(..) | ||
| { | ||
| for monitor_event in monitor_events.drain(..) { | ||
| match monitor_event { | ||
| MonitorEvent::HTLCEvent(htlc_update) => { | ||
| needs_persist = true; | ||
| let logger = WithContext::from( | ||
| &self.logger, | ||
| Some(counterparty_node_id), | ||
|
|
@@ -13078,6 +13118,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| }, | ||
| MonitorEvent::HolderForceClosed(_) | ||
| | MonitorEvent::HolderForceClosedWithInfo { .. } => { | ||
| needs_persist = true; | ||
| let per_peer_state = self.per_peer_state.read().unwrap(); | ||
| if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) { | ||
| let mut peer_state_lock = peer_state_mutex.lock().unwrap(); | ||
|
|
@@ -13110,6 +13151,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| } | ||
| }, | ||
| MonitorEvent::CommitmentTxConfirmed(_) => { | ||
| needs_persist = true; | ||
| let per_peer_state = self.per_peer_state.read().unwrap(); | ||
| if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) { | ||
| let mut peer_state_lock = peer_state_mutex.lock().unwrap(); | ||
|
|
@@ -13145,7 +13187,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| let _ = self.handle_error(err, counterparty_node_id); | ||
| } | ||
|
|
||
| has_pending_monitor_events | ||
| if needs_persist { | ||
| NotifyOption::DoPersist | ||
| } else { | ||
| NotifyOption::SkipPersistHandleEvents | ||
|
Comment on lines
13187
to
+13193
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: the function now uses two distinct mechanisms for signaling persistence need:
The |
||
| } | ||
| } | ||
|
|
||
| fn handle_holding_cell_free_result(&self, result: FreeHoldingCellsResult) { | ||
|
|
@@ -15171,8 +15217,6 @@ impl< | |
| fn get_and_clear_pending_msg_events(&self) -> Vec<MessageSendEvent> { | ||
| let events = RefCell::new(Vec::new()); | ||
| PersistenceNotifierGuard::optionally_notify(self, || { | ||
| let mut result = NotifyOption::SkipPersistNoEvents; | ||
|
|
||
| // This method is quite performance-sensitive. Not only is it called very often, but it | ||
| // *is* the critical path between generating a message for a peer and giving it to the | ||
| // `PeerManager` to send. Thus, we should avoid adding any more logic here than we | ||
|
|
@@ -15181,9 +15225,7 @@ impl< | |
|
|
||
| // TODO: This behavior should be documented. It's unintuitive that we query | ||
| // ChannelMonitors when clearing other events. | ||
| if self.process_pending_monitor_events() { | ||
| result = NotifyOption::DoPersist; | ||
| } | ||
| let mut result = self.process_pending_monitor_events(); | ||
|
|
||
| if self.maybe_generate_initial_closing_signed() { | ||
| result = NotifyOption::DoPersist; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
needs_persistcheck omitscommitted_outbound_htlc_sources(pruning optimization, replayable) andtx_signatures(resendable message). This is likely intentional, but consider adding a comment explaining their exclusion so future maintainers ofMonitorRestoreUpdatesknow the invariant.More importantly:
handle_channel_resumption(called above) invokesemit_channel_pending_event!andemit_initial_channel_ready_event!, which can push events toself.pending_eventsand mutate channel flags — neither of which is captured in thisneeds_persistcheck. This appears safe today because:emit_channel_pending_event!requiresis_funding_broadcastable() && !channel_pending_event_emitted— in practice this aligns withfunding_broadcastable.is_some()emit_initial_channel_ready_event!requiresis_usable() && !initial_channel_ready_event_emitted— in practice this aligns withchannel_ready.is_some()Both conditions imply
has_state_changes = true. However, this coupling is implicit. If those conditions ever diverge, events could land inpending_eventswithout triggering persistence. Inprocess_events_bodythe!pending_events.is_empty()check provides a safety net, but inget_and_clear_pending_msg_eventsthere is no such fallback.Consider adding a
debug_assertor explicit comment documenting this invariant.