Skip to content

Ignore stale splice initial commitment_signed#4751

Open
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:ignore-stale-initial-commitment-signed
Open

Ignore stale splice initial commitment_signed#4751
wpaulino wants to merge 1 commit into
lightningdevkit:mainfrom
wpaulino:ignore-stale-initial-commitment-signed

Conversation

@wpaulino

@wpaulino wpaulino commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

After we complete a splice negotiation and see a FundingTransactionReadyForSigning event, the counterparty may already have sent its initial commitment_signed for the splice funding transaction. If we then cancel the funding contribution, our local channel state no longer tracks the pending splice attempt and queues tx_abort, but the in-flight commitment_signed can still arrive first. Handling that message against the post-abort channel state attempts to validate a signature for the now-stale splice funding transaction and can force-close the still-live channel.

We fix this by checking the optional funding_txid (which we expect all implementations to always include by default) included in commitment_signed before validating the commitment signature. If it does not match the channel's locked funding txid, we can safely ignore the stale message.

Fixes #4730.

After we complete a splice negotiation and see a
`FundingTransactionReadyForSigning` event, the counterparty may already
have sent its initial `commitment_signed` for the splice funding
transaction. If we then cancel the funding contribution, our local
channel state no longer tracks the pending splice attempt and queues
`tx_abort`, but the in-flight `commitment_signed` can still arrive
first. Handling that message against the post-abort channel state
attempts to validate a signature for the now-stale splice funding
transaction and can force-close the still-live channel.

We fix this by checking the optional `funding_txid` (which we expect all
implementations to always include by default) included in
`commitment_signed` before validating the commitment signature. If it
does not match the channel's locked funding txid, we can safely ignore
the stale message.
@wpaulino wpaulino requested a review from jkczyz June 25, 2026 17:12
@wpaulino wpaulino self-assigned this Jun 25, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Jun 25, 2026

Copy link
Copy Markdown

👋 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.

@ldk-claude-review-bot

Copy link
Copy Markdown
Collaborator

No issues found.

The fix is correct and backward-compatible:

  • The new funding_txid check (channel.rs:8617-8625) only runs in the single (non-batch) commitment_signed path, which is reached only when pending_funding() is empty.
  • The .expect("funded channel must have known txid") cannot panic: commitment_signed is always preceded by commitment_signed_check_state (channel.rs:8610), which requires ChannelState::ChannelReady, guaranteeing a set funding outpoint.
  • msg.funding_txid is an optional TLV, so peers that omit it skip the check and retain prior behavior — no regression for legacy/non-splice channels.
  • The stale aborted-splice commitment_signed carries the discarded splice funding txid, which won't match the still-locked funding txid, so it is correctly ignored rather than validated/force-closing the channel.

@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.94%. Comparing base (6965bc9) to head (b3e2dc8).
⚠️ Report is 3224 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4751      +/-   ##
==========================================
+ Coverage   84.55%   86.94%   +2.38%     
==========================================
  Files         137      161      +24     
  Lines       77617   111643   +34026     
  Branches    77617   111643   +34026     
==========================================
+ Hits        65627    97065   +31438     
- Misses       9948    12071    +2123     
- Partials     2042     2507     +465     
Flag Coverage Δ
fuzzing-fake-hashes 8.43% <0.00%> (?)
fuzzing-real-hashes 32.41% <55.55%> (?)
tests 86.26% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +8617 to +8625
if let Some(funding_txid) = msg.funding_txid {
let locked_funding_txid =
self.funding.get_funding_txid().expect("funded channel must have known txid");
if funding_txid != locked_funding_txid {
return Err(ChannelError::Ignore(format!(
"Ignoring commitment_signed for stale funding txid {funding_txid}"
)));
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need to do this in commitment_signed_batch, too? If so, should we move the check to validate_commitment_signed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The initial commitment_signed should never go through that path, and commitment_signed_batch should already ignore stale funding attempts.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stale splice commitment_signed after local splice abort force-closes channel

4 participants