Resolve optional hash map TLV fields during ChannelManagerData deserialization
What changed, and why it matters
This commit is a small internal cleanup in the Lightning Dev Kit's channel manager. It moves the handling of two optional hash-map fields from a later processing stage into the deserialization stage, so the fields are always plain hash maps after loading. The change makes the code's invariants clearer and removes a potential source of inconsistency, but it does not appear to fix an active security bug on its own.
Treat as a routine code-quality and defensive-hardening change. No urgent security action is indicated, but downstream users should include it in normal updates. If this commit is part of a larger release, review the release notes for any related security advisories.
Security signals we found
Defensive deserialization hardening
Removal of optional fields that could lead to inconsistent state assumptions
No explicit security fix or CVE referenced in commit message
Evidence from the diff
The patch changes pending_intercepted_htlcs_legacy and decode_update_add_htlcs_legacy from Option<HashMap<...>> to HashMap<...> in ChannelManagerData. The unwrap_or_else(new_hash_map) resolution is moved from from_channel_manager_data (stage 2) into ChannelManagerData::read (stage 1). This ensures the fields are always initialized during deserialization and cannot accidentally be treated as optional later. The change is defensive and improves type safety, but the diff does not show a directly exploitable vulnerability being patched.
Changed components
lightning/src/ln/channelmanager.rsChannelManagerData deserializationpending_intercepted_htlcs_legacydecode_update_add_htlcs_legacyInspect captured patch +8 / −11
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 52fcf69..21cc7d2 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -17234,7 +17234,7 @@ pub(super) struct ChannelManagerData<SP: SignerProvider> {
// Marked `_legacy` because in versions > 0.2 we are taking steps to remove the requirement of
// regularly persisting the `ChannelManager` and instead rebuild the set of HTLC forwards from
// `Channel{Monitor}` data. See [`ChannelManager::read`].
- pending_intercepted_htlcs_legacy: Option<HashMap<InterceptId, PendingAddHTLCInfo>>,
+ pending_intercepted_htlcs_legacy: HashMap<InterceptId, PendingAddHTLCInfo>,
pending_outbound_payments: HashMap<PaymentId, PendingOutboundPayment>,
pending_claiming_payments: HashMap<PaymentHash, ClaimingPayment>,
received_network_pubkey: Option<PublicKey>,
@@ -17247,7 +17247,7 @@ pub(super) struct ChannelManagerData<SP: SignerProvider> {
// Marked `_legacy` because in versions > 0.2 we are taking steps to remove the requirement of
// regularly persisting the `ChannelManager` and instead rebuild the set of HTLC forwards from
// `Channel{Monitor}` data. See [`ChannelManager::read`].
- decode_update_add_htlcs_legacy: Option<HashMap<u64, Vec<msgs::UpdateAddHTLC>>>,
+ decode_update_add_htlcs_legacy: HashMap<u64, Vec<msgs::UpdateAddHTLC>>,
inbound_payment_id_secret: Option<[u8; 32]>,
in_flight_monitor_updates: Option<HashMap<(PublicKey, ChannelId), Vec<ChannelMonitorUpdate>>>,
peer_storage_dir: Option<Vec<(PublicKey, Vec<u8>)>>,
@@ -17496,7 +17496,8 @@ impl<'a, ES: EntropySource, SP: SignerProvider, L: Logger>
peer_init_features,
pending_events_read,
highest_seen_timestamp,
- pending_intercepted_htlcs_legacy,
+ pending_intercepted_htlcs_legacy: pending_intercepted_htlcs_legacy
+ .unwrap_or_else(new_hash_map),
pending_outbound_payments,
// unwrap safety: pending_claiming_payments is guaranteed to be `Some` after read_tlv_fields
pending_claiming_payments: pending_claiming_payments.unwrap(),
@@ -17508,7 +17509,8 @@ impl<'a, ES: EntropySource, SP: SignerProvider, L: Logger>
claimable_htlc_purposes,
probing_cookie_secret,
claimable_htlc_onion_fields,
- decode_update_add_htlcs_legacy,
+ decode_update_add_htlcs_legacy: decode_update_add_htlcs_legacy
+ .unwrap_or_else(new_hash_map),
inbound_payment_id_secret,
in_flight_monitor_updates,
peer_storage_dir,
@@ -17791,7 +17793,7 @@ impl<
peer_init_features,
mut pending_events_read,
highest_seen_timestamp,
- pending_intercepted_htlcs_legacy,
+ mut pending_intercepted_htlcs_legacy,
pending_outbound_payments,
pending_claiming_payments,
received_network_pubkey,
@@ -17800,18 +17802,13 @@ impl<
claimable_htlc_purposes,
mut probing_cookie_secret,
claimable_htlc_onion_fields,
- decode_update_add_htlcs_legacy,
+ mut decode_update_add_htlcs_legacy,
mut inbound_payment_id_secret,
mut in_flight_monitor_updates,
peer_storage_dir,
async_receive_offer_cache,
} = data;
- let mut pending_intercepted_htlcs_legacy =
- pending_intercepted_htlcs_legacy.unwrap_or_else(new_hash_map);
- let mut decode_update_add_htlcs_legacy =
- decode_update_add_htlcs_legacy.unwrap_or_else(new_hash_map);
-
let empty_peer_state = || PeerState {
channel_by_id: new_hash_map(),
inbound_channel_request_by_id: new_hash_map(),
Why this scored 24/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.