Skip to content

Commit a6f59a4

Browse files
Persistent mon events for off-chain outbound claims
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. In recent work, we added support for keeping monitor events around until they are explicitly acked by the ChannelManager, but would always ack monitor events immediately, which preserved the previous behavior and didn't break any tests. Up until this point, we only generated HTLC monitor events when a payment was claimed/failed on-chain. In this commit, we start generating persistent monitor events whenever a payment is claimed *off*-chain, specifically when new latest holder commitment data is provided to the monitor. For the purpose of making incremental progress on this feature, these events will be a no-op and/or continue to be acked immediately except in the narrow case of an off-chain outbound payment claim. HTLC forward claim monitor events will be a no-op, and on-chain outbound payment claim events continue to be acked immediately. Off-chain outbound payment claims, however, now have monitor events generated for them that will not be acked by the ChannelManager until the PaymentSent event is processed by the user. This also allows us to stop blocking the RAA monitor update that removes the preimage, because the purpose of that behavior was to ensure the user got a PaymentSent event and the monitor event now serves that purpose instead.
1 parent e913ece commit a6f59a4

13 files changed

Lines changed: 235 additions & 70 deletions

lightning/src/chain/channelmonitor.rs

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,6 +2231,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
22312231
self.inner.lock().unwrap().persistent_events_enabled = enabled;
22322232
}
22332233

2234+
pub(crate) fn has_pending_event_for_htlc(&self, source: &HTLCSource) -> bool {
2235+
self.inner.lock().unwrap().has_pending_event_for_htlc(source)
2236+
}
2237+
22342238
/// Copies [`MonitorEvent`] state from `other` into `self`.
22352239
/// Used in tests to align transient runtime state before equality comparison after a
22362240
/// serialization round-trip.
@@ -3824,20 +3828,34 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38243828
self.prev_holder_htlc_data = Some(htlc_data);
38253829

38263830
for (claimed_htlc_id, claimed_preimage) in claimed_htlcs {
3827-
#[cfg(debug_assertions)]
3828-
{
3829-
let cur_counterparty_htlcs = self
3830-
.funding
3831-
.counterparty_claimable_outpoints
3832-
.get(&self.funding.current_counterparty_commitment_txid.unwrap())
3833-
.unwrap();
3834-
assert!(cur_counterparty_htlcs.iter().any(|(_, source_opt)| {
3831+
let htlc_opt = self
3832+
.funding
3833+
.counterparty_claimable_outpoints
3834+
.get(&self.funding.current_counterparty_commitment_txid.unwrap())
3835+
.unwrap()
3836+
.iter()
3837+
.find_map(|(htlc, source_opt)| {
38353838
if let Some(source) = source_opt {
3836-
SentHTLCId::from_source(source) == *claimed_htlc_id
3837-
} else {
3838-
false
3839+
if SentHTLCId::from_source(source) == *claimed_htlc_id {
3840+
return Some((htlc, source));
3841+
}
38393842
}
3840-
}));
3843+
None
3844+
});
3845+
debug_assert!(htlc_opt.is_some());
3846+
if self.persistent_events_enabled {
3847+
if let Some((htlc, source)) = htlc_opt {
3848+
// If persistent_events_enabled is set, the ChannelMonitor is responsible for providing
3849+
// off-chain resolutions of HTLCs to the ChannelManager, will re-provide this event on
3850+
// startup until it is explicitly acked.
3851+
self.push_monitor_event(MonitorEvent::HTLCEvent(HTLCUpdate {
3852+
payment_hash: htlc.payment_hash,
3853+
payment_preimage: Some(*claimed_preimage),
3854+
source: *source.clone(),
3855+
htlc_value_satoshis: Some(htlc.amount_msat),
3856+
user_channel_id: self.user_channel_id,
3857+
}));
3858+
}
38413859
}
38423860
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
38433861
}
@@ -4655,6 +4673,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46554673
self.pending_monitor_events.retain(|(id, _)| *id != event_id);
46564674
}
46574675

