Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ package may import from a higher layer.
| [`lib/tx/checkpoint`](lib/tx/checkpoint/) | Checkpoint PSBT construction for OOR transfers |
| [`lib/tx/oor`](lib/tx/oor/) | OOR submit/finalize package builders and validators |
| [`lib/tx/psbtutil`](lib/tx/psbtutil/) | PSBT encoding, decoding, and signature attachment helpers |
| [`lib/recovery`](lib/recovery/) | Immutable recovery proof graph, session state machine, TLV codec for unilateral exit |
| [`unrollplan`](unrollplan/) | Pure dependency-resolution planner driving unilateral-exit broadcast/sweep ordering |

### Layer 2: Infrastructure (Chain, Storage, Messaging)

Expand Down
62 changes: 62 additions & 0 deletions lib/recovery/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# lib/recovery

## Purpose

Pure, immutable proof graph plus per-session planning state for unilateral
exit / recovery of a VTXO target outpoint. The package exposes the data model
(proof, session, durable state) and a TLV codec for crash-safe persistence;
actual broadcast orchestration lives downstream in later PRs.

## Key Types

- `Proof` — Immutable recovery graph: target outpoint, csv delay, topologically
layered transaction nodes, parent/child adjacencies, reachability-checked.
- `Node` / `NodeKind` — One recovery transaction and its role (tree /
checkpoint / ark).
- `Session` — Mutable planning object driven by caller-reported observations
(`MarkBroadcasted`, `MarkConfirmed`, `MarkFailed`). Goroutine-safe via
`sync.RWMutex`.
- `Snapshot` / `SessionStatus` — Caller-facing view of session progress at a
block height, including CSV maturity and ready/blocked frontiers.
- `SessionState` — Durable caller-owned state suitable for TLV persistence.
Optional fields use `fn.Option` instead of nilable pointers.
- `ComputeMaturityHeight` — Overflow-safe `targetConfirmHeight + csvDelay`
helper shared with `unrollplan`.

## Relationships

- **Depends on**: `lib/arkscript` (AnchorPkScript detection on nodes),
`lib/tree` (generic BFS `Queue[T]` for iterative ancestor traversal),
`github.com/lightningnetwork/lnd/fn/v2` (Option type),
`github.com/lightningnetwork/lnd/tlv` (state / proof codec).
- **Depended on by**: `unrollplan` (pure planning layer; re-uses
`Proof`, `Node`, `ComputeMaturityHeight`). Later recovery PRs (3/5, 4/5,
5/5) will consume the codec for checkpoint persistence.

## Invariants

- `csvDelay` is a raw block count (not a BIP-68-encoded sequence) and is
capped at `MaxCSVDelay` (65535, the BIP-68 height-mode limit).
- `len(nodes)` is capped at `MaxProofNodes` to bound the cost of graph
validation against adversarial inputs.
- Every node in a `Proof` is reachable (via parents) from the target outpoint;
unreachable nodes fail construction.
- Parent/child reachability traversal uses an iterative BFS (`tree.Queue`), so
a deeply-adversarial graph cannot blow the goroutine stack.
- Every `MarkConfirmed` call requires prior `MarkBroadcasted`, all parents
confirmed, a non-negative height, and refuses re-confirmation at a
different height. A same-height re-confirmation is idempotent.
- `MarkFailed` refuses to overwrite an existing terminal failure so the root
cause survives across a restart.
- `Session` methods are safe for concurrent use under `RWMutex`; internal
helpers assume the caller already holds the lock.
- The TLV codec is canonical (sorted by raw hash bytes) and carries an
explicit version byte; version mismatch is a hard decode error.
- `parseHash` via `chainhash.NewHashFromStr` is intentionally absent: raw
32-byte hashes are encoded directly to avoid the short-form / zero-pad
attack surface that JSON shipping with `chainhash.Hash.String()` would open.

## Deep Docs

- [ARCHITECTURE.md](../../ARCHITECTURE.md) — System-wide package map.
- [lib/CLAUDE.md](../CLAUDE.md) — Parent lib package overview.
62 changes: 62 additions & 0 deletions lib/recovery/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# lib/recovery

## Purpose

Pure, immutable proof graph plus per-session planning state for unilateral
exit / recovery of a VTXO target outpoint. The package exposes the data model
(proof, session, durable state) and a TLV codec for crash-safe persistence;
actual broadcast orchestration lives downstream in later PRs.

## Key Types

- `Proof` — Immutable recovery graph: target outpoint, csv delay, topologically
layered transaction nodes, parent/child adjacencies, reachability-checked.
- `Node` / `NodeKind` — One recovery transaction and its role (tree /
checkpoint / ark).
- `Session` — Mutable planning object driven by caller-reported observations
(`MarkBroadcasted`, `MarkConfirmed`, `MarkFailed`). Goroutine-safe via
`sync.RWMutex`.
- `Snapshot` / `SessionStatus` — Caller-facing view of session progress at a
block height, including CSV maturity and ready/blocked frontiers.
- `SessionState` — Durable caller-owned state suitable for TLV persistence.
Optional fields use `fn.Option` instead of nilable pointers.
- `ComputeMaturityHeight` — Overflow-safe `targetConfirmHeight + csvDelay`
helper shared with `unrollplan`.

