Skip to content

Commit 236780d

Browse files
committed
Drop forwarded HTLCs which were still pending at persist-time
If, after forwarding an intercepted payment to our counterparty, we restart with a ChannelMonitor update having been persisted, but the corresponding ChannelManager update not having been persisted, we'll still have the intercepted HTLC in the `pending_intercepted_htlcs` map on start (and potentially a pending `HTLCIntercepted` event). This will cause us to allow the user to handle the forwarded HTLC twice, potentially double-forwarding it. This builds on 0bb87dd, which provided a preemptive fix for the general relay case (though it was not an actual issue at the time). We simply check for the HTLCs having been forwarded on startup and remove them from the map. Fixes lightningdevkit#1858
1 parent 769f590 commit 236780d

File tree

2 files changed

+56
-13
lines changed

2 files changed

+56
-13
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7527,25 +7527,39 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
75277527
}
75287528
for (htlc_source, htlc) in monitor.get_all_current_outbound_htlcs() {
75297529
if let HTLCSource::PreviousHopData(prev_hop_data) = htlc_source {
7530+
let pending_forward_matches_htlc = |info: &PendingAddHTLCInfo| {
7531+
info.prev_funding_outpoint == prev_hop_data.outpoint &&
7532+
info.prev_htlc_id == prev_hop_data.htlc_id
7533+
};
75307534
// The ChannelMonitor is now responsible for this HTLC's
75317535
// failure/success and will let us know what its outcome is. If we
7532-
// still have an entry for this HTLC in `forward_htlcs`, we were
7533-
// apparently not persisted after the monitor was when forwarding
7534-
// the payment.
7536+
// still have an entry for this HTLC in `forward_htlcs` or
7537+
// `pending_intercepted_htlcs`, we were apparently not persisted after
7538+
// the monitor was when forwarding the payment.
75357539
forward_htlcs.retain(|_, forwards| {
75367540
forwards.retain(|forward| {
75377541
if let HTLCForwardInfo::AddHTLC(htlc_info) = forward {
7538-
if htlc_info.prev_short_channel_id == prev_hop_data.short_channel_id &&
7539-
htlc_info.prev_htlc_id == prev_hop_data.htlc_id
7540-
{
7542+
if pending_forward_matches_htlc(&htlc_info) {
75417543
log_info!(args.logger, "Removing pending to-forward HTLC with hash {} as it was forwarded to the closed channel {}",
75427544
log_bytes!(htlc.payment_hash.0), log_bytes!(monitor.get_funding_txo().0.to_channel_id()));
75437545
false
75447546
} else { true }
75457547
} else { true }
75467548
});
75477549
!forwards.is_empty()
7548-
})
7550+
});
7551+
pending_intercepted_htlcs.as_mut().unwrap().retain(|intercepted_id, htlc_info| {
7552+
if pending_forward_matches_htlc(&htlc_info) {
7553+
log_info!(args.logger, "Removing pending intercepted HTLC with hash {} as it was forwarded to the closed channel {}",
7554+
log_bytes!(htlc.payment_hash.0), log_bytes!(monitor.get_funding_txo().0.to_channel_id()));
7555+
pending_events_read.retain(|event| {
7556+
if let Event::HTLCIntercepted { intercept_id: ev_id, .. } = event {
7557+
intercepted_id != ev_id
7558+
} else { true }
7559+
});
7560+
false
7561+
} else { true }
7562+
});
75497563
}
75507564
}
75517565
}

lightning/src/ln/reload_tests.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -814,15 +814,17 @@ fn test_partial_claim_before_restart() {
814814
do_test_partial_claim_before_restart(true);
815815
}
816816

817-
fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_htlc: bool) {
817+
fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_htlc: bool, use_intercept: bool) {
818818
if !use_cs_commitment { assert!(!claim_htlc); }
819819
// If we go to forward a payment, and the ChannelMonitor persistence completes, but the
820820
// ChannelManager does not, we shouldn't try to forward the payment again, nor should we fail
821821
// it back until the ChannelMonitor decides the fate of the HTLC.
822822
// This was never an issue, but it may be easy to regress here going forward.
823823
let chanmon_cfgs = create_chanmon_cfgs(3);
824824
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
825-
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
825+
let mut intercept_forwards_config = test_default_channel_config();
826+
intercept_forwards_config.accept_intercept_htlcs = true;
827+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, Some(intercept_forwards_config), None]);
826828

827829
let persister;
828830
let new_chain_monitor;
@@ -833,7 +835,13 @@ fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_ht
833835
let chan_id_1 = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
834836
let chan_id_2 = create_announced_chan_between_nodes(&nodes, 1, 2, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
835837

836-
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000);
838+
let intercept_scid = nodes[1].node.get_intercept_scid();
839+
840+
let (mut route, payment_hash, payment_preimage, payment_secret) =
841+
get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000);
842+
if use_intercept {
843+
route.paths[0][1].short_channel_id = intercept_scid;
844+
}
837845
let payment_id = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes());
838846
let htlc_expiry = nodes[0].best_block_info().1 + TEST_FINAL_CLTV;
839847
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), payment_id).unwrap();
@@ -845,6 +853,20 @@ fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_ht
845853

846854
let node_encoded = nodes[1].node.encode();
847855

856+
if use_intercept {
857+
let events = nodes[1].node.get_and_clear_pending_events();
858+
assert_eq!(events.len(), 1);
859+
let (intercept_id, expected_outbound_amount_msat) = match events[0] {
860+
crate::util::events::Event::HTLCIntercepted {
861+
intercept_id, expected_outbound_amount_msat, ..
862+
} => {
863+
(intercept_id, expected_outbound_amount_msat)
864+
},
865+
_ => panic!()
866+
};
867+
nodes[1].node.forward_intercepted_htlc(intercept_id, &chan_id_2, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap();
868+
}
869+
848870
expect_pending_htlcs_forwardable!(nodes[1]);
849871

850872
let payment_event = SendEvent::from_node(&nodes[1]);
@@ -929,9 +951,16 @@ fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_ht
929951

930952
#[test]
931953
fn forwarded_payment_no_manager_persistence() {
932-
do_forwarded_payment_no_manager_persistence(true, true);
933-
do_forwarded_payment_no_manager_persistence(true, false);
934-
do_forwarded_payment_no_manager_persistence(false, false);
954+
do_forwarded_payment_no_manager_persistence(true, true, false);
955+
do_forwarded_payment_no_manager_persistence(true, false, false);
956+
do_forwarded_payment_no_manager_persistence(false, false, false);
957+
}
958+
959+
#[test]
960+
fn intercepted_payment_no_manager_persistence() {
961+
do_forwarded_payment_no_manager_persistence(true, true, true);
962+
do_forwarded_payment_no_manager_persistence(true, false, true);
963+
do_forwarded_payment_no_manager_persistence(false, false, true);
935964
}
936965

937966
#[test]

0 commit comments

Comments
 (0)