round: wire round-born VTXOs to the batch-canonicality gate (C6)#818
round: wire round-born VTXOs to the batch-canonicality gate (C6)#818ellemouton wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a reorg-safety mechanism by gating the round FSM's terminal transition on commitment finality rather than the first provisional confirmation. It adds a cache for provisional confirmation events, wires reorg-aware lifecycle refs to detect reorgs and finality, and registers confirmed batches with a canonicality manager to track consumed inputs and dependent VTXOs. Feedback on these changes highlights a potential memory leak where pendingCommitmentConfs and commitmentTxIndex are not cleaned up when rounds are cancelled or reaped.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| // ledger emissions, indexer notifications) because a reorg of the | ||
| // confirmation block would otherwise leave that state inconsistent | ||
| // with the canonical chain. | ||
| pendingCommitmentConfs map[chainhash.Hash]*ConfirmationEvent |
There was a problem hiding this comment.
Memory / State Leak on Failed or Cancelled Rounds
While pendingCommitmentConfs is correctly cleaned up on successful finalization in handleCommitmentFinalized, there is currently no cleanup of this map (or commitmentTxIndex) when a round is cancelled via handleCancelRound or reaped via reapFailedRounds.
If a round fails or is cancelled after receiving a provisional confirmation but before finalization, the cached confirmation event will remain in pendingCommitmentConfs indefinitely, leading to a memory leak. Additionally, for cancelled rounds, the TxID is never deleted from commitmentTxIndex because handleCancelRound deletes the round from a.rounds immediately, preventing reapFailedRounds from ever seeing and cleaning it up.
Suggested Fix:
- In
reapFailedRounds, delete the entry frompendingCommitmentConfs:delete(a.pendingCommitmentConfs, roundFSM.TxID)
- In
handleCancelRound, ensure bothcommitmentTxIndexandpendingCommitmentConfsare cleaned up:delete(a.commitmentTxIndex, targetFSM.TxID) delete(a.pendingCommitmentConfs, targetFSM.TxID)
9a99bae to
e3a9a64
Compare
232a141 to
a630201
Compare
e3a9a64 to
3efe711
Compare
a630201 to
7f8df41
Compare
Squashed for the btcd v2 port. batchcanon Availability vocab (available_final/provisional/unknown, limbo_reorg/conflict, invalidated) + CombineAvailability + store-driven LineageBlocked, and the vtxo.Manager coin-selection/forfeit admission gate that drops candidates whose batch lineage is limbo/invalidated. Permissive for unseen/unregistered; no-op when the store is nil.
3efe711 to
a18c0a4
Compare
7f8df41 to
b317f3c
Compare
Squashed for the btcd v2 port. The round registers its round-born batch + consumed inputs with the canonicality manager, and gates pre-commitment progression on consumed-input canonicality (finality gate kept as interim safety).
a18c0a4 to
b0e7182
Compare
b317f3c to
28576e7
Compare
C6 — Wire round-born VTXOs to the batch-canonicality gate
Part of the reorg-safety epic (lightninglabs/darepo#454). Stacked on the C5
availability gate (#796).
What this does
When a round's commitment tx confirms and the client materializes its owned
VTXOs, the round actor registers the batch with the
BatchCanonicalityManager(
RegisterBatchRequestcarrying the batch txid, consumed inputs = boardingoutpoints + forfeited VTXO outpoints, dependent VTXOs = round-born VTXO
outpoints, the batch confirmation pkScript, and the CSV expiry delta). The C5
gate (dormant until producers register batches) then governs those VTXOs:
a reorg-out or input double-spend excludes them from coin selection until the
batch is canonical again. The consumed-input set also seeds the manager's
reorg-aware spend watches, underpinning forfeited-VTXO restore (F6).
Registration is gated behind an optional
RoundClientConfig.BatchCanonicalityref —
Nonekeeps the gate dormant and preserves pre-C6 behavior, so existinghosts/tests are unaffected.
Unlocks acceptance F2/F3 (round-born VTXO reorg → limbo → usable / input
conflict → unavailable) and sets up F6 (forfeit-into-invalidated-round
restore).
Foundation commit
The first commit (
round: gate commitment-tx terminal transition on finality)is cherry-picked from #558 — it makes the round commitment watch
reorg-aware and gates the terminal transition on finality, which C6 builds on.
It is the interim home; #558 should drop it (or stack on C6) to avoid the
duplicate at merge time.
F1 (pre-commitment input gate): safety already holds; proactive abort deferred
F1's safety ("a round must not complete using a double-spent boarding input")
is already structurally guaranteed: an unconfirmed commitment never reaches
Confirmedand so never materializes VTXOs, and the reorg-aware finality gatenever finalizes a reorged/never-confirming commitment. The remaining value is
proactive abort (fail fast instead of waiting for the registration timeout) —
a liveness optimization. At
InputSigSentthe client is past the point of noreturn, so a forced FSM abort from an async spend event is intricate for
marginal gain; this is deferred as a focused follow-up, consistent with the
codebase's current detection-only reorg posture.
Activation (darepod) — deferred
Like C5, the round gate ships dormant. Constructing/starting the canonicality
manager and threading the store/ref to the vtxo + round actors is the
integration step that ties #795 + #796 + C6/C7/C8 together and needs the daemon
harness to validate.
Tests
Unit tests cover the populated
RegisterBatchRequest, the dormant no-op whenunwired, and the skip for an empty batch. End-to-end F2/F3/F6 land as client
systests in the acceptance pass.
🤖 Generated with Claude Code