4676+
fn has_pending_event_for_htlc(&self, htlc: &HTLCSource) -> bool {
4677+
let htlc_id = SentHTLCId::from_source(htlc);
4678+
self.pending_monitor_events.iter().any(|(_, ev)| {
4679+
if let MonitorEvent::HTLCEvent(upd) = ev {
4680+
return htlc_id == SentHTLCId::from_source(&upd.source);
4681+
}
4682+
false
4683+
})
4684+
}
4685+
46584686
fn get_and_clear_pending_monitor_events(&mut self) -> Vec<(u64, MonitorEvent)> {
46594687
if self.persistent_events_enabled {
46604688
let mut ret = Vec::new();

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ fn do_blinded_intercept_payment(intercept_node_fails: bool) {
854854
do_claim_payment_along_route(
855855
ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
856856
);
857-
expect_payment_sent(&nodes[0], payment_preimage, Some(Some(1000)), true, true);
857+
expect_payment_sent!(nodes[0], payment_preimage, Some(1000));
858858
}
859859

860860
#[test]
@@ -1399,7 +1399,7 @@ fn conditionally_round_fwd_amt() {
13991399
let mut args = ClaimAlongRouteArgs::new(&nodes[0], &expected_route[..], payment_preimage)
14001400
.allow_1_msat_fee_overpay();
14011401
let expected_fee = pass_claimed_payment_along_route(args);
1402-
expect_payment_sent(&nodes[0], payment_preimage, Some(Some(expected_fee)), true, true);
1402+
expect_payment_sent!(nodes[0], payment_preimage, Some(expected_fee));
14031403
}
14041404

14051405
#[test]

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2108,7 +2108,7 @@ fn monitor_update_claim_fail_no_response() {
21082108
let mut bs_updates = get_htlc_update_msgs(&nodes[1], &node_a_id);
21092109
nodes[0].node.handle_update_fulfill_htlc(node_b_id, bs_updates.update_fulfill_htlcs.remove(0));
21102110
do_commitment_signed_dance(&nodes[0], &nodes[1], &bs_updates.commitment_signed, false, false);
2111-
expect_payment_sent!(nodes[0], payment_preimage_1);
2111+
expect_payment_sent!(&nodes[0], payment_preimage_1);
21122112

21132113
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_2);
21142114
}
@@ -3450,7 +3450,10 @@ fn do_test_blocked_chan_preimage_release(completion_mode: BlockedUpdateComplMode
34503450
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
34513451
let persister;
34523452
let new_chain_mon;
3453-
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
3453+
let mut cfg = test_default_channel_config();
3454+
// If persistent_monitor_events is enabed, monitor updates will never be blocked.
3455+
cfg.override_persistent_monitor_events = Some(false);
3456+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, Some(cfg.clone()), Some(cfg)]);
34543457
let nodes_1_reload;
34553458
let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
34563459

@@ -3763,7 +3766,7 @@ fn do_test_inverted_mon_completion_order(
37633766
);
37643767

37653768
// Finally, check that the payment was, ultimately, seen as sent by node A.
3766-
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
3769+
expect_payment_sent!(&nodes[0], payment_preimage);
37673770
}
37683771

37693772
#[test]
@@ -4256,7 +4259,7 @@ fn do_test_glacial_peer_cant_hang(hold_chan_a: bool) {
42564259
nodes[0].node.handle_update_fulfill_htlc(node_b_id, update_fulfill);
42574260
let commitment = &a_update[0].commitment_signed;
42584261
do_commitment_signed_dance(&nodes[0], &nodes[1], commitment, false, false);
4259-
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
4262+
expect_payment_sent!(nodes[0], payment_preimage);
42604263
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], Some(1000), false, false);
42614264

42624265
pass_along_path(
@@ -4936,6 +4939,7 @@ fn native_async_persist() {
49364939
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
49374940
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
49384941
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
4942+
let persistent_monitor_events = nodes[0].node.test_persistent_monitor_events_enabled();
49394943

49404944
let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
49414945

@@ -5081,7 +5085,16 @@ fn native_async_persist() {
50815085
assert_eq!(update_status, ChannelMonitorUpdateStatus::InProgress);
50825086

50835087
persist_futures.poll_futures();
5084-
assert_eq!(async_chain_monitor.release_pending_monitor_events().len(), 0);
5088+
let events = async_chain_monitor.release_pending_monitor_events();
5089+
if persistent_monitor_events {
5090+
// With persistent monitor events, the LatestHolderCommitmentTXInfo update containing
5091+
// claimed_htlcs generates an HTLCEvent with the preimage.
5092+
assert_eq!(events.len(), 1);
5093+
assert_eq!(events[0].2.len(), 1);
5094+
assert!(matches!(events[0].2[0].1, MonitorEvent::HTLCEvent(..)));
5095+
} else {
5096+
assert!(events.is_empty());
5097+
}
50855098

50865099
let pending_writes = kv_store.list_pending_async_writes(
50875100
CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE,
@@ -5223,7 +5236,7 @@ fn test_mpp_claim_to_holding_cell() {
52235236
let claims = vec![(b_claim_msgs, node_b_id), (c_claim_msgs, node_c_id)];
52245237
pass_claimed_payment_along_route_from_ev(250_000, claims, args);
52255238

5226-
expect_payment_sent(&nodes[0], preimage_1, None, true, true);
5239+
expect_payment_sent!(nodes[0], preimage_1);
52275240

52285241
expect_and_process_pending_htlcs(&nodes[3], false);
52295242
expect_payment_claimable!(nodes[3], paymnt_hash_2, payment_secret_2, 400_000);

lightning/src/ln/channelmanager.rs

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10022,11 +10022,17 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1002210022
};
1002310023
Some(EventCompletionAction::ReleasePaymentCompleteChannelMonitorUpdate(release))
1002410024
} else {
10025-
Some(EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
10026-
channel_funding_outpoint: Some(next_channel_outpoint),
10027-
channel_id: next_channel_id,
10028-
counterparty_node_id: path.hops[0].pubkey,
10029-
})
10025+
if self.persistent_monitor_events {
10026+
monitor_event_id.map(|event_id| EventCompletionAction::AckMonitorEvent {
10027+
event_id: MonitorEventSource { channel_id: next_channel_id, event_id },
10028+
})
10029+
} else {
10030+
Some(EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
10031+
channel_funding_outpoint: Some(next_channel_outpoint),
10032+
channel_id: next_channel_id,
10033+
counterparty_node_id: path.hops[0].pubkey,
10034+
})
10035+
}
1003010036
};
1003110037
let logger = WithContext::for_payment(
1003210038
&self.logger,
@@ -10048,6 +10054,25 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1004810054
&self.pending_events,
1004910055
&logger,
1005010056
);
10057+
10058+
if matches!(
10059+
ev_completion_action,
10060+
Some(EventCompletionAction::AckMonitorEvent { .. })
10061+
) {
10062+
// If the `PaymentSent` for this redundant claim is still pending, add the event
10063+
// completion action here to ensure the `PaymentSent` will always be regenerated until it
10064+
// is processed by the user -- as long as the monitor event corresponding to this
10065+
// completion action is not acked, it will continue to be re-provided on startup.
10066+
let mut pending_events = self.pending_events.lock().unwrap();
10067+
for (ev, act_opt) in pending_events.iter_mut() {
10068+
let found_payment_sent = matches!(ev, Event::PaymentSent { payment_id: Some(id), .. } if *id == payment_id);
10069+
if found_payment_sent && act_opt.is_none() {
10070+
*act_opt = ev_completion_action.take();
10071+
break;
10072+
}
10073+
}
10074+
}
10075+
1005110076
// If an event was generated, `claim_htlc` set `ev_completion_action` to None, if
1005210077
// not, we should go ahead and run it now (as the claim was duplicative), at least
1005310078
// if a PaymentClaimed event with the same action isn't already pending.
@@ -12882,6 +12907,13 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1288212907
})
1288312908
}
1288412909

