Skip to content

Add Node::splice_out_max - #1181

Merged
jkczyz merged 2 commits into
lightningdevkit:mainfrom
tankyleo:2026-10-20-splice-out-with-all
Oct 21, 2026
Merged

jkczyz merged 2 commits into
lightningdevkit:mainfrom
tankyleo:2026-10-20-splice-out-with-all

Conversation

@tankyleo

@tankyleo tankyleo commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor
    Add `Node::splice_out_max`

    In case a user wants to withdraw on-chain the maximum amount possible
    from a channel, the sum of the value of the spliced output and the fees
    they contribute to the splice has to be equal to
    `ChannelDetails::next_splice_out_maximum_sat`.

    It is currently not ergonomic to express this via the current
    `Node::splice_out` API, so we add `Node::splice_out_max` to satisfy
    this gap.

    This API remains unfinished; we would like any fee bumps of such splices
    to decrease the value of the spliced output instead of increasing the
    total value of the inputs of the splice transaction.

    Co-Authored-By: HAL 9000

@ldk-reviews-bot

ldk-reviews-bot commented Oct 20, 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.

@tankyleo
tankyleo requested review from jkczyz and tnull October 20, 2026 05:09
@tankyleo

Copy link
Copy Markdown
Contributor Author

A heads up this currently conflicts with #1176

Comment thread src/lib.rs Outdated
return Err(Error::ChannelSplicingFailed);
};

// TODO: Retain maximum-withdrawal intent across restarts so its output can shrink

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.

The docs cover that a bump here may be funded by the on-chain wallet, but that fallback doesn't hold back the anchor reserve: select_confirmed_utxos has no reserve logic, and bump_channel_funding_fee doesn't check funds.

Since a max withdrawal leaves no channel balance for a higher fee, almost any bump (unless the channel balance has grown since) pays the whole output plus fees from the wallet, and can spend the reserve. A plain splice_out can hit this too, but here it's the expected path.

Maybe refuse the wallet fallback for these until the TODO is done, or check the reserve first? Related: #1166.

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.

I suggest we fix issue 1166 for both splice_out and splice_out_with_all in a separate PR.

Maybe refuse the wallet fallback for these until the TODO is done

This would require persisting the intention to withdraw the maximum from the channel instead of funding a destination address with a fixed amount. I think this should be done as part of a PR to shrink the size of the output instead of increasing the value of the inputs.

@tankyleo
tankyleo force-pushed the 2026-10-20-splice-out-with-all branch from cb33776 to 2a7d714 Compare October 20, 2026 18:00
splice_out refused any amount above the channel's outbound capacity.
That capacity is computed against the current channel reserve, but a
splice-out shrinks the channel and with it the reserve, so LDK can
accept a larger splice-out than the guard allowed. Compare against
LDK's next_splice_out_maximum_sat instead, which accounts for the
reduced reserve, so the guard only rejects amounts LDK would refuse.

Suggested by tankyleo in lightningdevkit#1103.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread src/lib.rs Outdated
/// This API is experimental. Currently, a splice-out will be marked as an inbound payment if
/// paid to an address associated with the on-chain wallet, but this classification may change
/// in the future.
pub fn splice_out_with_all(

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.

Should we rename this splice_out_max? The _with_all suffix refers to using all inputs when splicing in. But here we are withdrawing the maximum amount from the channel, which isn't the entire balance.

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.

Done below

Comment thread src/lib.rs Outdated
Comment on lines +2239 to +2240
// TODO: Retain maximum-withdrawal intent across restarts so its output can shrink
// to cover higher fees while preserving the channel debit.

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.

IIUC, this should be done upstream instead of having an intent. If so, could you open an issue upstream with the approach mentioned offline?

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.

Comment thread tests/integration_tests_rust.rs Outdated
do_splice_out_with_all(true, Some(FeeRate::from_sat_per_kwu(2_500))).await;
}

async fn do_splice_out_with_all(is_funder: bool, fee_rate: Option<FeeRate>) {

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.

Should we test RBF behavior here?

@tankyleo tankyleo Oct 20, 2026 •

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.

I had codex do a concise expansion to test RBF behavior below. As described it currently is not what the user expects: we keep the destination output the same value, and shift to using wallet inputs to fund the higher fee.

@tankyleo
tankyleo requested a review from jkczyz October 20, 2026 23:45

@jkczyz jkczyz left a comment

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.

LGTM. Please squash fixups.

@tankyleo
tankyleo force-pushed the 2026-10-20-splice-out-with-all branch 2 times, most recently from 05b0ce8 to 431a376 Compare October 21, 2026 21:38
In case a user wants to withdraw on-chain the maximum amount possible
from a channel, the sum of the value of the spliced output and the fees
they contribute to the splice has to be equal to
`ChannelDetails::next_splice_out_maximum_sat`.

It is currently not ergonomic to express this via the current
`Node::splice_out` API, so we add `Node::splice_out_max` to satisfy
this gap.

This API remains unfinished; we would like any fee bumps of such splices
to decrease the value of the spliced output instead of increasing the
total value of the inputs of the splice transaction.

Co-Authored-By: HAL 9000
@tankyleo
tankyleo force-pushed the 2026-10-20-splice-out-with-all branch from 431a376 to af0f5e1 Compare October 21, 2026 21:39
@tankyleo

Copy link
Copy Markdown
Contributor Author

squashed with edits to the tests, documentation, and changelog.

@tankyleo

Copy link
Copy Markdown
Contributor Author

Use git diff e7957ef7 af0f5e12 to view the overall diff.

@tankyleo
tankyleo requested a review from jkczyz October 21, 2026 21:48
@jkczyz jkczyz changed the title Add Node::splice_out_with_all Add Node::splice_out_max Oct 21, 2026
@jkczyz
jkczyz merged commit 7b47a0b into lightningdevkit:main Oct 21, 2026
17 of 27 checks passed
@tankyleo
tankyleo deleted the 2026-10-20-splice-out-with-all branch October 21, 2026 22:46
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.

4 participants