Skip to content

Implement option_simple_close (BOLT 2 Simple Closing Negotiation) #9099

@nGoline

Description

@nGoline

As discussed with @rustyrussell, this issue tracks the implementation of the "Simple Close" protocol as defined in the BOLT 2 specifications. The goal is to move away from the legacy iterative fee negotiation (closing_signed) which often stalls due to fee-rate disagreements, and instead adopt the simpler, one-shot negotiation using closing_complete and closing_sig.

Background

The current mutual close negotiation requires both parties to agree on a fee. If fee-rates change rapidly or nodes have different fee-rate sources, they can get stuck in a loop.

The new "Simple Close" protocol (option_simple_close) simplifies this by allowing each peer to create their own version of the closing transaction:

  1. Symmetric Initiation: Each peer independently sends a closing_complete message containing their desired fee and their scriptpubkey.
  2. Independent Transactions: Each peer pays the fee for the transaction they propose.
  3. Simple Acceptance: Upon receiving closing_complete, the recipient simply signs that transaction and sends back closing_sig.
  4. Resolution: This results in two valid, but conflicting, closing transactions. Either can be broadcast and the network will simply confirm whichever one reaches a block first.
  5. Dust Management: If an output is dust, it must be omitted.
    • In high-fee environments where both outputs might be dust, the protocol uses an OP_RETURN with a 0 value to ensure the transaction remains valid and relayable.
  6. CPFP over High Fees: The spec notes that nodes should prefer low fees and rely on CPFP (Child Pays For Parent) for urgency, though RBF is supported by re-sending closing_complete.
  7. Race Condition Handling: If both nodes update their closer_scriptpubkey simultaneously, they may receive a signature for an outdated script. We will implement the spec-mandated check: if scripts mismatch, ignore the closing_sig and reconnect to reset.

Technical Details (BOLT 2/3 Compliance)

  • Message Flow: Both peers MUST send closing_complete and receive closing_sig.
  • Fee Logic: The peer sending closing_complete (the "closer") pays the fee.
  • Output Omission: A peer can choose to omit their own output (e.g., if it's below dust or they want to donate it to fees) by sending an OP_RETURN script.
  • RBF Support: The transaction sequence must be set to 0xFFFFFFFD to allow for fee bumping by re-sending closing_complete.

Proposed Implementation Plan

  • Channeld Logic: Update the state machine to transition into simple closing if option_simple_close was negotiated.
    • Handle asynchronous receipt of closing_complete.
    • Verify that closing_sig matches the most recently sent closer_scriptpubkey.
    • Store the signature received from the peer to allow broadcasting our proposed version.
  • Transaction Construction: Ensure that:
    • The closer-pays-fees logic is correctly applied to the transaction template.
    • Implement OP_RETURN fallback for the "all-dust" corner case.
    • Set nSequence to 0xFFFFFFFD to signal RBF.
  • Recovery: Implement the "reconnect on script mismatch" logic to resolve race conditions during script updates.
  • Testing: Add a developer flag or configuration to force option_simple_close in tests.
    • Test the "omitted output" case using OP_RETURN.
    • Verify that both generated transactions are valid spends of the funding output.
    • Test that on sending multiple closing_complete the last one will prevail.
    • Add functional tests in tests/ verifying the one-shot close with various fee scenarios.

Technical References

Metadata

Metadata

Assignees

Labels

Status::AssignedThe issue has been given to a team member for resolution.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions