Skip to content

add-close-confirmation-info#10509

Merged
ziggie1984 merged 6 commits intolightningnetwork:masterfrom
ziggie1984:cnct-reorg-v2
Mar 19, 2026
Merged

add-close-confirmation-info#10509
ziggie1984 merged 6 commits intolightningnetwork:masterfrom
ziggie1984:cnct-reorg-v2

Conversation

@ziggie1984
Copy link
Copy Markdown
Collaborator

@ziggie1984 ziggie1984 commented Jan 16, 2026

Add the close confirmation info which is now needed since channels are not immediately considered closed now.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @ziggie1984, 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 overhauls the channel closure mechanism to enhance its resilience against blockchain reorganizations and provide more transparent status updates to users. It introduces dynamic confirmation scaling for cooperative closes, where the number of required confirmations is now proportional to the channel's value, offering a better balance between security and user experience. A state-machine-based approach is implemented in the chain_watcher to robustly manage the confirmation process of closing transactions, including handling reorgs and RBF replacements. Additionally, the minimum CLTV delta and incoming broadcast delta have been increased to bolster routing security, and a new RPC field provides real-time confirmation countdowns for channels awaiting closure.

Highlights

  • Dynamic Confirmation Scaling for Channel Closures: Cooperative channel closes now dynamically adjust the required number of confirmations based on channel capacity, ranging from 1 for small channels to 6 for large ones. This improves security against reorgs while maintaining efficiency for smaller value channels.
  • Enhanced Reorg Protection for Channel Closures: The chain_watcher component has been refactored into a robust state machine to handle chain reorganizations during the confirmation period of channel closing transactions. This ensures correct processing even if transactions are re-mined or replaced.
  • Improved User Visibility of Closing Channels: A new blocks_til_closed field has been added to the PendingChannels RPC response, providing users with a clear countdown of remaining confirmations until a channel is considered fully closed.
  • Increased CLTV Delta for Routing Security: The minimum CLTV delta (MinCLTVDelta) has been increased from 18 to 24 blocks, and DefaultIncomingBroadcastDelta from 10 to 16. This provides a larger safety margin for HTLCs and reduces the risk of force closures due to timing issues.
  • Comprehensive Testing for Reorg Scenarios: Extensive new unit and integration tests, including property-based testing, have been added to validate the new reorg-aware logic for various channel close types, ensuring the robustness of the updated system.

🧠 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 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 by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

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 pull request 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 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

  1. 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.

@ziggie1984
Copy link
Copy Markdown
Collaborator Author

Looks like this:

Screenshot 2026-01-16 at 23 00 46

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a 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 a more robust confirmation handling mechanism for channel closures, particularly for cooperative closes, to better withstand chain reorganizations. This is achieved by implementing a state machine in the chain_watcher to track spend confirmations and handle reorgs gracefully. The number of confirmations required for a cooperative close is now scaled based on channel capacity, with a minimum of 3 confirmations in production for reorg safety.

The changes also include:

  • A new CloseConfirmationHeight field in the channel database to track the confirmation height of the closing transaction.
  • A new blocks_til_closed field in the PendingChannels RPC response to show the remaining confirmations until a channel is fully closed.
  • An increase of MinCLTVDelta from 18 to 24 blocks for a larger safety margin.
  • Extensive new tests, including property-based tests, to validate the new reorg handling logic.

The code is well-structured, thoroughly tested, and the new logic for handling reorgs is a significant improvement in robustness. I have a couple of minor comments regarding a documentation inconsistency and a style guide violation.

Comment thread contractcourt/chain_watcher.go
Comment thread lnrpc/lightning.proto Outdated
Comment thread itest/lnd_coop_close_rbf_test.go Outdated
// confirmations until the channel is considered fully closed. It also tests
// that the field resets correctly after a chain reorg removes the closing
// transaction's confirmations.
func testWaitingCloseBlocksTilClosed(ht *lntest.HarnessTest) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively can add this assertion to the existing test?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes deleted the separated tests and added it to the existent ones.

@ziggie1984 ziggie1984 force-pushed the cnct-reorg-v2 branch 2 times, most recently from c064bad to 2628c3e Compare January 20, 2026 23:09
@saubyk saubyk added this to the v0.20.1 milestone Jan 21, 2026
@ziggie1984 ziggie1984 marked this pull request as ready for review January 21, 2026 19:35
@ziggie1984 ziggie1984 requested a review from Roasbeef January 21, 2026 21:50
Copy link
Copy Markdown
Contributor

@Abdulkbk Abdulkbk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

image

Comment thread lnrpc/lightning.proto Outdated
Comment thread itest/lnd_coop_close_rbf_test.go
@saubyk saubyk modified the milestones: v0.20.1, v0.21.0 Jan 27, 2026
@saubyk saubyk added this to v0.21 Jan 27, 2026
@ziggie1984 ziggie1984 added the P1 MUST be fixed or reviewed label Mar 5, 2026
@ziggie1984 ziggie1984 moved this to Ready in v0.21 Mar 5, 2026
@ziggie1984 ziggie1984 moved this from Ready to In review in v0.21 Mar 5, 2026
@saubyk saubyk self-requested a review March 5, 2026 16:07
@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

@Roasbeef: review reminder
@ziggie1984, remember to re-request review from reviewers when ready

Copy link
Copy Markdown
Collaborator

@saubyk saubyk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments regarding blocks_til_closed field in the response which I find confusing.

Additionally was wondering, if an existing channel is in "waiting close" state when upgrading what value would they show for blocks_til_closed in the rpc response?

Comment thread rpcserver.go Outdated
Comment thread rpcserver.go Outdated
@saubyk
Copy link
Copy Markdown
Collaborator

saubyk commented Mar 12, 2026

Also, think we need a release note entry here since we are adding a new field in the rpc response which the users should be made aware of.

@ziggie1984 ziggie1984 added rpc Related to the RPC interface and removed no-changelog labels Mar 13, 2026
@ziggie1984
Copy link
Copy Markdown
Collaborator Author

@claude review

@claude
Copy link
Copy Markdown

claude Bot commented Mar 13, 2026

Claude finished @ziggie1984's task in 3m 14s —— View job


PR Review

  • Gather context and read changed files
  • Review changed files
  • Provide comprehensive review feedback

Overall this is a well-structured addition. The TLV encoding, state management, and test coverage are solid. I have a few findings worth addressing before merging.


channeldb/channel.go

Inaccurate doc comment on MarkCloseConfirmationHeight (channeldb/channel.go:1608-1611):

"This is called when the closing tx confirms"

This is incorrect — looking at chain_watcher.go:766, it's called from processDetectedSpend when the spend is first detected (i.e., when the closing tx is first included in a block), not when it reaches its final confirmation count. The comment should read something like:

"This is called when the closing transaction is first detected in a block (spend height) and may be called with 0 to reset when the spending transaction is reorged out."

Fix this →


contractcourt/chain_watcher.go

MarkCloseConfirmationHeight called for all spend types (chain_watcher.go:766):

processDetectedSpend handles all funding output spend types — cooperative closes, force closes, and breach transactions. MarkCloseConfirmationHeight is now called unconditionally here for all of them. While this isn't a functional bug (force close channels appear under PendingForceClosingChannels not WaitingCloseChannels once fully processed), it does add extra DB writes for non-coop-close cases. A comment clarifying this is intentional would help future readers.


rpcserver.go

requiredConfs coupling between RPC server and chain watcher (rpcserver.go:4430-4435):

The blocks_til_close_confirmed countdown in the RPC server independently recomputes requiredConfs using lnwallet.CloseConfsForCapacity + dev config override. Meanwhile, the chain watcher computes the same value via requiredConfsForSpend() which uses c.cfg.chanCloseConfs.UnwrapOrFunc(...). If these two code paths ever drift (e.g., a third config knob is added that affects one but not the other), the displayed countdown would diverge from what the chain watcher is actually waiting for. This is not a bug today, but worth documenting as an invariant.

Upgrade path with CloseConfirmationHeight == 0 (rpcserver.go:4446-4449):

For channels already in WaitingClose state before this upgrade (where CloseConfirmationHeight will be 0 since it was never persisted), blocksTilCloseConfirmed will show the full requiredConfs value even if the closing tx was confirmed long ago. This was raised by @saubyk. The response to that concern would be good to document either in code or in the PR — is this an acceptable transient state that resolves itself after the next sync, or does it require a migration?

BlocksTilCloseConfirmed struct alignment (rpcserver.go:4458):

Nit: make lint may flag the unaligned struct literal field BlocksTilCloseConfirmed: blocksTilCloseConfirmed, relative to the other fields. Double-check with make lint.


itest/lnd_coop_close_rbf_test.go

Line length in comment (itest/lnd_coop_close_rbf_test.go:163-164):

// It also verifies the blocks_til_close_confirmed field correctly tracks remaining

This line exceeds 80 characters, which violates the LND style guide.

Fix this →

Test assertions look correct: The three blocks mined after the reorg (changed from 2 to 3) is reasonable to ensure the reorg is fully processed, and the wait.NoError polling pattern is appropriate for async block propagation.


lnrpc/lightning.proto + generated files

The proto comment now correctly matches the implementation (showing requiredConfs when unconfirmed). The lightning.swagger.json using "format": "int64" for a uint32 field is standard protobuf/grpc-gateway behavior — not a concern.


Summary

Severity Issue
Minor MarkCloseConfirmationHeight doc comment says "confirms" but means "detected in a block"
Minor Upgrade path with CloseConfirmationHeight == 0 needs comment/explanation
Nit MarkCloseConfirmationHeight called for all spend types without comment
Nit requiredConfs coupling between rpcserver and chain_watcher should be documented
Nit Line length in itest comment

The core logic for the targetHeight arithmetic (closeConfHeight + requiredConfs - 1) is correct. The TLV serialization/deserialization and the 0-sentinel pattern for reset are sound. Release notes look good.

@ziggie1984
Copy link
Copy Markdown
Collaborator Author

Regarding @saubyk's question about channels already in waiting-close state during an upgrade:

This is a transient state that resolves itself automatically. On startup, the chain watcher re-registers for spend notifications on the funding output. The chain notifier immediately re-delivers the already-confirmed spend event, which triggers MarkCloseConfirmationHeight with the correct block height. So blocks_til_close_confirmed will only show the full requiredConfs value for the brief window between startup and when the spend notification is re-delivered — no migration needed.

@ziggie1984 ziggie1984 requested a review from saubyk March 13, 2026 10:41
@ziggie1984 ziggie1984 added the severity-override-medium Manual override to medium label Mar 13, 2026
Copy link
Copy Markdown
Collaborator

@saubyk saubyk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack 🥇

@saubyk saubyk requested review from ellemouton and removed request for Roasbeef March 17, 2026 16:53
Copy link
Copy Markdown
Collaborator

@ellemouton ellemouton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good! a couple of suggestions

Comment thread channeldb/channel.go
Comment thread channeldb/channel.go Outdated
// CloseConfirmationHeight records the block height at which the closing
// transaction was first confirmed. This is used to track remaining
// confirmations until the channel is considered fully closed.
CloseConfirmationHeight uint32
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe just add what a zero value here means

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually - should we not just have this as fn.Option to start with?

Comment thread contractcourt/chain_watcher.go Outdated

// Reset the close confirmation height since the spend
// was reorged out.
err := c.cfg.chanState.MarkCloseConfirmationHeight(0)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps just worth having an explicit ResetConfHeight method instead of repurposing 0?

Comment thread lnrpc/lightning.proto
closing transaction is not yet confirmed, this value equals the
total number of confirmations required.
*/
uint32 blocks_til_close_confirmed = 6;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also just worth adding the closed height?

@ziggie1984 ziggie1984 force-pushed the cnct-reorg-v2 branch 2 times, most recently from 18c27b5 to ae33401 Compare March 19, 2026 17:46
…mation

This commit adds a new CloseConfirmationHeight field to the OpenChannel
struct which records the block height at which the closing transaction
was first confirmed. This is stored using TLV encoding (TlvType9) for
backwards compatibility.

A new MarkCloseConfirmationHeight method is added to persist this value,
which can be called when the closing tx confirms and also supports
updates in case of chain reorgs.
Update the chain watcher to set and reset the CloseConfirmationHeight
field when monitoring a channel close. When a spend is detected, we
record the spending height so users can see remaining confirmations.
When a reorg removes the close tx from the chain, we reset the height
to 0 to reflect that the transaction is no longer confirmed.
Copy link
Copy Markdown
Collaborator

@calvinrzachman calvinrzachman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the routing log fix 🙏

Add a new fields blocks_til_closed and close_height to the
WaitingCloseChannel message in PendingChannels RPC response.
This shows users how many more blocks until the waiting close
channel will be fully closed and removed.

The required confirmations are determined by CloseConfsForCapacity which
scales based on channel capacity for reorg safety. If the close tx is
not yet confirmed, the full required confirmations are shown.
Add an integration test that verifies the blocks_til_closed field and
the close_height field in the WaitingCloseChannel RPC response.
The test covers:

1. Initial state: shows full required confirmations when tx unconfirmed
2. Countdown: decrements as blocks are mined
3. Reorg handling: resets to full confirmations when close tx is
   reorged out of the chain
4. Recovery: countdown resumes correctly after close tx is re-mined
@ziggie1984
Copy link
Copy Markdown
Collaborator Author

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a 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 a new CloseConfirmationHeight field to track the block height at which a channel's closing transaction is first confirmed, enabling reorg-safe confirmation tracking for closed channels. The changes involve updating the channel database schema, modifying persistence logic, and integrating state management in the chain_watcher to record and reset this height during block confirmations and reorgs. The RPC API is extended with blocks_til_close_confirmed and close_height fields in the PendingChannelsResponse.WaitingCloseChannel message, providing users with real-time, reorg-aware confirmation status. Comprehensive integration tests were added to verify the correct behavior of these new fields under various scenarios, including reorgs. A minor logging improvement was also included.

@ziggie1984 ziggie1984 enabled auto-merge March 19, 2026 18:54
@ziggie1984 ziggie1984 merged commit 02206ab into lightningnetwork:master Mar 19, 2026
35 of 37 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in v0.21 Mar 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1 MUST be fixed or reviewed rpc Related to the RPC interface severity-override-medium Manual override to medium

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

7 participants