Persist FundedChannel::pending_splice
What changed, and why it matters
This commit fixes a data-loss bug in the Lightning Dev Kit's new channel-splicing feature. Previously, if a user constructed a splice funding transaction and then restarted their node, the in-progress splice state was not saved to disk, so the splice could not continue or recover correctly. The patch adds serialization (write/read) for the pending splice state so it survives restarts. There is no direct evidence this is exploitable by a remote attacker; the main risk is operational data loss and possible funds stuck in a half-completed splice.
Treat as a reliability/data-integrity fix rather than an active security vulnerability. Users running splicing-enabled nodes should upgrade before performing splice operations to avoid state loss on restart. Review whether any already-lost splice states require manual recovery guidance.
Security signals we found
Data-loss / state inconsistency in new protocol feature (splicing)
Missing serialization for in-progress channel state
Operational risk from node restart during splice
No remote attack vector visible in the diff
Evidence from the diff
The change adds TLV-based serialization for PendingFunding and FundingNegotiation, and includes self.pending_splice in the FundedChannel serialization and deserialization paths. Before this commit, pending_splice was initialized to None on load, meaning any splice in progress at shutdown would be silently dropped. This could lead to loss of channel state consistency, inability to complete signature exchange or RBF, and potential need for manual recovery. The patch is a straightforward state-persistence fix with no cryptographic or network-level changes.
Changed components
lightning/src/ln/channel.rsFundedChannel persistencePendingFunding serializationFundingNegotiation serializationChannel splicing workflowInspect captured patch +18 / −1
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index d0369af..893efe7 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2563,6 +2563,13 @@ struct PendingFunding {
received_funding_txid: Option<Txid>,
}
+impl_writeable_tlv_based!(PendingFunding, {
+ (1, funding_negotiation, upgradable_option),
+ (3, negotiated_candidates, required_vec),
+ (5, sent_funding_txid, option),
+ (7, received_funding_txid, option),
+});
+
enum FundingNegotiation {
AwaitingAck {
context: FundingNegotiationContext,
@@ -2576,6 +2583,13 @@ enum FundingNegotiation {
},
}
+impl_writeable_tlv_based_enum_upgradable!(FundingNegotiation,
+ (0, AwaitingSignatures) => {
+ (1, funding, required),
+ },
+ unread_variants: AwaitingAck, ConstructingTransaction
+);
+
impl FundingNegotiation {
fn as_funding(&self) -> Option<&FundingScope> {
match self {
@@ -14294,6 +14308,7 @@ where
(60, self.context.historical_scids, optional_vec), // Added in 0.2
(61, fulfill_attribution_data, optional_vec), // Added in 0.2
(63, holder_commitment_point_current, option), // Added in 0.2
+ (64, self.pending_splice, option), // Added in 0.2
(65, self.quiescent_action, option), // Added in 0.2
});
@@ -14655,6 +14670,7 @@ where
let mut minimum_depth_override: Option<u32> = None;
+ let mut pending_splice: Option<PendingFunding> = None;
let mut quiescent_action = None;
read_tlv_fields!(reader, {
@@ -14699,6 +14715,7 @@ where
(60, historical_scids, optional_vec), // Added in 0.2
(61, fulfill_attribution_data, optional_vec), // Added in 0.2
(63, holder_commitment_point_current_opt, option), // Added in 0.2
+ (64, pending_splice, option), // Added in 0.2
(65, quiescent_action, upgradable_option), // Added in 0.2
});
@@ -15059,7 +15076,7 @@ where
},
interactive_tx_signing_session,
holder_commitment_point,
- pending_splice: None,
+ pending_splice,
quiescent_action,
})
}
Why this scored 38/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.