Include NegotiationFailureReason in InteractiveTxMsgError
What changed, and why it matters
This commit is a small internal cleanup in the Lightning Dev Kit's code for handling failed channel-funding negotiations (splicing). It moves the 'reason the negotiation failed' into the same error object that already carries other failure details, instead of passing it separately through several function calls. The change itself does not fix a vulnerability; it makes the code easier to maintain and ensures the correct reason is reported when a splice is canceled locally. There is no evidence in the commit of a security bug or exploit.
No security action required. Treat as normal code-quality review; verify that all SpliceNegotiationFailed event reasons remain correct after the refactor.
Security signals we found
No security-relevant signal in commit message or diff
Refactor only: moves existing reason field into error struct
No new input validation, bounds checks, or cryptographic operations
No mention of vulnerability, CVE, bug bounty, or exploit in commit
Evidence from the diff
The patch refactors InteractiveTxMsgError in lightning/src/ln/channel.rs to include an optional negotiation_failure_reason field and adds helper constructors (new, with_negotiation_failure_reason, into_parts, reason_from_channel_error). It updates all call sites to use the new constructors and removes the separate Option
Changed components
lightning/src/ln/channel.rslightning/src/ln/channelmanager.rsInspect captured patch +62 / −37
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 3f3a6fe..3720aaa 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -1187,6 +1187,35 @@ pub(super) struct InteractiveTxMsgError {
/// If a splice was in progress when processing the message, this contains the splice funding
/// information for emitting a `SpliceNegotiationFailed` event.
pub(super) splice_funding_failed: Option<SpliceFundingFailed>,
+ /// The event reason to use if this error causes a `SpliceNegotiationFailed` event.
+ pub(super) negotiation_failure_reason: Option<NegotiationFailureReason>,
+}
+
+impl InteractiveTxMsgError {
+ fn new(err: ChannelError, splice_funding_failed: Option<SpliceFundingFailed>) -> Self {
+ Self { err, splice_funding_failed, negotiation_failure_reason: None }
+ }
+
+ fn with_negotiation_failure_reason(mut self, reason: NegotiationFailureReason) -> Self {
+ self.negotiation_failure_reason = Some(reason);
+ self
+ }
+
+ pub(super) fn into_parts(
+ self,
+ ) -> (ChannelError, Option<(SpliceFundingFailed, NegotiationFailureReason)>) {
+ let Self { err, splice_funding_failed, negotiation_failure_reason } = self;
+ let splice_failure = splice_funding_failed.map(|splice_funding_failed| {
+ let reason =
+ negotiation_failure_reason.unwrap_or_else(|| Self::reason_from_channel_error(&err));
+ (splice_funding_failed, reason)
+ });
+ (err, splice_failure)
+ }
+
+ fn reason_from_channel_error(err: &ChannelError) -> NegotiationFailureReason {
+ NegotiationFailureReason::NegotiationError { msg: format!("{:?}", err) }
+ }
}
/// The return value of `monitor_updating_restored`
@@ -1850,7 +1879,7 @@ where
},
};
- InteractiveTxMsgError { err: ChannelError::Abort(reason), splice_funding_failed }
+ InteractiveTxMsgError::new(ChannelError::Abort(reason), splice_funding_failed)
}
pub fn tx_add_input<L: Logger>(
@@ -1860,12 +1889,12 @@ where
Some(interactive_tx_constructor) => interactive_tx_constructor
.handle_tx_add_input(msg)
.map_err(|reason| self.fail_interactive_tx_negotiation(reason, logger)),
- None => Err(InteractiveTxMsgError {
- err: ChannelError::WarnAndDisconnect(
+ None => Err(InteractiveTxMsgError::new(
+ ChannelError::WarnAndDisconnect(
"Received unexpected interactive transaction negotiation message".to_owned(),
),
- splice_funding_failed: None,
- }),
+ None,
+ )),
}
}
@@ -1876,12 +1905,12 @@ where
Some(interactive_tx_constructor) => interactive_tx_constructor
.handle_tx_add_output(msg)
.map_err(|reason| self.fail_interactive_tx_negotiation(reason, logger)),
- None => Err(InteractiveTxMsgError {
- err: ChannelError::WarnAndDisconnect(
+ None => Err(InteractiveTxMsgError::new(
+ ChannelError::WarnAndDisconnect(
"Received unexpected interactive transaction negotiation message".to_owned(),
),
- splice_funding_failed: None,
- }),
+ None,
+ )),
}
}
@@ -1892,12 +1921,12 @@ where
Some(interactive_tx_constructor) => interactive_tx_constructor
.handle_tx_remove_input(msg)
.map_err(|reason| self.fail_interactive_tx_negotiation(reason, logger)),
- None => Err(InteractiveTxMsgError {
- err: ChannelError::WarnAndDisconnect(
+ None => Err(InteractiveTxMsgError::new(
+ ChannelError::WarnAndDisconnect(
"Received unexpected interactive transaction negotiation message".to_owned(),
),
- splice_funding_failed: None,
- }),
+ None,
+ )),
}
}
@@ -1908,12 +1937,12 @@ where
Some(interactive_tx_constructor) => interactive_tx_constructor
.handle_tx_remove_output(msg)
.map_err(|reason| self.fail_interactive_tx_negotiation(reason, logger)),
- None => Err(InteractiveTxMsgError {
- err: ChannelError::WarnAndDisconnect(
+ None => Err(InteractiveTxMsgError::new(
+ ChannelError::WarnAndDisconnect(
"Received unexpected interactive transaction negotiation message".to_owned(),
),
- splice_funding_failed: None,
- }),
+ None,
+ )),
}
}
@@ -1926,10 +1955,10 @@ where
.map_err(|reason| self.fail_interactive_tx_negotiation(reason, logger))?,
None => {
let err = "Received unexpected interactive transaction negotiation message";
- return Err(InteractiveTxMsgError {
- err: ChannelError::WarnAndDisconnect(err.to_owned()),
- splice_funding_failed: None,
- });
+ return Err(InteractiveTxMsgError::new(
+ ChannelError::WarnAndDisconnect(err.to_owned()),
+ None,
+ ));
},
};
@@ -12797,7 +12826,8 @@ where
// quiescent for it.
ChannelError::Ignore(str.into())
};
- return Ok(InteractiveTxMsgError { err, splice_funding_failed });
+ return Ok(InteractiveTxMsgError::new(err, splice_funding_failed)
+ .with_negotiation_failure_reason(NegotiationFailureReason::LocallyCanceled));
}
let funding_negotiation = self
@@ -12864,10 +12894,11 @@ where
debug_assert!(self.context.channel_state.is_quiescent());
let splice_funding_failed = self.reset_pending_splice_state();
debug_assert!(splice_funding_failed.is_some());
- Ok(InteractiveTxMsgError {
- err: ChannelError::Abort(AbortReason::ManualIntervention),
+ Ok(InteractiveTxMsgError::new(
+ ChannelError::Abort(AbortReason::ManualIntervention),
splice_funding_failed,
- })
+ )
+ .with_negotiation_failure_reason(NegotiationFailureReason::LocallyCanceled))
}
/// Checks during handling splice_init
@@ -14520,7 +14551,7 @@ where
debug_assert!(self.context.channel_state.is_quiescent());
self.exit_quiescence();
}
- InteractiveTxMsgError { err, splice_funding_failed: None }
+ InteractiveTxMsgError::new(err, None)
}
pub fn remove_legacy_scids_before_block(&mut self, height: u32) -> alloc::vec::Drain<'_, u64> {
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 9920be8..db64cc9 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -4982,7 +4982,6 @@ impl<
*channel_id,
counterparty_node_id,
user_channel_id,
- Some(events::NegotiationFailureReason::LocallyCanceled),
);
let _ = self.handle_error(Err::<(), _>(err), *counterparty_node_id);
self.event_persist_notifier.notify();
@@ -11956,9 +11955,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
fn handle_interactive_tx_msg_err(
&self, err: InteractiveTxMsgError, channel_id: ChannelId, counterparty_node_id: &PublicKey,
- user_channel_id: u128, reason: Option<events::NegotiationFailureReason>,
+ user_channel_id: u128,
) -> MsgHandleErrInternal {
- if let Some(splice_funding_failed) = err.splice_funding_failed {
+ let (err, splice_failure) = err.into_parts();
+ if let Some((splice_funding_failed, reason)) = splice_failure {
let (funding_info, contribution) = splice_funding_failed.into_parts();
let pending_events = &mut self.pending_events.lock().unwrap();
if let Some(funding_info) = funding_info {
@@ -11971,14 +11971,12 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
counterparty_node_id: *counterparty_node_id,
user_channel_id,
contribution,
- reason: reason.unwrap_or(events::NegotiationFailureReason::NegotiationError {
- msg: format!("{:?}", err.err),
- }),
+ reason,
},
None,
));
}
- MsgHandleErrInternal::from_chan_no_close(err.err, channel_id)
+ MsgHandleErrInternal::from_chan_no_close(err, channel_id)
}
fn internal_tx_msg<
@@ -12009,7 +12007,6 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
channel_id,
counterparty_node_id,
user_channel_id,
- None,
))
},
}
@@ -12145,7 +12142,6 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
msg.channel_id,
&counterparty_node_id,
user_channel_id,
- None,
))
},
}
@@ -13391,7 +13387,6 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
msg.channel_id,
counterparty_node_id,
user_channel_id,
- None,
))
},
}
@@ -13449,7 +13444,6 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
msg.channel_id,
counterparty_node_id,
user_channel_id,
- None,
))
},
}
Why this scored 21/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.