Skip to content

Commit 2d68183

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 #1858
1 parent 769f590 commit 2d68183

File tree

2 files changed

+74
-13
lines changed

2 files changed

+74
-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: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::ln::msgs;
1919
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
2020
use crate::util::enforcing_trait_impls::EnforcingSigner;
2121
use crate::util::test_utils;
22+
use crate::util::errors::APIError;
2223
use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
2324
use crate::util::ser::{Writeable, ReadableArgs};
2425
use crate::util::config::UserConfig;
@@ -814,15 +815,17 @@ fn test_partial_claim_before_restart() {
814815
do_test_partial_claim_before_restart(true);
815816
}
816817

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

827830
let persister;
828831
let new_chain_monitor;
@@ -833,7 +836,13 @@ fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_ht
833836
let chan_id_1 = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
834837
let chan_id_2 = create_announced_chan_between_nodes(&nodes, 1, 2, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
835838

836-
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000);
839+
let intercept_scid = nodes[1].node.get_intercept_scid();
840+
841+
let (mut route, payment_hash, payment_preimage, payment_secret) =
842+
get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000);
843+
if use_intercept {
844+
route.paths[0][1].short_channel_id = intercept_scid;
845+
}
837846
let payment_id = PaymentId(nodes[0].keys_manager.backing.get_secure_random_bytes());
838847
let htlc_expiry = nodes[0].best_block_info().1 + TEST_FINAL_CLTV;
839848
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), payment_id).unwrap();
@@ -843,8 +852,27 @@ fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_ht
843852
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
844853
commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
845854

855+
// Store the `ChannelManager` before handling the `PendingHTLCsForwardable`/`HTLCIntercepted`
856+
// events, expecting either event (and the HTLC itself) to be missing on reload even though its
857+
// present when we serialized.
846858
let node_encoded = nodes[1].node.encode();
847859

860+
let mut intercept_id = None;
861+
let mut expected_outbound_amount_msat = None;
862+
if use_intercept {
863+
let events = nodes[1].node.get_and_clear_pending_events();
864+
assert_eq!(events.len(), 1);
865+
match events[0] {
866+
Event::HTLCIntercepted { intercept_id: ev_id, expected_outbound_amount_msat: ev_amt, .. } => {
867+
intercept_id = Some(ev_id);
868+
expected_outbound_amount_msat = Some(ev_amt);
869+
},
870+
_ => panic!()
871+
}
872+
nodes[1].node.forward_intercepted_htlc(intercept_id.unwrap(), &chan_id_2,
873+
nodes[2].node.get_our_node_id(), expected_outbound_amount_msat.unwrap()).unwrap();
874+
}
875+
848876
expect_pending_htlcs_forwardable!(nodes[1]);
849877

850878
let payment_event = SendEvent::from_node(&nodes[1]);
@@ -872,8 +900,20 @@ fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_ht
872900
let chan_1_monitor_serialized = get_monitor!(nodes[1], chan_id_2).encode();
873901
reload_node!(nodes[1], node_encoded, &[&chan_0_monitor_serialized, &chan_1_monitor_serialized], persister, new_chain_monitor, nodes_1_deserialized);
874902

903+
// Note that this checks that this is the only event on nodes[1], implying the
904+
// `HTLCIntercepted` event has been removed in the `use_intercept` case.
875905
check_closed_event!(nodes[1], 1, ClosureReason::OutdatedChannelManager);
876906

907+
if use_intercept {
908+
// Attempt to forward the HTLC back out over nodes[1]' still-open channel, ensuring we get
909+
// a intercept-doesn't-exist error.
910+
let forward_err = nodes[1].node.forward_intercepted_htlc(intercept_id.unwrap(), &chan_id_1,
911+
nodes[0].node.get_our_node_id(), expected_outbound_amount_msat.unwrap()).unwrap_err();
912+
assert_eq!(forward_err, APIError::APIMisuseError {
913+
err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.unwrap().0))
914+
});
915+
}
916+
877917
let bs_commitment_tx = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
878918
assert_eq!(bs_commitment_tx.len(), 1);
879919

@@ -929,9 +969,16 @@ fn do_forwarded_payment_no_manager_persistence(use_cs_commitment: bool, claim_ht
929969

930970
#[test]
931971
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);
972+
do_forwarded_payment_no_manager_persistence(true, true, false);
973+
do_forwarded_payment_no_manager_persistence(true, false, false);
974+
do_forwarded_payment_no_manager_persistence(false, false, false);
975+
}
976+
977+
#[test]
978+
fn intercepted_payment_no_manager_persistence() {
979+
do_forwarded_payment_no_manager_persistence(true, true, true);
980+
do_forwarded_payment_no_manager_persistence(true, false, true);
981+
do_forwarded_payment_no_manager_persistence(false, false, true);
935982
}
936983

937984
#[test]

0 commit comments

Comments
 (0)