Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
dd6e65a
chainsource: reorg-aware conf/spend lifecycle + synthesized finality
ellemouton Jul 13, 2026
e7b4a80
txconfirm: reorg-aware confirmation watches with terminal seal
ellemouton Jul 13, 2026
6205861
chainbackends: forward lnd/lndclient reorg and finality signals
ellemouton Jul 13, 2026
bda120e
lwwallet: reorg-aware Esplora chain backend (TipPoller)
ellemouton Jul 13, 2026
34c2664
btcwbackend: reorg-aware Neutrino chain notifier forwarding
ellemouton Jul 13, 2026
3412852
multi: wire reorg-aware lifecycle into wallet, harness, systests
ellemouton Jul 13, 2026
39f521c
multi: batch canonicality data model (C2)
ellemouton Jul 8, 2026
2d3e632
batchcanon: BatchCanonicalityManager (C3/C4)
ellemouton Jul 8, 2026
8c19663
batchcanon+vtxo: VTXO lineage availability + admission gate (C5)
ellemouton Jul 8, 2026
8792fe7
round: wire round-born VTXOs to the batch-canonicality gate (C6)
ellemouton Jul 8, 2026
248fc44
vtxo+oor: multi-parent lineage gate + OOR-received registration (C7)
ellemouton Jul 8, 2026
38426c5
unroll: gate admission on source-lineage canonicality (C8)
ellemouton Jul 8, 2026
303a1c8
waved: activate the batch-canonicality reorg-safety gate (C9)
ellemouton Jul 8, 2026
684db36
vtxo: reconcile DB status when restoring a resident forfeited VTXO
ellemouton Jul 9, 2026
79e0c97
batchcanon: re-drive interrupted forfeit restores on reconcile
ellemouton Jul 9, 2026
2c1d066
round: drop cached commitment confs on round removal
ellemouton Jul 9, 2026
45fcd14
db: persist per-input conflict flags on batch_consumed_inputs
ellemouton Jul 15, 2026
d6dec4e
batchcanon: fix restart reconcile transiently clearing a conflict
ellemouton Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions batchcanon/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# batchcanon

## Purpose

