Checkpoint deferred ChannelManager with AwaitingSignatures splice
What changed, and why it matters
This commit is a temporary workaround in a fuzz-testing harness for the Lightning Dev Kit. It forces a save of the channel manager before restarting a test node when a specific in-progress splice state exists. The underlying issue is that the signing session for a splice isn't checkpointed before sending a commitment message, so if the node restarts at the wrong moment, it can reload stale state and force-close a channel unnecessarily. The commit does not fix the root cause; it only prevents the fuzzer from hitting the case.
Treat this as a known bug marker rather than a completed fix. The project should implement proper checkpointing of the signing session before sending `commitment_signed` for splices, then remove this fuzzer workaround and add a regression test.
Security signals we found
State persistence gap between commitment and signing session
Potential force-close after reload due to stale ChannelManager state
Splice candidate in AwaitingSignatures status
Deferred persistence path affected
FIXME comment indicating incomplete fix
Evidence from the diff
The change adds a helper has_awaiting_signature_splice() to detect channels with a splice candidate in SpliceCandidateStatus::AwaitingSignatures. In restart_node, if the node uses deferred persistence and has such a candidate, it now checkpoints the ChannelManager before proceeding. The commit message explicitly states this defers the real fix and is only to prevent the fuzzer from triggering a state where a commitment is made but the signing session has not yet been persisted, leading to an erroneous force-close after reload.
Changed components
fuzz/src/chanmon_consistency.rsLightning splice signing session persistenceChannelManager checkpoint/restart logic under testInspect captured patch +21 / −1
### fuzz/src/chanmon_consistency.rs
@@ -51,7 +51,9 @@ use lightning::events::{self, EventsProvider};
use lightning::ln::channel::{
FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS,
};
-use lightning::ln::channel_state::{ChannelDetails, InboundHTLCStateDetails, OutboundHTLCSource};
+use lightning::ln::channel_state::{
+ ChannelDetails, InboundHTLCStateDetails, OutboundHTLCSource, SpliceCandidateStatus,
+};
use lightning::ln::channelmanager::{
ChainParameters, ChannelManager, ChannelManagerReadArgs, PaymentId, RecentPaymentDetails,
TrustedChannelFeatures,
@@ -1251,6 +1253,16 @@ impl<'a> HarnessNode<'a> {
self.node.current_best_block().height
}
+ fn has_awaiting_signature_splice(&self) -> bool {
+ self.list_channels().iter().any(|channel| {
+ channel.splice_details.as_ref().map_or(false, |splice_details| {
+ splice_details.candidates.iter().any(|candidate| {
+ matches!(candidate.status, SpliceCandidateStatus::AwaitingSignatures { .. })
+ })
+ })
+ })
+ }
+
// Connects a block range to the ChannelManager, and to the ChainMonitor when
// sync_monitors is set. Reload syncs monitors separately because they can be
// at different heights than the manager, so it leaves them out here.
@@ -3861,6 +3873,14 @@ impl<'a, Out: Output + MaybeSend + MaybeSync> Harness<'a, Out> {
}
fn restart_node(&mut self, node_idx: usize, v: u8, router: &'a FuzzRouter) {
+ if self.nodes[node_idx].deferred && self.nodes[node_idx].has_awaiting_signature_splice() {
+ // FIXME: We don't currently checkpoint our signing session prior to sending
+ // `commitment_signed`, so we may run into a case where we commit to a splice
+ // candidate and reload with an outdated manager prior to the signing session
+ // existing. If the counterparty commits to the splice back, and is expecting to
+ // resume, we'll end up aborting erroneously leading to a force close.
+ self.nodes[node_idx].checkpoint_manager_persistence();
+ }
if !self.nodes[node_idx].deferred {
self.nodes[node_idx].checkpoint_manager_persistence();
}Why this scored 33/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.