Skip to content

Commit 2c447eb

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 b2a70a8 commit 2c447eb

13 files changed

Lines changed: 231 additions & 71 deletions

lightning/src/chain/channelmonitor.rs

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

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

38273831
for (claimed_htlc_id, claimed_preimage) in claimed_htlcs {
3828-
#[cfg(debug_assertions)]
3829-
{
3830-
let cur_counterparty_htlcs = self
3831-
.funding
3832-
.counterparty_claimable_outpoints
3833-
.get(&self.funding.current_counterparty_commitment_txid.unwrap())
3834-
.unwrap();
3835-
assert!(cur_counterparty_htlcs.iter().any(|(_, source_opt)| {
3832+
let htlc_opt = self
3833+
.funding
3834+
.counterparty_claimable_outpoints
3835+
.get(&self.funding.current_counterparty_commitment_txid.unwrap())
3836+
.unwrap()
3837+
.iter()
3838+
.find_map(|(htlc, source_opt)| {
38363839
if let Some(source) = source_opt {
3837-
SentHTLCId::from_source(source) == *claimed_htlc_id
3838-
} else {
3839-
false
3840+
if SentHTLCId::from_source(source) == *claimed_htlc_id {
3841+
return Some((htlc, source));
3842+
}
38403843
}
3841-
}));
3844+
None
3845+
});
3846+
debug_assert!(htlc_opt.is_some());
3847+
if self.persistent_events_enabled {
3848+
if let Some((htlc, source)) = htlc_opt {
3849+
self.push_monitor_event(MonitorEvent::HTLCEvent(HTLCUpdate {
3850+
payment_hash: htlc.payment_hash,
3851+
payment_preimage: Some(*claimed_preimage),
3852+
source: *source.clone(),
3853+
htlc_value_satoshis: Some(htlc.amount_msat),
3854+
user_channel_id: self.user_channel_id,
3855+
}));
3856+
}
38423857
}
38433858
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
38443859
}
@@ -4656,6 +4671,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46564671
self.pending_monitor_events.retain(|(id, _)| *id != event_id);
46574672
}
46584673

