Zero-fee taproot channels using v3 transactions (feature 82/83)#1330
Draft
t-bast wants to merge 20 commits intolightning:masterfrom
Draft
Zero-fee taproot channels using v3 transactions (feature 82/83)#1330t-bast wants to merge 20 commits intolightning:masterfrom
t-bast wants to merge 20 commits intolightning:masterfrom
Conversation
In this extension BOLT, we specify the initial flavor of taproot channels to be deployed. This channel type uses musig2 aggregated keys and signatures for the funding output, making it a normal single signature key path spend. All outputs are then updated to use P2T2 (segwit v1) outputs. The coop close process has been simplified to always terminate, and the co-op close transaction now also flags RBF to make way for future schemes that enable the process to be restarted which enables co-op close fee bumping. A top-level key spend output is used to the revocation of HTLC outputs. The revocation for the local output uses a script path to ensure that information needed to sweep the anchors by 3rd parties is always revealed on chain.
In this commit, we make a few changes to the local nonces map as
defined:
* Drop the length prefix, just encode the nonces concatenated to each
other. The number of nonces to decode can be computed from the size
of the nonces and a txid.
* Using the funding txid in the nonce map instead of the empty hash.
We must use the `next_local_nonces` TLV instead of a single nonce TLV in the following messages to be future-proof with splicing: - `channel_reestablish` - `revoke_and_ack` We must include one nonce for each commitment transaction that can be published: when there is a pending splice, that means we need one nonce for the current funding transaction, another nonce for the commitment transaction that spends the splice transaction, and additional nonces for each RBF attempt of that splice transaction (if any). All of those commitment transactions use the same `per_commitment_point` and are all revoked with the same secret: even though we send multiple `commit_sig` messages while splices are pending, we send always send a single `revoke_and_ack` message. This is why it must contain the map of all next local nonces.
If we want to be future-proof with splicing, we must include the `funding_txid` in the shachain root, which allows having distinct shachains for each splice transaction to deterministically derive local commitment nonces.
The specification for `option_simple_close` was inconsistent with regards to closee and closer nonce. We fix the incorrect or unclear requirements.
While `lnd` uses the legacy `closing_signed` mechanism for early taproot channels support, this shouldn't be in the BOLT: we should require `option_simple_close` whenever using taproot channels, which was explicitly designed for taproot.
Updates to simple taproot channels for cross-compatibility
Remove the redundant nSequence paragraph from the cooperative close section. The `option_simple_close` spec in BOLT #2 already specifies that `nSequence` MUST be exactly `0xFFFFFFFD`, so restating it here with the weaker "less-than-or-equal" phrasing was both redundant and inconsistent. Also fix "next closee nonce" to "closer nonce" in the JIT nonce description for `closing_complete`, matching the terminology used throughout the rest of the spec.
…ions Update the embedded JSON test vectors to match the corrected lnd test vector generator output. The key changes are: The `remote_partial_sig` fields now contain actual 32-byte MuSig2 partial signature scalars instead of the dummy 8-byte DER stub `3006020100020100` that was leaking through from the zeroed `CommitSig` field. Each test case also now includes `local_nonce` and `remote_nonce` fields (66-byte compressed public nonces) so that verifiers can reconstruct the combined MuSig2 signature independently. The HTLC resolution transactions have been corrected in two ways: the `remote_partial_sig_hex` values are now correctly mapped to their transactions using BIP 69 output index ordering (fixing the invalid HTLC-timeout signatures that eclair reported), and the HTLC-success witness stacks now include the preimage and redeem script in the correct witness slots. The "commitment tx with some HTLCs trimmed" test case now uses dust_limit=2500 instead of 546, which properly exercises zero-fee HTLC trimming by pushing the three smallest test HTLCs (1000, 2000, 2000 sats) below the dust threshold, reducing the HTLC count from 5 to 2.
Add detailed explanatory sections to the test vectors appendix so that implementers can reproduce the vectors without needing to reverse- engineer the generator code. The new documentation covers: The complete set of key derivation labels used with the `SHA256(seed || label)` pattern, so implementations can derive all private keys from the seed independently. MuSig2 nonce generation: the deterministic signing randomness labels (`"local-signing-rand"` / `"remote-signing-rand"`) that produce reproducible nonces. Each test case now includes `local_nonce` and `remote_nonce` fields so implementations can cross-check their nonce derivation before attempting signature verification. The distinction between commitment transaction signatures (32-byte MuSig2 partial sig scalars using a keyspend path) and HTLC resolution signatures (64-byte Schnorr signatures using a tapscript spend path). The BIP 69 output index ordering convention for `htlc_descs` entries, matching the order that `htlc_signature` messages are exchanged during the commitment protocol. The full taproot witness stack layout for both HTLC-success (5 elements including preimage) and HTLC-timeout (4 elements) transactions. The zero-fee HTLC trimming semantics, explaining why trimming depends solely on the dust limit rather than the fee rate.
In this commit, we expand the MuSig2 Nonce Generation section of the test vector documentation to describe the newly added secret nonce fields (local_sec_nonce and remote_sec_nonce). Different MuSig2 implementations may produce different nonces from the same randomness input, so providing raw 97-byte secret nonces allows interop implementations to inject them directly rather than re-derive them. The documentation now clarifies that all nonces in a test case correspond to the same commitment transaction (the local party's commitment), explains the distinction between the local verification nonce and the remote JIT signing nonce, and provides step-by-step instructions for replaying MuSig2 signing from the test vectors.
Update the embedded JSON test vectors to include the new local_sec_nonce and remote_sec_nonce fields, and correct the local_nonce values. The local_nonce now reflects the local party's verification nonce for their own commitment transaction (from LocalSession), rather than the JIT signing nonce for the remote party's commitment that was previously (incorrectly) used.
… form The HTLC-timeout second-level transaction script was using the non-prod form (OP_CHECKSIG + OP_CSV OP_DROP) while the HTLC-success script was already using the prod form (OP_CHECKSIGVERIFY + OP_CSV). Both scripts should be identical in structure since they share the same tapscript tree construction. This aligns the spec with the implementation which uses WithProdScripts() for all taproot channel scripts.
Update the embedded test vectors with HTLC signatures that use BIP-340 standard nonce derivation (zero auxrand) instead of RFC6979. This ensures HTLC second-level transaction signatures are reproducible across different Schnorr implementations (libsecp256k1, btcd, etc). Also document that implementations must use BIP-340 deterministic signing with zero auxrand to reproduce the HTLC signatures in the test vectors.
We introduce a new `channel_type` that leverages v3 (TRUC) transactions, taproot, pay-to-anchor outputs and ephemeral dust. This `channel_type` unifies `zero_fee_commitments` (which are defined for segwit v0) and `option_simple_taproot` (which is defined for `nVersion = 2`). Combining those two channel types results in a cleaner transaction architecture: - by using a single pay-to-anchor instead of keyed segwit v0 anchors, we don't need to reveal funding keys when spending output to allow late spending of the anchor outputs: this lets us use the revocation key as internal key *everywhere*, which gets rid of the NUMS point introduced by `option_simple_taproot` - by using TRUC replacement rules, we don't need the `CSV 1` on outputs and the main remote output can directly be a key-path spend, which can be used to CPFP the commitment transaction - similarly, HTLC outputs can be used to CPFP the commitment transaction It should be preferred to all existing channel types, once enough bitcoin nodes correctly relay v3 transactions.
t-bast
added a commit
to ACINQ/eclair
that referenced
this pull request
Apr 3, 2026
We add support for the zero-fee commitment format specified in lightning/bolts#1330. Channels using this commitment format benefit from better protection against pinning attacks (thanks to TRUC/v3 transactions), don't need the `update_fee` mechanism, have less dust exposure risk, and use an overall simpler state machine, while benefiting from the privacy improvements of taproot channels.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
We introduce a new
channel_typethat leverages v3 (TRUC) transactions, taproot, pay-to-anchor outputs and ephemeral dust. Thischannel_typeunifieszero_fee_commitments(which are defined for segwit v0) andoption_simple_taproot(which is defined fornVersion = 2), by building on top of #995 and #1228.Combining those two channel types results in a cleaner transaction architecture:
option_simple_taprootCSV 1on outputs and the main remote output can directly be a key-path spend, which can be used to CPFP the commitment transactionIt should be preferred to all existing channel types, once enough bitcoin nodes correctly relay v3 transactions.
TODO: