Documentation
¶
Overview ¶
Command repair-proposervm is a ONE-TIME, fail-closed surgical tool to reconcile a proposervm chain-state DB onto a known-canonical outer block at a single height.
Motivation (incident 1082814, Lux mainnet C-Chain): a sub-quorum proposervm envelope A (3-of-5 ACCEPT) was locally accepted on some nodes while the network finalized the supermajority sibling B (4-of-5) that wraps the IDENTICAL inner EVM block. The two siblings differ ONLY in the proposervm outer envelope; the inner EVM state is byte-identical (no EVM divergence). On restart those nodes re-seed finality from their persisted proposervm lastAccepted=A and fatal on B's cert (EQUIVOCATION). This tool swaps the persisted record of `height` from A to the canonical B, leaving the inner EVM completely untouched (no EVM rollback).
It is NOT a blind hex edit: it opens the exact same typed proposervm state (luxfi/node/vms/proposervm/state) over the exact same nested keyspace luxd uses (chainID -> "vm" -> "proposervm" -> versiondb -> chain/block/height), and writes via the state's own PutBlock / SetBlockIDAtHeight / SetLastAccepted so the on-disk bytes are identical to what luxd itself wrote for B on the canonical node.
proposervm invariant honored: proLastAcceptedHeight must never be < the inner VM's last-accepted height (vm.repairAcceptedChainByHeight). The inner EVM is at `height` (it accepted the shared inner block under A), so the recovery target is the canonical block AT `height` (B), never height-1 — keeping outer==inner height.
Modes:
inspect : read-only. Print lastAccepted, height index at H and H-1, and the
outer block currently recorded at H.
export : read-only. Read the outer block recorded at H and write its raw
stateless bytes to --block-file (run against a canonical node's DB).
repair : read-write, fail-closed. Parse --block-file (canonical B), assert it is
the expected block, assert the DB is in the expected bad state (lastAccepted
and heightIndex[H] both == the expected sub-quorum block A, and B's parent
== heightIndex[H-1]); then PutBlock(B), SetBlockIDAtHeight(H,B),
SetLastAccepted(B), Commit. Idempotent: if already on B, it no-ops.
The DB uses an exclusive LOCK; luxd MUST be stopped on the target before `repair`.