Skip to content

Commit b03e434

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 0c5e5df commit b03e434

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
@@ -3371,7 +3371,25 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
33713371
for &(ref htlc, ref source_option) in latest_outpoints.iter() {
33723372
if let &Some(ref source) = source_option {
33733373
let htlc_id = SentHTLCId::from_source(source);
3374-
if !us.htlcs_resolved_to_user.contains(&htlc_id) {
3374+
let skip_htlc = if us.persistent_events_enabled {
3375+
let htlc_resolved_on_chain = match htlc.transaction_output_index {
3376+
Some(idx) => us
3377+
.htlcs_resolved_on_chain
3378+
.iter()
3379+
.any(|r| r.commitment_tx_output_idx == Some(idx)),
3380+
None => {
3381+
// Dust HTLCs are implicitly resolved when the commitment reaches
3382+
// ANTI_REORG_DELAY
3383+
us.confirmed_funding_spend_past_anti_reorg().is_some()
3384+
},
3385+
};
3386+
// If the HTLC was resolved on-chain and we don't have a pending monitor event for
3387+
// it, that implies the event was acked due to the HTLC being resolved to the user.
3388+
htlc_resolved_on_chain && !us.has_pending_event_for_htlc(source)
3389+
} else {
3390+
us.htlcs_resolved_to_user.contains(&htlc_id)
3391+
};
3392+
if !skip_htlc {
33753393
let preimage_opt =
33763394
us.counterparty_fulfilled_htlcs.get(&htlc_id).cloned();
33773395
res.insert((**source).clone(), (htlc.clone(), preimage_opt));
@@ -7601,7 +7619,10 @@ mod tests {
76017619
assert!(events.iter().any(|e| matches!(e, Event::PaymentPathFailed { .. })));
76027620
assert!(events.iter().any(|e| matches!(e, Event::PaymentFailed { .. })));
76037621
assert!(events.iter().any(|e| matches!(e, Event::ChannelClosed { .. })));
7604-
check_added_monitors(&nodes[1], 2);
7622+
// If persistent monitor events are enabled, there won't be a ReleasePaymentComplete monitor
7623+
// update.
7624+
let persistent_mon_evs = nodes[1].node.test_persistent_monitor_events_enabled();
7625+
check_added_monitors(&nodes[1], if persistent_mon_evs { 1 } else { 2 });
76057626
} else {
76067627
check_closed_event(&nodes[1], 1, ClosureReason::CommitmentTxConfirmed, &[nodes[0].node.get_our_node_id()], 100000);
76077628
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
@@ -5814,8 +5814,10 @@ fn do_test_late_counterparty_commitment_update_after_funding_spend(fully_confirm
58145814
false,
58155815
PaymentFailedConditions::new(),
58165816
);
5817-
// The payment failure generates a ReleasePaymentComplete monitor update.
5818-
check_added_monitors(&nodes[0], 1);
5817+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
5818+
// The payment failure generates a ReleasePaymentComplete monitor update.
5819+
check_added_monitors(&nodes[0], 1);
5820+
}
58195821
}
58205822

58215823
#[test]
@@ -5916,7 +5918,9 @@ fn do_test_late_counterparty_commitment_update_after_holder_commitment_spend(dus
59165918
false,
59175919
PaymentFailedConditions::new(),
59185920
);
5919-
check_added_monitors(&nodes[0], 1);
5921+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
5922+
check_added_monitors(&nodes[0], 1); // ReleasePaymentComplete monitor update
5923+
}
59205924

59215925
// Verify HTLC X was NOT failed (no payment failure event for it at this point).
59225926
assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
@@ -5940,7 +5944,9 @@ fn do_test_late_counterparty_commitment_update_after_holder_commitment_spend(dus
59405944
false,
59415945
PaymentFailedConditions::new(),
59425946
);
5943-
check_added_monitors(&nodes[0], 1);
5947+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
5948+
check_added_monitors(&nodes[0], 1); // ReleasePaymentComplete monitor update
5949+
}
59445950
}
59455951

59465952
#[test]

lightning/src/ln/channelmanager.rs

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

14224-
let completion_update = if self.persistent_monitor_events
14225-
&& we_are_sender && !from_onchain
14226-
{
14227-
EventCompletionAction::AckMonitorEvent {
14228-
event_id: monitor_event_source,
14229-
}
14230-
} else {
14231-
EventCompletionAction::ReleasePaymentCompleteChannelMonitorUpdate(PaymentCompleteUpdate {
14232-
counterparty_node_id,
14233-
channel_funding_outpoint: funding_outpoint,
14234-
channel_id,
14235-
htlc_id: SentHTLCId::from_source(&htlc_update.source),
14236-
})
14237-
};
14224+
let completion_update =
14225+
if self.persistent_monitor_events && we_are_sender {
14226+
EventCompletionAction::AckMonitorEvent {
14227+
event_id: monitor_event_source,
14228+
}
14229+
} else {
14230+
EventCompletionAction::ReleasePaymentCompleteChannelMonitorUpdate(PaymentCompleteUpdate {
14231+
counterparty_node_id,
14232+
channel_funding_outpoint: funding_outpoint,
14233+
channel_id,
14234+
htlc_id: SentHTLCId::from_source(&htlc_update.source),
14235+
})
14236+
};
1423814237

