Prevent downgrades in case holder-selected reserve is zero satoshis
What changed, and why it matters
This commit adds a marker to saved channel data so that older versions of the software will refuse to load it if the channel uses a zero-satoshi reserve chosen by the local user. That prevents accidental downgrades that could mishandle such channels, because older releases only understood zero-reserve channels when the remote side chose the zero reserve. It is a forward-compatibility guard, not a fix for an active attack.
No immediate action required. Users running 0.3+ should not downgrade to pre-0.3 releases for channels with holder-selected zero reserve. Treat this as a compatibility note rather than a vulnerability patch.
Security signals we found
Serialization sentinel added to block downgrade to pre-0.3 releases
Guard specifically targets holder-selected zero-reserve channels
No runtime logic change; purely state-format compatibility control
Evidence from the diff
In lightning/src/ln/channel.rs, the serialization of FundedChannel now writes an optional sentinel field (70, has_0reserve, option) when funding.holder_selected_channel_reserve_satoshis == 0. The corresponding deserialization reads the same field into _has_0reserve but does not otherwise use it. Because unknown optional fields are silently ignored by newer code, this only affects behavior when an older release (pre-0.3) reads a state file written by 0.3+: the presence of field 70 will cause the old parser to reject the state, preventing a downgrade. The commit explicitly notes that FundingScope serialization does not need the sentinel because pending scopes inherit the current scope’s reserve.
Changed components
lightning/src/ln/channel.rsFundedChannel serialization/deserializationChannel state persistence formatInspect captured patch +8 / −0
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index c8c93ee..5370920 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -15302,6 +15302,11 @@ impl<SP: SignerProvider> Writeable for FundedChannel<SP> {
}
let is_manual_broadcast = Some(self.context.is_manual_broadcast);
+ // We prevent downgrades from 0.3 only in the case where the holder-selected reserve
+ // is 0, as we've had support for counterparty selected 0-reserves in prior
+ // releases.
+ let has_0reserve =
+ (self.funding.holder_selected_channel_reserve_satoshis == 0).then_some(());
let holder_commitment_point_previous_revoked =
self.holder_commitment_point.previous_revoked_point();
let holder_commitment_point_last_revoked =
@@ -15371,6 +15376,7 @@ impl<SP: SignerProvider> Writeable for FundedChannel<SP> {
// 65 was previously used for quiescent_action
(67, pending_outbound_held_htlc_flags, optional_vec), // Added in 0.2
(69, holding_cell_held_htlc_flags, optional_vec), // Added in 0.2
+ (70, has_0reserve, option), // Added in 0.3 to prevent downgrades
(71, holder_commitment_point_previous_revoked, option), // Added in 0.3
(73, holder_commitment_point_last_revoked, option), // Added in 0.3
(75, inbound_committed_update_adds, optional_vec),
@@ -15744,6 +15750,7 @@ impl<'a, 'b, 'c, ES: EntropySource, SP: SignerProvider>
let mut malformed_htlcs: Option<Vec<(u64, u16, [u8; 32])>> = None;
let mut monitor_pending_update_adds: Option<Vec<msgs::UpdateAddHTLC>> = None;
+ let mut _has_0reserve: Option<()> = None;
let mut holder_commitment_point_previous_revoked_opt: Option<PublicKey> = None;
let mut holder_commitment_point_last_revoked_opt: Option<PublicKey> = None;
let mut holder_commitment_point_current_opt: Option<PublicKey> = None;
@@ -15814,6 +15821,7 @@ impl<'a, 'b, 'c, ES: EntropySource, SP: SignerProvider>
// 65 quiescent_action: Added in 0.2; removed in 0.3
(67, pending_outbound_held_htlc_flags_opt, optional_vec), // Added in 0.2
(69, holding_cell_held_htlc_flags_opt, optional_vec), // Added in 0.2
+ (70, _has_0reserve, option), // Added in 0.3 to prevent downgrades
(71, holder_commitment_point_previous_revoked_opt, option), // Added in 0.3
(73, holder_commitment_point_last_revoked_opt, option), // Added in 0.3
(75, inbound_committed_update_adds_opt, optional_vec),
Why this scored 22/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.