Skip to content

Conversation

@jkczyz
Copy link
Contributor

@jkczyz jkczyz commented Dec 18, 2025

Rather than requiring the user to pass FundingTxInputs when initiating a splice, generate a FundingNeeded event once the channel has become quiescent. This simplifies error handling and UTXO / change address clean-up by consolidating it in SpliceFailed event handling.

Later, this event will be used for opportunistic contributions (i.e., when the counterparty wins quiescence or initiates), dual-funding, and RBF.

Based on #4261.

This is still fairly rough. It does not yet include any code for creating a FundingNegotiationContext from a FundingContribution. The former may need to a dedicated struct instead so that any data needed from ChannelManager or ChannelContext can be produced internally. Alternatively, that data could be included in FundingContribution, but it would need to be serializable.

When adding support for mixed splice-in and splice-out, the contribution
amount will need to be computed based on the splice-in and splice-out
values. Rather than add a third variant to SpliceContribution, which
could have an invalid contribution amount, use a more general struct
that can represent splice-in, splice-out, and mixed. Constructors are
provided for the typical splice-in and splice-out case whereas support
for the mixed case will be added in an independent change.
Some splicing use cases require to simultaneously splice in and out in
the same splice transaction. Add support for such splices using the
funding inputs to pay the appropriate fees just like the splice-in case,
opposed to using the channel value like the splice-out case. This
requires using the contributed input value when checking if the inputs
are sufficient to cover fees, not the net contributed value. The latter
may be negative in the net splice-out case.
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Dec 18, 2025

👋 Thanks for assigning @wpaulino 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 jkczyz self-assigned this Dec 18, 2025
Rather than requiring the user to pass FundingTxInputs when initiating a
splice, generate a FundingNeeded event once the channel has become
quiescent. This simplifies error handling and UTXO / change address
clean-up by consolidating it in SpliceFailed event handling.

Later, this event will be used for opportunistic contributions (i.e.,
when the counterparty wins quiescence or initiates), dual-funding, and
RBF.
@jkczyz jkczyz force-pushed the 2025-12-new-splice-api branch from f5933e5 to 854e9ca Compare January 12, 2026 17:47
@jkczyz
Copy link
Contributor Author

jkczyz commented Jan 12, 2026

@TheBlueMatt @wpaulino Looking for some high-level feedback on the API introduced in the last commit. In summary:

  • User passes SpliceContribution -- which no longer contains any FundingTxInputs -- to ChannelManager::splice_channel
  • Upon quiescence LDK generates a FundingNeeded event which contains a FundingTemplate
  • User calls FundingTemplate::build or FundingTemplate::build_sync with a WalletSource or WalletSourceSync, respectively, to produce a FundingContribution
  • User passes FundingContribution -- which contains the FundingTxInputs -- to ChannelManager::funding_contributed
  • LDK validates that the FundingContribution can pay for inputs / outputs, causing LDK to either send splice_init or produce a SpliceFailed event.

The same mechanism can be used later for contributing inputs for counterparty-initiated splices or v2 channel opens since FundingTemplate and FundingContribution contains the context.

Test code still needs to be fixed up, and change_script generation will follow in another commit.

Copy link
Contributor

@wpaulino wpaulino left a comment

Choose a reason for hiding this comment

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

The API design LGTM, though there's one issue with WalletSource. One thing users need to keep in mind now is that from the moment they receive FundingNeeded, they need to act quickly to ensure the counterparty doesn't disconnect due to quiescence taking too long.

fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()>;

///
fn select_confirmed_utxos(
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding this here now requires implementers to satisfy this method when, in the context of anchor channels, WalletSource is only intended to be used such that we perform coin selection on behalf of the user. Ideally, we also give users the option between choosing WalletSource/CoinSelectionSource when funding channels.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Right, I guess I'm a bit confused why we can't use select_confirmed_utxos as-is? Indeed the claim_id is annoying, but we can make that either an enum across a ClaimId and some unit value describing a splice or just make it an Option. Aside from that it seems to be basically what we want.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding this here now requires implementers to satisfy this method when, in the context of anchor channels, WalletSource is only intended to be used such that we perform coin selection on behalf of the user. Ideally, we also give users the option between choosing WalletSource/CoinSelectionSource when funding channels.

Hmm... I see. Would a separate trait be desirable? Also, see my reply to @TheBlueMatt below.

Right, I guess I'm a bit confused why we can't use select_confirmed_utxos as-is? Indeed the claim_id is annoying, but we can make that either an enum across a ClaimId and some unit value describing a splice or just make it an Option. Aside from that it seems to be basically what we want.

The return value also isn't compatible. It contains Utxos but we also need the previous tx and sequence number as part of each FundingTxInput. Though its constructor will give a default sequence number.

We could change CoinSelection to use FundingTxInput instead of Utxo, but that would be odd for use with the anchor context.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Honestly that seems fine to me? We expect ~all of our users to want to use splicing, which implies they need to support the "return coin selection with full transactions" interface. So what if anchors throw away some of that data?

If we feel strongly about it we can add a new trait method that does return the full transactions and provide a default implementation for the current method so that those that really want to avoid always fetching the transaction data can.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, I don't have a strong opinion, but note that Wallet's implementation of CoinSelectionSource::select_confirmed_utxos delegates to WalletSource::list_confirmed_utxos. So it might be expensive to use that abstraction. @wpaulino WDYT?

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Aside from the above which-interface question I think the API is good.

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