chancloser: update RBF close documentation for taproot support
What changed, and why it matters
This commit only adds documentation to a markdown file describing how taproot channel cooperative closes work in LND. It changes no source code, no configuration, and no executable behavior. There is no security issue in the commit itself.
No action needed. This is a documentation-only change with no security relevance.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff is a pure documentation update to lnwallet/chancloser/rbf_close.md. It adds sections explaining the RBF nonce flow, taproot/MuSig2 nonce handling, JIT nonce pattern, asymmetric closer/closee roles, wire message TLV extensions, validation requirements, and implementation notes. No Go code, build files, tests, or protocol logic were modified.
Changed components
lnwallet/chancloser/rbf_close.mdInspect captured patch +123 / −0
diff --git a/lnwallet/chancloser/rbf_close.md b/lnwallet/chancloser/rbf_close.md
index ac532c5..0a149a7 100644
--- a/lnwallet/chancloser/rbf_close.md
+++ b/lnwallet/chancloser/rbf_close.md
@@ -178,6 +178,35 @@ The `CloseErr` state provides recovery paths when protocol violations occur:
Recovery typically involves restarting the negotiation with a new closing offer.
+### RBF Nonce Flow Example
+
+Here's how nonces flow through an RBF cooperative close with taproot:
+
+1. **Initial Shutdown**:
+ - Alice sends `shutdown` with her closee nonce `NA`
+ - Bob sends `shutdown` with his closee nonce `NB`
+
+2. **First Close Attempt** (Alice as closer):
+ - Alice sends `closing_complete`:
+ - Uses Bob's nonce NB (from shutdown) to create her closer signature
+ - Includes `PartialSigWithNonce` with her next closee nonce `NA2`
+ - Bob sends `closing_sig`:
+ - Uses Alice's nonce NA (from shutdown) to create his closee signature
+ - Includes `NextCloseeNonce` with his next closee nonce `NB2`
+
+3. **RBF Iteration** (Bob as closer):
+ - Bob sends `closing_complete`:
+ - Uses Alice's nonce NA2 (from previous `PartialSigWithNonce`) to create
+ his closer signature
+ - Includes `PartialSigWithNonce` with his next closee nonce `NB3`
+ - Alice sends `closing_sig`:
+ - Uses Bob's nonce NB2 (from previous `NextCloseeNonce`) to create her
+ closee signature
+ - Includes `NextCloseeNonce` with her next closee nonce `NA3`
+
+The pattern continues with each party using the nonce they received in the
+previous round.
+
## Example Scenarios
### Standard Cooperative Close
@@ -211,9 +240,103 @@ Recovery typically involves restarting the negotiation with a new closing offer.
5. When agreement is reached on new fees: `ClosePending` → `CloseFin` (via
`txn_confirmation`)
+## Taproot Channel Support
+
+### MuSig2 Nonce Handling
+
+For taproot channels, the cooperative close process requires coordination for
+MuSig2 signature creation using a JIT (Just-In-Time) nonce pattern:
+
+#### Nonce Exchange During Shutdown
+
+For taproot channels using the modern RBF cooperative close flow:
+- The `shutdown` message includes a single nonce field:
+ - `shutdown_nonce` (TLV type 8): The sender's "closee nonce" used when they
+ send `closing_sig`
+- This simplified approach works because nonces are sent JIT with signatures
+
+#### JIT (Just-In-Time) Nonce Pattern
+
+The protocol uses an asymmetric signature pattern for taproot channels that
+optimizes nonce delivery:
+
+**Asymmetric Roles**:
+- **Closer**: The party proposing a fee (sends `closing_complete`)
+- **Closee**: The party accepting the fee (sends `closing_sig`)
+
+**ClosingComplete (from Closer)**:
+- Uses `PartialSigWithNonce` (98 bytes total):
+ - The partial signature (32 bytes)
+ - The sender's next closee nonce (66 bytes)
+- Bundles the nonce because the closee hasn't seen it yet
+- TLV types 5, 6, 7 (distinct from non-taproot types 1, 2, 3)
+
+**ClosingSig (from Closee)**:
+- Uses `PartialSig` (32 bytes) + separate `NextCloseeNonce`:
+ - The partial signature in TLV types 5, 6, 7
+ - The next closee nonce in TLV type 22 (66 bytes)
+- Separates the nonce because the closer already knows the current nonce from
+ shutdown or previous `PartialSigWithNonce`
+
+This asymmetric pattern minimizes redundancy while ensuring both parties always
+have the nonces they need for signing.
+
+#### Nonce State Management
+
+The state machine maintains a simplified `NonceState` structure with only 2 fields:
+- `LocalCloseeNonce`: Our closee nonce sent in our shutdown message
+- `RemoteCloseeNonce`: The peer's closee nonce from their shutdown message
+
+The JIT pattern eliminates complex nonce rotation:
+- New nonces arrive with signatures, not pre-generated
+- Remote nonces are updated automatically from `PartialSigWithNonce` in
+ `closing_complete`
+- Local nonces are generated on-demand when creating signatures
+
+### Wire Message Extensions
+
+The following messages have been extended with optional TLV fields for taproot:
+
+**shutdown**:
+- Type 8: `shutdown_nonce` - Sender's closee nonce for cooperative close signing
+
+**closing_complete**:
+- Types 5, 6, 7: `PartialSigWithNonce` - Partial signature with embedded next closee nonce
+ - Type 5: `closer_no_closee` (closer has output, closee is dust)
+ - Type 6: `no_closer_closee` (closer is dust, closee has output)
+ - Type 7: `closer_and_closee` (both have outputs)
+
+**closing_sig**:
+- Types 5, 6, 7: `PartialSig` - Just the partial signature (32 bytes)
+ - Same TLV type meanings as above
+- Type 22: `NextCloseeNonce` - Next closee nonce for RBF iterations (66 bytes)
+
+### Validation Requirements
+
+For taproot channels:
+- Shutdown messages MUST include the sender's closee nonce
+- ClosingComplete messages MUST use PartialSigWithNonce (includes next nonce
+ bundled with signature)
+- ClosingSig messages MUST use PartialSig with separate NextCloseeNonce field
+- Terminal offers (final RBF attempts) MAY omit next nonces to signal finality
+
+### Implementation Notes for Nonce Handling
+
+The MuSig2 session's `InitRemoteNonce` method is called at two specific times:
+1. When processing the remote's `shutdown` message (to initialize with their
+ closee nonce)
+
+2. After receiving a `closing_sig` message that contains a `NextCloseeNonce`
+ (for RBF iterations)
+
+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.
+
## Implementation Notes
- This state machine is implemented in the `peer.go` and `channel.go` files
within the lnd codebase
- State transitions are logged at the debug level
- The `ChanCloser` interface manages the state machine execution
+- Taproot support requires the `MusigSession` interface for nonce coordination
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.