4674+
fn has_pending_event_for_htlc(&self, htlc: &HTLCSource) -> bool {
4675+
let htlc_id = SentHTLCId::from_source(htlc);
4676+
self.pending_monitor_events.iter().any(|(_, ev)| {
4677+
if let MonitorEvent::HTLCEvent(upd) = ev {
4678+
return htlc_id == SentHTLCId::from_source(&upd.source);
4679+
}
4680+
false
4681+
})
4682+
}
4683+
46594684
fn get_and_clear_pending_monitor_events(&mut self) -> Vec<(u64, MonitorEvent)> {
46604685
if self.persistent_events_enabled {
46614686
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
@@ -846,7 +846,7 @@ fn do_blinded_intercept_payment(intercept_node_fails: bool) {
846846
do_claim_payment_along_route(
847847
ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
848848
);
849-
expect_payment_sent(&nodes[0], payment_preimage, Some(Some(1000)), true, true);
849+
expect_payment_sent!(nodes[0], payment_preimage, Some(1000));
850850
}
851851

852852
#[test]
@@ -1391,7 +1391,7 @@ fn conditionally_round_fwd_amt() {
13911391
let mut args = ClaimAlongRouteArgs::new(&nodes[0], &expected_route[..], payment_preimage)
13921392
.allow_1_msat_fee_overpay();
13931393
let expected_fee = pass_claimed_payment_along_route(args);
1394-
expect_payment_sent(&nodes[0], payment_preimage, Some(Some(expected_fee)), true, true);
1394+
expect_payment_sent!(nodes[0], payment_preimage, Some(expected_fee));
13951395
}
13961396

13971397
#[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(
@@ -4931,6 +4934,7 @@ fn native_async_persist() {
49314934
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
49324935
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
49334936
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
4937+
let persistent_monitor_events = nodes[0].node.test_persistent_monitor_events_enabled();
49344938

49354939
let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
49364940

@@ -5076,7 +5080,16 @@ fn native_async_persist() {
50765080
assert_eq!(update_status, ChannelMonitorUpdateStatus::InProgress);
50775081

50785082
persist_futures.poll_futures();
5079-
assert_eq!(async_chain_monitor.release_pending_monitor_events().len(), 0);
5083+
let events = async_chain_monitor.release_pending_monitor_events();
5084+
if persistent_monitor_events {
5085+
// With persistent monitor events, the LatestHolderCommitmentTXInfo update containing
5086+
// claimed_htlcs generates an HTLCEvent with the preimage.
5087+
assert_eq!(events.len(), 1);
5088+
assert_eq!(events[0].2.len(), 1);
5089+
assert!(matches!(events[0].2[0].1, MonitorEvent::HTLCEvent(..)));
5090+
} else {
5091+
assert!(events.is_empty());
5092+
}
50805093

50815094
let pending_writes = kv_store.list_pending_async_writes(
50825095
CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE,
@@ -5218,7 +5231,7 @@ fn test_mpp_claim_to_holding_cell() {
52185231
let claims = vec![(b_claim_msgs, node_b_id), (c_claim_msgs, node_c_id)];
52195232
pass_claimed_payment_along_route_from_ev(250_000, claims, args);
52205233

5221-
expect_payment_sent(&nodes[0], preimage_1, None, true, true);
5234+
expect_payment_sent!(nodes[0], preimage_1);
52225235

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

lightning/src/ln/channelmanager.rs

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10035,11 +10035,17 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1003510035
};
1003610036
Some(EventCompletionAction::ReleasePaymentCompleteChannelMonitorUpdate(release))
1003710037
} else {
10038-
Some(EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
10039-
channel_funding_outpoint: Some(next_channel_outpoint),
10040-
channel_id: next_channel_id,
10041-
counterparty_node_id: path.hops[0].pubkey,
10042-
})
10038+
if self.persistent_monitor_events {
10039+
monitor_event_id.map(|event_id| EventCompletionAction::AckMonitorEvent {
10040+
event_id: MonitorEventSource { channel_id: next_channel_id, event_id },
10041+
})
10042+
} else {
10043+
Some(EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
10044+
channel_funding_outpoint: Some(next_channel_outpoint),
10045+
channel_id: next_channel_id,
10046+
counterparty_node_id: path.hops[0].pubkey,
10047+
})
10048+
}
1004310049
};
1004410050
let logger = WithContext::for_payment(
1004510051
&self.logger,
@@ -10061,6 +10067,25 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1006110067
&self.pending_events,
1006210068
&logger,
1006310069
);
10070+
10071+
if matches!(
10072+
ev_completion_action,
10073+
Some(EventCompletionAction::AckMonitorEvent { .. })
10074+
) {
10075+
// If the `PaymentSent` for this redundant claim is still pending, add the event
10076+
// completion action here to ensure the `PaymentSent` will always be regenerated until it
10077+
// is processed by the user -- as long as the monitor event corresponding to this
10078+
// completion action is not acked, it will continue to be re-provided on startup.
10079+
let mut pending_events = self.pending_events.lock().unwrap();
10080+
for (ev, act_opt) in pending_events.iter_mut() {
10081+
let found_payment_sent = matches!(ev, Event::PaymentSent { payment_id: Some(id), .. } if *id == payment_id);
10082+
if found_payment_sent && act_opt.is_none() {
10083+
*act_opt = ev_completion_action.take();
10084+
break;
10085+
}
10086+
}
10087+
}
10088+
1006410089
// If an event was generated, `claim_htlc` set `ev_completion_action` to None, if
1006510090
// not, we should go ahead and run it now (as the claim was duplicative), at least
1006610091
// if a PaymentClaimed event with the same action isn't already pending.
@@ -12895,6 +12920,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1289512920
})
1289612921
}
1289712922

