Skip to content

Commit effb00f

Browse files
Persistent HTLC forward claim monitor events
Currently, the resolution of HTLCs (and decisions on when HTLCs can be forwarded) is the responsibility of Channel objects (a part of ChannelManager) until the channel is closed, and then the ChannelMonitor thereafter. This leads to some complexity around race conditions for HTLCs right around channel closure. Additionally, there is lots of complexity reconstructing the state of all HTLCs in the ChannelManager deserialization/loading logic. Instead, we want to do all resolution in ChannelMonitors (in response to ChannelMonitorUpdates) and pass them back to ChannelManager in the form of MonitorEvents (similar to how HTLCs are resolved after channels are closed). In order to have reliable resolution, we'll need to keep MonitorEvents around in the ChannelMonitor until the ChannelManager has finished processing them. This will simplify things - on restart instead of examining the set of HTLCs in monitors we can simply replay all the pending MonitorEvents. Here we add MonitorEvent resolution of forwarded HTLC claims, which allows us to remove usage of RAA monitor update blocking actions and a significant amount of complex startup reconstruction code.
1 parent aa3a863 commit effb00f

7 files changed

Lines changed: 770 additions & 55 deletions

File tree

lightning/src/chain/channelmonitor.rs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,6 +2288,18 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
22882288
inner.ack_monitor_event(event_id);
22892289
}
22902290

2291+
/// Returns true if this monitor has any pending or provided `HTLCEvent`s containing a
2292+
/// payment preimage, indicating a claim is in progress.
2293+
#[cfg(any(test, feature = "_test_utils"))]
2294+
pub fn has_pending_claim_monitor_events(&self) -> bool {
2295+
let inner = self.inner.lock().unwrap();
2296+
inner.pending_monitor_events.iter().chain(inner.provided_monitor_events.iter()).any(
2297+
|(_, ev)| {
2298+
matches!(ev, MonitorEvent::HTLCEvent(HTLCUpdate { payment_preimage: Some(_), .. }))
2299+
},
2300+
)
2301+
}
2302+
22912303
/// Enables persistent monitor events mode. When enabled, monitor events are retained until
22922304
/// explicitly acked rather than cleared on read.
22932305
pub(crate) fn set_persistent_events_enabled(&self, enabled: bool) {
@@ -3887,20 +3899,32 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38873899
self.prev_holder_htlc_data = Some(htlc_data);
38883900

38893901
for claim in claimed_htlcs {
3890-
#[cfg(debug_assertions)]
3891-
{
3892-
let cur_counterparty_htlcs = self
3893-
.funding
3894-
.counterparty_claimable_outpoints
3895-
.get(&self.funding.current_counterparty_commitment_txid.unwrap())
3896-
.unwrap();
3897-
assert!(cur_counterparty_htlcs.iter().any(|(_, source_opt)| {
3902+
let htlc_opt = self
3903+
.funding
3904+
.counterparty_claimable_outpoints
3905+
.get(&self.funding.current_counterparty_commitment_txid.unwrap())
3906+
.unwrap()
3907+
.iter()
3908+
.find_map(|(htlc, source_opt)| {
38983909
if let Some(source) = source_opt {
3899-
SentHTLCId::from_source(source) == claim.htlc_id
3900-
} else {
3901-
false
3910+
if SentHTLCId::from_source(source) == claim.htlc_id {
3911+
return Some((htlc, source));
3912+
}
39023913
}
3903-
}));
3914+
None
3915+
});
3916+
debug_assert!(htlc_opt.is_some());
3917+
if self.persistent_events_enabled {
3918+
if let Some((htlc, source)) = htlc_opt {
3919+
self.push_monitor_event(MonitorEvent::HTLCEvent(HTLCUpdate {
3920+
payment_hash: htlc.payment_hash,
3921+
payment_preimage: Some(claim.preimage),
3922+
source: *source.clone(),
3923+
htlc_value_satoshis: Some(htlc.amount_msat),
3924+
skimmed_fee_msat: claim.skimmed_fee_msat,
3925+
next_user_channel_id: self.user_channel_id,
3926+
}));
3927+
}
39043928
}
39053929
self.counterparty_fulfilled_htlcs.insert(claim.htlc_id, claim.preimage);
39063930
}

0 commit comments

Comments
 (0)