12910+
/// Returns `true` if `ChannelManager::persistent_monitor_events` is enabled. This flag will only
12911+
/// be set randomly in tests for now.
12912+
#[cfg(any(test, feature = "_test_utils"))]
12913+
pub fn test_persistent_monitor_events_enabled(&self) -> bool {
12914+
self.persistent_monitor_events
12915+
}
12916+
1288512917
#[cfg(any(test, feature = "_test_utils"))]
1288612918
pub(crate) fn test_raa_monitor_updates_held(
1288712919
&self, counterparty_node_id: PublicKey, channel_id: ChannelId,
@@ -13588,22 +13620,29 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1358813620
.channel_by_id
1358913621
.contains_key(&channel_id)
1359013622
});
13591-
// Claim the funds from the previous hop, if there is one. Because this is in response to a
13592-
// chain event, no attribution data is available.
13593-
self.claim_funds_internal(
13594-
htlc_update.source,
13595-
preimage,
13596-
htlc_update.htlc_value_satoshis.map(|v| v * 1000),
13597-
None,
13598-
from_onchain,
13599-
counterparty_node_id,
13600-
funding_outpoint,
13601-
channel_id,
13602-
htlc_update.user_channel_id,
13603-
None,
13604-
None,
13605-
Some(event_id),
13606-
);
13623+
let we_are_sender =
13624+
matches!(htlc_update.source, HTLCSource::OutboundRoute { .. });
13625+
if from_onchain | we_are_sender {
13626+
// Claim the funds from the previous hop, if there is one. In the future we can
13627+
// store attribution data in the `ChannelMonitor` and provide it here.
13628+
self.claim_funds_internal(
13629+
htlc_update.source,
13630+
preimage,
13631+
htlc_update.htlc_value_satoshis.map(|v| v * 1000),
13632+
None,
13633+
from_onchain,
13634+
counterparty_node_id,
13635+
funding_outpoint,
13636+
channel_id,
13637+
htlc_update.user_channel_id,
13638+
None,
13639+
None,
13640+
Some(event_id),
13641+
);
13642+
}
13643+
if from_onchain | !we_are_sender {
13644+
self.chain_monitor.ack_monitor_event(monitor_event_source);
13645+
}
1360713646
} else {
1360813647
log_trace!(logger, "Failing HTLC from our monitor");
1360913648
let failure_reason = LocalHTLCFailureReason::OnChainTimeout;
@@ -13623,8 +13662,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1362313662
failure_type,
1362413663
completion_update,
1362513664
);
13665+
self.chain_monitor.ack_monitor_event(monitor_event_source);
1362613666
}
13627-
self.chain_monitor.ack_monitor_event(monitor_event_source);
1362813667
},
1362913668
MonitorEvent::HolderForceClosed(_)
1363013669
| MonitorEvent::HolderForceClosedWithInfo { .. } => {
@@ -19055,6 +19094,14 @@ impl<
1905519094
break;
1905619095
}
1905719096
}
19097+
if persistent_monitor_events {
19098+
// This will not be necessary once we have persistent events for HTLC failures, we
19099+
// can delete this whole loop and wait to re-process the pending monitor events
19100+
// rather than failing them proactively below.
19101+
if monitor.has_pending_event_for_htlc(&channel_htlc_source) {
19102+
found_htlc = true;
19103+
}
19104+
}
1905819105
if !found_htlc {
1905919106
// If we have some HTLCs in the channel which are not present in the newer
1906019107
// ChannelMonitor, they have been removed and should be failed back to

lightning/src/ln/functional_test_utils.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3074,7 +3074,7 @@ macro_rules! expect_payment_sent {
30743074
$expected_payment_preimage,
30753075
$expected_fee_msat_opt.map(|o| Some(o)),
30763076
$expect_paths,
3077-
true,
3077+
if $node.node.test_persistent_monitor_events_enabled() { false } else { true },
30783078
)
30793079
};
30803080
}
@@ -4235,7 +4235,15 @@ pub fn claim_payment_along_route(
42354235
do_claim_payment_along_route(args) + expected_extra_total_fees_msat;
42364236

42374237
if !skip_last {
4238-
expect_payment_sent!(origin_node, payment_preimage, Some(expected_total_fee_msat))
4238+
let expect_post_ev_mon_update =
4239+
if origin_node.node.test_persistent_monitor_events_enabled() { false } else { true };
4240+
expect_payment_sent(
4241+
origin_node,
4242+
payment_preimage,
4243+
Some(Some(expected_total_fee_msat)),
4244+
true,
4245+
expect_post_ev_mon_update,
4246+
)
42394247
} else {
42404248
(None, Vec::new())
42414249
}

lightning/src/ln/functional_tests.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,10 @@ pub fn do_test_multiple_package_conflicts(p2a_anchor: bool) {
13581358
};
13591359
assert_eq!(updates.update_fulfill_htlcs.len(), 1);
13601360
nodes[0].node.handle_update_fulfill_htlc(node_b_id, updates.update_fulfill_htlcs.remove(0));
1361+
if nodes[0].node.test_persistent_monitor_events_enabled() {
1362+
// If persistent_monitor_events is enabled, the RAA monitor update is not blocked.
1363+
check_added_monitors(&nodes[0], 1);
1364+
}
13611365
do_commitment_signed_dance(&nodes[0], &nodes[1], &updates.commitment_signed, false, false);
13621366
expect_payment_sent!(nodes[0], preimage_2);
13631367

@@ -2672,7 +2676,10 @@ pub fn test_simple_peer_disconnect() {
26722676
_ => panic!("Unexpected event"),
26732677
}
26742678
}
2675-
check_added_monitors(&nodes[0], 1);
2679+
check_added_monitors(
2680+
&nodes[0],
2681+
if nodes[0].node.test_persistent_monitor_events_enabled() { 0 } else { 1 },
2682+
);
26762683