1423914238
self.fail_htlc_backwards_internal(
1424014239
&htlc_update.source,
@@ -14244,7 +14243,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1424414243
Some(completion_update),
1424514244
);
1424614245
}
14247-
if from_onchain | !we_are_sender {
14246+
if !we_are_sender {
1424814247
self.chain_monitor.ack_monitor_event(monitor_event_source);
1424914248
}
1425014249
},
@@ -21274,6 +21273,13 @@ impl<
2127421273
htlc_source;
2127521274
let failure_type = source.failure_type(counterparty_id, channel_id);
2127621275
let reason = HTLCFailReason::from_failure_code(failure_reason);
21276+
if channel_manager.persistent_monitor_events
21277+
&& matches!(source, HTLCSource::OutboundRoute { .. })
21278+
{
21279+
// The persistent monitor event generated for the failed HTLC will drive the failure
21280+
// instead.
21281+
continue;
21282+
}
2127721283
channel_manager.fail_htlc_backwards_internal(
2127821284
&source,
2127921285
&hash,

lightning/src/ln/functional_test_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3501,8 +3501,8 @@ pub fn expect_payment_failed_conditions<'a, 'b, 'c, 'd, 'e>(
35013501
check_added_monitors(node, 0);
35023502
}
35033503
let events = node.node.get_and_clear_pending_events();
3504-
if conditions.from_mon_update {
3505-
check_added_monitors(node, 1);
3504+
if conditions.from_mon_update && !node.node.test_persistent_monitor_events_enabled() {
3505+
check_added_monitors(node, 1); // ReleasePaymentComplete update
35063506
}
35073507
expect_payment_failed_conditions_event(
35083508
events,

lightning/src/ln/functional_tests.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,7 +2069,8 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(
20692069
check_added_monitors(&nodes[1], 0);
20702070
let events = nodes[1].node.get_and_clear_pending_events();
20712071
if deliver_bs_raa {
2072-
check_added_monitors(&nodes[1], 2);
2072+
let persistent_mon_evs = nodes[1].node.test_persistent_monitor_events_enabled();
2073+
check_added_monitors(&nodes[1], if persistent_mon_evs { 1 } else { 2 });
20732074
} else {
20742075
check_added_monitors(&nodes[1], 1);
20752076
}
@@ -5895,7 +5896,9 @@ fn do_test_failure_delay_dust_htlc_local_commitment(announce_latest: bool) {
58955896
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
58965897
check_added_monitors(&nodes[0], 0);
58975898
let events = nodes[0].node.get_and_clear_pending_events();
5898-
check_added_monitors(&nodes[0], 2);
5899+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
5900+
check_added_monitors(&nodes[0], 2); // ReleasePaymentComplete updates
5901+
}
58995902
// Only 2 PaymentPathFailed events should show up, over-dust HTLC has to be failed by timeout tx
59005903
assert_eq!(events.len(), 4);
59015904
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
@@ -1790,7 +1790,9 @@ fn onchain_failed_probe_yields_event() {
17901790

17911791
check_added_monitors(&nodes[0], 0);
17921792
let mut events = nodes[0].node.get_and_clear_pending_events();
1793-
check_added_monitors(&nodes[0], 1);
1793+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
1794+
check_added_monitors(&nodes[0], 1);
1795+
}
17941796
assert_eq!(events.len(), 2);
17951797
let mut found_probe_failed = false;
17961798
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
@@ -2266,12 +2266,9 @@ fn do_test_splice_commitment_broadcast(splice_status: SpliceStatus, claim_htlcs:
22662266
2
22672267
);
22682268
}
2269-
let expected_mons = if nodes[0].node.test_persistent_monitor_events_enabled() && claim_htlcs {
2270-
0
2271-
} else {
2272-
2 // Two `ReleasePaymentComplete` monitor updates
2273-
};
2274-
check_added_monitors(&nodes[0], expected_mons);
2269+
if !nodes[0].node.test_persistent_monitor_events_enabled() {
2270+
check_added_monitors(&nodes[0], 2); //`ReleasePaymentComplete` monitor updates
2271+
}
22752272

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

0 commit comments

Comments
 (0)