-
Notifications
You must be signed in to change notification settings - Fork 2.2k
multi: add support for zero fee channels (v3+P2A) #10479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
This commit updates the core Bitcoin dependencies to versions that include support for pay-to-anchor (P2A) outputs as defined in BIP-431. The P2A output type is essential for implementing v3 zero-fee commitment transactions where fee bumping is done via CPFP on a shared P2A anchor output. Key dependency updates: - btcd v0.25.0-beta.rc1 with txscript.PayToAnchorScript support - btcwallet v0.16.17 with updated neutrino interface - neutrino v0.16.2 with Start(context.Context) API - Updated protobuf and grpc dependencies for compatibility The neutrino Start() API change requires corresponding updates in calling code which will be addressed in a subsequent commit.
This commit adds the feature bits for zero_fee_commitments as defined in BOLT PR lightningnetwork#1228. Feature bit 40 is required and bit 41 is optional. These bits signal that a node supports channels using v3 transactions with zero-fee commitment and HTLC transactions, where fee bumping is performed via CPFP on a shared P2A (pay-to-anchor) output rather than through the traditional update_fee mechanism. The feature name mapping is added to the Features map for proper display in feature vector string representations.
This commit adds ZeroFeeCommitmentsBit (1<<12) to the ChannelType bitmask. This bit indicates that a channel uses v3 transactions with zero-fee commitment and HTLC transactions, where fee bumping is done via CPFP on a P2A (pay-to-anchor) output. The HasZeroFeeCommitments() accessor method is added to provide a convenient way to check for this channel type throughout the codebase.
This commit adds CommitmentTypeZeroFee to the CommitmentType enum to represent v3 zero-fee commitment channels. The new commitment type has the following properties per BOLT PR lightningnetwork#1228: - Uses v3 transactions for commitment and HTLCs (per BIP-431/TRUC) - Zero fees on commitment/HTLC txs (paid via CPFP on P2A anchor) - Single shared P2A anchor instead of two per-party anchors - No 1-block CSV delay on second-level HTLC inputs - Max 114 HTLCs per party (228 total) due to v3 10kvB transaction limit The HasStaticRemoteKey(), HasAnchors(), and HasZeroFeeCommitments() methods are updated to include the new type. The String() method now returns "zero-fee-v3" for this commitment type. The VerifyConstraints function signature is updated to accept a CommitmentType parameter. This enables enforcing the lower HTLC limit (114 vs 483) for zero-fee channels in the subsequent commit that implements the validation logic. The commitType field is also added to ChannelReservation for tracking during negotiation.
This commit adds the ZeroFeeCommitments configuration option to the ProtocolOptions struct. When set via --protocol.zero-fee-commitments, LND will create and accept requests for channels using v3 zero-fee commitment transactions with P2A anchor outputs. This feature is disabled by default and requires explicit opt-in, as v3 transactions are a new protocol feature that may not be widely supported by all nodes or network infrastructure yet.
This commit adds the NoZeroFeeCommitments configuration option to the feature manager. When set to true (the default), the zero-fee commitment feature bits are unset from the advertised feature vectors. The feature is disabled by default because zero-fee commitments is an experimental protocol feature that requires explicit opt-in via the --protocol.zero-fee-commitments configuration flag.
This commit adds ZeroFeeCommitmentsOptional to the SetInit and SetNodeAnn feature sets in the default set descriptor. This enables advertising zero-fee commitment support during connection initialization and in node announcements when the feature is enabled. The feature is gated by the NoZeroFeeCommitments config option in the feature manager, so it will only be advertised when explicitly enabled.
This commit wires the ZeroFeeCommitmentsEnabled protocol option to the feature manager's NoZeroFeeCommitments configuration. The logic is inverted because the protocol option enables the feature while the feature manager option disables it.
This commit adds two new constants for v3 zero-fee commitment channels: ZeroFeeCommitWeight is the base weight of a zero-fee commitment transaction. These transactions use a single shared P2A anchor output instead of two per-party anchor outputs, saving one anchor output's worth of weight compared to traditional anchor channels. MaxHTLCNumberV3 (228 total, 114 per party) is the maximum number of HTLCs that can be included in a v3 commitment transaction. This is lower than the standard MaxHTLCNumber (966) due to v3 transactions being limited to 10kvB (10,000 vbytes) per BIP-431/TRUC. The limit ensures that even with maximum HTLCs, the commitment transaction stays within size limits.
This commit adds the ZeroFeeAnchorSpend witness type (35) for spending
P2A (pay-to-anchor) outputs on v3 zero-fee commitment transactions.
The P2A output script is OP_1 <0x4e73>, a witness v1 program that is
intentionally anyone-can-spend by design. Per BIP-431, P2A outputs must
be spent with an empty witness. This makes them ideal for CPFP fee
bumping since any party can add inputs to pay fees.
The WitnessGenerator returns an empty wire.TxWitness{} and the
SizeUpperBound returns 1 (just the witness element count byte of 0).
This commit implements the core logic for building v3 zero-fee commitment transactions as specified in BOLT PR lightningnetwork#1228. Zero-fee commitment transactions use version 3 (TRUC transactions per BIP-431) instead of version 2. This is checked via HasZeroFeeCommitments() on the channel type, which triggers the version 3 code path. Instead of two per-party anchor outputs at 330 sats each, v3 channels use a single shared P2A (pay-to-anchor) output. The P2A script is OP_1 <0x4e73> which is intentionally anyone-can-spend for CPFP purposes. The anchor amount is computed via CalculateSharedAnchorAmount as min(trimmed_htlcs + rounded_msats, 240 sats). If there are no trimmed outputs, a default of 240 sats is used to ensure there's always an anchor available for fee bumping. The commitment builder now tracks trimmed HTLC values so they can be reflected in the shared anchor amount. HtlcSecondLevelInputSequence returns 0 for zero-fee channels (no CSV delay on second-level inputs) versus 1 for traditional anchor channels. CommitWeight returns ZeroFeeCommitWeight which accounts for the single shared anchor. Finally, CoopCloseBalance adds back 240 sats for zero-fee channels instead of 660 sats for traditional anchor channels.
This commit updates CreateHtlcSuccessTx and CreateHtlcTimeoutTx to use transaction version 3 when building second-level HTLC transactions for zero-fee commitment channels. Per BIP-431/TRUC, v3 transactions form a package with the parent commitment transaction, allowing fee bumping via CPFP on the P2A anchor. The version 3 designation enables mempool policy that supports this package relay behavior.
This commit adds ErrUpdateFeeNotAllowed and enforces rejection of update_fee messages for zero-fee commitment channels. Per BOLT#2, nodes using zero_fee_commitments MUST NOT send update_fee since fees are always zero and paid via CPFP on the P2A anchor output. Both UpdateFee (for sending) and ReceiveUpdateFee (for receiving) now return ErrUpdateFeeNotAllowed when the channel type has zero-fee commitments enabled. This enforcement at the channel state machine level ensures that the protocol constraint is applied regardless of how the channel is accessed.
This commit adds the ErrUpdateFeeNotAllowed error code to the link failure enumeration. This error is used when an update_fee message is received on a zero-fee commitment channel, which does not use the update_fee mechanism for fee management.
This commit updates the HTLC switch link to properly handle update_fee messages for zero-fee commitment channels per BOLT#2. In handleUpdateFee, we skip sending update_fee for zero-fee channels since fees are always zero and managed via CPFP on the P2A anchor. In processRemoteUpdateFee, receiving an update_fee on a zero-fee channel is a protocol violation. Per the spec, we MUST ignore the message, SHOULD send a warning, and disconnect. The implementation logs a warning and triggers a non-permanent disconnect (not a channel failure) so the channel can be resumed later with a well-behaved peer.
This commit adds support for negotiating zero-fee commitment channels during the funding flow. In explicit negotiation, four new cases are added to handle zero-fee commitments with various optional features: base zero-fee with just static remote key, zero-fee with scid alias, zero-fee with zero conf, and zero-fee with both scid alias and zero conf. Each case validates that both peers support the required features before accepting the channel type. In implicit negotiation, zero-fee commitments is added as the highest priority channel type. When both peers signal support for the ZeroFeeCommitmentsOptional feature bit, the implicit negotiation will select this as the preferred commitment type. This makes v3 channels the default when both peers have explicitly enabled the feature.
This commit adds two pieces of zero-fee commitment handling in the funding manager. For incoming open_channel messages (fundeeProcessOpenChannel), we validate that if the negotiated channel type includes zero_fee_commitments, the feerate_per_kw must be 0. Per BOLT#2, receiving a non-zero feerate for a zero-fee channel is a protocol violation and we fail the funding flow. For outgoing open_channel messages (handleInitFundingMsg), we set the feerate_per_kw to 0 when the negotiated commitment type has zero-fee commitments. This ensures we comply with the spec requirement that senders MUST set feerate_per_kw to 0 for these channels. Additionally, the VerifyConstraints call is updated to pass the commit type, enabling enforcement of the lower HTLC limit (114 vs 483) for zero-fee channels.
This commit updates the VerifyConstraints call in the wallet's handleContributionMsg to pass the negotiated commitment type. This enables the constraints validation to enforce the correct maximum HTLC limit based on whether the channel uses zero-fee commitments (114 max) or traditional commitments (483 max).
This commit updates the anchor resolver to use the correct witness type when sweeping P2A anchor outputs from v3 zero-fee commitment channels. When launching an anchor sweep, the resolver now checks if the channel type has zero-fee commitments. If so, it uses ZeroFeeAnchorSpend instead of CommitmentAnchor. The ZeroFeeAnchorSpend witness type generates an empty witness as required by BIP-431 for P2A outputs.
This commit fixes the channel arbitrator's anchor sweeping logic to properly handle P2A (pay-to-anchor) outputs from v3 zero-fee commitment channels. The prepareAnchorSweep function now checks if the anchor output script is a P2A script using txscript.IsPayToAnchorScript. If so, it uses the ZeroFeeAnchorSpend witness type instead of CommitmentAnchor. Additionally, the budget calculation is updated to use the actual anchor output value from the sign descriptor rather than a hardcoded 330 sats. This is important because P2A anchors can have values between 0 and 240 sats depending on trimmed HTLC amounts, unlike legacy anchors which are always 330 sats.
This commit adds ZERO_FEE = 7 to the CommitmentType enum in the proto definition. This value represents channels using v3 transactions with zero-fee commitment and HTLC transactions, where fee bumping is done via CPFP on the shared P2A (pay-to-anchor) output per BOLT PR lightningnetwork#1228.
This commit regenerates all protobuf Go files after adding the ZERO_FEE enum value to CommitmentType. The changes include the new enum constant and updated swagger documentation.
This commit updates rpcCommitmentType to return the ZERO_FEE RPC enum value when a channel has the ZeroFeeCommitmentsBit set. This enables RPC clients to identify v3 zero-fee commitment channels.
This commit adds a test case to TestRpcCommitmentType verifying that ZeroFeeCommitmentsBit maps to the ZERO_FEE RPC enum value.
This commit updates all callsites of the neutrino ChainService.Start method to pass a context.Context parameter. The neutrino package was updated to accept a context for proper shutdown coordination, and all LND code that starts neutrino services must be updated accordingly. The affected files are config_builder.go for production code, and backend.go, test_interface.go, and interface_test.go for test code.
This commit updates the test to pass the new trimmedAmount parameter to CreateCommitTx. A value of 0 is used since the test doesn't exercise zero-fee commitment channels with trimmed HTLCs.
This commit adds the official BOLT#3 test vectors for zero-fee commitment transactions and a comprehensive test suite that validates the implementation against them. The test vectors (test_vectors_zero_fee_commitments.json) contain multiple test cases with varying dust limits, balance distributions, and HTLC configurations. Each test case includes the expected signed commitment transaction and associated HTLC success/timeout transactions. The test file (zero_fee_transactions_test.go) parses these vectors and validates several properties of the implementation: - Transaction version is 3 for both commitment and HTLC transactions - P2A anchor output exists with script OP_1 <0x4e73> - Anchor amount is capped at 240 sats maximum - HTLC transaction inputs have sequence 0 (no CSV delay) - Output counts match expected structure (to_local, to_remote, anchor, and any HTLC outputs) The test vectors come directly from the BOLT specification PR lightningnetwork#1228 and serve as a canonical reference for correct transaction construction.
This commit adds comprehensive tests for the ZeroFeeAnchorSpend witness type which is used for spending P2A anchor outputs. TestZeroFeeAnchorSpendWitness verifies that the witness generator produces an empty witness as required by BIP-431 for P2A outputs. TestZeroFeeAnchorSpendSize confirms the size upper bound is 1 byte (just the witness element count of zero). TestZeroFeeAnchorSpendString and TestZeroFeeAnchorSpendValue verify the string representation and numeric value of the witness type. TestPayToAnchorScript and TestP2AScriptIsPayToAnchor validate btcd's P2A script handling, confirming the script format (OP_1 <0x4e73>) and that the IsPayToAnchorScript detection works correctly.
This commit adds comprehensive tests for the anchor resolver's handling of P2A (pay-to-anchor) outputs from v3 zero-fee commitment channels. TestAnchorResolverWitnessType validates that the correct witness type is selected based on channel type: CommitmentAnchor for legacy channels, TaprootAnchorSweepSpend for taproot channels, and ZeroFeeAnchorSpend for v3 zero-fee commitment channels. TestAnchorResolverZeroFeeWitnessSize verifies that ZeroFeeAnchorSpend returns the expected size of 1 byte. TestAnchorResolverChannelTypePriority confirms that zero-fee commitment detection takes precedence if both taproot and zero-fee bits are set. TestZeroFeeAnchorOutputValue validates the economic properties of P2A anchor outputs. TestAnchorResolverSupplementState verifies that SupplementState properly propagates channel type information to the resolver.
This commit adds TestUpdateFeeZeroFeeCommitmentRejected which verifies that update_fee operations are properly rejected for zero-fee commitment channels per BOLT#2. The test creates a channel with ZeroFeeCommitmentsBit set and confirms that both UpdateFee (sending) and ReceiveUpdateFee (receiving) return ErrUpdateFeeNotAllowed. This validates the protocol constraint that zero-fee channels must not use the update_fee mechanism.
This commit adds TestCalculateSharedAnchorAmount and updates the existing TestCoopCloseBalance to properly handle zero-fee commitment channels. TestCalculateSharedAnchorAmount validates the anchor amount calculation logic with various combinations of trimmed HTLC values and rounded millisatoshis. Key test cases include: - No trimmed outputs returns 0 - Values under 240 sats are preserved - Values at or above 240 sats are capped at P2AAnchorMaxAmount - Millisatoshi truncation behavior is correct The TestCoopCloseBalance property-based test is updated to include ZeroFeeCommitmentsBit in the channel type generator and to use the correct anchor amount (240 sats for P2A vs 660 sats for legacy) when calculating expected balances.
This commit adds TestVerifyConstraintsMaxHTLCs which validates that VerifyConstraints enforces the correct maximum HTLC limits based on the commitment type. For zero-fee commitment channels, the limit is 114 HTLCs per party (228 total) due to v3 transaction size limits. For traditional channels, the limit is 483 per party (966 total). The test verifies that values at and below the limit pass validation, while values above the limit fail with ErrMaxHtlcNumTooLarge.
Summary of ChangesHello @Roasbeef, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request lays the groundwork for supporting v3 zero-fee channels, a new channel type that utilizes zero-fee commitment and HTLC transactions. Fee bumping for these channels is managed via a single shared P2A (pay-to-anchor) output, allowing for more efficient fee management through CPFP. The changes include introducing the necessary channel type flags, feature bits for negotiation, and logic to correctly construct and validate these new transaction types, including specific rules for anchor output amounts and HTLC limits. While this PR provides the core infrastructure, full end-to-end integration tests are pending external dependency updates, and it is not yet ready for production deployment. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
This won't build as is on master until |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces support for v3 zero-fee commitment channels, a significant protocol update. The changes span across various packages, including channel database, contract court, funding logic, and the wallet, to accommodate the new channel type. New feature bits, configuration options, and RPC fields have been added. The logic for creating commitment and HTLC transactions has been updated to support v3 transactions, P2A anchors, and new fee handling mechanisms. The PR also includes a comprehensive set of unit tests and spec vector tests, which is great to see.
My review found a potential issue in the calculation of the P2A anchor amount, where rounded millisatoshis are not being included, and the anchor amount is being unnecessarily forced to its maximum value. Additionally, the go.mod file contains local replace directives that should be removed before merging. Overall, this is a well-structured PR for a complex feature.
| anchorAmount := CalculateSharedAnchorAmount( | ||
| trimmedAmount, 0, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The roundedMsats value, which is calculated from the main to_local and to_remote outputs, is not being passed to CalculateSharedAnchorAmount. The call is CalculateSharedAnchorAmount(trimmedAmount, 0), but it should be CalculateSharedAnchorAmount(trimmedAmount, roundedMsats) to correctly follow the spec (sum of all trimmed HTLCs + rounded-down msats).
| anchorAmount := CalculateSharedAnchorAmount( | |
| trimmedAmount, 0, | |
| ) | |
| anchorAmount := CalculateSharedAnchorAmount( | |
| trimmedAmount, roundedMsats, | |
| ) |
| if anchorAmount == 0 { | ||
| anchorAmount = P2AAnchorMaxAmount | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic forces the anchorAmount to P2AAnchorMaxAmount if it calculates to 0. The spec implies that a 0-value anchor is valid. A 0-value output can still be used for CPFP, and forcing it to 240 sats unnecessarily locks up funds in the anchor when there are no trimmed amounts. This block should be removed.
| // Use local btcutil with P2A AddressPayToAnchor support | ||
| replace github.com/btcsuite/btcd/btcutil => /Users/roasbeef/gocode/src/github.com/btcsuite/btcd/btcutil | ||
|
|
||
| // Use local btcwallet with NeutrinoChainService interface fix for Start(ctx) | ||
| replace github.com/btcsuite/btcwallet => /Users/roasbeef/gocode/src/github.com/btcsuite/btcwallet | ||
|
|
||
| // Use local neutrino with Start(ctx) method | ||
| replace github.com/lightninglabs/neutrino => /Users/roasbeef/gocode/src/github.com/lightninglabs/neutrino |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
Just adding this here for visibility #10481 |
In this PR, we do the preliminary work to add support for the new v3 channel type. Notably integration tests are left out as right now we use
btcdas the main mining code (bitcoindcan be swapped in as the relay node however), which doesn't yet support the new v3 logic, though this is in development.Aide from integration tests, we focus on unit tests are all the newly affected sights.
For the most part, sweeping doesn't change too much, though it's likely the case that the sweeper now needs to adhere to the new v3 rules in the case where we may be (inadvertently) spending unconfirmed change. As an example, if a user does
BumpFee, for a v3 transaction, the sweeper needs to ensure a v3 txn is used for that bump.We also need to tweak the force closing logic as now we must always use some sort of package submission to ensure everything is accepted at once. There is no true package relay protocol extension deployed today, instead neutrino nodes must assume (as there's no p2p version bump for this or any other signalling), that their set of connected nodes supports this feature to be able to safely force close.
One other important thing missing from this PR is updating our rules around the amount reserved for anchor channels. Before we had a max of 100k reserved, relying on the fact that we still used update fee to keep track of what'll likely allow us to allow the mempool "soon". With this change, the commitment txn is zero fee, so all force closes require a fee bump. This means that we either need to allocate on a per-channel-per-utxo basis, or we upgrade the sweeper to do RBF bumps across pending force close transactions to add additional inputs to anchor down. There're may be some complications here w.r.t the new v3 rules, and prior BIP 125 adherence (eg: no adding new unconfirmed inputs).
Gaps aside, this isn't really ready for actual prod deployment as we still need more of the relay network to update to be able to safely rely on the new policy rules.
For now this is a mega PR that I'll add to until everything is working e2e. Once that milestone is reached, I'll start to break it up, as there're many independent changes.