Skip to content

Commit cf346fd

Browse files
Persistent monitor events for onchain outbound fails
We are in the process of moving the resolution of all HTLCs from the Channel to the ChannelMonitor, via persistent MonitorEvents. This will simplify how we reconstruct/reconcile the ChannelManager's pending HTLC set on startup and avoid needing to persist pending HTLCs explicitly in the manager, as we can just replay pending monitor events to reconstruct the set instead. In recent commits, we started generating persistent monitor events whenever an HTLC is failed off-chain. We also made those monitor events the sole driver of off-chain outbound payment failures, failing those HTLCs when the monitor event is processed. This replaced the previous behavior of failing when we initially receive update_fail_htlc from our peer. In this commit, we continue that work by making persistent monitor events the sole drivers of *on*chain outbound payment failures. When persistent_monitor_events is enabled, we will skip existing failure paths for on-chain outbound HTLCs, and instead (a) fail them when the monitor event is processed, and (b) similar to what we do for claims, the monitor event will not be acked and will continue to be re-provided on startup until the resulting PaymentFailed event is processed by the user. Forward HTLC failure paths are unchanged and will be migrated separately in upcoming work.
1 parent f5c926a commit cf346fd

9 files changed

Lines changed: 79 additions & 36 deletions

lightning/src/chain/channelmonitor.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3343,7 +3343,25 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
33433343
for &(ref htlc, ref source_option) in latest_outpoints.iter() {
33443344
if let &Some(ref source) = source_option {
33453345
let htlc_id = SentHTLCId::from_source(source);
3346-
if !us.htlcs_resolved_to_user.contains(&htlc_id) {
3346+
let skip_htlc = if us.persistent_events_enabled {
3347+
let htlc_resolved_on_chain = match htlc.transaction_output_index {
3348+
Some(idx) => us
3349+
.htlcs_resolved_on_chain
3350+
.iter()
3351+
.any(|r| r.commitment_tx_output_idx == Some(idx)),
3352+
None => {
3353+
// Dust HTLCs are implicitly resolved when the commitment reaches
3354+
// ANTI_REORG_DELAY
3355+
us.confirmed_funding_spend_past_anti_reorg().is_some()
3356+
},
3357+
};
3358+
// If the HTLC was resolved on-chain and we don't have a pending monitor event for
3359+
// it, that implies the event was acked due to the HTLC being resolved to the user.
3360+
htlc_resolved_on_chain && !us.has_pending_event_for_htlc(source)
3361+
} else {
3362+
us.htlcs_resolved_to_user.contains(&htlc_id)
3363+
};
3364+
if !skip_htlc {
33473365
let preimage_opt =
33483366
us.counterparty_fulfilled_htlcs.get(&htlc_id).cloned();
33493367
res.insert((**source).clone(), (htlc.clone(), preimage_opt));
@@ -7542,7 +7560,10 @@ mod tests {
75427560
assert!(events.iter().any(|e| matches!(e, Event::PaymentPathFailed { .. })));
75437561
assert!(events.iter().any(|e| matches!(e, Event::PaymentFailed { .. })));
75447562
assert!(events.iter().any(|e| matches!(e, Event::ChannelClosed { .. })));
7545-
check_added_monitors(&nodes[1], 2);
7563+
// If persistent monitor events are enabled, there won't be a ReleasePaymentComplete monitor
7564+
// update.
7565+
let persistent_mon_evs = nodes[1].node.test_persistent_monitor_events_enabled();
7566+
check_added_monitors(&nodes[1], if persistent_mon_evs { 1 } else { 2 });
75467567
} else {
75477568
check_closed_event(&nodes[1], 1, ClosureReason::CommitmentTxConfirmed, &[nodes[0].node.get_our_node_id()], 100000);
75487569
check_added_monitors(&nodes[1], 1);

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5775,8 +5775,10 @@ fn do_test_late_counterparty_commitment_update_after_funding_spend(fully_confirm
57755775
false,
57765776
PaymentFailedConditions::new(),
57775777
);
5778-
// The payment failure generates a ReleasePaymentComplete monitor update.
5779-
check_added_monitors(&nodes[0], 1);
5778+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
5779+
// The payment failure generates a ReleasePaymentComplete monitor update.
5780+
check_added_monitors(&nodes[0], 1);
5781+
}
57805782
}
57815783

57825784
#[test]
@@ -5877,7 +5879,9 @@ fn do_test_late_counterparty_commitment_update_after_holder_commitment_spend(dus
58775879
false,
58785880
PaymentFailedConditions::new(),
58795881
);
5880-
check_added_monitors(&nodes[0], 1);
5882+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
5883+
check_added_monitors(&nodes[0], 1); // ReleasePaymentComplete monitor update
5884+
}
58815885

58825886
// Verify HTLC X was NOT failed (no payment failure event for it at this point).
58835887
assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
@@ -5901,7 +5905,9 @@ fn do_test_late_counterparty_commitment_update_after_holder_commitment_spend(dus
59015905
false,
59025906
PaymentFailedConditions::new(),
59035907
);
5904-
check_added_monitors(&nodes[0], 1);
5908+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
5909+
check_added_monitors(&nodes[0], 1); // ReleasePaymentComplete monitor update
5910+
}
59055911
}
59065912

59075913
#[test]

lightning/src/ln/channelmanager.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13986,20 +13986,19 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1398613986
.source
1398713987
.failure_type(counterparty_node_id, channel_id);
1398813988

13989-
let completion_update = if self.persistent_monitor_events
13990-
&& we_are_sender && !from_onchain
13991-
{
13992-
EventCompletionAction::AckMonitorEvent {
13993-
event_id: monitor_event_source,
13994-
}
13995-
} else {
13996-
EventCompletionAction::ReleasePaymentCompleteChannelMonitorUpdate(PaymentCompleteUpdate {
13997-
counterparty_node_id,
13998-
channel_funding_outpoint: funding_outpoint,
13999-
channel_id,
14000-
htlc_id: SentHTLCId::from_source(&htlc_update.source),
14001-
})
14002-
};
13989+
let completion_update =
13990+
if self.persistent_monitor_events && we_are_sender {
13991+
EventCompletionAction::AckMonitorEvent {
13992+
event_id: monitor_event_source,
13993+
}
13994+
} else {
13995+
EventCompletionAction::ReleasePaymentCompleteChannelMonitorUpdate(PaymentCompleteUpdate {
13996+
counterparty_node_id,
13997+
channel_funding_outpoint: funding_outpoint,
13998+
channel_id,
13999+
htlc_id: SentHTLCId::from_source(&htlc_update.source),
14000+
})
14001+
};
1400314002

