Skip to content

Commit 791306a

Browse files
Persistent monitor events for HTLC forward claims
XXX
1 parent ccf2fa5 commit 791306a

File tree

7 files changed

+607
-67
lines changed

7 files changed

+607
-67
lines changed

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 444 additions & 16 deletions
Large diffs are not rendered by default.

lightning/src/ln/channelmanager.rs

Lines changed: 96 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3619,14 +3619,23 @@ impl TrustedChannelFeatures {
36193619
struct ClaimCompletionActionParams {
36203620
definitely_duplicate: bool,
36213621
inbound_htlc_value_msat: Option<u64>,
3622+
inbound_edge_closed: bool,
36223623
}
36233624

36243625
impl ClaimCompletionActionParams {
36253626
fn new_claim(inbound_htlc_value_msat: u64) -> Self {
3626-
Self { definitely_duplicate: false, inbound_htlc_value_msat: Some(inbound_htlc_value_msat) }
3627+
Self {
3628+
definitely_duplicate: false,
3629+
inbound_htlc_value_msat: Some(inbound_htlc_value_msat),
3630+
inbound_edge_closed: false,
3631+
}
36273632
}
36283633
fn duplicate_claim() -> Self {
3629-
Self { definitely_duplicate: true, inbound_htlc_value_msat: None }
3634+
Self {
3635+
definitely_duplicate: true,
3636+
inbound_htlc_value_msat: None,
3637+
inbound_edge_closed: false,
3638+
}
36303639
}
36313640
}
36323641

@@ -9635,16 +9644,56 @@ impl<
96359644
monitor_event_id
96369645
.map(|event_id| MonitorEventSource { event_id, channel_id: next_channel_id }),
96379646
|claim_completion_action_params| {
9638-
let ClaimCompletionActionParams { definitely_duplicate, inbound_htlc_value_msat } =
9639-
claim_completion_action_params;
9647+
let ClaimCompletionActionParams {
9648+
definitely_duplicate,
9649+
inbound_htlc_value_msat,
9650+
inbound_edge_closed,
9651+
} = claim_completion_action_params;
96409652
let chan_to_release = EventUnblockedChannel {
96419653
counterparty_node_id: next_channel_counterparty_node_id,
96429654
funding_txo: next_channel_outpoint,
96439655
channel_id: next_channel_id,
96449656
blocking_action: completed_blocker,
96459657
};
96469658

9647-
if definitely_duplicate && startup_replay {
9659+
if self.persistent_monitor_events {
9660+
let monitor_event_source = monitor_event_id.map(|event_id| {
9661+
MonitorEventSource { event_id, channel_id: next_channel_id }
9662+
});
9663+
// If persistent_monitor_events is enabled, then we'll get a MonitorEvent for this HTLC
9664+
// claim re-provided to us until we explicitly ack it.
9665+
// * If the inbound edge is closed, then we can ack it when we know the preimage is
9666+
// durably persisted there + the user has processed a `PaymentForwarded` event
9667+
// * If the inbound edge is open, then we'll ack the monitor event when HTLC has been
9668+
// irrevocably removed via revoke_and_ack. This prevents forgetting to claim the HTLC
9669+
// backwards if we lose the off-chain HTLC from the holding cell after a restart.
9670+
if definitely_duplicate {
9671+
if inbound_edge_closed {
9672+
if let Some(id) = monitor_event_source {
9673+
self.chain_monitor.ack_monitor_event(id);
9674+
}
9675+
}
9676+
(None, None)
9677+
} else if let Some(event) =
9678+
make_payment_forwarded_event(inbound_htlc_value_msat)
9679+
{
9680+
let preimage_update_action =
9681+
MonitorUpdateCompletionAction::EmitForwardEvent {
9682+
event,
9683+
post_event_ackable_monitor_event: inbound_edge_closed
9684+
.then_some(monitor_event_source)
9685+
.flatten(),
9686+
};
9687+
(Some(preimage_update_action), None)
9688+
} else if inbound_edge_closed {
9689+
let preimage_update_action = monitor_event_source.map(|src| {
9690+
MonitorUpdateCompletionAction::AckMonitorEvents { event_ids: vec![src] }
9691+
});
9692+
(preimage_update_action, None)
9693+
} else {
9694+
(None, None)
9695+
}
9696+
} else if definitely_duplicate && startup_replay {
96489697
// On startup we may get redundant claims which are related to
96499698
// monitor updates still in flight. In that case, we shouldn't
96509699
// immediately free, but instead let that monitor update complete
@@ -9977,6 +10026,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
997710026
let (action_opt, raa_blocker_opt) = completion_action(ClaimCompletionActionParams {
997810027
definitely_duplicate: false,
997910028
inbound_htlc_value_msat: None,
10029+
inbound_edge_closed: true,
998010030
});
998110031

998210032
if let Some(raa_blocker) = raa_blocker_opt {
@@ -12691,23 +12741,28 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1269112741
chan.update_fulfill_htlc(&msg),
1269212742
chan_entry
1269312743
);
12694-
let prev_hops = match &res.0 {
12695-
HTLCSource::PreviousHopData(prev_hop) => vec![prev_hop],
12696-
HTLCSource::TrampolineForward { previous_hop_data, .. } => {
12697-
previous_hop_data.iter().collect()
12698-
},
12699-
_ => vec![],
12700-
};
12701-
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
12702-
for prev_hop in prev_hops {
12703-
log_trace!(logger,
12704-
"Holding the next revoke_and_ack until the preimage is durably persisted in the inbound edge's ChannelMonitor",
12705-
);
12706-
peer_state
12707-
.actions_blocking_raa_monitor_updates
12708-
.entry(msg.channel_id)
12709-
.or_insert_with(Vec::new)
12710-
.push(RAAMonitorUpdateBlockingAction::from_prev_hop_data(prev_hop));
12744+
if !self.persistent_monitor_events {
12745+
let prev_hops = match &res.0 {
12746+
HTLCSource::PreviousHopData(prev_hop) => vec![prev_hop],
12747+
HTLCSource::TrampolineForward { previous_hop_data, .. } => {
12748+
previous_hop_data.iter().collect()
12749+
},
12750+
_ => vec![],
12751+
};
12752+
let logger =
12753+
WithChannelContext::from(&self.logger, &chan.context, None);
12754+
for prev_hop in prev_hops {
12755+
log_trace!(logger,
12756+
"Holding the next revoke_and_ack until the preimage is durably persisted in the inbound edge's ChannelMonitor",
12757+
);
12758+
peer_state
12759+
.actions_blocking_raa_monitor_updates
12760+
.entry(msg.channel_id)
12761+
.or_insert_with(Vec::new)
12762+
.push(RAAMonitorUpdateBlockingAction::from_prev_hop_data(
12763+
prev_hop,
12764+
));
12765+
}
1271112766
}
1271212767

1271312768
// Note that we do not need to push an `actions_blocking_raa_monitor_updates`
@@ -13709,29 +13764,22 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1370913764
.channel_by_id
1371013765
.contains_key(&channel_id)
1371113766
});
13712-
let we_are_sender =
13713-
matches!(htlc_update.source, HTLCSource::OutboundRoute { .. });
13714-
if from_onchain | we_are_sender {
13715-
// Claim the funds from the previous hop, if there is one. Because this is in response to a
13716-
// chain event, no attribution data is available.
13717-
self.claim_funds_internal(
13718-
htlc_update.source,
13719-
preimage,
13720-
htlc_update.htlc_value_msat,
13721-
None,
13722-
from_onchain,
13723-
counterparty_node_id,
13724-
funding_outpoint,
13725-
channel_id,
13726-
htlc_update.user_channel_id,
13727-
None,
13728-
None,
13729-
Some(event_id),
13730-
);
13731-
}
13732-
if !we_are_sender {
13733-
self.chain_monitor.ack_monitor_event(monitor_event_source);
13734-
}
13767+
// Claim the funds from the previous hop, if there is one. Because this is in response to a
13768+
// chain event, no attribution data is available.
13769+
self.claim_funds_internal(
13770+
htlc_update.source,
13771+
preimage,
13772+
htlc_update.htlc_value_msat,
13773+
None,
13774+
from_onchain,
13775+
counterparty_node_id,
13776+
funding_outpoint,
13777+
channel_id,
13778+
htlc_update.user_channel_id,
13779+
None,
13780+
None,
13781+
Some(event_id),
13782+
);
1373513783
} else {
1373613784
log_trace!(logger, "Failing HTLC from our monitor");
1373713785
let failure_reason = LocalHTLCFailureReason::OnChainTimeout;
@@ -20658,6 +20706,9 @@ impl<
2065820706
downstream_user_channel_id,
2065920707
) in pending_claims_to_replay
2066020708
{
20709+
if channel_manager.persistent_monitor_events {
20710+
continue;
20711+
}
2066120712
// We use `downstream_closed` in place of `from_onchain` here just as a guess - we
2066220713
// don't remember in the `ChannelMonitor` where we got a preimage from, but if the
2066320714
// channel is closed we just assume that it probably came from an on-chain claim.

lightning/src/ln/functional_test_utils.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3166,7 +3166,7 @@ pub fn expect_payment_forwarded<CM: AChannelManager, H: NodeHolder<CM = CM>>(
31663166
macro_rules! expect_payment_forwarded {
31673167
($node: expr, $prev_node: expr, $next_node: expr, $expected_fee: expr, $upstream_force_closed: expr, $downstream_force_closed: expr) => {
31683168
let mut events = $node.node.get_and_clear_pending_events();
3169-
assert_eq!(events.len(), 1);
3169+
assert_eq!(events.len(), 1, "{events:?}");
31703170
$crate::ln::functional_test_utils::expect_payment_forwarded(
31713171
events.pop().unwrap(),
31723172
&$node,
@@ -5673,7 +5673,13 @@ pub fn reconnect_nodes<'a, 'b, 'c, 'd>(args: ReconnectArgs<'a, 'b, 'c, 'd>) {
56735673
node_a.node.handle_revoke_and_ack(node_b_id, &bs_revoke_and_ack);
56745674
check_added_monitors(
56755675
&node_a,
5676-
if pending_responding_commitment_signed_dup_monitor.1 { 0 } else { 1 },
5676+
if pending_responding_commitment_signed_dup_monitor.1
5677+
&& !node_a.node.test_persistent_monitor_events_enabled()
5678+
{
5679+
0
5680+
} else {
5681+
1
5682+
},
56775683
);
56785684
if !allow_post_commitment_dance_msgs.1 {
56795685
assert!(node_a.node.get_and_clear_pending_msg_events().is_empty());

lightning/src/ln/functional_tests.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7916,7 +7916,15 @@ fn do_test_onchain_htlc_settlement_after_close(
79167916
_ => panic!("Unexpected event"),
79177917
};
79187918
nodes[1].node.handle_revoke_and_ack(node_c_id, &carol_revocation);
7919-
check_added_monitors(&nodes[1], 1);
7919+
if nodes[1].node.test_persistent_monitor_events_enabled() {
7920+
if broadcast_alice && !go_onchain_before_fulfill {
7921+
check_added_monitors(&nodes[1], 1);
7922+
} else {
7923+
check_added_monitors(&nodes[1], 2);
7924+
}
7925+
} else {
7926+
check_added_monitors(&nodes[1], 1);
7927+
}
79207928

79217929
// If this test requires the force-closed channel to not be on-chain until after the fulfill,
79227930
// here's where we put said channel's commitment tx on-chain.
@@ -7950,6 +7958,13 @@ fn do_test_onchain_htlc_settlement_after_close(
79507958
check_spends!(bob_txn[0], chan_ab.3);
79517959
}
79527960
}
7961+
if nodes[1].node.test_persistent_monitor_events_enabled() {
7962+
if !broadcast_alice || go_onchain_before_fulfill {
7963+
// In some cases we'll replay the claim via a MonitorEvent and be unable to detect that it's
7964+
// a duplicate since the inbound edge is on-chain.
7965+
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], fee, went_onchain, false);
7966+
}
7967+
}
79537968

79547969
// Step (6):
79557970
// Finally, check that Bob broadcasted a preimage-claiming transaction for the HTLC output on the

lightning/src/ln/payment_tests.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -927,8 +927,33 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) {
927927
let fulfill_msg = htlc_fulfill.update_fulfill_htlcs.remove(0);
928928
nodes[1].node.handle_update_fulfill_htlc(node_c_id, fulfill_msg);
929929
check_added_monitors(&nodes[1], 1);
930-
do_commitment_signed_dance(&nodes[1], &nodes[2], &htlc_fulfill.commitment_signed, false, false);
931-
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], None, true, false);
930+
{
931+
// Drive the commitment signed dance manually so we can account for the extra monitor
932+
// update when persistent monitor events are enabled.
933+
let persistent = nodes[1].node.test_persistent_monitor_events_enabled();
934+
nodes[1].node.handle_commitment_signed_batch_test(node_c_id, &htlc_fulfill.commitment_signed);
935+
check_added_monitors(&nodes[1], 1);
936+
let (extra_msg, cs_raa, htlcs) =
937+
do_main_commitment_signed_dance(&nodes[1], &nodes[2], false);
938+
assert!(htlcs.is_empty());
939+
assert!(extra_msg.is_none());
940+
// nodes[1] handles nodes[2]'s RAA. When persistent monitor events are enabled, this
941+
// triggers the re-provided outbound monitor event, generating an extra preimage update
942+
// on the (closed) inbound channel.
943+
nodes[1].node.handle_revoke_and_ack(node_c_id, &cs_raa);
944+
check_added_monitors(&nodes[1], if persistent { 2 } else { 1 });
945+
}
946+
if nodes[1].node.test_persistent_monitor_events_enabled() {
947+
// The re-provided monitor event generates a duplicate PaymentForwarded against the
948+
// closed inbound channel.
949+
let events = nodes[1].node.get_and_clear_pending_events();
950+
assert_eq!(events.len(), 2);
951+
for event in events {
952+
assert!(matches!(event, Event::PaymentForwarded { .. }));
953+
}
954+
} else {
955+
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], None, true, false);
956+
}
932957

933958
if confirm_before_reload {
934959
let best_block = nodes[0].blocks.lock().unwrap().last().unwrap().clone();

lightning/src/ln/reload_tests.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,6 +1972,13 @@ fn test_reload_node_with_preimage_in_monitor_claims_htlc() {
19721972
Some(true)
19731973
);
19741974

1975+
if nodes[1].node.test_persistent_monitor_events_enabled() {
1976+
// Polling messages causes us to re-release the unacked HTLC claim monitor event, which
1977+
// regenerates a preimage monitor update and forward event below.
1978+
let msgs = nodes[1].node.get_and_clear_pending_msg_events();
1979+
assert!(msgs.is_empty());
1980+
}
1981+
19751982
// When the claim is reconstructed during reload, a PaymentForwarded event is generated.
19761983
// Fetching events triggers the pending monitor update (adding preimage) to be applied.
19771984
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], Some(1000), false, false);

lightning/src/util/test_utils.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,9 @@ pub struct TestChainMonitor<'a> {
523523
pub pause_flush: AtomicBool,
524524
/// Buffer of the last 20 monitor updates, most recent first.
525525
pub recent_monitor_updates: Mutex<Vec<(ChannelId, ChannelMonitorUpdate)>>,
526+
/// When set to `true`, `release_pending_monitor_events` sorts events by `ChannelId` to
527+
/// ensure deterministic processing order regardless of HashMap iteration order.
528+
pub deterministic_mon_events_order: AtomicBool,
526529
}
527530
impl<'a> TestChainMonitor<'a> {
528531
pub fn new(
@@ -584,6 +587,7 @@ impl<'a> TestChainMonitor<'a> {
584587
write_blocker: Mutex::new(None),
585588
pause_flush: AtomicBool::new(false),
586589
recent_monitor_updates: Mutex::new(Vec::new()),
590+
deterministic_mon_events_order: AtomicBool::new(false),
587591
}
588592
}
589593

@@ -745,7 +749,11 @@ impl<'a> chain::Watch<TestChannelSigner> for TestChainMonitor<'a> {
745749
let count = self.chain_monitor.pending_operation_count();
746750
self.chain_monitor.flush(count, &self.logger);
747751
}
748-
return self.chain_monitor.release_pending_monitor_events();
752+
let mut events = self.chain_monitor.release_pending_monitor_events();
753+
if self.deterministic_mon_events_order.load(Ordering::Acquire) {
754+
events.sort_by_key(|(_, channel_id, _, _)| *channel_id);
755+
}
756+
events
749757
}
750758

751759
fn ack_monitor_event(&self, source: MonitorEventSource) {

0 commit comments

Comments
 (0)