Skip to content

Deferred Execution (Weigh on Possession, Execute on Selection)

Status: implemented design rationale. The current implementation is HeaderSync plus BodyPipeline; some component names and measurements below describe the earlier prototype. See body acquisition for the concise current boundary.

Any peer may lie, withhold, stall, or be honestly pruned; a withholding miner is normal operation, not an edge case. Security comes from per-item verification plus objectively comparable work, under partial synchrony. The one non-peer trust edge is the node’s own configured immediate parent. Publishing a block’s bytes is, and remains, the price of being weighed: this design never weakens the network’s data-availability enforcement, which today lives — easily missed — inside the import gate itself.

Acceptance is one gate today: a block enters the accepted graph and contributes fork-choice weight only after its body is fetched, every transaction resolved, and its state transition executed and checked against the declared post-state. The gate is sound (nothing invalid is ever accepted) but it prices weighing a block at the cost of using it.

The expensive parts are not the bytes. A block’s own volume is skeleton-sized (~1 KB), and a losing sibling’s transactions are largely the same content-addressed objects as its canonical rival’s — already held, deduplicated by CID. What each accepted block actually costs is the state execution, the materialized state it durably stores, and its occupancy of the single sequential import lane. On the live testnet child, ~74% of the accepted graph is losing same-height siblings: the majority of execution and state-storage work is spent on blocks whose only consensus contribution is their weight — which never required executing them. (On a fork-free chain this design saves nothing; every block is spine and must be executed before anyone builds on it.)

A node weighs a block when it possesses and structurally verifies it, and executes it only when the block matters. Two tiers of belief:

  • Weighed: the node holds the block’s header and has verified everything a header proves: the proof of work over it, parent linkage, and for a child block its securing-work proof and reference-level continuity. The header alone gives weight - the work commits to the body by its root and does not need it - so a block whose body the node does not hold is weighed all the same. Its declared post-state is recorded as an unverified claim.
  • Validated: the state transition has been executed and the declared post-state checked. Only now may the node use the block — build templates at its tip, serve its state, or issue the parent-side continuity facts children consume. The complete acted-on set, which additionally covers asserting a head externally, is enumerated under Pivotality below.

Execution runs when a block becomes load-bearing, under two triggers:

  • Selection: fork choice selects a branch; the node validates it forward from its last validated ancestor before producing on or serving its tip. What the node acts on meanwhile is the act-on tip, the heaviest executed tip: descend from genesis and, at each fork, follow the heaviest child that is executed (heaviest by its whole subtree’s header work); stop where no child is executed. A heavier branch takes over at its fork once its first block there is executed - until then the node keeps acting on the branch it has validated, and does not retreat to the fork point on headers alone. A block whose body the node does not hold is weighed and unvalidated, never invalid.
  • Pivotality: everything the node acts on — exhaustively: building templates, serving state, issuing continuity facts, and asserting a head externally (head announcements, read and status views of the canonical tip — never possession inventory, which advertises what the node holds and is governed by the possession boundary, not by pivotality) — is the act-on tip and its ancestry, all executed. Unvalidated weight ranks and is never itself acted on: it chooses which executed child is followed at a fork, and a block that is not executed is never stepped into, however heavy. A block that could change the act-on tip is therefore one that matters: at each fork on the chain the node acts on, every child heavier than the executed one it follows — at the tip, every child — is asked for and executed when its body is held. Whichever executes is followed; a heavier one is followed once it executes. Nothing tells the node that a body will never come, so no wait ends in a verdict and a body not held holds nothing up: the node keeps acting on what it has executed. The asymmetry that makes deferral work is structural: a losing sibling sits on the losing side at its own fork base and never needs executing — so the measured majority is deferrable by construction. The cost is at forks: each alternative that arrives heavier than those executed before it is executed too, so a fork whose children arrive lightest first executes more than one of them. A stale sibling’s descendants are not executed unless it becomes the tip.

Invariants:

  • Acted-on decisions are uniform over obtained bytes. Every decision a node acts on is identical to that of a node which validated every block whose bytes it obtained. Computed weight includes work beneath blocks a validating node has excluded — as does that node’s own, since exclusion removes no weight (spec §9.9); it weighs, and no excluded or unexecuted block is ever acted on. A validate-at-import node and a deferred-execution node therefore differ only in when work is examined, never in any decision either acts on, and the two interoperate on one network.
  • Deferral without the seam is forbidden. No implementation may count weight it has not validated unless it can exclude what it later proves invalid; deferring execution is only safe together with the exclusion seam below.
  • Availability never judges. Execution invalidity is recorded only from a complete judgment: every named input resolved and verified against its CID, the transition executed, the declared post-state mismatched (or a chain-committed validity rule failed). The verdict is a pure function of content-addressed inputs, identical on every node that reaches it regardless of which prefix that node validated. Failure to obtain an input is never a verdict — it is an availability gap, retried indefinitely, excluding nothing. The durable record of an exclusion is the evidence of the failing check, sufficient to re-derive the verdict, and the inputs that proved it are retained as an obligation — an unre-derivable marking is not a judgment. Anything short of a completed deterministic check is retryable, never recorded.
  • Exclusion is chain-local and never touches work. A proven execution-invalid block’s subtree keeps its weight — work weighs, validity selects (spec §9.9) — but the excluding chain’s own canonical descent never steps into it, so nothing at or below it is selected, extended or attested; the exclusion is a staged, durable, replayed fact like any other, so a restart selects the same head. A securing grind’s contribution to a child chain is independent of carrier validity, so the work facts the chain serves are likewise unaffected, and the runs it serves only grow (spec §9.10). Nothing is ever pruned — verified work facts and invalidity evidence are both kept forever; exclusion changes where the chain’s own descent may step, not what it weighs, knows or serves.
  • Children consume continuity from the validated subgraph only. A weighed block’s declared post-state is an unverified claim, so parent-state continuity paths are computed over validated blocks alone. Carrier securing-work proofs are exempt — carriers need not be valid, by spec. The binding constraint is narrower than it first appears: a child binds to its carrier’s pre-state, which is the carrier’s predecessor’s post-state — so a carrier that forks one block off the validated spine already names a state the parent holds. The residual coupling: a parent that tracks a child validates the branch prefixes that child’s carriers name, regardless of the parent’s own fork choice — the same tracked-child obligation eviction already carries. For untracked children, the general guarantee only. The cost of this coupling is liveness for deep-off-branch carriers, never child safety.
  • Child deployments must not defer silently. A child genesis anchored in an unselected carrier is real — canonicity never changes child validity — and discovering it requires the carrier’s transaction bodies. Whatever surface serves child discovery must account for every possessed block, not only the validated spine.

The prior “declining work cheaply” companion (operator-finality) is narrowed, not subsumed: this design removes execution and state storage for unselected blocks, but weighing a child block still requires soliciting its securing-work proof, so the per-sibling round trip that companion worries about is unchanged and remains the open question, under the boundaries operator-finality states.

The three designs compose: eviction is weight-preserving (operator-finality), transport is an ordered stream (bulk-sync-stream), and execution is deferred to load-bearing blocks — weigh what you hold, move bytes in order, execute exactly what matters.

  • Weight comparison and heaviest selection are untouched. What changes is what acceptance asserts: the accepted graph records possessed, structurally-verified work; validity becomes a second, recorded, deterministic judgment. The consensus spec must be amended explicitly, at minimum: the meaning of “accepted” in the effective-work invariant (and its rider that exclusion removes no weight while the descent never steps into an excluded block); the never-prunes invariant’s rider that exclusion is not pruning and excluded facts remain served; the import procedure’s split into per-tier gates with per-tier durability ordering, the invalidity marking included; and continuity’s “connected accepted graph” becoming the validated subgraph. The sibling fork-choice doc’s description of deferred execution as “consensus-neutral” is corrected by the same change — this is consensus-adjacent and is treated with that gravity.
  • Data availability never judges and never un-weighs: a withheld branch keeps its header weight and is simply never acted on, because the act-on tip follows executed blocks only. Honest work chains on the act-on tip meanwhile, so withholding buys what a secret chain already buys and no more.
  • Recovery and boot invariants hold per tier; no index, page, or advertisement promises a tier the node has not reached for that block. Served surfaces already advertise possession truthfully — possession and acceptance coincide at the weighed tier by construction.
  • Serving a block whose bytes match their CID is never a serving fault, regardless of whether its transition later proves invalid — minting a PoW-valid, execution-invalid block is available to anyone, so relaying one attributes nothing to the relayer. Attribution is reserved for bytes that do not match what was advertised.
  • Durable formats evolve additively or by flag day per the standing compatibility constraint; the interoperability claim above is behavioral (same decisions, different timing), not a license to skip that discipline.

Source: lattice-node/docs/design/weight-first-acquisition.md at 9abd15b1bb05