Skip to content

Commit 414d457

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 5c6f09d commit 414d457

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
@@ -2249,6 +2249,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
22492249
self.inner.lock().unwrap().persistent_events_enabled = enabled;
22502250
}
22512251

2252+
pub(crate) fn has_pending_event_for_htlc(&self, source: &HTLCSource) -> bool {
2253+
self.inner.lock().unwrap().has_pending_event_for_htlc(source)
2254+
}
2255+
22522256
/// Copies [`MonitorEvent`] state from `other` into `self`.
22532257
/// Used in tests to align transient runtime state before equality comparison after a
22542258
/// serialization round-trip.
@@ -3842,20 +3846,34 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38423846
self.prev_holder_htlc_data = Some(htlc_data);
38433847

38443848
for (claimed_htlc_id, claimed_preimage) in claimed_htlcs {
3845-
#[cfg(debug_assertions)]
3846-
{
3847-
let cur_counterparty_htlcs = self
3848-
.funding
3849-
.counterparty_claimable_outpoints
3850-
.get(&self.funding.current_counterparty_commitment_txid.unwrap())
3851-
.unwrap();
3852-
assert!(cur_counterparty_htlcs.iter().any(|(_, source_opt)| {
3849+
let htlc_opt = self
3850+
.funding
3851+
.counterparty_claimable_outpoints
3852+
.get(&self.funding.current_counterparty_commitment_txid.unwrap())
3853+
.unwrap()
3854+
.iter()
3855+
.find_map(|(htlc, source_opt)| {
38533856
if let Some(source) = source_opt {
3854-
SentHTLCId::from_source(source) == *claimed_htlc_id
3855-
} else {
3856-
false
3857+
if SentHTLCId::from_source(source) == *claimed_htlc_id {
3858+
return Some((htlc, source));
3859+
}
38573860
}
3858-
}));
3861+
None
3862+
});
3863+
debug_assert!(htlc_opt.is_some());
3864+
if self.persistent_events_enabled {
3865+
if let Some((htlc, source)) = htlc_opt {
3866+
// If persistent_events_enabled is set, the ChannelMonitor is responsible for providing
3867+
// off-chain resolutions of HTLCs to the ChannelManager, will re-provide this event on
3868+
// startup until it is explicitly acked.
3869+
self.push_monitor_event(MonitorEvent::HTLCEvent(HTLCUpdate {
3870+
payment_hash: htlc.payment_hash,
3871+
payment_preimage: Some(*claimed_preimage),
3872+
source: *source.clone(),
3873+
htlc_value_satoshis: Some(htlc.amount_msat),
3874+
user_channel_id: self.user_channel_id,
3875+
}));
3876+
}
38593877
}
38603878
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
38613879
}
@@ -4702,6 +4720,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
47024720
self.pending_monitor_events.retain(|(id, _)| *id != event_id);
47034721
}
47044722

4723+
fn has_pending_event_for_htlc(&self, htlc: &HTLCSource) -> bool {
4724+
let htlc_id = SentHTLCId::from_source(htlc);
4725+
self.pending_monitor_events.iter().any(|(_, ev)| {
4726+
if let MonitorEvent::HTLCEvent(upd) = ev {
4727+
return htlc_id == SentHTLCId::from_source(&upd.source);
4728+
}
4729+
false
4730+
})
4731+
}
4732+
47054733
fn get_and_clear_pending_monitor_events(&mut self) -> Vec<(u64, MonitorEvent)> {
47064734
if self.persistent_events_enabled {
47074735
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
@@ -856,7 +856,7 @@ fn do_blinded_intercept_payment(intercept_node_fails: bool) {
856856
do_claim_payment_along_route(
857857
ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
858858
);
859-
expect_payment_sent(&nodes[0], payment_preimage, Some(Some(1000)), true, true);
859+
expect_payment_sent!(nodes[0], payment_preimage, Some(1000));
860860
}
861861

862862
#[test]
@@ -1401,7 +1401,7 @@ fn conditionally_round_fwd_amt() {
14011401
let mut args = ClaimAlongRouteArgs::new(&nodes[0], &expected_route[..], payment_preimage)
14021402
.allow_1_msat_fee_overpay();
14031403
let expected_fee = pass_claimed_payment_along_route(args);
1404-
expect_payment_sent(&nodes[0], payment_preimage, Some(Some(expected_fee)), true, true);
1404+
expect_payment_sent!(nodes[0], payment_preimage, Some(expected_fee));
14051405
}
14061406

14071407
#[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
@@ -10189,11 +10189,17 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1018910189
};
1019010190
Some(EventCompletionAction::ReleasePaymentCompleteChannelMonitorUpdate(release))
1019110191
} else {
10192-
Some(EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
10193-
channel_funding_outpoint: Some(next_channel_outpoint),
10194-
channel_id: next_channel_id,
10195-
counterparty_node_id: path.hops[0].pubkey,
10196-
})
10192+
if self.persistent_monitor_events {
10193+
monitor_event_id.map(|event_id| EventCompletionAction::AckMonitorEvent {
10194+
event_id: MonitorEventSource { channel_id: next_channel_id, event_id },
10195+
})
10196+
} else {
10197+
Some(EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
10198+
channel_funding_outpoint: Some(next_channel_outpoint),
10199+
channel_id: next_channel_id,
10200+
counterparty_node_id: path.hops[0].pubkey,
10201+
})
10202+
}
1019710203
};
1019810204
let logger = WithContext::for_payment(
1019910205
&self.logger,
@@ -10215,6 +10221,25 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1021510221
&self.pending_events,
1021610222
&logger,
1021710223
);
10224+
10225+
if matches!(
10226+
ev_completion_action,
10227+
Some(EventCompletionAction::AckMonitorEvent { .. })
10228+
) {
10229+
// If the `PaymentSent` for this redundant claim is still pending, add the event
10230+
// completion action here to ensure the `PaymentSent` will always be regenerated until it
10231+
// is processed by the user -- as long as the monitor event corresponding to this
10232+
// completion action is not acked, it will continue to be re-provided on startup.
10233+
let mut pending_events = self.pending_events.lock().unwrap();
10234+
for (ev, act_opt) in pending_events.iter_mut() {
10235+
let found_payment_sent = matches!(ev, Event::PaymentSent { payment_id: Some(id), .. } if *id == payment_id);
10236+
if found_payment_sent && act_opt.is_none() {
10237+
*act_opt = ev_completion_action.take();
10238+
break;
10239+
}
10240+
}
10241+
}
10242+
1021810243
// If an event was generated, `claim_htlc` set `ev_completion_action` to None, if
1021910244
// not, we should go ahead and run it now (as the claim was duplicative), at least
1022010245
// if a PaymentClaimed event with the same action isn't already pending.
@@ -13024,6 +13049,13 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1302413049
})
1302513050
}
1302613051

13052+
/// Returns `true` if `ChannelManager::persistent_monitor_events` is enabled. This flag will only
13053+
/// be set randomly in tests for now.
13054+
#[cfg(any(test, feature = "_test_utils"))]
13055+
pub fn test_persistent_monitor_events_enabled(&self) -> bool {
13056+
self.persistent_monitor_events
13057+
}
13058+
1302713059
#[cfg(any(test, feature = "_test_utils"))]
1302813060
pub(crate) fn test_raa_monitor_updates_held(
1302913061
&self, counterparty_node_id: PublicKey, channel_id: ChannelId,
@@ -13758,22 +13790,29 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1375813790
.channel_by_id
1375913791
.contains_key(&channel_id)
1376013792
});
13761-
// Claim the funds from the previous hop, if there is one. Because this is in response to a
13762-
// chain event, no attribution data is available.
13763-
self.claim_funds_internal(
13764-
htlc_update.source,
13765-
preimage,
13766-
htlc_update.htlc_value_satoshis.map(|v| v * 1000),
13767-
None,
13768-
from_onchain,
13769-
counterparty_node_id,
13770-
funding_outpoint,
13771-
channel_id,
13772-
htlc_update.user_channel_id,
13773-
None,
13774-
None,
13775-
Some(event_id),
13776-
);
13793+
let we_are_sender =
13794+
matches!(htlc_update.source, HTLCSource::OutboundRoute { .. });
13795+
if from_onchain | we_are_sender {
13796+
// Claim the funds from the previous hop, if there is one. In the future we can
13797+
// store attribution data in the `ChannelMonitor` and provide it here.
13798+
self.claim_funds_internal(
13799+
htlc_update.source,
13800+
preimage,
13801+
htlc_update.htlc_value_satoshis.map(|v| v * 1000),
13802+
None,
13803+
from_onchain,
13804+
counterparty_node_id,
13805+
funding_outpoint,
13806+
channel_id,
13807+
htlc_update.user_channel_id,
13808+
None,
13809+
None,
13810+
Some(event_id),
13811+
);
13812+
}
13813+
if from_onchain | !we_are_sender {
13814+
self.chain_monitor.ack_monitor_event(monitor_event_source);
13815+
}
1377713816
} else {
1377813817
log_trace!(logger, "Failing HTLC from our monitor");
1377913818
let failure_reason = LocalHTLCFailureReason::OnChainTimeout;
@@ -13793,8 +13832,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1379313832
failure_type,
1379413833
completion_update,
1379513834
);
13835+
self.chain_monitor.ack_monitor_event(monitor_event_source);
1379613836
}
13797-
self.chain_monitor.ack_monitor_event(monitor_event_source);
1379813837
},
1379913838
MonitorEvent::HolderForceClosed(_)
1380013839
| MonitorEvent::HolderForceClosedWithInfo { .. } => {
@@ -19365,6 +19404,14 @@ impl<
1936519404
break;
1936619405
}
1936719406
}
19407+
if persistent_monitor_events {
19408+
// This will not be necessary once we have persistent events for HTLC failures, we
19409+
// can delete this whole loop and wait to re-process the pending monitor events
19410+
// rather than failing them proactively below.
19411+
if monitor.has_pending_event_for_htlc(&channel_htlc_source) {
19412+
found_htlc = true;
19413+
}
19414+
}
1936819415
if !found_htlc {
1936919416
// If we have some HTLCs in the channel which are not present in the newer
1937019417
// 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
@@ -3078,7 +3078,7 @@ macro_rules! expect_payment_sent {
30783078
$expected_payment_preimage,
30793079
$expected_fee_msat_opt.map(|o| Some(o)),
30803080
$expect_paths,
3081-
true,
3081+
if $node.node.test_persistent_monitor_events_enabled() { false } else { true },
30823082
)
30833083
};
30843084
}
@@ -4241,7 +4241,15 @@ pub fn claim_payment_along_route(
42414241
do_claim_payment_along_route(args) + expected_extra_total_fees_msat;
42424242

42434243
if !skip_last {
4244-
expect_payment_sent!(origin_node, payment_preimage, Some(expected_total_fee_msat))
4244+
let expect_post_ev_mon_update =
4245+
if origin_node.node.test_persistent_monitor_events_enabled() { false } else { true };
4246+
expect_payment_sent(
4247+
origin_node,
4248+
payment_preimage,
4249+
Some(Some(expected_total_fee_msat)),
4250+
true,
4251+
expect_post_ev_mon_update,
4252+
)
42454253
} else {
42464254
(None, Vec::new())
42474255
}

lightning/src/ln/functional_tests.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,10 @@ pub fn do_test_multiple_package_conflicts(p2a_anchor: bool) {
13611361
};
13621362
assert_eq!(updates.update_fulfill_htlcs.len(), 1);
13631363
nodes[0].node.handle_update_fulfill_htlc(node_b_id, updates.update_fulfill_htlcs.remove(0));
1364+
if nodes[0].node.test_persistent_monitor_events_enabled() {
1365+
// If persistent_monitor_events is enabled, the RAA monitor update is not blocked.
1366+
check_added_monitors(&nodes[0], 1);
1367+
}
13641368
do_commitment_signed_dance(&nodes[0], &nodes[1], &updates.commitment_signed, false, false);
13651369
expect_payment_sent!(nodes[0], preimage_2);
13661370

@@ -2675,7 +2679,10 @@ pub fn test_simple_peer_disconnect() {
26752679
_ => panic!("Unexpected event"),
26762680
}
26772681
}
2678-
check_added_monitors(&nodes[0], 1);
2682+
check_added_monitors(
2683+
&nodes[0],
2684+
if nodes[0].node.test_persistent_monitor_events_enabled() { 0 } else { 1 },
2685+
);
26792686

26802687
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage_4);
26812688
fail_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_hash_6);
@@ -4300,7 +4307,7 @@ pub fn test_duplicate_payment_hash_one_failure_one_success() {
43004307

43014308
nodes[0].node.handle_update_fulfill_htlc(node_b_id, updates.update_fulfill_htlcs.remove(0));
43024309
do_commitment_signed_dance(&nodes[0], &nodes[1], &updates.commitment_signed, false, false);
4303-
expect_payment_sent(&nodes[0], our_payment_preimage, None, true, true);
4310+
expect_payment_sent!(&nodes[0], our_payment_preimage);
43044311
}
43054312

43064313
#[xtest(feature = "_externalize_tests")]
@@ -8579,7 +8586,9 @@ pub fn test_inconsistent_mpp_params() {
85798586
pass_along_path(&nodes[0], path_b, real_amt, hash, Some(payment_secret), event, true, None);
85808587

85818588
do_claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], &[path_a, path_b], preimage));
8582-
expect_payment_sent(&nodes[0], preimage, Some(None), true, true);
8589+
let expect_post_ev_mon_update =
8590+
if nodes[0].node.test_persistent_monitor_events_enabled() { false } else { true };
8591+
expect_payment_sent(&nodes[0], preimage, Some(None), true, expect_post_ev_mon_update);
85838592
}
85848593

85858594
#[xtest(feature = "_externalize_tests")]
@@ -9941,7 +9950,10 @@ fn do_test_multi_post_event_actions(do_reload: bool) {
99419950
let chanmon_cfgs = create_chanmon_cfgs(3);
99429951
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
99439952
let (persister, chain_monitor);
9944-
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
9953+
let mut cfg = test_default_channel_config();
9954+
// If persistent_monitor_events is enabled, RAAs will not be blocked on events.
9955+
cfg.override_persistent_monitor_events = Some(false);
9956+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[Some(cfg), None, None]);
99459957
let node_a_reload;
99469958
let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
99479959

lightning/src/ln/invoice_utils.rs

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

13531353
#[test]

0 commit comments

Comments
 (0)