Repository navigation
fix(genesis): install canonical EIP-2935 block hash history - #74
Conversation
dghelm
left a comment
There was a problem hiding this comment.
@shu-unifra Codex and Claude both reviewed this, and the fix is correct and matches dogeos-reth. From block 1 the system call records the parent hash (block 1 stores the genesis hash), and post-Feynman BLOCKHASH reads the same ring. Without this account, BLOCKHASH returns 0 on a chain that starts with Feynman active, so this is an opcode fix as well as a history fix. Testnet's manually deployed account already matches the canonical runtime (same code hash) with nonce 1, so the new verify check passes there.
Mainnet genesis gets regenerated before the Oct 13 rehearsal, so we'd like to merge this soon. Three small changes first:
- Say explicitly, in the PR and in
docs/eip-2935.md, that adding the account changes the mainnet genesis hash. The reth mainnet genesis JSON,DOGEOS_MAINNET_GENESIS_HASHand its test, the rollup-node chain spec, and the release images and tag must be regenerated together, alongside the other pending mainnet genesis fixes. verify-contracts.js: warn instead of failing when the history account is missing, so older devnets that predate it still verify. Keep failing on a wrong runtime or nonce.- Fix the broken Markdown table in
docs/eip-2935.md(around line 15).
Follow-up, not blocking: the essential change is about 40 lines: the runtime constant, the genesis install and its tests, and the generator-entrypoint check (keep that one, since the Forge harness installs the account directly and wouldn't catch a missing call in the generator). These could move to a separate tooling and docs PR:
- the geth Go integration test (no
go.modhere, and it doesn't run in CI); - the RPC checker and its tests;
- most of the 183-line doc, which covers activation on existing networks.
Separately, the CI genesis-only check cold-compiles everything. Linking artifacts/ and cache/ into its staging dir should save a few minutes.
The fresh-chain genesis template enables Feynman at timestamp zero, but the generated alloc omitted its EIP-2935 history account. Both inspected DogeOS execution client families already issue the pre-transaction system call after Feynman, so a missing account leaves L2 hash history empty and makes post-Feynman
BLOCKHASHreturn zero. From block 1 the system call records the parent hash (block 1 stores the genesis hash), andBLOCKHASHreads the same history ring.Install the canonical 83-byte runtime at
0x0000F90827F1C53a10cb7A02335B175320002935, with nonce 1 and empty history. Pin its code hash in tests and install it directly without a proxy. The existing fork schedule is unchanged.This changes the mainnet genesis state root and genesis hash. Before the October 13, 2026 mainnet rehearsal, regenerate the Reth mainnet genesis JSON,
DOGEOS_MAINNET_GENESIS_HASHand its test, the rollup-node chain spec, and the release images and tag together, alongside the other pending mainnet genesis fixes. All must refer to the same regenerated genesis.Add runtime and genesis serialization tests, a reproducible generated-genesis/geth StateProcessor integration test, and a read-only RPC smoke checker that can compare history, block hashes, and state roots across endpoints. State reads use EIP-1898 canonical block-hash snapshots, the expected parent is anchored to the selected header, and a final header check rejects reorgs during verification. Unsupported snapshot requests fail without a height-based fallback. The Foundry CI job runs both the RPC regression tests and the actual
gen-configs.shimage entrypoint with output-account assertions on the final YAML artifact (including frontend and address exports); the latter does not require a client checkout and linksartifacts/andcache/into staging to reuse the preceding CI build. Document the audited client revisions, system-call requirements, and separate installation paths for fresh and existing networks.The existing
docker/scripts/verify.shworkflow also checks the canonical history account usingcast. It compares exact runtime and nonce 1 at a single block hash, independently of deployment-address configuration. Missing code (0x) emits a warning and skips the history runtime/nonce check so older devnets can still verify. Noncanonical runtime, an installed account with a nonce other than 1, or an RPC failure still causes a nonzero exit. A missing history account does not hide other source-verification failures. This is explicitly reported as runtime/account validation; the Solidity constants library is not submitted as the history runtime's explorer source.Validation:
Dockerfile.baseand all three runtime Dockerfiles locally on linux/amd64 using the pinned Foundry v1.3.0-rc2. Ran the default gen-configs ENTRYPOINT and validated its actual YAML artifacts; ran the deploy ENTRYPOINT with real simulation/broadcast against a disposable Anvil initialized from that genesis; verified deployed contracts and preservation of the history account. Ran the verify image ENTRYPOINT with real cast and mocked explorer submissions: canonical account succeeds; missing runtime and wrong nonce fail. No image was published and no live network was used.python3 -m unittest discover -s scripts/integration -p 'test_block_hash_history.py' -v: 5 passed; the original smoke checker fails the new regression tests.python3 scripts/integration/test-eip2935.py --genesis-only: passed. RemovingsetBlockHashHistory()from the production genesis entry point in an isolated copy makes this check fail with a missing-account error. Both checks are wired into Foundry CI.forge test --evm-version cancun: 556 passed, 0 failed.python3 scripts/integration/test-eip2935.py --geth-repo /path/to/go-ethereum, using revision33c46866196da34484bf1bfbaab20aa4929b3fcc: passed. Runs the generator image entrypoint in a temporary directory, unwraps its genesis YAML for geth; signed user transactions in blocks 1 and 2 read the correct parent through the history contract and BLOCKHASH. Block gas equals receipt gas.node --test docker/scripts/verify.test.js: 16 passed after the compatibility update. Covers protocol address, fixed-block reads, missing-account warnings, malformed/noncanonical bytecode, wrong nonce, RPC failures, and preservation of source-verification failures when history is missing.git diff --check: passed.Scope and remaining release gates:
--testpath without the test-utils build support; no successful Reth integration run is claimed.Refs #73. Keep the issue open for the remaining client/network acceptance criteria.