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.
Sync trust boundary
Section titled “Sync trust boundary”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/submitBlockneed 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 noassumevalid: 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