Skip to content

Commit e6a0742

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 b6b4426 commit e6a0742

File tree

7 files changed

+772
-55
lines changed

7 files changed

+772
-55
lines changed

lightning/src/chain/channelmonitor.rs

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

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

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

0 commit comments

Comments
 (0)