26772684
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage_4);
26782685
fail_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_hash_6);
@@ -4295,7 +4302,7 @@ pub fn test_duplicate_payment_hash_one_failure_one_success() {
42954302

42964303
nodes[0].node.handle_update_fulfill_htlc(node_b_id, updates.update_fulfill_htlcs.remove(0));
42974304
do_commitment_signed_dance(&nodes[0], &nodes[1], &updates.commitment_signed, false, false);
4298-
expect_payment_sent(&nodes[0], our_payment_preimage, None, true, true);
4305+
expect_payment_sent!(&nodes[0], our_payment_preimage);
42994306
}
43004307

43014308
#[xtest(feature = "_externalize_tests")]
@@ -8574,7 +8581,9 @@ pub fn test_inconsistent_mpp_params() {
85748581
pass_along_path(&nodes[0], path_b, real_amt, hash, Some(payment_secret), event, true, None);
85758582

85768583
do_claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], &[path_a, path_b], preimage));
8577-
expect_payment_sent(&nodes[0], preimage, Some(None), true, true);
8584+
let expect_post_ev_mon_update =
8585+
if nodes[0].node.test_persistent_monitor_events_enabled() { false } else { true };
8586+
expect_payment_sent(&nodes[0], preimage, Some(None), true, expect_post_ev_mon_update);
85788587
}
85798588

85808589
#[xtest(feature = "_externalize_tests")]
@@ -9932,7 +9941,10 @@ fn do_test_multi_post_event_actions(do_reload: bool) {
99329941
let chanmon_cfgs = create_chanmon_cfgs(3);
99339942
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
99349943
let (persister, chain_monitor);
9935-
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
9944+
let mut cfg = test_default_channel_config();
9945+
// If persistent_monitor_events is enabled, RAAs will not be blocked on events.
9946+
cfg.override_persistent_monitor_events = Some(false);
9947+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[Some(cfg), None, None]);
99369948
let node_a_reload;
99379949
let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
99389950

lightning/src/ln/invoice_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1344,7 +1344,7 @@ mod test {
13441344
&[&[&nodes[fwd_idx]]],
13451345
payment_preimage,
13461346
));
1347-
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
1347+
expect_payment_sent!(&nodes[0], payment_preimage);
13481348
}
13491349

13501350
#[test]

0 commit comments

Comments
 (0)