Skip to content

fix(prevrandao): bring the seed fork up to its siblings' rollout scaffolding - #1319

Open
MavenRain wants to merge 8 commits into
mainfrom
fix/1310-prevrandao-scaffolding
Open

fix(prevrandao): bring the seed fork up to its siblings' rollout scaffolding#1319
MavenRain wants to merge 8 commits into
mainfrom
fix/1310-prevrandao-scaffolding

Conversation

@MavenRain

@MavenRain MavenRain commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Closes #1310. (Cantina #9)

Problem

#1249 (fix #1247) moved the mix_hash/PREVRANDAO derivation behind a fork gate, but did not land the rollout scaffolding the repo wraps around every epoch-gated fork, established twice over by the seed-signature (#1032) and multi-workers (#554) forks:

  • The startup fork-schedule log omits PREVRANDAO_FORK_EPOCH, so operators cannot diff the compiled schedule across the fleet before an arming PR; the non-adiri arm's genesis-active message omits the fork entirely.
  • TN_PREVRANDAO_FORK_EPOCH exists in code but nothing sets it: the Makefile declares and exports only the two sibling variables, and the e2e harness pins only those two on spawned nodes, so the seeded derivation is unreachable in every default e2e lane. Worse, on non-adiri builds arming the seed fork alone silently arms PREVRANDAO too (prevrandao_seed_active conjoins the two gates, and the PREVRANDAO fork point is genesis-active off adiri), breaking the harness's documented arm-independently property.
  • No test pins the arm switch to the constant: every prev_randao test builds its leader at epoch 0, so each build compiles and runs exactly one arm selected by the feature set, and the fail-closed conjunction has zero direct coverage.
  • The engine's basefee-penalty test hardcodes the post-fork oracle and fails under --features adiri, where the fork is dormant and the fixture leader sits at epoch 0. CI cannot see the failure because the adiri test lane omits tn-engine, violating the lane's own stated rule.
  • docs/src/evm-compatibility.md and the tn-reth README still describe only the legacy XOR, and nothing at the EVM-visible surface records the accepted last-actor bias.

Changes

  • crates/telcoin-network-cli/src/node.rs: log prevrandao_fork_epoch in the adiri fork-schedule line; name prevrandao_seed in the non-adiri genesis-active message.
  • crates/types/src/forks.rs: carry the siblings' fleet-diff rationale and the accepted-bias note on PREVRANDAO_FORK_EPOCH; new conjunction pin prevrandao_stays_legacy_while_seed_fork_is_dormant, which pins the seed fork to never-fires and the fork point to genesis in its own process (loud OnceLock-latch guards, anti-vacuity assertion that the fork point is actually active) and asserts every seed-dormant epoch stays on the legacy arm.
  • Makefile: declare TN_PREVRANDAO_FORK_EPOCH ?= 4294967295 (dormant) and export it on exactly the recipe lines that pin the two siblings, never a bare --workspace run, so the override's inertness test keeps a clean process. The comment documents the deliberate two-knob arming: TN_SEED_SIGNATURE_FORK_EPOCH=0 TN_PREVRANDAO_FORK_EPOCH=0 make test-e2e.
  • crates/e2e-tests/src/lib.rs: add the variable to the harness pin loop so spawned nodes default it to dormant, with the conjunction documented. A seed-armed lane no longer arms PREVRANDAO as a side effect.
  • crates/engine/tests/it/main.rs: the hand-composed mix-hash oracle in test_simple_basefee_penalty now dispatches on prevrandao_seed_active, the same predicate production dispatches on, recomposing by hand whichever derivation the build's fork schedule selects (legacy XOR under adiri at epoch 0, seeded elsewhere).
  • etc/ci-lanes.sh: tn-engine joins the adiri lane (-p tn-engine, tn-engine/adiri), so the pre-fork legacy arm actually runs in CI.
  • crates/types/src/primary/output.rs: fork-boundary keeper prev_randao_flips_arms_exactly_at_the_fork_epoch derives both grid points from the constant, so an arming PR retargets it with no edit. Under adiri the legacy XOR must replay byte-identically at PREVRANDAO_FORK_EPOCH - 1 and the seeded derivation must fire at the fork epoch itself (>=, not >); without adiri every epoch must take the seeded arm. Anti-vacuity tripwires fail loudly on ambient overrides.
  • Docs: docs/src/evm-compatibility.md gets the fork-gated mix_hash row plus a "Deriving the mix_hash field" section with indexer guidance (branch on the block's epoch, recoverable from the upper 32 bits of nonce; the seed chain value advances per commit, with the closing commit's value published as extra_data on the epoch's closing block). The tn-reth README row is updated to match, and the bias note lands on the derivation docs, the fork constant, the payload attribute, and the EVM block module.

Threat model

The seeded derivation closes payload grinding: every input (domain tag, digest-pinned seed chain value, consensus block number, batch index) is fixed by the committed order, so a leader cannot generate candidate values by re-cutting its payload. The gate is fail-closed: the seed_signature_active conjunct means a mis-ordered fork schedule keeps the status quo instead of promoting the forkable legacy leader-aggregate seed into PREVRANDAO, and the new conjunction test observes that ordering directly. What remains, and is now recorded at every reader-facing surface, is the accepted last-actor residual: the committing leader sees every value its commit will produce before broadcasting and keeps one propose-or-withhold choice per commit, so contracts needing unbiasable randomness must not use PREVRANDAO alone. No consensus behavior changes in this PR: the gate stays dormant on adiri (Epoch::MAX placeholder) and the non-adiri derivation is unchanged.

Testing

  • cargo +nightly-2026-03-20 fmt --all -- --check green.
  • Local battery (serialized via tnquiet, log ~/Documents/tn-1310-gate.log): scoped cargo check --all-targets over the five touched crates; the prevrandao/basefee test filter under default features and again under tn-types/adiri,tn-engine/adiri . . . the arm CI could not previously see; and a mutation-confirm that drops the seed conjunct from prevrandao_seed_active and watches the new conjunction test fail, with a compile pre-check so a broken mutant cannot masquerade as caught.
  • CI on push runs the default lane plus the adiri lane, which now includes tn-engine.
  • The pinned cargo +1.94 attest runs on the second machine post-push.

…folding

Closes #1310.

- log PREVRANDAO_FORK_EPOCH in the adiri startup fork schedule and name
  prevrandao_seed in the non-adiri genesis-active message
- wire TN_PREVRANDAO_FORK_EPOCH as a third sibling: Makefile default
  (dormant), exported on the fork-pinned lanes only, and added to the e2e
  harness pin loop, so a seed-armed lane no longer arms PREVRANDAO as a
  side effect and a seeded lane arms both knobs deliberately
- dispatch the engine basefee-penalty mix-hash oracle on
  prevrandao_seed_active, recomposing whichever derivation the build's
  fork schedule selects (fixes the red adiri run), and add tn-engine to
  the adiri CI lane so the pre-fork legacy arm runs somewhere
- fork-boundary keeper deriving both grid points from the constant
  (adiri: legacy XOR byte-identical below the fork, seeded at the fork
  epoch; non-adiri: seeded everywhere) with anti-vacuity tripwires, plus
  a conjunction pin: seed-dormant epochs stay legacy regardless of the
  fork point
- document the fork-gated derivation with indexer guidance and the
  accepted last-actor bias in docs/src/evm-compatibility.md, the tn-reth
  README, and at the derivation sites

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
@MavenRain MavenRain self-assigned this Aug 30, 2026
…caffolding

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…caffolding

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…caffolding

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>

# Conflicts:
#	crates/telcoin-network-cli/src/node.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant