AI-generated analysisPublished automatically and not human-verified. Validated context appears in community notes below.
← Watch feed
Low 32 Bitcoin

contractcourt+server: insta-dispatch CLOSED_CHANNEL on first conf

Public commit record

What the developer wrote

Authored by Olaoluwa Osuntokun

91/100 · Strong
contractcourt+server: insta-dispatch CLOSED_CHANNEL on first conf

PR #10331 introduced a multi-confirmation reorg-aware dispatch in the
chain watcher. In production builds CloseConfsForCapacity is at least
3, so the chain watcher waits for three confirmations of a close tx
before running dispatchCooperativeClose, MarkChannelClosed, and
NotifyClosedChannel. Subscribers of the SubscribeChannelEvents stream
that used to receive a CLOSED_CHANNEL event after a single
confirmation in v0.20.1 stopped seeing the event entirely on shorter
test cycles and were delayed by two extra blocks on longer ones. This
is the regression alexbosworth reported on zero-conf channels.

The intent behind the original change was to wait three confirmations
under the hood for reorg safety while still dispatching a
CLOSED_CHANNEL event to RPC subscribers immediately, matching the
v0.20.1 surface. That insta-dispatch was wired into
peer.WaitForChanToClose for the local CloseChannel response stream
but was never extended to the channel-notifier path that drives
SubscribeChannelEvents.

In this commit, we wire a new optional notifyEarlyCoopClose callback
into the chain watcher's processDetectedSpend. The first time a coop
close spend is detected on chain, the chain watcher synthesizes a
ChannelCloseSummary with IsPending=true and dispatches a
CLOSED_CHANNEL event over the channel notifier, no DB round-trip
required. The callback is plumbed through ChainArbitratorConfig
.NotifyEarlyClosedChannel to the new
ChannelNotifier.NotifyEarlyClosedChannelEvent. The summary builder
shared with dispatchCooperativeClose is extracted into
buildCoopCloseSummary so the early and post-N-conf paths produce
equivalent payloads.

A coopCloseEarlyDispatched flag on the chain watcher keeps the
dispatch idempotent across blockbeat replays of the same spend, and
the closeObserver clears it on negativeConfChan so a re-mined or
replacement close after a deep reorg re-fires the preliminary event
with its own summary. The early-dispatch call sits before the
fast-path check so numConfs==1 also fires the early event through the
same code path.

Suppressing the duplicate notify at MarkChannelClosed time happens
inline in the chain_arbitrator MarkChannelClosed callback: after
CloseChannel succeeds, NotifyClosedChannel is fired only when the
close type is not CooperativeClose. Force, breach, and abandon paths
intentionally remain on the existing N-confirmation dispatch contract.
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification✓ Links an issue, advisory, or supporting reference
The short version

What changed, and why it matters

This commit fixes a regression in the LND Lightning node where users subscribing to channel-closure events stopped receiving timely notifications after a previous update added extra confirmation waiting. The fix restores the old behavior of notifying immediately when a cooperative channel close is first seen on the blockchain, while still waiting the full number of confirmations internally before finalizing the closure in the database. It is a bug-fix for event timing, not a security vulnerability, and does not introduce an attack vector.

Recommended action

Review and merge as a regression fix. Monitor that the early-dispatch summary fields (especially IsPending=true and close initiator fallback) are correctly interpreted by downstream RPC consumers and that the duplicate-suppression logic covers all cooperative-close paths, including fast-path (numConfs==1) where early dispatch is intentionally skipped.

Security signals we found

01

Regression fix restoring prior event-dispatch semantics

02

New early-dispatch callback path for cooperative closes only

03

Atomic flag used to avoid duplicate CLOSED_CHANNEL events across goroutines

04

Negative-confirmation handling clears flag to allow reorg-safe re-dispatch

05

CloseType gating suppresses duplicate notify at MarkChannelClosed time

06

No new cryptographic, network, or privilege assumptions introduced

Risk score

Why this scored 32/100

Our methodology →
Potential impact 8/30
Exploitability 0/25
Stealth signal 3/15
Affected reach 7/15
Confidence 9/10
Evidence quality 5/5
Human-validated context

Community notes

Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.

No validated notes yet.

The AI analysis stands alone for now. Submit a note if you can add evidence or important context.