1400414003
self.fail_htlc_backwards_internal(
1400514004
&htlc_update.source,
@@ -14009,7 +14008,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1400914008
Some(completion_update),
1401014009
);
1401114010
}
14012-
if from_onchain | !we_are_sender {
14011+
if !we_are_sender {
1401314012
self.chain_monitor.ack_monitor_event(monitor_event_source);
1401414013
}
1401514014
},
@@ -20903,6 +20902,13 @@ impl<
2090320902
htlc_source;
2090420903
let failure_type = source.failure_type(counterparty_id, channel_id);
2090520904
let reason = HTLCFailReason::from_failure_code(failure_reason);
20905+
if channel_manager.persistent_monitor_events
20906+
&& matches!(source, HTLCSource::OutboundRoute { .. })
20907+
{
20908+
// The persistent monitor event generated for the failed HTLC will drive the failure
20909+
// instead.
20910+
continue;
20911+
}
2090620912
channel_manager.fail_htlc_backwards_internal(
2090720913
&source,
2090820914
&hash,

lightning/src/ln/functional_test_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3486,8 +3486,8 @@ pub fn expect_payment_failed_conditions<'a, 'b, 'c, 'd, 'e>(
34863486
check_added_monitors(node, 0);
34873487
}
34883488
let events = node.node.get_and_clear_pending_events();
3489-
if conditions.from_mon_update {
3490-
check_added_monitors(node, 1);
3489+
if conditions.from_mon_update && !node.node.test_persistent_monitor_events_enabled() {
3490+
check_added_monitors(node, 1); // ReleasePaymentComplete update
34913491
}
34923492
expect_payment_failed_conditions_event(
34933493
events,

lightning/src/ln/functional_tests.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,7 +2066,8 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(
20662066
check_added_monitors(&nodes[1], 0);
20672067
let events = nodes[1].node.get_and_clear_pending_events();
20682068
if deliver_bs_raa {
2069-
check_added_monitors(&nodes[1], 2);
2069+
let persistent_mon_evs = nodes[1].node.test_persistent_monitor_events_enabled();
2070+
check_added_monitors(&nodes[1], if persistent_mon_evs { 1 } else { 2 });
20702071
} else {
20712072
check_added_monitors(&nodes[1], 1);
20722073
}
@@ -5890,7 +5891,9 @@ fn do_test_failure_delay_dust_htlc_local_commitment(announce_latest: bool) {
58905891
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
58915892
check_added_monitors(&nodes[0], 0);
58925893
let events = nodes[0].node.get_and_clear_pending_events();
5893-
check_added_monitors(&nodes[0], 2);
5894+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
5895+
check_added_monitors(&nodes[0], 2); // ReleasePaymentComplete updates
5896+
}
58945897
// Only 2 PaymentPathFailed events should show up, over-dust HTLC has to be failed by timeout tx
58955898
assert_eq!(events.len(), 4);
58965899
let mut first_failed = false;

lightning/src/ln/monitor_tests.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,7 +1622,9 @@ fn do_test_revoked_counterparty_commitment_balances(keyed_anchors: bool, p2a_anc
16221622

16231623
check_added_monitors(&nodes[1], 0);
16241624
let mut payment_failed_events = nodes[1].node.get_and_clear_pending_events();
1625-
check_added_monitors(&nodes[1], 2);
1625+
if !nodes[1].node.test_persistent_monitor_events_enabled() {
1626+
check_added_monitors(&nodes[1], 2); // ReleasePaymentComplete monitor updates
1627+
}
16261628
expect_payment_failed_conditions_event(payment_failed_events[..2].to_vec(),
16271629
missing_htlc_payment_hash, false, PaymentFailedConditions::new());
16281630
expect_payment_failed_conditions_event(payment_failed_events[2..].to_vec(),
@@ -1633,7 +1635,9 @@ fn do_test_revoked_counterparty_commitment_balances(keyed_anchors: bool, p2a_anc
16331635
test_spendable_output(&nodes[1], &claim_txn[0], false);
16341636
check_added_monitors(&nodes[1], 0);
16351637
let mut payment_failed_events = nodes[1].node.get_and_clear_pending_events();
1636-
check_added_monitors(&nodes[1], 2);
1638+
if !nodes[1].node.test_persistent_monitor_events_enabled() {
1639+
check_added_monitors(&nodes[1], 2); // ReleasePaymentComplete monitor updates
1640+
}
16371641
expect_payment_failed_conditions_event(payment_failed_events[..2].to_vec(),
16381642
live_payment_hash, false, PaymentFailedConditions::new());
16391643
expect_payment_failed_conditions_event(payment_failed_events[2..].to_vec(),
@@ -1648,7 +1652,9 @@ fn do_test_revoked_counterparty_commitment_balances(keyed_anchors: bool, p2a_anc
16481652
test_spendable_output(&nodes[1], &claim_txn[0], false);
16491653
check_added_monitors(&nodes[1], 0);
16501654
let mut payment_failed_events = nodes[1].node.get_and_clear_pending_events();
1651-
check_added_monitors(&nodes[1], 2);
1655+
if !nodes[1].node.test_persistent_monitor_events_enabled() {
1656+
check_added_monitors(&nodes[1], 2); // ReleasePaymentComplete monitor updates
1657+
}
16521658
expect_payment_failed_conditions_event(payment_failed_events[..2].to_vec(),
16531659
live_payment_hash, false, PaymentFailedConditions::new());
16541660
expect_payment_failed_conditions_event(payment_failed_events[2..].to_vec(),

lightning/src/ln/payment_tests.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,9 @@ fn onchain_failed_probe_yields_event() {
17741774

17751775
check_added_monitors(&nodes[0], 0);
17761776
let mut events = nodes[0].node.get_and_clear_pending_events();
1777-
check_added_monitors(&nodes[0], 1);
1777+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
1778+
check_added_monitors(&nodes[0], 1);
1779+
}
17781780
assert_eq!(events.len(), 2);
17791781
let mut found_probe_failed = false;
17801782
for event in events.drain(..) {

lightning/src/ln/reorg_tests.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,9 @@ fn do_test_split_htlc_expiry_tracking(use_third_htlc: bool, reorg_out: bool, p2a
13231323
if use_third_htlc {
13241324
check_added_monitors(&nodes[0], 0);
13251325
let failed_events = nodes[0].node.get_and_clear_pending_events();
1326-
check_added_monitors(&nodes[0], 1);
1326+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
1327+
check_added_monitors(&nodes[0], 1);
1328+
}
13271329
assert_eq!(failed_events.len(), 2);
13281330
let mut found_expected_events = [false, false];
13291331
for event in failed_events {

lightning/src/ln/splicing_tests.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,12 +1895,9 @@ fn do_test_splice_commitment_broadcast(splice_status: SpliceStatus, claim_htlcs:
18951895
2
18961896
);
18971897
}
1898-
let expected_mons = if nodes[0].node.test_persistent_monitor_events_enabled() && claim_htlcs {
1899-
0
1900-
} else {
1901-
2 // Two `ReleasePaymentComplete` monitor updates
1902-
};
1903-
check_added_monitors(&nodes[0], expected_mons);
1898+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
1899+
check_added_monitors(&nodes[0], 2); //`ReleasePaymentComplete` monitor updates
1900+
}
19041901

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

0 commit comments

Comments
 (0)