Client-side **batch canonicality data model** for the reorg-safety epic
(darepo#454, task C2). Holds the durable, reorg-aware record of how each batch
(commitment) transaction is faring against the best chain: its canonicality
state, current confirmation observation, recompute inputs for effective
expiry, the inputs it consumes, the VTXOs it anchors, and the reverse
dependencies needed to restore a provisionally consumed VTXO.

This package is **data + query/update interface only**. It contains no
interpretation, no chain watching, and no admission behavior — those belong to
the (later) `BatchCanonicalityManager` and the VTXO manager. Keeping the model
in its own package, separate from `chainsource` (raw observation) and `vtxo`
(admission), preserves the epic's observation → interpretation → action split.

## Key Types

- `State` — canonicality state enum: `StateUnseen`, `StateProvisional`,
`StateFinalized`, `StateReorgedOut`, `StateConflictProvisional`,
`StateConflictFinalized`. Reorg-reversible; **no state is a terminal
verdict** at this layer. Persisted as an append-only typed INTEGER column —
values must never be renumbered.
- `PolicyState` — reserved policy classification slot (`PolicyStateDefault`
only); persisted and round-tripped, no business meaning yet.
- `Record` — per-batch record keyed by `BatchTxID`. Identity is by **txid**,
never `(txid, block hash)`; `ConfirmationBlock` is an observation attribute
only. `EffectiveExpiry()` derives the absolute expiry as
`ConfirmationHeight + CSVExpiryDelta`, returning `None` when unconfirmed —
the structural guarantee that expiry is recomputed on every
reconfirmation rather than frozen.
- `ProvisionalConsumer` — reverse-dependency edge (consumed VTXO → consumer
batch) enabling VTXO restore if a consumer batch never becomes canonical.
- `Availability` — derived (never persisted) VTXO-lineage spendability:
`AvailableFinal`, `AvailableProvisional`, `AvailabilityUnknown`,
`LimboReorg`, `LimboConflict`, `Invalidated`. `AvailabilityForState`
maps one batch's `State`; `CombineAvailability` takes the worst across a
multi-parent lineage; `Usable()` is true only for confirmed lineage.
`LineageAvailability`/`LineageBlocked` load each parent batch from the
`Store` and produce the combined availability / block decision the VTXO
manager's admission gate (C5 wiring) calls per candidate. The gate is
permissive: unseen / not-yet-registered lineage does not block — only
limbo/invalidated lineage does.
- `Store` — behavior-free durable query/update interface. Implemented by
`db.BatchCanonicalityPersistenceStore` over the `000020`/`000021` schema;
backfilled from existing VTXOs via
`db.BatchCanonicalityPersistenceStore.BackfillFromVTXOs`.
- `Manager` — the actor that interprets chain observation into canonicality
state (the sole client-side interpreter). Registered under
`ManagerServiceKey`. `RegisterBatchRequest` arms one reorg-aware
confirmation watch on the batch tx and one reorg-aware spend watch per
consumed input (deduped per batch, idempotent — repeats merge dependent
VTXOs). It maps chainsource `ConfirmationEvent`/`ConfReorgedEvent`/
`ConfDoneEvent` and `SpendEvent`/`SpendReorgedEvent`/`SpendDoneEvent` onto
its own mailbox and derives `State` per the priority
`conflict_finalized > conflict_provisional > reorged_out >
finalized/provisional > unseen`. `Reconcile` re-arms watches for non-final
batches after restart without downgrading persisted state.
`GetBatchStateRequest` reads the persisted record. `NewManager` returns the
behavior; the caller registers it, then calls `SetSelfRef(ref.TellRef())`
and `Reconcile`.

## Relationships

- **Depends on**: `btcd/chaincfg/chainhash`, `btcd/wire`, `lnd/fn/v2` only.
- **Depended on by**: `db` (concrete store), and — in later tasks — the
batch canonicality manager and `vtxo` admission.

## Invariants

- Identity is by txid / outpoint, never by `(txid, block hash)`.
- Expiry is never persisted as a standalone or terminal value; it is always
derived from `CSVExpiryDelta` + the current confirmation observation.
- State enum integer values are append-only (persisted column).

## Expiry-as-terminal audit (darepo#454 C2)

C2 requires auditing every site that treats `BatchExpiry`/`Expired` as a
one-way terminal fact. These are flagged for rework when the
BatchCanonicalityManager (task C3/C4) rewires expiry consumers onto
`Record.EffectiveExpiry()`; **no behavior is changed by C2**:

- `vtxo/transitions.go` (`ExpiryStatusExpired → FailedState{Recoverable:
false}`, and the Critical/Expired escalations) — the primary offender: a
reorg that lowers the confirmation height could otherwise push a VTXO
permanently into non-recoverable `Failed`.
- `vtxo/expiry.go` (`CheckExpiry`, `BlocksUntilExpiry`) — compute from the
frozen absolute `vtxo.BatchExpiry`; must consume effective (recomputable)
expiry instead.
- `vtxo/actor.go` — schedules on the frozen absolute `BatchExpiry`.
- `waved/vhtlc_recovery_target.go` — folds multiple roots into a
most-restrictive absolute `batchExpiry`.
- `unroll/proof_assembler.go` (`BatchExpiry == 0`) — treats zero as "unset",
not terminal; benign, documented for completeness.

## Deep Docs

- [ARCHITECTURE.md](../ARCHITECTURE.md) — System-wide package map.
</content>
100 changes: 100 additions & 0 deletions batchcanon/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# batchcanon

## Purpose

Client-side **batch canonicality data model** for the reorg-safety epic
(darepo#454, task C2). Holds the durable, reorg-aware record of how each batch
(commitment) transaction is faring against the best chain: its canonicality
state, current confirmation observation, recompute inputs for effective
expiry, the inputs it consumes, the VTXOs it anchors, and the reverse
dependencies needed to restore a provisionally consumed VTXO.

This package is **data + query/update interface only**. It contains no
interpretation, no chain watching, and no admission behavior — those belong to
the (later) `BatchCanonicalityManager` and the VTXO manager. Keeping the model
in its own package, separate from `chainsource` (raw observation) and `vtxo`
(admission), preserves the epic's observation → interpretation → action split.

## Key Types

- `State` — canonicality state enum: `StateUnseen`, `StateProvisional`,
`StateFinalized`, `StateReorgedOut`, `StateConflictProvisional`,
`StateConflictFinalized`. Reorg-reversible; **no state is a terminal
verdict** at this layer. Persisted as an append-only typed INTEGER column —
values must never be renumbered.
- `PolicyState` — reserved policy classification slot (`PolicyStateDefault`
only); persisted and round-tripped, no business meaning yet.
- `Record` — per-batch record keyed by `BatchTxID`. Identity is by **txid**,
never `(txid, block hash)`; `ConfirmationBlock` is an observation attribute
only. `EffectiveExpiry()` derives the absolute expiry as
`ConfirmationHeight + CSVExpiryDelta`, returning `None` when unconfirmed —
the structural guarantee that expiry is recomputed on every
reconfirmation rather than frozen.
- `ProvisionalConsumer` — reverse-dependency edge (consumed VTXO → consumer
batch) enabling VTXO restore if a consumer batch never becomes canonical.
- `Availability` — derived (never persisted) VTXO-lineage spendability:
`AvailableFinal`, `AvailableProvisional`, `AvailabilityUnknown`,
`LimboReorg`, `LimboConflict`, `Invalidated`. `AvailabilityForState`
maps one batch's `State`; `CombineAvailability` takes the worst across a
multi-parent lineage; `Usable()` is true only for confirmed lineage.
`LineageAvailability`/`LineageBlocked` load each parent batch from the
`Store` and produce the combined availability / block decision the VTXO
manager's admission gate (C5 wiring) calls per candidate. The gate is
permissive: unseen / not-yet-registered lineage does not block — only
limbo/invalidated lineage does.
- `Store` — behavior-free durable query/update interface. Implemented by
`db.BatchCanonicalityPersistenceStore` over the `000020`/`000021` schema;
backfilled from existing VTXOs via
`db.BatchCanonicalityPersistenceStore.BackfillFromVTXOs`.
- `Manager` — the actor that interprets chain observation into canonicality
state (the sole client-side interpreter). Registered under
`ManagerServiceKey`. `RegisterBatchRequest` arms one reorg-aware
confirmation watch on the batch tx and one reorg-aware spend watch per
consumed input (deduped per batch, idempotent — repeats merge dependent
VTXOs). It maps chainsource `ConfirmationEvent`/`ConfReorgedEvent`/
`ConfDoneEvent` and `SpendEvent`/`SpendReorgedEvent`/`SpendDoneEvent` onto
its own mailbox and derives `State` per the priority
`conflict_finalized > conflict_provisional > reorged_out >
finalized/provisional > unseen`. `Reconcile` re-arms watches for non-final
batches after restart without downgrading persisted state.
`GetBatchStateRequest` reads the persisted record. `NewManager` returns the
behavior; the caller registers it, then calls `SetSelfRef(ref.TellRef())`
and `Reconcile`.

## Relationships

- **Depends on**: `btcd/chaincfg/chainhash`, `btcd/wire`, `lnd/fn/v2` only.
- **Depended on by**: `db` (concrete store), and — in later tasks — the
batch canonicality manager and `vtxo` admission.

## Invariants

- Identity is by txid / outpoint, never by `(txid, block hash)`.
- Expiry is never persisted as a standalone or terminal value; it is always
derived from `CSVExpiryDelta` + the current confirmation observation.
- State enum integer values are append-only (persisted column).

## Expiry-as-terminal audit (darepo#454 C2)

C2 requires auditing every site that treats `BatchExpiry`/`Expired` as a
one-way terminal fact. These are flagged for rework when the
BatchCanonicalityManager (task C3/C4) rewires expiry consumers onto
`Record.EffectiveExpiry()`; **no behavior is changed by C2**:

- `vtxo/transitions.go` (`ExpiryStatusExpired → FailedState{Recoverable:
false}`, and the Critical/Expired escalations) — the primary offender: a
reorg that lowers the confirmation height could otherwise push a VTXO
permanently into non-recoverable `Failed`.
- `vtxo/expiry.go` (`CheckExpiry`, `BlocksUntilExpiry`) — compute from the
frozen absolute `vtxo.BatchExpiry`; must consume effective (recomputable)
expiry instead.
- `vtxo/actor.go` — schedules on the frozen absolute `BatchExpiry`.
- `waved/vhtlc_recovery_target.go` — folds multiple roots into a
most-restrictive absolute `batchExpiry`.
- `unroll/proof_assembler.go` (`BatchExpiry == 0`) — treats zero as "unset",
not terminal; benign, documented for completeness.

## Deep Docs

- [ARCHITECTURE.md](../ARCHITECTURE.md) — System-wide package map.
</content>
Loading
Loading