Skip to content

Commit 0344807

Browse files
Hold back HTLC mon events while updates in-progress
Recently, we began generating monitor events when HTLCs fail off-chain. In upcoming commits, we'll begin relying on those events to resolve forwarded HTLC fails on the inbound edge. One requirement for that is that we can't fail an HTLC backwards from a monitor event until the monitor update removing that HTLC on the outbound edge is durably persisted. Otherwise, if we crash and lose that monitor update at the wrong time, the outbound edge counterparty could theoretically change their fail resolution to a claim and we'd have no recourse because we already failed the HTLC back on the inbound edge. As such, here we begin not surfacing HTLC-related monitor events to the ChannelManager if there are in-flight monitor updates present, to prevent this scenario.
1 parent 43999b4 commit 0344807

3 files changed

Lines changed: 72 additions & 26 deletions

File tree

lightning/src/chain/chainmonitor.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,10 @@ where
828828
#[cfg(any(test, fuzzing))]
829829
pub fn force_channel_monitor_updated(&self, channel_id: ChannelId, monitor_update_id: u64) {
830830
let monitors = self.monitors.read().unwrap();
831-
let monitor = &monitors.get(&channel_id).unwrap().monitor;
831+
let monitor_state = monitors.get(&channel_id).unwrap();
832+
let monitor = &monitor_state.monitor;
833+
let mut pending_monitor_updates = monitor_state.pending_monitor_updates.lock().unwrap();
834+
pending_monitor_updates.retain(|update_id| *update_id > monitor_update_id);
832835
monitor.push_monitor_event(MonitorEvent::Completed {
833836
funding_txo: monitor.get_funding_txo(),
834837
channel_id,
@@ -1646,7 +1649,20 @@ where
16461649
let monitors = self.monitors.read().unwrap();
16471650
let mut pending_monitor_events = Vec::new();
16481651
for monitor_state in monitors.values() {
1649-
let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
1652+
// Hold back HTLC monitor events for channels with in-flight updates. The monitor may have
1653+
// queued an event based on in-memory state from an as-yet-unpersisted update; surfacing it
1654+
// before persistence would let us act on (e.g. fail upstream) state that could be lost on a
1655+
// crash + reconnect. Other monitor events (e.g., channel close) aren't subject to those
1656+
// restrictions and can be released immediately.
1657+
let has_pending_updates = {
1658+
let pending_updates = monitor_state.pending_monitor_updates.lock().unwrap();
1659+
monitor_state.has_pending_updates(&pending_updates)
1660+
};
1661+
let monitor_events = if has_pending_updates {
1662+
monitor_state.monitor.get_and_clear_pending_non_htlc_monitor_events()
1663+
} else {
1664+
monitor_state.monitor.get_and_clear_pending_monitor_events()
1665+
};
16501666
if monitor_events.len() > 0 {
16511667
let monitor_funding_txo = monitor_state.monitor.get_funding_txo();
16521668
let monitor_channel_id = monitor_state.monitor.channel_id();

lightning/src/chain/channelmonitor.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2361,7 +2361,17 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
23612361
/// Get the list of HTLCs who's status has been updated on chain. This should be called by
23622362
/// ChannelManager via [`chain::Watch::release_pending_monitor_events`].
23632363
pub fn get_and_clear_pending_monitor_events(&self) -> Vec<(u64, MonitorEvent)> {
2364-
self.inner.lock().unwrap().get_and_clear_pending_monitor_events()
2364+
self.inner.lock().unwrap().get_and_clear_pending_monitor_events_filtered(|_| true)
2365+
}
2366+
2367+
/// Drains and returns pending monitor events except [`MonitorEvent::HTLCEvent`]s. Used by the
2368+
/// [`crate::chain::chainmonitor::ChainMonitor`] to avoid surfacing HTLC resolutions that arise
2369+
/// from a monitor update which has been propagated to the monitor in-memory but not yet durably
2370+
/// persisted.
2371+
pub(super) fn get_and_clear_pending_non_htlc_monitor_events(&self) -> Vec<(u64, MonitorEvent)> {
2372+
self.inner.lock().unwrap().get_and_clear_pending_monitor_events_filtered(|ev| {
2373+
!matches!(ev, MonitorEvent::HTLCEvent(_))
2374+
})
23652375
}
23662376

23672377
pub(super) fn push_monitor_event(&self, event: MonitorEvent) {
@@ -4937,17 +4947,27 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49374947
})
49384948
}
49394949

4940-
fn get_and_clear_pending_monitor_events(&mut self) -> Vec<(u64, MonitorEvent)> {
4950+
/// Drains and returns the pending monitor events for which `predicate` returns true. Events that
4951+
/// don't match the predicate stay in `pending_monitor_events` so they're eligible for release on
4952+
/// a later call.
4953+
fn get_and_clear_pending_monitor_events_filtered<F: FnMut(&MonitorEvent) -> bool>(
4954+
&mut self, mut predicate: F,
4955+
) -> Vec<(u64, MonitorEvent)> {
4956+
let mut released = Vec::new();
4957+
let mut retained = Vec::new();
4958+
// Note: we can use Vec::extract_if here once MSRV reaches 1.87
4959+
for entry in self.pending_monitor_events.drain(..) {
4960+
if predicate(&entry.1) {
4961+
released.push(entry);
4962+
} else {
4963+
retained.push(entry);
4964+
}
4965+
}
4966+
self.pending_monitor_events = retained;
49414967
if self.persistent_events_enabled {
4942-
let mut ret = Vec::new();
4943-
mem::swap(&mut ret, &mut self.pending_monitor_events);
4944-
self.provided_monitor_events.extend(ret.iter().cloned());
4945-
ret
4946-
} else {
4947-
let mut ret = Vec::new();
4948-
mem::swap(&mut ret, &mut self.pending_monitor_events);
4949-
ret
4968+
self.provided_monitor_events.extend(released.iter().cloned());
49504969
}
4970+
released
49514971
}
49524972

49534973
/// Gets the set of events that are repeated regularly (e.g. those which RBF bump

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5553,15 +5553,8 @@ fn native_async_persist() {
55535553

55545554
persist_futures.poll_futures();
55555555
let events = async_chain_monitor.release_pending_monitor_events();
5556-
if persistent_monitor_events {
5557-
// With persistent monitor events, the LatestHolderCommitmentTXInfo update containing
5558-
// claimed_htlcs generates an HTLCEvent with the preimage.
5559-
assert_eq!(events.len(), 1);
5560-
assert_eq!(events[0].2.len(), 1);
5561-
assert!(matches!(events[0].2[0].1, MonitorEvent::HTLCEvent(..)));
5562-
} else {
5563-
assert!(events.is_empty());
5564-
}
5556+
// HTLC resolution monitor events are held back while monitor updates are in-progress.
5557+
assert!(events.is_empty());
55655558

55665559
let pending_writes = kv_store.list_pending_async_writes(
55675560
CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE,
@@ -5593,12 +5586,29 @@ fn native_async_persist() {
55935586
);
55945587
persist_futures.poll_futures();
55955588
let completed_persist = async_chain_monitor.release_pending_monitor_events();
5596-
assert_eq!(completed_persist.len(), 1);
5597-
assert_eq!(completed_persist[0].2.len(), 1);
5598-
if let (_, MonitorEvent::Completed { monitor_update_id, .. }) = &completed_persist[0].2[0] {
5599-
assert_eq!(*monitor_update_id, 4);
5589+
if persistent_monitor_events {
5590+
let mon_evs: Vec<_> = completed_persist.iter().flat_map(|(_, _, evs, _)| evs).collect();
5591+
assert_eq!(mon_evs.len(), 2);
5592+
// We previously held back an HTLC event because there was an in-flight update in progress, but
5593+
// now that that's completed the event will be surfaced.
5594+
match mon_evs[0].1 {
5595+
MonitorEvent::HTLCEvent(..) => {},
5596+
_ => panic!(),
5597+
}
5598+
match mon_evs[1].1 {
5599+
MonitorEvent::Completed { monitor_update_id, .. } => {
5600+
assert_eq!(monitor_update_id, 4);
5601+
},
5602+
_ => panic!(),
5603+
}
56005604
} else {
5601-
panic!();
5605+
assert_eq!(completed_persist.len(), 1);
5606+
assert_eq!(completed_persist[0].2.len(), 1);
5607+
if let (_, MonitorEvent::Completed { monitor_update_id, .. }) = &completed_persist[0].2[0] {
5608+
assert_eq!(*monitor_update_id, 4);
5609+
} else {
5610+
panic!();
5611+
}
56025612
}
56035613
}
56045614

0 commit comments

Comments
 (0)