Skip to content

Consensus and fork-choice ownership

Consensus is defined by Lattice, not by lattice-node. The canonical rules are the protocol specification and work and fork-choice rationale.

The node owns only the operational boundary around those rules:

  • route facts between co-hosted parent and child levels;
  • acquire the exact sparse evidence Lattice requests;
  • persist accepted fact batches before exposing their effects;
  • retain and replay the same immutable facts after restart;
  • derive each unique grind’s work from its content-addressed child proof; and
  • project the one canonical chain delta returned by Lattice.

Work observations are joined by grind identity before they are totaled. A verified observation of one root whose root hash clears the terminal child’s target credits exactly workForTarget of the root-most target it cleared along that proof, raised if greater by the terminal child’s own (never a max over every cleared target; Lattice 35.0.1, spec §9.5); an observation that does not clear the terminal target credits nothing; one chain-local location holds the strongest such observation; different roots sum. The contribution affects GHOST only after its terminal child is accepted and connected. Parent import and canonicity do not create or remove that physical work; later parent blocks descending from the carrier add their own work to the child block through the carrier’s run (spec §9.10), never to a grind’s contribution. Exact work ties prefer the lexicographically smaller canonical CID of the competing child blocks (Lattice spec §9.4), never arrival order or an incumbent preference.

The node must not implement a second fork-choice metric, recursively choose descendant tips, or send parent canonical-tip commands across the hierarchy plane. It never accepts a work total as fork-choice input. The one report it accepts about work it did not verify is the run report of spec §9.10, and only from its configured immediate parent: the node binds it (own directory, the named child block, one of the carrier’s grinds already credited there), derives the credit itself as runWork − ownWork under an identity keyed by the carrier and directory, applies it only as a strict increase over the value it holds, and never revokes it.

A parent fork that omits a hosted child (commits nothing into its directory) changes no child fork choice. Its blocks join the run of their nearest carrier above the fork, so their work is attributed at that carrier’s child block, a common ancestor of every child fork below it, and to neither fork. The child work carried by the losing parent fork is not revoked: work weighs, validity selects, and the parent is not authoritative over which child fork is canonical. Likewise, a withdrawal anchored on a losing parent fork is valid: an anchor needs parent-state continuity, not canonicity, and consensus fork choice alone settles it. There is no standard confirmation depth. The omission is pinned by CrossLevelForkChoiceTests; the withdrawal by Lattice #79’s CrossChainReorgReplayEdgeTests.

Cold sync pays two independent costs, and only one is cheap:

  • Fork choice (which tip is heaviest) depends only on work facts and DAG shape. Work facts are self-verifying from content-addressed bytes and deduplicated per root-CID grind (ConsensusBlockInput/submitBlock need only the block header and its claimed state CIDs — never the materialized state). Cumulative PoW is objective, so a fresh node determines the heaviest tip with no weak-subjectivity checkpoint — the property a PoW chain has and a PoS chain cannot. Bitcoin-anchoring the golden genesis fixes which genesis out-of-band, which is the one thing raw PoW can’t self-anchor. Together they give a trustless, checkpoint-free fork-choice start.

  • State validity (that postStateCID == STF(prevState, txs)) is not attested by work — a heavier chain can commit a wrong post-state, and content addressing proves only that the producer committed those bytes, never that the transition was honest. So canonical state is re-executed from genesis by design. There is no assumevalid: the only trustless anchor a fresh node possesses is genesis (segment-anchored catch-up trusts a node’s own last-validated context, which a fresh node lacks). This is the trust model, not a missing optimization — and it is why “why can’t we just assumevalid?” has no trustless answer short of succinct validity proofs (a consensus change).

Content addressing does bound the working set, not just integrity: canonical re-execution fetches sparse state slices verified against each block’s prevStateCID, so it never materializes the full historical state — strictly cheaper than an account-trie chain, though CPU over canonical transactions stays inherent. The remaining lever is skipping execution of abandoned forks (place side-fork blocks in the DAG on the cheap work path; execute state only on load-bearing membership). It is NOT consensus-neutral: it changes what acceptance asserts and requires an execution-invalidity exclusion seam — the design, its invariants (availability never judges; only executed blocks are acted on), and the required spec amendments live in weight-first-acquisition.md. Its delicate requirement stands: a heaviest-but-invalid path is always demoted to the heaviest valid one.


Source: lattice-node/docs/design/consensus-fork-choice.md at 9abd15b1bb05