12923+
#[cfg(test)]
12924+
pub(crate) fn test_persistent_monitor_events_enabled(&self) -> bool {
12925+
self.persistent_monitor_events
12926+
}
12927+
1289812928
#[cfg(any(test, feature = "_test_utils"))]
1289912929
pub(crate) fn test_raa_monitor_updates_held(
1290012930
&self, counterparty_node_id: PublicKey, channel_id: ChannelId,
@@ -13601,22 +13631,29 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1360113631
.channel_by_id
1360213632
.contains_key(&channel_id)
1360313633
});
13604-
// Claim the funds from the previous hop, if there is one. Because this is in response to a
13605-
// chain event, no attribution data is available.
13606-
self.claim_funds_internal(
13607-
htlc_update.source,
13608-
preimage,
13609-
htlc_update.htlc_value_satoshis.map(|v| v * 1000),
13610-
None,
13611-
from_onchain,
13612-
counterparty_node_id,
13613-
funding_outpoint,
13614-
channel_id,
13615-
htlc_update.user_channel_id,
13616-
None,
13617-
None,
13618-
Some(event_id),
13619-
);
13634+
let we_are_sender =
13635+
matches!(htlc_update.source, HTLCSource::OutboundRoute { .. });
13636+
if from_onchain | we_are_sender {
13637+
// Claim the funds from the previous hop, if there is one. Because this is in response to a
13638+
// chain event, no attribution data is available.
13639+
self.claim_funds_internal(
13640+
htlc_update.source,
13641+
preimage,
13642+
htlc_update.htlc_value_satoshis.map(|v| v * 1000),
13643+
None,
13644+
from_onchain,
13645+
counterparty_node_id,
13646+
funding_outpoint,
13647+
channel_id,
13648+
htlc_update.user_channel_id,
13649+
None,
13650+
None,
13651+
Some(event_id),
13652+
);
13653+
}
13654+
if from_onchain | !we_are_sender {
13655+
self.chain_monitor.ack_monitor_event(monitor_event_source);
13656+
}
1362013657
} else {
1362113658
log_trace!(logger, "Failing HTLC from our monitor");
1362213659
let failure_reason = LocalHTLCFailureReason::OnChainTimeout;
@@ -13636,8 +13673,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1363613673
failure_type,
1363713674
completion_update,
1363813675
);
13676+
self.chain_monitor.ack_monitor_event(monitor_event_source);
1363913677
}
13640-
self.chain_monitor.ack_monitor_event(monitor_event_source);
1364113678
},
1364213679
MonitorEvent::HolderForceClosed(_)
1364313680
| MonitorEvent::HolderForceClosedWithInfo { .. } => {
@@ -19185,6 +19222,14 @@ impl<
1918519222
break;
1918619223
}
1918719224
}
19225+
if persistent_monitor_events {
19226+
// This will not be necessary once we have persistent events for HTLC failures, we
19227+
// can delete this whole loop and wait to re-process the pending monitor events
19228+
// rather than failing them proactively below.
19229+
if monitor.has_pending_event_for_htlc(&channel_htlc_source) {
19230+
found_htlc = true;
19231+
}
19232+
}
1918819233
if !found_htlc {
1918919234
// If we have some HTLCs in the channel which are not present in the newer
1919019235
// 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
@@ -3088,7 +3088,7 @@ macro_rules! expect_payment_sent {
30883088
$expected_payment_preimage,
30893089
$expected_fee_msat_opt.map(|o| Some(o)),
30903090
$expect_paths,
3091-
true,
3091+
if $node.node.test_persistent_monitor_events_enabled() { false } else { true },
30923092
)
30933093
};
30943094
}
@@ -4249,7 +4249,15 @@ pub fn claim_payment_along_route(
42494249
do_claim_payment_along_route(args) + expected_extra_total_fees_msat;
42504250

42514251
if !skip_last {
4252-
expect_payment_sent!(origin_node, payment_preimage, Some(expected_total_fee_msat))
4252+
let expect_post_ev_mon_update =
4253+
if origin_node.node.test_persistent_monitor_events_enabled() { false } else { true };
4254+
expect_payment_sent(
4255+
origin_node,
4256+
payment_preimage,
4257+
Some(Some(expected_total_fee_msat)),
4258+
true,
4259+
expect_post_ev_mon_update,
4260+
)
42534261
} else {
42544262
(None, Vec::new())
42554263
}

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")]
@@ -8569,7 +8576,9 @@ pub fn test_inconsistent_mpp_params() {
85698576
pass_along_path(&nodes[0], path_b, real_amt, hash, Some(payment_secret), event, true, None);
85708577

85718578
do_claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], &[path_a, path_b], preimage));
8572-
expect_payment_sent(&nodes[0], preimage, Some(None), true, true);
8579+
let expect_post_ev_mon_update =
8580+
if nodes[0].node.test_persistent_monitor_events_enabled() { false } else { true };
8581+
expect_payment_sent(&nodes[0], preimage, Some(None), true, expect_post_ev_mon_update);
85738582
}
85748583

85758584
#[xtest(feature = "_externalize_tests")]
@@ -9921,7 +9930,10 @@ fn do_test_multi_post_event_actions(do_reload: bool) {
99219930
let chanmon_cfgs = create_chanmon_cfgs(3);
99229931
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
99239932
let (persister, chain_monitor);
9924-
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
9933+
let mut cfg = test_default_channel_config();
9934+
// If persistent_monitor_events is enabled, RAAs will not be blocked on events.
9935+
cfg.override_persistent_monitor_events = Some(false);
9936+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[Some(cfg), None, None]);
99259937
let node_a_reload;
99269938
let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
99279939

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)