Skip to content

Commit 845bc02

Browse files
joostjagerclaude
andcommitted
Resolve legacy TLV fields during ChannelManagerData deserialization
Move the resolution of legacy/compatibility TLV fields from from_channel_manager_data (stage 2) into ChannelManagerData::read (stage 1). This keeps ChannelManagerData minimal by consolidating mutually exclusive fields into their final form during deserialization: - pending_outbound_payments: Merge TLV 3, TLV 1 (no_retry), and non-TLV compat fields into a single HashMap - in_flight_monitor_updates: Convert legacy TLV 10 (keyed by OutPoint) to TLV 17 format (keyed by ChannelId) - pending_events: Apply events_override (TLV 8) if present Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 617e4ab commit 845bc02

1 file changed

Lines changed: 45 additions & 48 deletions

File tree

lightning/src/ln/channelmanager.rs

Lines changed: 45 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17330,19 +17330,14 @@ where
1733017330
peer_init_features: Vec<(PublicKey, InitFeatures)>,
1733117331
pending_events: VecDeque<(events::Event, Option<EventCompletionAction>)>,
1733217332
highest_seen_timestamp: u32,
17333-
pending_outbound_payments_compat: HashMap<PaymentId, PendingOutboundPayment>,
17334-
pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>>,
1733517333
pending_intercepted_htlcs: Option<HashMap<InterceptId, PendingAddHTLCInfo>>,
17336-
pending_outbound_payments: Option<HashMap<PaymentId, PendingOutboundPayment>>,
17334+
pending_outbound_payments: HashMap<PaymentId, PendingOutboundPayment>,
1733717335
pending_claiming_payments: HashMap<PaymentHash, ClaimingPayment>,
1733817336
received_network_pubkey: Option<PublicKey>,
1733917337
monitor_update_blocked_actions_per_peer:
1734017338
Vec<(PublicKey, BTreeMap<ChannelId, Vec<MonitorUpdateCompletionAction>>)>,
1734117339
fake_scid_rand_bytes: Option<[u8; 32]>,
17342-
events_override: Option<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
1734317340
claimable_htlc_purposes: Option<Vec<events::PaymentPurpose>>,
17344-
legacy_in_flight_monitor_updates:
17345-
Option<HashMap<(PublicKey, OutPoint), Vec<ChannelMonitorUpdate>>>,
1734617341
probing_cookie_secret: Option<[u8; 32]>,
1734717342
claimable_htlc_onion_fields: Option<Vec<Option<RecipientOnionFields>>>,
1734817343
decode_update_add_htlcs: Option<HashMap<u64, Vec<msgs::UpdateAddHTLC>>>,
@@ -17539,6 +17534,48 @@ where
1753917534
(21, async_receive_offer_cache, (default_value, async_receive_offer_cache)),
1754017535
});
1754117536

17537+
// Merge legacy pending_outbound_payments fields into a single HashMap.
17538+
// Priority: pending_outbound_payments (TLV 3) > pending_outbound_payments_no_retry (TLV 1)
17539+
// > pending_outbound_payments_compat (non-TLV legacy)
17540+
let pending_outbound_payments = if let Some(payments) = pending_outbound_payments {
17541+
payments
17542+
} else if let Some(mut pending_outbound_payments_no_retry) = pending_outbound_payments_no_retry
17543+
{
17544+
let mut outbounds = new_hash_map();
17545+
for (id, session_privs) in pending_outbound_payments_no_retry.drain() {
17546+
outbounds.insert(id, PendingOutboundPayment::Legacy { session_privs });
17547+
}
17548+
outbounds
17549+
} else {
17550+
pending_outbound_payments_compat
17551+
};
17552+
17553+
// Merge legacy in-flight monitor updates (keyed by OutPoint) into the new format (keyed by
17554+
// ChannelId).
17555+
if let Some(legacy_in_flight_upds) = legacy_in_flight_monitor_updates {
17556+
// We should never serialize an empty map.
17557+
if legacy_in_flight_upds.is_empty() {
17558+
return Err(DecodeError::InvalidValue);
17559+
}
17560+
if in_flight_monitor_updates.is_none() {
17561+
let in_flight_upds = in_flight_monitor_updates.get_or_insert_with(new_hash_map);
17562+
for ((counterparty_node_id, funding_txo), updates) in legacy_in_flight_upds {
17563+
// All channels with legacy in flight monitor updates are v1 channels.
17564+
let channel_id = ChannelId::v1_from_funding_outpoint(funding_txo);
17565+
in_flight_upds.insert((counterparty_node_id, channel_id), updates);
17566+
}
17567+
} else if in_flight_monitor_updates.as_ref().unwrap().is_empty() {
17568+
// Both TLVs present - the new one takes precedence but must not be empty.
17569+
return Err(DecodeError::InvalidValue);
17570+
}
17571+
}
17572+
17573+
// Resolve events_override: if present, it replaces pending_events.
17574+
let mut pending_events = pending_events;
17575+
if let Some(events) = events_override {
17576+
pending_events = events;
17577+
}
17578+
1754217579
Ok(ChannelManagerData {
1754317580
chain_hash,
1754417581
best_block_height,
@@ -17549,8 +17586,6 @@ where
1754917586
peer_init_features,
1755017587
pending_events,
1755117588
highest_seen_timestamp,
17552-
pending_outbound_payments_compat,
17553-
pending_outbound_payments_no_retry,
1755417589
pending_intercepted_htlcs,
1755517590
pending_outbound_payments,
1755617591
// unwrap safety: pending_claiming_payments is guaranteed to be `Some` after read_tlv_fields
@@ -17560,9 +17595,7 @@ where
1756017595
monitor_update_blocked_actions_per_peer: monitor_update_blocked_actions_per_peer
1756117596
.unwrap(),
1756217597
fake_scid_rand_bytes,
17563-
events_override,
1756417598
claimable_htlc_purposes,
17565-
legacy_in_flight_monitor_updates,
1756617599
probing_cookie_secret,
1756717600
claimable_htlc_onion_fields,
1756817601
decode_update_add_htlcs,
@@ -17897,7 +17930,7 @@ where
1789717930
let mut claimable_htlcs_list = data.claimable_htlcs;
1789817931
let mut pending_intercepted_htlcs_legacy =
1789917932
data.pending_intercepted_htlcs.unwrap_or_else(new_hash_map);
17900-
let mut pending_outbound_payments = data.pending_outbound_payments;
17933+
let pending_outbound_payments = data.pending_outbound_payments;
1790117934
let mut fake_scid_rand_bytes = data.fake_scid_rand_bytes;
1790217935
let mut probing_cookie_secret = data.probing_cookie_secret;
1790317936
let mut decode_update_add_htlcs_legacy =
@@ -18200,26 +18233,12 @@ where
1820018233
inbound_payment_id_secret = Some(args.entropy_source.get_secure_random_bytes());
1820118234
}
1820218235

18203-
if let Some(events) = data.events_override {
18204-
pending_events_read = events;
18205-
}
18206-
1820718236
if !channel_closures.is_empty() {
1820818237
pending_events_read.append(&mut channel_closures);
1820918238
}
1821018239

18211-
if pending_outbound_payments.is_none() && data.pending_outbound_payments_no_retry.is_none()
18212-
{
18213-
pending_outbound_payments = Some(data.pending_outbound_payments_compat);
18214-
} else if pending_outbound_payments.is_none() {
18215-
let mut outbounds = new_hash_map();
18216-
for (id, session_privs) in data.pending_outbound_payments_no_retry.unwrap().drain() {
18217-
outbounds.insert(id, PendingOutboundPayment::Legacy { session_privs });
18218-
}
18219-
pending_outbound_payments = Some(outbounds);
18220-
}
1822118240
let pending_outbounds =
18222-
OutboundPayments::new(pending_outbound_payments.unwrap(), args.logger.clone());
18241+
OutboundPayments::new(pending_outbound_payments, args.logger.clone());
1822318242

1822418243
if let Some(peer_storage_dir) = data.peer_storage_dir {
1822518244
for (peer_pubkey, peer_storage) in peer_storage_dir {
@@ -18229,28 +18248,6 @@ where
1822918248
}
1823018249
}
1823118250

18232-
// Handle transitioning from the legacy TLV to the new one on upgrades.
18233-
if let Some(legacy_in_flight_upds) = data.legacy_in_flight_monitor_updates {
18234-
// We should never serialize an empty map.
18235-
if legacy_in_flight_upds.is_empty() {
18236-
return Err(DecodeError::InvalidValue);
18237-
}
18238-
if in_flight_monitor_updates.is_none() {
18239-
let in_flight_upds =
18240-
in_flight_monitor_updates.get_or_insert_with(|| new_hash_map());
18241-
for ((counterparty_node_id, funding_txo), updates) in legacy_in_flight_upds {
18242-
// All channels with legacy in flight monitor updates are v1 channels.
18243-
let channel_id = ChannelId::v1_from_funding_outpoint(funding_txo);
18244-
in_flight_upds.insert((counterparty_node_id, channel_id), updates);
18245-
}
18246-
} else {
18247-
// We should never serialize an empty map.
18248-
if in_flight_monitor_updates.as_ref().unwrap().is_empty() {
18249-
return Err(DecodeError::InvalidValue);
18250-
}
18251-
}
18252-
}
18253-
1825418251
// We have to replay (or skip, if they were completed after we wrote the `ChannelManager`)
1825518252
// each `ChannelMonitorUpdate` in `in_flight_monitor_updates`. After doing so, we have to
1825618253
// check that each channel we have isn't newer than the latest `ChannelMonitorUpdate`(s) we

0 commit comments

Comments
 (0)