Skip to content

Commit d323b9c

Browse files
Splicing fix: use outbound_alias in manager maps
Since we have added/are adding splicing support, the scid of a channel is liable to change post-splice. Some maps in the ChannelManager are keyed by the scid of a channel, which is an issue now -- if we forward an HTLC from a channel and then splice that channel before the HTLC is resolved, we'll end up with an HTLC source with an scid that doesn't correspond to any open channel. This may result in loss of the HTLC resolution. The outbound scid alias of a channel is stable even post-splice, so for the short term here we switch to using that instead. In the medium term we should update these maps to use (PublicKey, ChannelId) like everything else. We don't always use the alias for outbound forwarded HTLCs, since we tend to use whatever outbound scid is in the onion. That's fine because we properly handle the case where the outbound channel cannot be found; the main problem is in inbound HTLCs and forgetting their resolution.
1 parent e82ef2c commit d323b9c

File tree

1 file changed

+50
-49
lines changed

1 file changed

+50
-49
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ pub(super) struct PendingAddHTLCInfo {
452452
// HTLCs.
453453
//
454454
// Note that this may be an outbound SCID alias for the associated channel.
455-
prev_short_channel_id: u64,
455+
prev_outbound_scid_alias: u64,
456456
prev_htlc_id: u64,
457457
prev_counterparty_node_id: PublicKey,
458458
prev_channel_id: ChannelId,
@@ -467,7 +467,7 @@ impl PendingAddHTLCInfo {
467467
_ => None,
468468
};
469469
HTLCPreviousHopData {
470-
short_channel_id: self.prev_short_channel_id,
470+
prev_outbound_scid_alias: self.prev_outbound_scid_alias,
471471
user_channel_id: Some(self.prev_user_channel_id),
472472
outpoint: self.prev_funding_outpoint,
473473
channel_id: self.prev_channel_id,
@@ -735,14 +735,14 @@ impl Default for OptionalOfferPaymentParams {
735735
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
736736
/// Uniquely describes an HTLC by its source. Just the guaranteed-unique subset of [`HTLCSource`].
737737
pub(crate) enum SentHTLCId {
738-
PreviousHopData { short_channel_id: u64, htlc_id: u64 },
738+
PreviousHopData { prev_outbound_scid_alias: u64, htlc_id: u64 },
739739
OutboundRoute { session_priv: [u8; SECRET_KEY_SIZE] },
740740
}
741741
impl SentHTLCId {
742742
pub(crate) fn from_source(source: &HTLCSource) -> Self {
743743
match source {
744744
HTLCSource::PreviousHopData(hop_data) => Self::PreviousHopData {
745-
short_channel_id: hop_data.short_channel_id,
745+
prev_outbound_scid_alias: hop_data.prev_outbound_scid_alias,
746746
htlc_id: hop_data.htlc_id,
747747
},
748748
HTLCSource::OutboundRoute { session_priv, .. } => {
@@ -753,15 +753,15 @@ impl SentHTLCId {
753753
}
754754
impl_writeable_tlv_based_enum!(SentHTLCId,
755755
(0, PreviousHopData) => {
756-
(0, short_channel_id, required),
756+
(0, prev_outbound_scid_alias, required),
757757
(2, htlc_id, required),
758758
},
759759
(2, OutboundRoute) => {
760760
(0, session_priv, required),
761761
},
762762
);
763763

764-
// (src_channel_id, src_counterparty_node_id, src_funding_outpoint, src_chan_id, src_user_chan_id)
764+
// (src_outbound_scid_alias, src_counterparty_node_id, src_funding_outpoint, src_chan_id, src_user_chan_id)
765765
type PerSourcePendingForward =
766766
(u64, PublicKey, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>);
767767

@@ -792,8 +792,7 @@ mod fuzzy_channelmanager {
792792
/// Tracks the inbound corresponding to an outbound HTLC
793793
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
794794
pub struct HTLCPreviousHopData {
795-
// Note that this may be an outbound SCID alias for the associated channel.
796-
pub short_channel_id: u64,
795+
pub prev_outbound_scid_alias: u64,
797796
pub user_channel_id: Option<u128>,
798797
pub htlc_id: u64,
799798
pub incoming_packet_shared_secret: [u8; 32],
@@ -2718,11 +2717,8 @@ pub struct ChannelManager<
27182717
/// See `ChannelManager` struct-level documentation for lock order requirements.
27192718
pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
27202719

2721-
/// SCID/SCID Alias -> pending `update_add_htlc`s to decode.
2722-
///
2723-
/// Note that because we may have an SCID Alias as the key we can have two entries per channel,
2724-
/// though in practice we probably won't be receiving HTLCs for a channel both via the alias
2725-
/// and via the classic SCID.
2720+
/// Outbound SCID Alias -> pending `update_add_htlc`s to decode.
2721+
/// We use the scid alias because regular scids may change if a splice occurs.
27262722
///
27272723
/// Note that no consistency guarantees are made about the existence of a channel with the
27282724
/// `short_channel_id` here, nor the `channel_id` in `UpdateAddHTLC`!
@@ -6441,7 +6437,7 @@ where
64416437
) -> Result<(), APIError> {
64426438
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
64436439

6444-
let next_hop_scid = {
6440+
let outbound_scid_alias = {
64456441
let peer_state_lock = self.per_peer_state.read().unwrap();
64466442
let peer_state_mutex =
64476443
peer_state_lock.get(&next_node_id).ok_or_else(|| APIError::ChannelUnavailable {
@@ -6461,10 +6457,7 @@ where
64616457
),
64626458
});
64636459
}
6464-
funded_chan
6465-
.funding
6466-
.get_short_channel_id()
6467-
.unwrap_or(funded_chan.context.outbound_scid_alias())
6460+
funded_chan.context.outbound_scid_alias()
64686461
} else {
64696462
return Err(APIError::ChannelUnavailable {
64706463
err: format!(
@@ -6512,7 +6505,7 @@ where
65126505
blinded,
65136506
incoming_cltv_expiry,
65146507
hold_htlc,
6515-
short_channel_id: next_hop_scid,
6508+
short_channel_id: outbound_scid_alias,
65166509
}
65176510
},
65186511
_ => unreachable!(), // Only `PendingHTLCRouting::Forward`s are intercepted
@@ -6527,7 +6520,7 @@ where
65276520
};
65286521

65296522
let mut per_source_pending_forward = [(
6530-
payment.prev_short_channel_id,
6523+
payment.prev_outbound_scid_alias,
65316524
payment.prev_counterparty_node_id,
65326525
payment.prev_funding_outpoint,
65336526
payment.prev_channel_id,
@@ -6588,11 +6581,12 @@ where
65886581
}
65896582
};
65906583

6591-
'outer_loop: for (incoming_scid, update_add_htlcs) in decode_update_add_htlcs {
6584+
'outer_loop: for (incoming_scid_alias, update_add_htlcs) in decode_update_add_htlcs {
65926585
// If any decoded update_add_htlcs were processed, we need to persist.
65936586
should_persist = true;
6594-
let incoming_channel_details_opt =
6595-
self.do_funded_channel_callback(incoming_scid, |chan: &mut FundedChannel<SP>| {
6587+
let incoming_channel_details_opt = self.do_funded_channel_callback(
6588+
incoming_scid_alias,
6589+
|chan: &mut FundedChannel<SP>| {
65966590
let counterparty_node_id = chan.context.get_counterparty_node_id();
65976591
let channel_id = chan.context.channel_id();
65986592
let funding_txo = chan.funding.get_funding_txo().unwrap();
@@ -6605,7 +6599,8 @@ where
66056599
user_channel_id,
66066600
accept_underpaying_htlcs,
66076601
)
6608-
});
6602+
},
6603+
);
66096604
let (
66106605
incoming_counterparty_node_id,
66116606
incoming_channel_id,
@@ -6674,7 +6669,7 @@ where
66746669

66756670
// Process the HTLC on the incoming channel.
66766671
match self.do_funded_channel_callback(
6677-
incoming_scid,
6672+
incoming_scid_alias,
66786673
|chan: &mut FundedChannel<SP>| {
66796674
let logger = WithChannelContext::from(
66806675
&self.logger,
@@ -6747,7 +6742,7 @@ where
67476742
// Process all of the forwards and failures for the channel in which the HTLCs were
67486743
// proposed to as a batch.
67496744
let pending_forwards = (
6750-
incoming_scid,
6745+
incoming_scid_alias,
67516746
incoming_counterparty_node_id,
67526747
incoming_funding_txo,
67536748
incoming_channel_id,
@@ -6769,7 +6764,12 @@ where
67696764
}
67706765
},
67716766
};
6772-
self.forward_htlcs.lock().unwrap().entry(incoming_scid).or_default().push(failure);
6767+
self.forward_htlcs
6768+
.lock()
6769+
.unwrap()
6770+
.entry(incoming_scid_alias)
6771+
.or_default()
6772+
.push(failure);
67736773
self.pending_events.lock().unwrap().push_back((
67746774
events::Event::HTLCHandlingFailed {
67756775
prev_channel_id: incoming_channel_id,
@@ -6906,7 +6906,7 @@ where
69066906
match forward_info {
69076907
HTLCForwardInfo::AddHTLC(payment) => {
69086908
let PendingAddHTLCInfo {
6909-
prev_short_channel_id,
6909+
prev_outbound_scid_alias,
69106910
prev_htlc_id,
69116911
prev_channel_id,
69126912
prev_funding_outpoint,
@@ -7021,7 +7021,7 @@ where
70217021
);
70227022
match create_res {
70237023
Ok(info) => phantom_receives.push((
7024-
prev_short_channel_id,
7024+
prev_outbound_scid_alias,
70257025
prev_counterparty_node_id,
70267026
prev_funding_outpoint,
70277027
prev_channel_id,
@@ -7118,7 +7118,7 @@ where
71187118
HTLCForwardInfo::AddHTLC(ref payment) => {
71197119
let htlc_source = HTLCSource::PreviousHopData(payment.htlc_previous_hop_data());
71207120
let PendingAddHTLCInfo {
7121-
prev_short_channel_id,
7121+
prev_outbound_scid_alias,
71227122
forward_info:
71237123
PendingHTLCInfo {
71247124
payment_hash,
@@ -7212,7 +7212,7 @@ where
72127212
"alternate"
72137213
};
72147214
log_trace!(logger, "Forwarding HTLC from SCID {} with payment_hash {} and next hop SCID {} over {} channel {} with corresponding peer {}",
7215-
prev_short_channel_id, &payment_hash, short_chan_id, channel_description, optimal_channel.context.channel_id(), &counterparty_node_id);
7215+
prev_outbound_scid_alias, &payment_hash, short_chan_id, channel_description, optimal_channel.context.channel_id(), &counterparty_node_id);
72167216
if let Err((reason, msg)) = optimal_channel.queue_add_htlc(
72177217
*outgoing_amt_msat,
72187218
*payment_hash,
@@ -7461,9 +7461,10 @@ where
74617461
let counterparty_node_id = $htlc.prev_hop.counterparty_node_id;
74627462
let incoming_packet_shared_secret =
74637463
$htlc.prev_hop.incoming_packet_shared_secret;
7464+
let prev_outbound_scid_alias = $htlc.prev_hop.prev_outbound_scid_alias;
74647465
failed_forwards.push((
74657466
HTLCSource::PreviousHopData(HTLCPreviousHopData {
7466-
short_channel_id: $htlc.prev_hop.short_channel_id,
7467+
prev_outbound_scid_alias,
74677468
user_channel_id: $htlc.prev_hop.user_channel_id,
74687469
counterparty_node_id,
74697470
channel_id: prev_channel_id,
@@ -8268,7 +8269,7 @@ where
82688269
}
82698270
},
82708271
HTLCSource::PreviousHopData(HTLCPreviousHopData {
8271-
ref short_channel_id,
8272+
ref prev_outbound_scid_alias,
82728273
ref htlc_id,
82738274
ref incoming_packet_shared_secret,
82748275
ref phantom_shared_secret,
@@ -8311,7 +8312,7 @@ where
83118312
};
83128313

83138314
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
8314-
match forward_htlcs.entry(*short_channel_id) {
8315+
match forward_htlcs.entry(*prev_outbound_scid_alias) {
83158316
hash_map::Entry::Occupied(mut entry) => {
83168317
entry.get_mut().push(failure);
83178318
},
@@ -8574,7 +8575,7 @@ where
85748575
) {
85758576
let counterparty_node_id = prev_hop.counterparty_node_id.or_else(|| {
85768577
let short_to_chan_info = self.short_to_chan_info.read().unwrap();
8577-
short_to_chan_info.get(&prev_hop.short_channel_id).map(|(cp_id, _)| *cp_id)
8578+
short_to_chan_info.get(&prev_hop.prev_outbound_scid_alias).map(|(cp_id, _)| *cp_id)
85788579
});
85798580
let counterparty_node_id = if let Some(node_id) = counterparty_node_id {
85808581
node_id
@@ -9225,19 +9226,19 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
92259226
);
92269227

92279228
let counterparty_node_id = channel.context.get_counterparty_node_id();
9228-
let short_channel_id = channel.funding.get_short_channel_id().unwrap_or(channel.context.outbound_scid_alias());
9229+
let outbound_scid_alias = channel.context.outbound_scid_alias();
92299230

92309231
let mut htlc_forwards = None;
92319232
if !pending_forwards.is_empty() {
92329233
htlc_forwards = Some((
9233-
short_channel_id, channel.context.get_counterparty_node_id(),
9234+
outbound_scid_alias, channel.context.get_counterparty_node_id(),
92349235
channel.funding.get_funding_txo().unwrap(), channel.context.channel_id(),
92359236
channel.context.get_user_id(), pending_forwards
92369237
));
92379238
}
92389239
let mut decode_update_add_htlcs = None;
92399240
if !pending_update_adds.is_empty() {
9240-
decode_update_add_htlcs = Some((short_channel_id, pending_update_adds));
9241+
decode_update_add_htlcs = Some((outbound_scid_alias, pending_update_adds));
92419242
}
92429243

92439244
if channel.context.is_connected() {
@@ -10846,8 +10847,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1084610847

1084710848
fn push_decode_update_add_htlcs(&self, mut update_add_htlcs: (u64, Vec<msgs::UpdateAddHTLC>)) {
1084810849
let mut decode_update_add_htlcs = self.decode_update_add_htlcs.lock().unwrap();
10849-
let scid = update_add_htlcs.0;
10850-
match decode_update_add_htlcs.entry(scid) {
10850+
let src_outbound_scid_alias = update_add_htlcs.0;
10851+
match decode_update_add_htlcs.entry(src_outbound_scid_alias) {
1085110852
hash_map::Entry::Occupied(mut e) => {
1085210853
e.get_mut().append(&mut update_add_htlcs.1);
1085310854
},
@@ -10860,7 +10861,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1086010861
#[inline]
1086110862
fn forward_htlcs(&self, per_source_pending_forwards: &mut [PerSourcePendingForward]) {
1086210863
for &mut (
10863-
prev_short_channel_id,
10864+
prev_outbound_scid_alias,
1086410865
prev_counterparty_node_id,
1086510866
prev_funding_outpoint,
1086610867
prev_channel_id,
@@ -10889,7 +10890,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1088910890
Some(payment_hash),
1089010891
);
1089110892
let pending_add = PendingAddHTLCInfo {
10892-
prev_short_channel_id,
10893+
prev_outbound_scid_alias,
1089310894
prev_counterparty_node_id,
1089410895
prev_funding_outpoint,
1089510896
prev_channel_id,
@@ -15064,7 +15065,7 @@ where
1506415065
log_trace!(logger, "Releasing held htlc with intercept_id {}", intercept_id);
1506515066

1506615067
let mut per_source_pending_forward = [(
15067-
htlc.prev_short_channel_id,
15068+
htlc.prev_outbound_scid_alias,
1506815069
htlc.prev_counterparty_node_id,
1506915070
htlc.prev_funding_outpoint,
1507015071
htlc.prev_channel_id,
@@ -15406,7 +15407,7 @@ impl_writeable_tlv_based_enum!(BlindedFailure,
1540615407
);
1540715408

1540815409
impl_writeable_tlv_based!(HTLCPreviousHopData, {
15409-
(0, short_channel_id, required),
15410+
(0, prev_outbound_scid_alias, required),
1541015411
(1, phantom_shared_secret, option),
1541115412
(2, outpoint, required),
1541215413
(3, blinded_failure, option),
@@ -15578,7 +15579,7 @@ impl Writeable for HTLCSource {
1557815579
impl_writeable_tlv_based!(PendingAddHTLCInfo, {
1557915580
(0, forward_info, required),
1558015581
(1, prev_user_channel_id, (default_value, 0)),
15581-
(2, prev_short_channel_id, required),
15582+
(2, prev_outbound_scid_alias, required),
1558215583
(4, prev_htlc_id, required),
1558315584
(6, prev_funding_outpoint, required),
1558415585
// Note that by the time we get past the required read for type 6 above, prev_funding_outpoint will be
@@ -17001,9 +17002,9 @@ where
1700117002
// still have an entry for this HTLC in `forward_htlcs` or
1700217003
// `pending_intercepted_htlcs`, we were apparently not persisted after
1700317004
// the monitor was when forwarding the payment.
17004-
decode_update_add_htlcs.retain(|scid, update_add_htlcs| {
17005+
decode_update_add_htlcs.retain(|src_outb_alias, update_add_htlcs| {
1700517006
update_add_htlcs.retain(|update_add_htlc| {
17006-
let matches = *scid == prev_hop_data.short_channel_id &&
17007+
let matches = *src_outb_alias == prev_hop_data.prev_outbound_scid_alias &&
1700717008
update_add_htlc.htlc_id == prev_hop_data.htlc_id;
1700817009
if matches {
1700917010
log_info!(logger, "Removing pending to-decode HTLC with hash {} as it was forwarded to the closed channel {}",
@@ -17198,7 +17199,7 @@ where
1719817199
// to replay this claim to get the preimage into the inbound
1719917200
// edge monitor but the channel is closed (and thus we'll
1720017201
// immediately panic if we call claim_funds_from_hop).
17201-
if short_to_chan_info.get(&prev_hop.short_channel_id).is_none() {
17202+
if short_to_chan_info.get(&prev_hop.prev_outbound_scid_alias).is_none() {
1720217203
log_error!(args.logger,
1720317204
"We need to replay the HTLC claim for payment_hash {} (preimage {}) but cannot do so as the HTLC was forwarded prior to LDK 0.0.124.\
1720417205
All HTLCs that were forwarded by LDK 0.0.123 and prior must be resolved prior to upgrading to LDK 0.1",
@@ -17324,7 +17325,7 @@ where
1732417325
if htlc.prev_hop.counterparty_node_id.is_some() {
1732517326
continue;
1732617327
}
17327-
if short_to_chan_info.get(&htlc.prev_hop.short_channel_id).is_some() {
17328+
if short_to_chan_info.get(&htlc.prev_hop.prev_outbound_scid_alias).is_some() {
1732817329
log_error!(args.logger,
1732917330
"We do not have the required information to claim a pending payment with payment hash {} reliably.\
1733017331
As long as the channel for the inbound edge of the forward remains open, this may work okay, but we may panic at runtime!\

0 commit comments

Comments
 (0)