Skip to content

Commit 07b3def

Browse files
Split method to reconstruct pending HTLCs into two
In the next commit, we want to dedup fields between the InboundUpdateAdd::Forwarded's HTLCPreviousHopData and the outer InboundHTLCOutput/Channel structs, since many fields are duplicated in both places at the moment. As part of doing this cleanly, we first refactor the method that retrieves these InboundUpdateAdds for reconstructing the set of pending HTLCs during ChannelManager deconstruction. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent eb31aeb commit 07b3def

2 files changed

Lines changed: 64 additions & 50 deletions

File tree

lightning/src/ln/channel.rs

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ impl InboundHTLCState {
315315
///
316316
/// Useful for reconstructing the pending HTLC set on startup.
317317
#[derive(Debug, Clone)]
318-
pub(super) enum InboundUpdateAdd {
318+
enum InboundUpdateAdd {
319319
/// The inbound committed HTLC's update_add_htlc message.
320320
WithOnion { update_add_htlc: msgs::UpdateAddHTLC },
321321
/// This inbound HTLC is a forward that was irrevocably committed to the outbound edge, allowing
@@ -7885,10 +7885,35 @@ where
78857885
Ok(())
78867886
}
78877887

7888-
/// Useful for reconstructing the set of pending HTLCs when deserializing the `ChannelManager`.
7889-
pub(super) fn inbound_committed_unresolved_htlcs(
7888+
/// Returns true if any committed inbound HTLCs were received pre-LDK 0.3 and cannot be used
7889+
/// during `ChannelManager` deserialization to reconstruct the set of pending HTLCs.
7890+
pub(super) fn has_legacy_inbound_htlcs(&self) -> bool {
7891+
self.context.pending_inbound_htlcs.iter().any(|htlc| {
7892+
matches!(
7893+
&htlc.state,
7894+
InboundHTLCState::Committed { update_add_htlc: InboundUpdateAdd::Legacy }
7895+
)
7896+
})
7897+
}
7898+
7899+
/// Returns committed inbound HTLCs whose onion has not yet been decoded and processed. Useful
7900+
/// for reconstructing the set of pending HTLCs when deserializing the `ChannelManager`.
7901+
pub(super) fn inbound_htlcs_pending_decode(
7902+
&self,
7903+
) -> impl Iterator<Item = msgs::UpdateAddHTLC> + '_ {
7904+
self.context.pending_inbound_htlcs.iter().filter_map(|htlc| match &htlc.state {
7905+
InboundHTLCState::Committed {
7906+
update_add_htlc: InboundUpdateAdd::WithOnion { update_add_htlc },
7907+
} => Some(update_add_htlc.clone()),
7908+
_ => None,
7909+
})
7910+
}
7911+
7912+
/// Returns committed inbound HTLCs that have been forwarded but not yet fully resolved. Useful
7913+
/// when reconstructing the set of pending HTLCs when deserializing the `ChannelManager`.
7914+
pub(super) fn inbound_forwarded_htlcs(
78907915
&self,
7891-
) -> Vec<(PaymentHash, InboundUpdateAdd)> {
7916+
) -> impl Iterator<Item = (PaymentHash, HTLCPreviousHopData, u64)> + '_ {
78927917
// We don't want to return an HTLC as needing processing if it already has a resolution that's
78937918
// pending in the holding cell.
78947919
let htlc_resolution_in_holding_cell = |id: u64| -> bool {
@@ -7902,19 +7927,17 @@ where
79027927
})
79037928
};
79047929

7905-
self.context
7906-
.pending_inbound_htlcs
7907-
.iter()
7908-
.filter_map(|htlc| match &htlc.state {
7909-
InboundHTLCState::Committed { update_add_htlc } => {
7910-
if htlc_resolution_in_holding_cell(htlc.htlc_id) {
7911-
return None;
7912-
}
7913-
Some((htlc.payment_hash, update_add_htlc.clone()))
7914-
},
7915-
_ => None,
7916-
})
7917-
.collect()
7930+
self.context.pending_inbound_htlcs.iter().filter_map(move |htlc| match &htlc.state {
7931+
InboundHTLCState::Committed {
7932+
update_add_htlc: InboundUpdateAdd::Forwarded { hop_data, outbound_amt_msat },
7933+
} => {
7934+
if htlc_resolution_in_holding_cell(htlc.htlc_id) {
7935+
return None;
7936+
}
7937+
Some((htlc.payment_hash, hop_data.clone(), *outbound_amt_msat))
7938+
},
7939+
_ => None,
7940+
})
79187941
}
79197942

