Skip to content

splice: add zero-conf splice support per BOLT #2#8985

Open
vincenzopalazzo wants to merge 5 commits intoElementsProject:masterfrom
vincenzopalazzo:claude/eloquent-lehmann
Open

splice: add zero-conf splice support per BOLT #2#8985
vincenzopalazzo wants to merge 5 commits intoElementsProject:masterfrom
vincenzopalazzo:claude/eloquent-lehmann

Conversation

@vincenzopalazzo
Copy link
Collaborator

@vincenzopalazzo vincenzopalazzo commented Mar 25, 2026

Summary

  • Allow splice_locked to be sent immediately at depth 0 for channels with option_zeroconf negotiated (minimum_depth == 0)
  • Prohibit tx_init_rbf on zero-conf channels (both as initiator and acceptor), since RBF would double-spend the unconfirmed funding output
  • Implements spec requirements from Channel Splicing (feature 62/63) lightning/bolts#1160

Fixes #7002

Test plan

  • Verify existing splice tests still pass (pytest tests/test_splice.py)
  • Verify existing zero-conf tests still pass (pytest tests/test_opening.py -k zeroconf)
  • Add test_splice_zeroconf integration test
  • Verify RBF rejection on zero-conf splice channels

@vincenzopalazzo vincenzopalazzo requested a review from ddustin March 25, 2026 13:01
@vincenzopalazzo vincenzopalazzo force-pushed the claude/eloquent-lehmann branch from cb99987 to 2be428c Compare March 25, 2026 13:01
When option_zeroconf is negotiated on a channel, send splice_locked
immediately at depth 0 instead of waiting for confirmations. Also
prohibit tx_init_rbf on zero-conf channels since RBF would double-spend
the unconfirmed funding output.

This implements the spec requirements from lightning/bolts#1160:
- splice_depth_cb: allow depth==0 through for minimum_depth==0 channels
- handle_splice_stfu_success: block RBF initiation on zero-conf
- splice_acceptor: reject incoming tx_init_rbf on zero-conf

Changelog-Added: splice: Support zero-conf splicing on channels with option_zeroconf negotiated.
@vincenzopalazzo vincenzopalazzo force-pushed the claude/eloquent-lehmann branch from 2be428c to 7ca893f Compare March 25, 2026 13:02
Verify that when option_zeroconf is negotiated on a channel,
splice_locked fires after just 1 block instead of the usual 6,
and the channel remains usable after the splice completes.

Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
@vincenzopalazzo vincenzopalazzo marked this pull request as ready for review March 25, 2026 13:50
if (depth == 0 && inflight->channel->minimum_depth != 0) {
return KEEP_WATCHING;
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not the right place to be trying to do zero-conf. Instead we should initiate splice_locked in channeld right after sending tx_signatures which occurs in resume_splice_negotiation.

This needs to be done with extra care because this flow can occur from initiate, accepter, or during the reestablish flow. We need to make sure it's behaving correctly in each of these flows.

Probably the best approach is to take the code in handle_funding_depth that confirms the splice and move it out into it's own function so that we can additionally call it from resume_splice_negotiation.

It would be very important to duplicate the tests in test_splicing_disconnect.py with this new zero conf splice setting enabled.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 7bd121c.

I moved zero-conf splice_locked initiation into resume_splice_negotiation() so we send it immediately after each local tx_signatures write, which covers the initiator, accepter, and reestablish paths in one place. For the reconnect cases I also persist i_sent_sigs, cache early splice_locked until the local signature path is complete, and treat channel_ready during zero-conf reestablish as an implied peer splice_locked once we have already sent ours.

I also duplicated the disconnect regressions for zero-conf in tests/test_splicing_disconnect.py and re-ran the zero-conf happy-path splice test. Verified with targeted runs of test_splice_zeroconf, test_splice_disconnect_sig_zeroconf, and test_splice_disconnect_commit_zeroconf.

@ddustin
Copy link
Collaborator

ddustin commented Mar 25, 2026

Because zero-conf splices are so dangerous. It's important that we make real tests for two scenarios (and actually while I'm thinking about we need tests that handle both):

  1. If a channel goes through these states: open (0-conf) -> splice (0-conf) -> splice(0-conf) -> splice(0-conf)
    a) Does it work at all? Do things break
  2. If a malicious peer 0-conf adds funds, spends it less than all the funds, then double spends the splice -- can we claim the little bit they steal from us back with a force close?
    a) This test being weird, because obviously they can steal all our funds as is explicitly allowed by using 0-conf.
  3. We should have tests for the peer double spending at each stage, open, splice1, splice2, splice3
    a) Lets confirm that our node at least attempts to close the channel in each of these stages.

I believe we need to put all this behind a config flag that is definitely off by default. We should name it something very scary, perhaps --recklessly-okay-losing-all-my-funds-0-conf-splice could be a good candidate name.

One of the weird problems this brings up, is we PROBABLY should aggressively RBF or CPFP our closes in this case up 100% of our funds. If we burn all our funds to miners, at least we can create some kind of deterrence for the peer who can easily steal our funds. Who knows, maybe we're successful at 50% going to miner fees and we save some.

Another issue to be figured out, is what we're about channel scids and splice candidate & inflight locked_scid values. These will need to be totally reworked for a 0-conf world, since scid's contain the blockheight we locked in at.

@ddustin
Copy link
Collaborator

ddustin commented Mar 25, 2026

Because 0-conf splices are giving a blank check to our peer and hoping they don't cash it, perhaps we should only implement 0-conf splice if they come through Swap-in-Potentiam, which requires at least 1 conf and provides at least some security. This is what acinq does.

That would be a large project however 🤔.

@vincenzopalazzo
Copy link
Collaborator Author

IDK but I think 0 conf splice is used by LSP anyway like the people on the issue #7002 so I think the trust assumption is different there and would be fine to have it?

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.

Allow 0-conf splices

2 participants