## Relationships

- **Depends on**: `lib/arkscript` (AnchorPkScript detection on nodes),
`lib/tree` (generic BFS `Queue[T]` for iterative ancestor traversal),
`github.com/lightningnetwork/lnd/fn/v2` (Option type),
`github.com/lightningnetwork/lnd/tlv` (state / proof codec).
- **Depended on by**: `unrollplan` (pure planning layer; re-uses
`Proof`, `Node`, `ComputeMaturityHeight`). Later recovery PRs (3/5, 4/5,
5/5) will consume the codec for checkpoint persistence.

## Invariants

- `csvDelay` is a raw block count (not a BIP-68-encoded sequence) and is
capped at `MaxCSVDelay` (65535, the BIP-68 height-mode limit).
- `len(nodes)` is capped at `MaxProofNodes` to bound the cost of graph
validation against adversarial inputs.
- Every node in a `Proof` is reachable (via parents) from the target outpoint;
unreachable nodes fail construction.
- Parent/child reachability traversal uses an iterative BFS (`tree.Queue`), so
a deeply-adversarial graph cannot blow the goroutine stack.
- Every `MarkConfirmed` call requires prior `MarkBroadcasted`, all parents
confirmed, a non-negative height, and refuses re-confirmation at a
different height. A same-height re-confirmation is idempotent.
- `MarkFailed` refuses to overwrite an existing terminal failure so the root
cause survives across a restart.
- `Session` methods are safe for concurrent use under `RWMutex`; internal
helpers assume the caller already holds the lock.
- The TLV codec is canonical (sorted by raw hash bytes) and carries an
explicit version byte; version mismatch is a hard decode error.
- `parseHash` via `chainhash.NewHashFromStr` is intentionally absent: raw
32-byte hashes are encoded directly to avoid the short-form / zero-pad
attack surface that JSON shipping with `chainhash.Hash.String()` would open.

## Deep Docs

- [ARCHITECTURE.md](../../ARCHITECTURE.md) — System-wide package map.
- [lib/CLAUDE.md](../CLAUDE.md) — Parent lib package overview.
63 changes: 63 additions & 0 deletions lib/recovery/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Package recovery models the data plane of unilateral-exit recovery for one
// VTXO target outpoint.
//
// # Mental model
//
// A "recovery proof" is the set of transactions a user must broadcast, in
// dependency order, to unilaterally materialize an on-chain output they own
// inside an Ark tree (or OOR lineage) — and, after a CSV timeout, spend it.
// Conceptually the proof is a DAG:
//
// roots (self-funded by the user)
// │
// ▼
// tree / checkpoint intermediates
// │
// ▼
// target node (creates the spendable outpoint)
// │
// ▼
// (CSV delay elapses)
// │
// ▼
// sweep (spends target outpoint to a destination)
//
// This package is deliberately narrow: it owns the graph (`Proof`), the
// per-session state machine (`Session`), and the durable projection of that
// state (`SessionState`) plus a TLV codec in state_codec.go / proof_codec.go.
// It does NOT:
//
// - broadcast transactions
// - talk to a chain backend
// - schedule retries
// - spawn goroutines
//
// All of those concerns live in downstream consumers (the planner in
// `unrollplan`, and the actor wiring that follows in later PRs in the stack).
// Keeping recovery I/O-free and synchronous makes the data model amenable to
// property-based testing and lets consumers pick their own reliability
// mechanics.
//
// # Layering
//
// Every node's position is precomputed by a Kahn-style topological layering
// so consumers never have to recurse over the DAG themselves; they iterate
// layer-by-layer from roots to the target and ask the session which nodes at
// each layer are ready, in flight, awaiting confirmation, or blocked.
//
// # Invariants worth knowing
//
// - CSV delay is a raw block count bounded by MaxCSVDelay (BIP-68 height-
// mode limit, 65535 blocks). Any caller who sources the delay from a
// BIP-68-encoded sequence must decode the block count first.
// - NewProof rejects: nil nodes, duplicate txids, a target not in the
// graph, an out-of-bounds target output index, unreachable nodes
// (nodes that cannot be connected to the target via the parents map),
// and cycles.
// - Session is goroutine-safe under an RWMutex. MarkConfirmed enforces the
// full topological invariant (parents confirmed before children) so that
// a reorg-aware caller cannot accidentally corrupt the session.
// - SessionState validation is symmetric with the Session state machine —
// a persisted state that would have been rejected by MarkConfirmed is
// also rejected by NewSessionFromState.
package recovery
Loading
Loading