79207943
/// Useful when reconstructing the set of pending HTLC forwards when deserializing the

lightning/src/ln/channelmanager.rs

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ use crate::ln::chan_utils::selected_commitment_sat_per_1000_weight;
5959
use crate::ln::channel::QuiescentAction;
6060
use crate::ln::channel::{
6161
self, hold_time_since, Channel, ChannelError, ChannelUpdateStatus, DisconnectResult,
62-
FundedChannel, FundingTxSigned, InboundUpdateAdd, InboundV1Channel, OutboundV1Channel,
63-
PendingV2Channel, ReconnectionMsg, ShutdownResult, SpliceFundingFailed, StfuResponse,
64-
UpdateFulfillCommitFetch, WithChannelContext,
62+
FundedChannel, FundingTxSigned, InboundV1Channel, OutboundV1Channel, PendingV2Channel,
63+
ReconnectionMsg, ShutdownResult, SpliceFundingFailed, StfuResponse, UpdateFulfillCommitFetch,
64+
WithChannelContext,
6565
};
6666
use crate::ln::channel_state::ChannelDetails;
6767
use crate::ln::funding::SpliceContribution;
@@ -10185,10 +10185,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1018510185
let per_peer_state = self.per_peer_state.read().unwrap();
1018610186
let peer_state = per_peer_state.get(&cp_id).map(|state| state.lock().unwrap()).unwrap();
1018710187
let chan = peer_state.channel_by_id.get(&chan_id).and_then(|c| c.as_funded()).unwrap();
10188-
chan.inbound_committed_unresolved_htlcs()
10189-
.iter()
10190-
.filter(|(_, htlc)| matches!(htlc, InboundUpdateAdd::WithOnion { .. }))
10191-
.count()
10188+
chan.inbound_htlcs_pending_decode().count()
1019210189
}
1019310190

1019410191
#[cfg(test)]
@@ -18626,33 +18623,27 @@ impl<
1862618623
if reconstruct_manager_from_monitors {
1862718624
if let Some(chan) = peer_state.channel_by_id.get(channel_id) {
1862818625
if let Some(funded_chan) = chan.as_funded() {
18626+
// Legacy HTLCs are from pre-LDK 0.3 and cannot be reconstructed.
18627+
if funded_chan.has_legacy_inbound_htlcs() {
18628+
return Err(DecodeError::InvalidValue);
18629+
}
18630+
// Reconstruct `ChannelManager::decode_update_add_htlcs` from the serialized
18631+
// `Channel` as part of removing the requirement to regularly persist the
18632+
// `ChannelManager`.
1862918633
let scid_alias = funded_chan.context.outbound_scid_alias();
18630-
let inbound_committed_update_adds =
18631-
funded_chan.inbound_committed_unresolved_htlcs();
18632-
for (payment_hash, htlc) in inbound_committed_update_adds {
18633-
match htlc {
18634-
InboundUpdateAdd::WithOnion { update_add_htlc } => {
18635-
// Reconstruct `ChannelManager::decode_update_add_htlcs` from the serialized
18636-
// `Channel` as part of removing the requirement to regularly persist the
18637-
// `ChannelManager`.
18638-
decode_update_add_htlcs
18639-
.entry(scid_alias)
18640-
.or_insert_with(Vec::new)
18641-
.push(update_add_htlc);
18642-
},
18643-
InboundUpdateAdd::Forwarded {
18644-
hop_data,
18645-
outbound_amt_msat,
18646-
} => {
18647-
already_forwarded_htlcs
18648-
.entry((hop_data.channel_id, payment_hash))
18649-
.or_insert_with(Vec::new)
18650-
.push((hop_data, outbound_amt_msat));
18651-
},
18652-
InboundUpdateAdd::Legacy => {
18653-
return Err(DecodeError::InvalidValue)
18654-
},
18655-
}
18634+
for update_add_htlc in funded_chan.inbound_htlcs_pending_decode() {
18635+
decode_update_add_htlcs
18636+
.entry(scid_alias)
18637+
.or_insert_with(Vec::new)
18638+
.push(update_add_htlc);
18639+
}
18640+
for (payment_hash, hop_data, outbound_amt_msat) in
18641+
funded_chan.inbound_forwarded_htlcs()
18642+
{
18643+
already_forwarded_htlcs
18644+
.entry((hop_data.channel_id, payment_hash))
18645+
.or_insert_with(Vec::new)
18646+
.push((hop_data, outbound_amt_msat));
1865618647
}
1865718648
}
1865818649
}

0 commit comments

Comments
 (0)