Skip to content

Commit 591144b

Browse files
Persistent monitor events for onchain 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. Here we start acking monitor events for on-chain HTLC claims when the user processes the PaymentSent event, if the persistent_monitor_events feature is enabled. This allows us to stop issuing ReleasePaymentComplete monitor updates for onchain payment claims, because the purpose of that behavior is to ensure we won't keep repeatedly issuing PaymentSent events, and the monitor event and acking it on PaymentSent processing now serves that purpose instead.
1 parent 8c4df02 commit 591144b

7 files changed

Lines changed: 78 additions & 46 deletions

File tree

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3954,7 +3954,7 @@ fn do_test_durable_preimages_on_closed_channel(
39543954

39553955
mine_transactions(&nodes[0], &[&as_closing_tx[0], bs_preimage_tx]);
39563956
check_closed_broadcast(&nodes[0], 1, false);
3957-
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
3957+
expect_payment_sent!(&nodes[0], payment_preimage);
39583958

39593959
if close_chans_before_reload && !hold_post_reload_mon_update {
39603960
// For close_chans_before_reload with hold=false, the deferred completions

lightning/src/ln/channelmanager.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10013,7 +10013,18 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1001310013
"We don't support claim_htlc claims during startup - monitors may not be available yet");
1001410014
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
1001510015

10016-
let mut ev_completion_action = if from_onchain {
10016+
let mut ev_completion_action = if self.persistent_monitor_events {
10017+
// If persistent monitor events is enabled:
10018+
// * If the channel is on-chain, we don't need to use ReleasePaymentComplete like we do
10019+
// below because we will stop hearing about this payment after the relevant monitor event
10020+
// is acked
10021+
// * If the channel is open, we don't need to block the preimage-removing monitor update
10022+
// like we do below because we will keep hearing about the preimage until we explicitly
10023+
// ack the monitor event for this payment
10024+
monitor_event_id.map(|event_id| EventCompletionAction::AckMonitorEvent {
10025+
event_id: MonitorEventSource { channel_id: next_channel_id, event_id },
10026+
})
10027+
} else if from_onchain {
1001710028
let release = PaymentCompleteUpdate {
1001810029
counterparty_node_id: next_channel_counterparty_node_id,
1001910030
channel_funding_outpoint: next_channel_outpoint,
@@ -10022,17 +10033,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1002210033
};
1002310034
Some(EventCompletionAction::ReleasePaymentCompleteChannelMonitorUpdate(release))
1002410035
} else {
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-
}
10036+
Some(EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
10037+
channel_funding_outpoint: Some(next_channel_outpoint),
10038+
channel_id: next_channel_id,
10039+
counterparty_node_id: path.hops[0].pubkey,
10040+
})
1003610041
};
1003710042
let logger = WithContext::for_payment(
1003810043
&self.logger,
@@ -13640,7 +13645,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1364013645
Some(event_id),
1364113646
);
1364213647
}
13643-
if from_onchain | !we_are_sender {
13648+
if !we_are_sender {
1364413649
self.chain_monitor.ack_monitor_event(monitor_event_source);
1364513650
}
1364613651
} else {
@@ -19730,6 +19735,12 @@ impl<
1973019735
..
1973119736
} => {
1973219737
if let Some(preimage) = preimage_opt {
19738+
if persistent_monitor_events {
19739+
// If persistent_monitor_events is enabled, then the monitor will keep
19740+
// providing us with a monitor event for this claim until the ChannelManager
19741+
// explicitly marks it as resolved, no need to explicitly handle it here.
19742+
continue;
19743+
}
1973319744
let pending_events = Mutex::new(pending_events_read);
1973419745
let update = PaymentCompleteUpdate {
1973519746
counterparty_node_id: monitor.get_counterparty_node_id(),

lightning/src/ln/functional_tests.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1629,7 +1629,10 @@ pub fn test_htlc_on_chain_success() {
16291629
check_closed_broadcast(&nodes[0], 1, true);
16301630
check_added_monitors(&nodes[0], 1);
16311631
let events = nodes[0].node.get_and_clear_pending_events();
1632-
check_added_monitors(&nodes[0], 2);
1632+
check_added_monitors(
1633+
&nodes[0],
1634+
if nodes[0].node.test_persistent_monitor_events_enabled() { 0 } else { 2 },
1635+
);
16331636
assert_eq!(events.len(), 5);
16341637
let mut first_claimed = false;
16351638
for event in events {

lightning/src/ln/monitor_tests.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ fn archive_fully_resolved_monitors() {
274274

275275
// Finally, we process the pending `MonitorEvent` from nodes[0], allowing the `ChannelMonitor`
276276
// to be archived `ARCHIVAL_DELAY_BLOCKS` blocks later.
277-
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
277+
expect_payment_sent!(&nodes[0], payment_preimage);
278278
nodes[0].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors();
279279
assert_eq!(nodes[0].chain_monitor.chain_monitor.list_monitors().len(), 1);
280280
connect_blocks(&nodes[0], ARCHIVAL_DELAY_BLOCKS - 1);
@@ -747,9 +747,9 @@ fn do_test_claim_value_force_close(keyed_anchors: bool, p2a_anchor: bool, prev_c
747747
mine_transaction(&nodes[0], &b_broadcast_txn[0]);
748748
if prev_commitment_tx {
749749
expect_payment_path_successful!(nodes[0]);
750-
check_added_monitors(&nodes[0], 1);
750+
check_added_monitors(&nodes[0], if nodes[0].node.test_persistent_monitor_events_enabled() { 0 } else { 1 });
751751
} else {
752-
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
752+
expect_payment_sent!(&nodes[0], payment_preimage);
753753
}
754754
assert_eq!(sorted_vec(vec![sent_htlc_balance.clone(), sent_htlc_timeout_balance.clone()]),
755755
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(chan_id).unwrap().get_claimable_balances()));
@@ -1015,7 +1015,7 @@ fn do_test_balances_on_local_commitment_htlcs(keyed_anchors: bool, p2a_anchor: b
10151015
// Now confirm nodes[1]'s HTLC claim, giving nodes[0] the preimage. Note that the "maybe
10161016
// claimable" balance remains until we see ANTI_REORG_DELAY blocks.
10171017
mine_transaction(&nodes[0], &bs_htlc_claim_txn[0]);
1018-
expect_payment_sent(&nodes[0], payment_preimage_2, None, true, true);
1018+
expect_payment_sent!(&nodes[0], payment_preimage_2);
10191019
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
10201020
amount_satoshis: 1_000_000 - 10_000 - 20_000 - commitment_tx_fee - anchor_outputs_value,
10211021
confirmation_height: node_a_commitment_claimable,
@@ -2097,7 +2097,7 @@ fn do_test_revoked_counterparty_aggregated_claims(keyed_anchors: bool, p2a_ancho
20972097
as_revoked_txn[1].clone()
20982098
};
20992099
mine_transaction(&nodes[1], &htlc_success_claim);
2100-
expect_payment_sent(&nodes[1], claimed_payment_preimage, None, true, true);
2100+
expect_payment_sent!(&nodes[1], claimed_payment_preimage);
21012101

21022102
let mut claim_txn_2 = nodes[1].tx_broadcaster.txn_broadcast();
21032103
// Once B sees the HTLC-Success transaction it splits its claim transaction into two, though in
@@ -3547,9 +3547,13 @@ fn do_test_lost_preimage_monitor_events(on_counterparty_tx: bool, p2a_anchor: bo
35473547
// After the background events are processed in `get_and_clear_pending_events`, above, node B
35483548
// will create the requisite `ChannelMontiorUpdate` for claiming the forwarded payment back.
35493549
// The HTLC, however, is added to the holding cell for replay after the peer connects, below.
3550-
// It will also apply a `ChannelMonitorUpdate` to let the `ChannelMonitor` know that the
3551-
// payment can now be forgotten as the `PaymentSent` event was handled.
3552-
check_added_monitors(&nodes[1], 2);
3550+
if nodes[1].node.test_persistent_monitor_events_enabled() {
3551+
check_added_monitors(&nodes[1], 1);
3552+
} else {
3553+
// It will also apply a `ChannelMonitorUpdate` to let the `ChannelMonitor` know that the
3554+
// payment can now be forgotten as the `PaymentSent` event was handled.
3555+
check_added_monitors(&nodes[1], 2);
3556+
}
35533557

35543558
nodes[0].node.peer_disconnected(nodes[1].node.get_our_node_id());
35553559

@@ -3943,8 +3947,7 @@ fn test_ladder_preimage_htlc_claims() {
39433947
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
39443948
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
39453949

3946-
expect_payment_sent(&nodes[0], payment_preimage1, None, true, false);
3947-
check_added_monitors(&nodes[0], 1);
3950+
expect_payment_sent!(&nodes[0], payment_preimage1);
39483951

39493952
nodes[1].node.claim_funds(payment_preimage2);
39503953
expect_payment_claimed!(&nodes[1], payment_hash2, 1_000_000);
@@ -3966,6 +3969,5 @@ fn test_ladder_preimage_htlc_claims() {
39663969
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
39673970
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
39683971

3969-
expect_payment_sent(&nodes[0], payment_preimage2, None, true, false);
3970-
check_added_monitors(&nodes[0], 1);
3972+
expect_payment_sent!(&nodes[0], payment_preimage2);
39713973
}

lightning/src/ln/payment_tests.rs

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) {
957957
assert_eq!(txn[0].compute_txid(), as_commitment_tx.compute_txid());
958958
}
959959
mine_transaction(&nodes[0], &bs_htlc_claim_txn);
960-
expect_payment_sent(&nodes[0], payment_preimage_1, None, true, true);
960+
expect_payment_sent!(&nodes[0], payment_preimage_1);
961961
connect_blocks(&nodes[0], TEST_FINAL_CLTV * 4 + 20);
962962
let (first_htlc_timeout_tx, second_htlc_timeout_tx) = {
963963
let mut txn = nodes[0].tx_broadcaster.unique_txn_broadcast();
@@ -1368,7 +1368,7 @@ fn do_test_dup_htlc_onchain_doesnt_fail_on_reload(
13681368
let conditions = PaymentFailedConditions::new().from_mon_update();
13691369
expect_payment_failed_conditions(&nodes[0], payment_hash, false, conditions);
13701370
} else {
1371-
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
1371+
expect_payment_sent!(&nodes[0], payment_preimage);
13721372
}
13731373
// Note that if we persist the monitor before processing the events, above, we'll always get
13741374
// them replayed on restart no matter what
@@ -1406,18 +1406,22 @@ fn do_test_dup_htlc_onchain_doesnt_fail_on_reload(
14061406
} else {
14071407
expect_payment_sent(&nodes[0], payment_preimage, None, true, false);
14081408
}
1409-
if persist_manager_post_event {
1410-
// After reload, the ChannelManager identified the failed payment and queued up the
1411-
// PaymentSent (or not, if `persist_manager_post_event` resulted in us detecting we
1412-
// already did that) and corresponding ChannelMonitorUpdate to mark the payment
1413-
// handled, but while processing the pending `MonitorEvent`s (which were not processed
1414-
// before the monitor was persisted) we will end up with a duplicate
1415-
// ChannelMonitorUpdate.
1416-
check_added_monitors(&nodes[0], 2);
1409+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
1410+
if persist_manager_post_event {
1411+
// After reload, the ChannelManager identified the failed payment and queued up the
1412+
// PaymentSent (or not, if `persist_manager_post_event` resulted in us detecting we
1413+
// already did that) and corresponding ChannelMonitorUpdate to mark the payment
1414+
// handled, but while processing the pending `MonitorEvent`s (which were not processed
1415+
// before the monitor was persisted) we will end up with a duplicate
1416+
// ChannelMonitorUpdate.
1417+
check_added_monitors(&nodes[0], 2);
1418+
} else {
1419+
// ...unless we got the PaymentSent event, in which case we have de-duplication logic
1420+
// preventing a redundant monitor event.
1421+
check_added_monitors(&nodes[0], 1);
1422+
}
14171423
} else {
1418-
// ...unless we got the PaymentSent event, in which case we have de-duplication logic
1419-
// preventing a redundant monitor event.
1420-
check_added_monitors(&nodes[0], 1);
1424+
check_added_monitors(&nodes[0], 0);
14211425
}
14221426
}
14231427

@@ -4198,10 +4202,14 @@ fn do_no_missing_sent_on_reload(persist_manager_with_payment: bool, at_midpoint:
41984202
check_added_monitors(&nodes[0], 0);
41994203
nodes[0].node.test_process_background_events();
42004204
check_added_monitors(&nodes[0], 1);
4201-
// Then once we process the PaymentSent event we'll apply a monitor update to remove the
4202-
// pending payment from being re-hydrated on the next startup.
42034205
let events = nodes[0].node.get_and_clear_pending_events();
4204-
check_added_monitors(&nodes[0], 1);
4206+
if nodes[0].node.test_persistent_monitor_events_enabled() {
4207+
check_added_monitors(&nodes[0], 0);
4208+
} else {
4209+
// Once we process the PaymentSent event we'll apply a monitor update to remove the
4210+
// pending payment from being re-hydrated on the next startup.
4211+
check_added_monitors(&nodes[0], 1);
4212+
}
42054213
assert_eq!(events.len(), 3, "{events:?}");
42064214
if let Event::ChannelClosed { reason: ClosureReason::OutdatedChannelManager, .. } = events[0] {
42074215
} else {

lightning/src/ln/reorg_tests.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ fn test_counterparty_revoked_reorg() {
254254

255255
// Connect the HTLC claim transaction for HTLC 3
256256
mine_transaction(&nodes[1], &unrevoked_local_txn[2]);
257-
expect_payment_sent(&nodes[1], payment_preimage_3, None, true, true);
257+
expect_payment_sent!(&nodes[1], payment_preimage_3);
258258
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
259259

260260
// Connect blocks to confirm the unrevoked commitment transaction
@@ -1228,7 +1228,10 @@ fn do_test_split_htlc_expiry_tracking(use_third_htlc: bool, reorg_out: bool, p2a
12281228

12291229
check_added_monitors(&nodes[0], 0);
12301230
let sent_events = nodes[0].node.get_and_clear_pending_events();
1231-
check_added_monitors(&nodes[0], 2);
1231+
check_added_monitors(
1232+
&nodes[0],
1233+
if nodes[0].node.test_persistent_monitor_events_enabled() { 0 } else { 2 },
1234+
);
12321235
assert_eq!(sent_events.len(), 4, "{sent_events:?}");
12331236
let mut found_expected_events = [false, false, false, false];
12341237
for event in sent_events {

lightning/src/ln/splicing_tests.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1984,7 +1984,12 @@ fn do_test_splice_commitment_broadcast(splice_status: SpliceStatus, claim_htlcs:
19841984
2
19851985
);
19861986
}
1987-
check_added_monitors(&nodes[0], 2); // Two `ReleasePaymentComplete` monitor updates
1987+
let expected_mons = if nodes[0].node.test_persistent_monitor_events_enabled() && claim_htlcs {
1988+
0
1989+
} else {
1990+
2 // Two `ReleasePaymentComplete` monitor updates
1991+
};
1992+
check_added_monitors(&nodes[0], expected_mons);
19881993

19891994
// When the splice never confirms and we see a commitment transaction broadcast and confirm for
19901995
// the current funding instead, we should expect to see an `Event::DiscardFunding` for the

0 commit comments

Comments
 (0)