Skip to content

Commit 94cb95d

Browse files
Fix missing user_channel_id in PaymentForwarded
Previously, if a forwarding node reloaded mid-HTLC-forward with a preimage in the outbound edge monitor and the outbound edge channel still open, and subsequently reclaimed the inbound HTLC backwards, the PaymetForwarded event would be missing the next_user_channel_id field.
1 parent 6086415 commit 94cb95d

2 files changed

Lines changed: 36 additions & 24 deletions

File tree

lightning/src/ln/channelmanager.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18719,14 +18719,16 @@ impl<
1871918719
}
1872018720
}
1872118721
for (channel_id, monitor) in args.channel_monitors.iter() {
18722-
let mut is_channel_closed = true;
18722+
let (mut is_channel_closed, mut user_channel_id_opt) = (true, None);
1872318723
let counterparty_node_id = monitor.get_counterparty_node_id();
1872418724
if let Some(peer_state_mtx) = per_peer_state.get(&counterparty_node_id) {
1872518725
let mut peer_state_lock = peer_state_mtx.lock().unwrap();
1872618726
let peer_state = &mut *peer_state_lock;
18727-
is_channel_closed = !peer_state.channel_by_id.contains_key(channel_id);
18728-
if reconstruct_manager_from_monitors && !is_channel_closed {
18729-
if let Some(chan) = peer_state.channel_by_id.get(channel_id) {
18727+
if let Some(chan) = peer_state.channel_by_id.get(channel_id) {
18728+
is_channel_closed = false;
18729+
user_channel_id_opt = Some(chan.context().get_user_id());
18730+
18731+
if reconstruct_manager_from_monitors {
1873018732
if let Some(funded_chan) = chan.as_funded() {
1873118733
for (payment_hash, prev_hop) in funded_chan.outbound_htlc_forwards()
1873218734
{
@@ -19026,7 +19028,7 @@ impl<
1902619028

1902719029
Some((htlc_source, payment_preimage, htlc.amount_msat,
1902819030
is_channel_closed, monitor.get_counterparty_node_id(),
19029-
monitor.get_funding_txo(), monitor.channel_id(), None))
19031+
monitor.get_funding_txo(), monitor.channel_id(), user_channel_id_opt))
1903019032
} else { None }
1903119033
} else {
1903219034
// If it was an outbound payment, we've handled it above - if a preimage

lightning/src/ln/reload_tests.rs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1883,7 +1883,22 @@ fn test_reload_node_with_preimage_in_monitor_claims_htlc() {
18831883
//
18841884
// Here we ensure that inbound HTLCs in case (b) above will not be failed backwards on manager
18851885
// reload.
1886+
do_test_reclaim_after_reload(true);
1887+
}
1888+
1889+
#[test]
1890+
fn test_reclaim_after_reload_payment_forwarded_ev() {
1891+
// Test that when a forwarding node reloads:
1892+
// - mid-HTLC-forward
1893+
// - with a preimage in the outbound edge monitor
1894+
// - and the outbound edge channel is still open
1895+
// - and we subsequently re-claim the inbound HTLC backwards
1896+
// the `PaymentForwarded` event contains `next_user_channel_id`. Previously,
1897+
// `next_user_channel_id` would always be None even if the outbound edge channel was still open.
1898+
do_test_reclaim_after_reload(false);
1899+
}
18861900

1901+
fn do_test_reclaim_after_reload(outbound_htlc_fully_removed: bool) {
18871902
let chanmon_cfgs = create_chanmon_cfgs(3);
18881903
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
18891904
let persister;
@@ -1924,29 +1939,22 @@ fn test_reload_node_with_preimage_in_monitor_claims_htlc() {
19241939
let updates_2_1 = get_htlc_update_msgs(&nodes[2], &node_1_id);
19251940
nodes[1].node.handle_update_fulfill_htlc(node_2_id, updates_2_1.update_fulfill_htlcs[0].clone());
19261941
check_added_monitors(&nodes[1], 1);
1927-
do_commitment_signed_dance(&nodes[1], &nodes[2], &updates_2_1.commitment_signed, false, false);
1928-
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], Some(1000), false, false);
19291942

1930-
// Clear the holding cell's claim entry on chan_0_1 before serialization.
1931-
// This simulates a crash where the HTLC was fully removed from the outbound edge but is still
1932-
// present on the inbound edge without a resolution.
1933-
nodes[1].node.test_clear_channel_holding_cell(node_0_id, chan_id_0_1);
1943+
if outbound_htlc_fully_removed {
1944+
// Complete the commitment dance so the HTLC is fully resolved on the outbound edge.
1945+
do_commitment_signed_dance(&nodes[1], &nodes[2], &updates_2_1.commitment_signed, false, false);
1946+
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], Some(1000), false, false);
1947+
1948+
// Clear the holding cell's claim entry on chan_0_1 before serialization.
1949+
// This simulates a crash where the HTLC was fully removed from the outbound edge but is still
1950+
// present on the inbound edge without a resolution.
1951+
nodes[1].node.test_clear_channel_holding_cell(node_0_id, chan_id_0_1);
1952+
}
19341953

1935-
// At this point:
1936-
// - The inbound HTLC on nodes[1] (from nodes[0]) is in ::Forwarded state
1937-
// - The preimage IS in nodes[1]'s monitor for chan_1_2
1938-
// - The outbound HTLC to nodes[2] is resolved
1939-
//
1940-
// Serialize nodes[1] state and monitors before reloading.
19411954
let node_1_serialized = nodes[1].node.encode();
19421955
let mon_0_1_serialized = get_monitor!(nodes[1], chan_id_0_1).encode();
19431956
let mon_1_2_serialized = get_monitor!(nodes[1], chan_id_1_2).encode();
19441957

1945-
// Reload nodes[1].
1946-
// During deserialization, we track inbound HTLCs that purport to already be forwarded on the
1947-
// outbound edge. If any are entirely missing from the outbound edge with no preimage available,
1948-
// they will be failed backwards. Otherwise, as in this case where a preimage is available, the
1949-
// payment should be claimed backwards.
19501958
reload_node!(
19511959
nodes[1],
19521960
node_1_serialized,
@@ -1958,9 +1966,11 @@ fn test_reload_node_with_preimage_in_monitor_claims_htlc() {
19581966
);
19591967

19601968
// When the claim is reconstructed during reload, a PaymentForwarded event is generated.
1961-
// Fetching events triggers the pending monitor update (adding preimage) to be applied.
19621969
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], Some(1000), false, false);
1963-
check_added_monitors(&nodes[1], 1);
1970+
if outbound_htlc_fully_removed {
1971+
// Fetching events triggers the pending monitor update (adding preimage) to be applied.
1972+
check_added_monitors(&nodes[1], 1);
1973+
}
19641974

19651975
// Reconnect nodes[1] to nodes[0]. The claim should be in nodes[1]'s holding cell.
19661976
let mut reconnect_args = ReconnectArgs::new(&nodes[1], &nodes[0]);

0 commit comments

Comments
 (0)