Skip to content

Allow cancellation of pending splice funding negotiations#4490

Open
wpaulino wants to merge 1 commit intolightningdevkit:mainfrom
wpaulino:cancel-splice
Open

Allow cancellation of pending splice funding negotiations#4490
wpaulino wants to merge 1 commit intolightningdevkit:mainfrom
wpaulino:cancel-splice

Conversation

@wpaulino
Copy link
Contributor

A user may wish to cancel an in-flight funding negotiation for whatever reason (e.g., mempool feerates have gone down, inability to sign, etc.), so we should make it possible for them to do so. Note that this can only be done for splice funding negotiations for which the user has made a contribution to.

A user may wish to cancel an in-flight funding negotiation for whatever
reason (e.g., mempool feerates have gone down, inability to sign, etc.),
so we should make it possible for them to do so. Note that this can only
be done for splice funding negotiations for which the user has made a
contribution to.
@wpaulino wpaulino added this to the 0.3 milestone Mar 17, 2026
@wpaulino wpaulino requested a review from jkczyz March 17, 2026 18:00
@wpaulino wpaulino self-assigned this Mar 17, 2026
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Mar 17, 2026

👋 Thanks for assigning @jkczyz as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

Comment on lines +4941 to +4942
let splice_funding_failed = splice_funding_failed
.expect("Only splices with local contributions can be canceled");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This .expect() can panic in release builds. While cancel_splice in channel.rs verifies made_contribution is true, the maybe_create_splice_funding_failed! macro additionally subtracts contributions that overlap with prior RBF rounds (via prior_contributed_inputs/outputs). For a non-initiator who reuses the same UTXOs across RBF attempts with no explicit output contributions, the subtraction can empty the lists, causing the macro to return None (line 6740-6742 of channel.rs: if !is_initiator && contributed_inputs.is_empty() && contributed_outputs.is_empty() { return None; }).

The debug_assert!(splice_funding_failed.is_some()) at channel.rs:12323 catches this in debug, but this expect will crash in release for that edge case. Consider handling None gracefully, e.g. by returning an APIError or skipping the events.

Comment on lines +12281 to +12286
FundingNegotiation::AwaitingSignatures { .. } => self
.context
.interactive_tx_signing_session
.as_ref()
.expect("We have a pending splice awaiting signatures")
.has_local_contribution(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the contribution check here (has_local_contribution) differs in how it computes "has contributions" compared to the AwaitingAck/ConstructingTransaction branches. has_local_contribution() uses the signing session's local_inputs_count/local_outputs_count (which filter by is_local on the constructed transaction metadata), while the other branches use contributed_inputs()/contributed_outputs() from the context/constructor (which use our_funding_inputs/our_funding_outputs).

These should be semantically equivalent since both exclude the shared input and reflect local contributions, but the different computation paths mean a divergence in one path wouldn't be caught by the other. Consider adding a comment noting the equivalence, or unifying the approach.

@ldk-claude-review-bot
Copy link
Collaborator

Review Summary

The approach is sound—making cancel_splice a public API, routing through handle_error for the tx_abort + holding cell release, and adding the ManualIntervention abort reason are all clean. Two inline comments posted:

  1. Potential panic via .expect() in channelmanager.rs: For a non-initiator in an RBF scenario reusing the same UTXOs, the prior-contribution subtraction in maybe_create_splice_funding_failed! can produce None, which the expect at line 4942 doesn't handle. This would crash in release builds.

  2. Contribution-check divergence across FundingNegotiation variants: The AwaitingSignatures branch uses a different code path (has_local_contribution) than the other variants to determine whether a contribution was made.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants