Skip to content

Add splice RBF support#888

Open
jkczyz wants to merge 7 commits into
lightningdevkit:mainfrom
jkczyz:2026-04-splicing-payment-using-tx-type
Open

Add splice RBF support#888
jkczyz wants to merge 7 commits into
lightningdevkit:mainfrom
jkczyz:2026-04-splicing-payment-using-tx-type

Conversation

@jkczyz
Copy link
Copy Markdown
Contributor

@jkczyz jkczyz commented Apr 24, 2026

Adds a public Node::rbf_channel API that replaces an in-flight splice transaction with a higher-feerate version.

An RBF leaves multiple candidate splice transactions in flight; only one of them will confirm on chain, and the payment record needs to reflect whichever one did — its amount and this node's share of the fee. The transition from Pending to Succeeded should also match when the Lightning protocol actually considers the new channel state usable — the moment ChannelReady fires — rather than after ANTI_REORG_DELAY confirmations, which doesn't fit the zero-conf extreme on one end or higher-confirmation-depth peer configurations on the other.

To support those properties, this PR:

  • ties channel-funding and splice payment status to channel lifecycle instead of confirmation depth;
  • retains each candidate's contribution on the pending record until one of them confirms;
  • updates the record's amount and fee to match the confirmed candidate's contribution;
  • discards a funding payment when its channel closes without the funding ever confirming.

Every broadcast from LDK now triggers a store write. For a remote KV store, running that write on LDK's thread would block message handling for hundreds of milliseconds per call, so persistence happens asynchronously while still guaranteeing that the record is committed before the transaction reaches the chain client.

Based on #878.

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Apr 24, 2026

I've assigned @tankyleo 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.

@jkczyz
Copy link
Copy Markdown
Contributor Author

jkczyz commented Apr 24, 2026

@jkczyz jkczyz force-pushed the 2026-04-splicing-payment-using-tx-type branch 2 times, most recently from 198bfbf to d5b6bda Compare April 28, 2026 16:14
@tnull
Copy link
Copy Markdown
Collaborator

tnull commented Apr 29, 2026

Btw, we just landed #839 - maybe it would be nice to add splicing interop tests with CLN and/or Eclair (see #880 though), in this PR or in a follow-up?

Copy link
Copy Markdown
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Excuse the delay. Took a first look, generally makes sense, though it's kind off odd to block the broadcast queue on persistence. Hopefully we'd never get a lot of backpressure from remote persistence, for example.

I have yet to review the (rather verbose) new code for updating/persisting the pending payments in wallet.rs in more detail.

Comment thread src/lib.rs Outdated
@tnull
Copy link
Copy Markdown
Collaborator

tnull commented May 21, 2026

@jkczyz Is this ready for review or are we waiting for any upstream changes still?

@jkczyz
Copy link
Copy Markdown
Contributor Author

jkczyz commented May 21, 2026

@jkczyz Is this ready for review or are we waiting for any upstream changes still?

Yeah, upstream has landed but this is now based on the splicing portion of #882. We can take those here if you prefer.

@jkczyz jkczyz marked this pull request as ready for review May 21, 2026 14:39
@ldk-reviews-bot ldk-reviews-bot requested a review from tankyleo May 21, 2026 14:40
@tnull
Copy link
Copy Markdown
Collaborator

tnull commented May 21, 2026

@jkczyz Is this ready for review or are we waiting for any upstream changes still?

Yeah, upstream has landed but this is now based on the splicing portion of #882. We can take those here if you prefer.

Ah, was unaware of that cross dependency, will see to review #882 next to keep things moving then.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Copy Markdown
Contributor

@Camillarhi Camillarhi left a comment

Choose a reason for hiding this comment

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

Thanks for this. I took a look at this, and I have a few questions. I have taken a little look at splicing in general.
For this PR, if a user calls bump_fee_rbf on a record with funding_details, the replacement is picked up as a WalletEvent::TxReplaced event on the next sync, and this will currently rewrite the funding_details as NONE. Is bumping a funding/splice payment through bump_fee_rbf meant to be possible at all, or should those records be excluded here in favor of rbf_channel? AND is overwriting an existing funding_details with None intended in the TxReplaced path?

Comment thread src/wallet/mod.rs Outdated
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 4th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 5th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 6th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

jkczyz and others added 5 commits June 4, 2026 11:50
When a splice is already pending, the user needs a way to replace
its funding transaction at a higher feerate. This adds rbf_channel()
to handle that case and guards splice_in/splice_out against being
called while a pending splice exists, directing users to rbf_channel
instead.

Also fixes signing for RBF replacements, which requires accessing
outputs spent by unconfirmed transactions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Channel-opening and splice transactions transition to Succeeded when
ChannelReady fires, not after ANTI_REORG_DELAY confirmations. This
matches the point at which the Lightning layer considers the channel
usable: a zero-conf channel graduates as soon as its counterparty
signals, and a high-conf channel waits however many confirmations the
peer requires, rather than always stopping at six.

For splice RBF, the payment records whichever candidate actually
confirmed, with that candidate's amount and this node's share of the fee
— not the fee-estimate used for weight at coin-selection time, and not
the whole-tx fee for a multi-contributor splice.

A channel closure whose funding or splice never confirmed discards its
payment record instead of leaving it pending forever.

Generated with assistance from Claude Code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The TxReplaced wallet event rebuilt the payment record from scratch,
dropping its funding details. When a wallet sync fell between a splice
broadcast and its RBF, the replacement of the original candidate cleared
those details, so the payment no longer graduated to Succeeded on
ChannelReady. Funding records are managed by the classify path and the
Lightning lifecycle handlers, so leave them untouched on replacement.

Co-Authored-By: Claude <noreply@anthropic.com>
bump_fee_rbf accepted channel-funding and splice payments because they
are recorded as outbound, unconfirmed on-chain payments. Replacing such
a transaction via wallet RBF would broadcast one LDK isn't tracking, and
for splices the shared input can't be wallet-signed. Reject these and
leave splice fee-bumping to rbf_channel.

Co-Authored-By: Claude <noreply@anthropic.com>
jkczyz and others added 2 commits June 5, 2026 14:41
Co-Authored-By: Claude <noreply@anthropic.com>
Previously the BroadcasterInterface implementation wrote the payment
record synchronously when LDK invoked it. With a remote KV store this
could block LDK's message handling for hundreds of milliseconds per
call, noticeably during force-close bursts or splice broadcasts.

Persistence now happens asynchronously and must complete before the
transaction is sent to the chain client. If persistence fails, the
broadcast is dropped: a payment record must exist for every on-chain tx
we emit, otherwise a crash could leave the tx confirmed with no
matching record.

Generated with assistance from Claude Code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jkczyz jkczyz force-pushed the 2026-04-splicing-payment-using-tx-type branch from c4d3282 to bec7723 Compare June 5, 2026 19:46
@jkczyz
Copy link
Copy Markdown
Contributor Author

jkczyz commented Jun 5, 2026

Rebased and dropped commits added in #912.

Ah, was unaware of that cross dependency, will see to review #882 next to keep things moving then.

Sorry, that should have said #872. Relevant commits were moved to #912, which has been merged.

Thanks for this. I took a look at this, and I have a few questions. I have taken a little look at splicing in general. For this PR, if a user calls bump_fee_rbf on a record with funding_details, the replacement is picked up as a WalletEvent::TxReplaced event on the next sync, and this will currently rewrite the funding_details as NONE. Is bumping a funding/splice payment through bump_fee_rbf meant to be possible at all, or should those records be excluded here in favor of rbf_channel? AND is overwriting an existing funding_details with None intended in the TxReplaced path?

Good catch @Camillarhi! Updated to disallow using bump_fee_rbf since that won't work for interactively constructed transactions. We need use the interactive-tx construction protocol given some signatures come from the counterparty.

Overwriting the existing funding_details is also unintended. Added a guard to prevent that.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 7th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants