7.2 KiB
RBF Co-op Close State Machine
Abstract
The Lightning Network Daemon (lnd) implements a cooperative channel closing mechanism that allows two connected peers to negotiate a mutually agreed-upon closing transaction. This document outlines the state machine that governs the cooperative closing process, defining the various states a channel progresses through from active operation to final closure.
Cooperative closing is distinct from force closing in that it requires both channel participants to agree on the final channel state and closing transaction, resulting in lower fees and immediate fund availability compared to unilateral channel closures.
State Machine Overview
The cooperative closing state machine manages the lifecycle of a channel from the moment a closing request is initiated until the closing transaction is confirmed on the blockchain. The state transitions ensure proper negotiation of closing fees, validation of signatures, and handling of protocol violations.
High-Level Flow
- An active channel receives a shutdown request (locally or remotely)
- Channel stops accepting new HTLCs and begins flushing existing ones
- Once flushed, nodes begin negotiating closing transaction fee
- Upon agreement, nodes sign and broadcast the transaction
- RBF Iteration (optional): While waiting for confirmation, either node can propose a fee increase via Replace-By-Fee (RBF), restarting the negotiation process with higher fees
- Process completes when transaction confirms on-chain
States and Transitions
---
title: Co-Op Close V2
---
stateDiagram-v2
state CoopCloseV2 {
[*] --> ChannelActive
ChannelActive --> ShutdownPending: send_shutdown
ChannelActive --> ShutdownPending: shutdown_received
ShutdownPending --> ChannelFlushing: shutdown_received
ShutdownPending --> ChannelFlushing: shutdown_complete
ChannelFlushing --> ClosingNegotiation: channel_flushed
state ClosingNegotiation {
state LocalSide {
[*] --> LocalCloseStart
LocalCloseStart --> LocalOfferSent: send_offer
LocalOfferSent --> ClosePending: local_sig_received
}
state RemoteSide {
[*] --> RemoteCloseStart
RemoteCloseStart --> ClosePending: offer_received
}
CloseErr --> LocalCloseStart: send_offer_rbf
CloseErr --> RemoteCloseStart: offer_received_rbf
LocalCloseStart --> CloseErr: protocol_violation
RemoteCloseStart --> CloseErr: protocol_violation
ClosePending --> LocalCloseStart: send_offer_rbf
ClosePending --> RemoteCloseStart: offer_received_rbf
}
ClosingNegotiation --> CloseFin: txn_confirmation
}
ChannelActive
The initial state where the channel is fully operational.
- Transitions:
send_shutdown→ShutdownPending(Local node initiates closing process)shutdown_received→ShutdownPending(Remote node initiates closing process)
ShutdownPending
The channel has entered the closing process but is still processing existing HTLCs.
- Transitions:
-
shutdown_received→ChannelFlushing(When both parties have exchanged shutdown messages) -
shutdown_complete→ChannelFlushing(When local shutdown processing is complete)
-
ChannelFlushing
The channel no longer accepts new HTLCs and is waiting for existing HTLCs to resolve.
- Transitions:
channel_flushed→ClosingNegotiation(When all HTLCs are settled)
ClosingNegotiation
This composite state encompasses multiple substates related to the negotiation
of the closing transaction fee. It's divided into local and remote operation flows.
LocalSide
-
LocalCloseStart: Ready to initiate or respond to closing negotiation from local perspective.
- Transitions:
send_offer→LocalOfferSent(Local node proposes a closing fee)protocol_violation→CloseErr(Invalid message or state detected)
- Transitions:
-
LocalOfferSent: Local node has sent a closing offer and is awaiting confirmation.
- Transitions:
local_sig_received→ClosePending(Local signature validated and accepted)
- Transitions:
RemoteSide
- RemoteCloseStart: Ready to process closing negotiation initiated by remote
node.
- Transitions:
offer_received→ClosePending(Remote node proposal received and accepted)protocol_violation→CloseErr(Invalid message or state detected)
- Transitions:
Shared States
-
ClosePending: Closing transaction has been negotiated, signed by both parties, and broadcast to the network. Waiting for on-chain confirmation.
- Transitions:
-
send_offer_rbf→LocalCloseStart(Local node initiates fee increase via RBF) -
offer_received_rbf→RemoteCloseStart(Remote node initiates fee increase via RBF)
-
- Transitions:
-
CloseErr: Error state for handling protocol violations.
- Transitions:
-
send_offer_rbf→LocalCloseStart(Local node attempts recovery with new offer) -
offer_received_rbf→RemoteCloseStart(Remote node attempts recovery with new offer)
-
- Transitions:
CloseFin
The closing transaction has been confirmed on the blockchain, and the channel is considered closed.
- Transitions: None (terminal state)
Fee Negotiation Process
Fee negotiation is a critical component of the cooperative close process. The protocol supports Replace-By-Fee (RBF) to handle changing network fee conditions:
- Initial fee proposal is based on current network fee estimation
- Either party can propose higher fees using RBF if confirmation is taking too long
- Nodes can alternate proposals until reaching agreement
- Each proposal must satisfy RBF requirements (incremental fee increase)
Protocol Violation Handling
The CloseErr state provides recovery paths when protocol violations occur:
- Invalid signature formats
- Out-of-sequence messages
- Timeout violations
- Malformed closing transactions
Recovery typically involves restarting the negotiation with a new closing offer.
Example Scenarios
Standard Cooperative Close
-
Local node initiates closing:
ChannelActive→ShutdownPending(viasend_shutdown) -
Remote acknowledges:
ShutdownPending→ChannelFlushing(viashutdown_received) -
HTLCs resolve:
ChannelFlushing→ClosingNegotiation(viachannel_flushed) -
Local proposes fee:
LocalCloseStart→LocalOfferSent(viasend_offer) -
Local signature validated:
LocalOfferSent→ClosePending(vialocal_sig_received) -
Transaction confirms:
ClosePending→CloseFin(viatxn_confirmation)
Fee Bump Due to Network Congestion
-
... (steps 1-5 same as above)
-
While in
ClosePending, network fees increase -
Local node initiates RBF:
ClosePending→LocalCloseStart(viasend_offer_rbf) -
New proposal cycle begins with higher fees
-
When agreement is reached on new fees:
ClosePending→CloseFin(viatxn_confirmation)
Implementation Notes
- This state machine is implemented in the
peer.goandchannel.gofiles within the lnd codebase - State transitions are logged at the debug level
- The
ChanCloserinterface manages the state machine execution