-
Notifications
You must be signed in to change notification settings - Fork 2.2k
multi: add taproot support to the new RBF close flow #10063
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: splice-nonces
Are you sure you want to change the base?
Conversation
In this commit we, add support for taproot partial signatures with nonces to the ClosingComplete message. This is the foundation for taproot RBF cooperative channel closing, implementing the JIT nonce pattern required for the modern taproot closing flow. The changes include a new TaprootClosingSigs struct that mirrors the existing ClosingSigs but uses PartialSigWithNonce for taproot channels. The decoding and encoding functions are updated to handle both regular ECDSA signatures and taproot partial signatures. For taproot channels, the TaprootClosingSigs field is populated while ClosingSigs remains empty, maintaining backward compatibility. We also fix a minor typo in the comment for CloserNoClosee field (clsoee -> closee).
In this commit we, extend the ClosingSig message to support taproot partial signatures for the RBF cooperative close flow. The ClosingSig message is sent by the closee in response to a ClosingComplete message. For taproot channels, we add TaprootPartialSigs which contains partial signatures without nonces since the remote party already knows our nonce from the previous ClosingComplete message. We also add a NextCloseeNonce field for RBF iterations, allowing the closee to provide a new nonce for the next potential RBF round. The decoding and encoding functions are updated to handle both regular signatures and taproot partial signatures, maintaining backward compatibility with existing non-taproot channels while enabling the advanced taproot RBF flow.
In this commit we, add support for closee nonces in the Shutdown message to enable taproot RBF cooperative channel closing. The ShutdownNonce field allows taproot channels to exchange the initial nonces required for the MuSig2 signing process during cooperative closure. This nonce represents the closee nonce that the remote party will use when they act as the closer in the RBF flow. The nonce is transmitted as part of the shutdown flow and is essential for the JIT nonce pattern used in the modern taproot closing protocol. The changes maintain backward compatibility as the ShutdownNonce field is optional and only used for taproot channels that support the enhanced RBF flow.
In this commit we, update the test message utilities to include the new taproot signature fields added to ClosingComplete and ClosingSig messages. This ensures the wire protocol tests properly exercise the new taproot partial signature functionality.
…ment In this commit we, add the fundamental infrastructure for taproot RBF cooperative channel closing. This includes adding taproot channel detection, MuSig2 session management, and nonce state tracking throughout the closing state machine. Key additions include the IsTaproot method on Environment to detect taproot channels based on the presence of MuSig sessions, and LocalMusigSession/RemoteMusigSession fields for managing the different signing contexts. We add NonceState tracking to maintain closee nonces exchanged during the shutdown phase. The SendShutdown and ShutdownReceived events are extended to carry closee nonces for taproot channels, and we add proper error handling for missing nonces in taproot shutdown messages. These changes provide the foundation for the taproot-specific state transitions while maintaining compatibility with existing non-taproot channels.
Pull reviewers statsStats of the last 30 days for lnd:
|
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.
Summary of Changes
Hello @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 significantly extends LND's cooperative channel closing capabilities by integrating Taproot channel support into the RBF (Replace-by-Fee) flow. It introduces a sophisticated Just-In-Time nonce management system using MuSig2 signatures, along with necessary wire protocol and state machine adjustments, ensuring a secure and efficient closing process for modern Lightning channels while maintaining full backward compatibility.
Highlights
- Taproot RBF Cooperative Close Support: This PR implements comprehensive support for Taproot channels within the existing RBF (Replace-by-Fee) cooperative channel closing protocol. This includes using MuSig2 signatures and a JIT (Just-In-Time) nonce pattern for secure and efficient nonce management during RBF iterations.
- Wire Protocol Extensions: The
shutdown
,closing_complete
, andclosing_sig
messages have been extended with Taproot-specific TLV fields. Theshutdown
message now carries the sender's closee nonce.closing_complete
usesPartialSigWithNonce
to bundle partial signatures with next-round nonces, andclosing_sig
uses separatePartialSig
andNextCloseeNonce
fields. - State Machine Enhancements: The cooperative close state machine has been updated to detect Taproot channels via an
IsTaproot()
method and manage nonce states. It now maintains separateLocalMusigSession
andRemoteMusigSession
contexts to handle the asymmetric closer/closee roles in the Taproot closing flow. - Signature Handling and Compatibility: New helpers for signature extraction and validation have been introduced to properly handle both Taproot partial signatures and traditional ECDSA signatures. All changes maintain full backward compatibility, activating Taproot functionality only when MuSig2 sessions are present.
Using Gemini Code Assist
The 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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command>
or @gemini-code-assist <command>
. Below is a summary of the supported commands.
Feature | Command | Description |
---|---|---|
Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
Help | /gemini help |
Displays a list of available commands. |
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 .gemini/
folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and 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 to provide feedback.
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
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
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 PR introduces comprehensive support for Taproot channels in the RBF cooperative close flow. The changes are extensive, touching the wire protocol, state machine, and adding MuSig2 signing logic with a JIT nonce pattern. The refactoring of integration tests to cover both taproot and non-taproot channels is well-executed.
My review has identified a critical issue in the nonce handling logic for taproot channels that could lead to signature failures, and a style guide violation regarding line length.
// Extract their closer nonce from PartialSigWithNonce and use it to verify their signature | ||
initRemoteMusigCloseeNonce(env, jitNonce) |
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 call to initRemoteMusigCloseeNonce
with jitNonce
is incorrect and will lead to signature failures for taproot channels.
The jitNonce
is extracted from the PartialSigWithNonce
field of a closing_complete
message. This nonce is the next closee nonce of the sender (the closer), intended for the subsequent RBF iteration. It should not be used to initialize the musig session for the current round of signing/verification.
The musig session should have already been initialized with the nonces exchanged during the shutdown
phase. This incorrect re-initialization will corrupt the session state.
This is also in direct contradiction with the documentation in rbf_close.md
, which states: "The nonce from PartialSigWithNonce
in closing_complete
is stored but not immediately used with InitRemoteNonce
- it's used when we need to sign as the closee in the next round."
Please remove this line. The jitNonce
should be stored for the next round, likely by updating c.NonceState.RemoteCloseeNonce
in updateAndValidateCloseTerms
for the OfferReceivedEvent
case, similar to how it's handled for LocalSigReceived
.
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.
@gemini-code-assist this was reworked. The new _itended flow is:
1. When we receive their shutdown (they might become closee):
- They send us their closee nonce in the shutdown message
- We call
InitRemoteNonce
on ourLocalMusigSession
with their closee nonce - This prepares our LocalMusigSession for when we act as closer
2. When we send our shutdown (we might become closee):
- We generate our closee nonce by calling
ClosingNonce()
onRemoteMusigSession
- This sets the internal localNonce field in RemoteMusigSession
- We send this nonce in our shutdown message
3. When we act as closer (sending ClosingComplete):
- We call
ClosingNonce()
onLocalMusigSession
to generate our JIT closer nonce - We sign with this nonce
- We send both our signature and this JIT nonce in
ClosingComplete
- The nonce in
PartialSigWithNonce
is the JIT closer nonce we used to sign
4. When we act as closee (receiving ClosingComplete):
- They send us their JIT closer nonce in
ClosingComplete
- We call
InitRemoteNonce
onRemoteMusigSession
with their JIT closer nonce - This completes the RemoteMusigSession so we can verify their signature
- We respond with
ClosingSig
containing our partial signature
5. When sending ClosingSig (as closee):
- We include our partial signature (no nonce needed, they already know it)
- We include
NextCloseeNonce
for future RBF iterations
6. When receiving ClosingSig (as closer):
- We extract their
NextCloseeNonce
from the message - We store it as
RemoteCloseeNonce
for future RBF iterations - If we want to RBF, we'll use this nonce when calling
InitRemoteNonce
on our `LocalMusigSession
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.
Thank you for the detailed breakdown of the intended nonce flow, Roasbeef. This clarifies the design significantly.
However, I've noticed a direct contradiction between point 4 of your explanation and the documentation in rbf_close.md
that was added in this very pull request.
Your point 4 states:
- When we act as closee (receiving ClosingComplete):
- They send us their JIT closer nonce in
ClosingComplete
- We call
InitRemoteNonce
onRemoteMusigSession
with their JIT closer nonce- This completes the RemoteMusigSession so we can verify their signature
Yet, rbf_close.md
explicitly states under "Implementation Notes for Nonce Handling":
The nonce from
PartialSigWithNonce
inclosing_complete
is stored but not immediately used withInitRemoteNonce
- it's used when we need to sign as the closee in the next round.
The code snippet I originally commented on (initRemoteMusigCloseeNonce(env, jitNonce)
) aligns with your point 4, meaning it does immediately use the jitNonce
from closing_complete
to initialize the RemoteMusigSession
for the current round's verification. This directly contradicts the documentation.
Could you please clarify which behavior is the correct and intended one? It's crucial for the code and its documentation to be consistent to avoid future misunderstandings and potential bugs. If the code's current behavior (as described in your point 4) is correct, then the documentation should be updated to reflect that jitNonce
is used immediately for verification. If the documentation is correct, then the code needs to be adjusted to store jitNonce
for the next round instead of using it for the current one.
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.
rbf_close.md
is out of date, left over from an earlier iteration.
This section:
The nonce from PartialSigWithNonce in closing_complete is stored but not immediately used with InitRemoteNonce - it's used when we need to sign as the closee in the next round.
Should be closing_sig
, and just the nonce sent as a distinct TLV field in that message.
In this commit we, implement the complete taproot RBF cooperative close state machine transitions. This is a comprehensive change that adds all the necessary components for taproot channel closing support. The implementation includes several key areas: First, we add nonce management helpers including initLocalMusigCloseeNonce and initRemoteMusigCloseeNonce for properly initializing MuSig2 sessions with the appropriate closee nonces during the RBF flow. Second, we implement signature extraction and validation helpers including partialSigToWireSig for converting partial signatures to wire format, and extractTaprootSigAndNonce, extractSigAndNonce, and validateAndExtractSigAndNonce for handling both taproot and regular signatures with proper validation. Third, we add comprehensive signature encoding logic with encodeClosingSignatures that creates appropriate signature structures for both channel types, and helper functions like processRemoteTaprootSig, createLocalCloseeSignature, and createClosingSigMessage for managing the complex taproot signing flow. Fourth, we extend the shutdown validation logic to require nonces for taproot channels and update all state transitions to properly handle nonce exchange, MuSig2 session initialization, and the dual signature paths for taproot vs non-taproot channels. Finally, we add signature preparation logic with prepareClosingSignatures and extraction helpers like extractSigAndNonceFromComplete that handle the complex musig signature combination required for taproot channels while maintaining compatibility with existing ECDSA signatures. The changes maintain backward compatibility with existing non-taproot channels while enabling the full taproot RBF cooperative close flow with proper nonce rotation and signature handling.
In this commit we, extend the RBF cooperative close test suite to support taproot channels. This includes adding schnorr signature test constants, taproot channel test helpers, and comprehensive test coverage for the taproot RBF flow. The changes add localSchnorrSig and remoteSchnorrSig test constants to mirror the existing ECDSA signatures, and include proper imports for musig2, chainhash, and lnwallet to support the taproot testing infrastructure. The test modifications ensure that both taproot and non-taproot channels are properly tested throughout the RBF cooperative close state machine, validating the dual signature handling paths and nonce management logic introduced in the main implementation.
In this commit we, update the chancloser test utilities and message mapping functions to properly handle the new taproot-specific fields in the RBF cooperative close flow. The changes ensure that test harnesses and message mapping functions are aware of the taproot signature fields and nonce handling required for the extended wire protocol support. This maintains test coverage for both existing non-taproot functionality and the new taproot capabilities.
In this commit we, extend the integration test suite to cover taproot RBF cooperative close scenarios. The tests validate the complete end-to-end flow for taproot channels including nonce exchange during shutdown, MuSig2 signature handling, and proper RBF iteration with nonce rotation. The integration tests ensure that the taproot closing flow works correctly in a real network environment, exercising both the happy path and various edge cases specific to taproot channel closing. This provides comprehensive coverage for the enhanced cooperative close functionality.
In this commit we, integrate the taproot RBF cooperative close functionality throughout the LND stack. This includes updating protocol configuration and peer connection handling to support the new taproot closing flow. The changes wire through the taproot channel detection, nonce exchange during shutdown, and proper handling of the enhanced wire protocol messages in the peer layer. This completes the integration of taproot RBF cooperative close functionality, providing a complete alternate closing path for taproot channels that leverages MuSig2 signatures and implements proper nonce rotation for secure RBF scenarios.
In this commit we, update the RBF cooperative close documentation to comprehensively cover the taproot channel closing flow. The documentation now explains the JIT nonce pattern, asymmetric signature roles, and the complete nonce exchange protocol for taproot channels. Key additions include detailed explanations of how nonces flow through the RBF process, the distinction between closer and closee roles, and the specific wire message extensions for PartialSigWithNonce and NextCloseeNonce fields. The documentation also covers validation requirements and implementation notes specific to taproot channels. This documentation provides a complete reference for understanding and implementing the enhanced taproot RBF cooperative close protocol.
@Roasbeef, remember to re-request review from reviewers when ready |
In this PR, we implement comprehensive taproot RBF cooperative channel closing support for LND. The implementation extends the existing RBF cooperative close protocol to support taproot channels using MuSig2 signatures and introduces a JIT (Just-In-Time) nonce pattern for secure and efficient nonce management during RBF iterations.
wire + state machien changes
The implementation introduces taproot-specific wire protocol extensions for the
shutdown
,closing_complete
, andclosing_sig
messages. Theshutdown
message now carries closee nonces for taproot channels, whileclosing_complete
usesPartialSigWithNonce
structures that bundle partial signatures with next-round nonces. Theclosing_sig
message uses separatePartialSig
andNextCloseeNonce
fields to optimize nonce delivery timing.The cooperative close state machine has been extended with taproot channel detection through the
IsTaproot()
method and comprehensive nonce state management. The implementation maintains separateLocalMusigSession
andRemoteMusigSession
contexts for handling the asymmetric closer/closee roles in the taproot closing flow.New signature extraction and validation helpers handle both taproot partial signatures and traditional ECDSA signatures with proper type validation. The
partialSigToWireSig
,extractTaprootSigAndNonce
, andvalidateAndExtractSigAndNonce
functions provide robust signature handling while maintaining backward compatibility.The state transitions have been updated to properly initialize MuSig2 sessions with appropriate closee nonces, handle signature preparation for both channel types, and manage the complex nonce rotation required for secure RBF iterations. The implementation includes comprehensive error handling for taproot-specific validation requirements.
Fixes #9662