Skip to content

fix: hardcode testnet gap blocks (69038325–69071492) in BlockStore - #166

Merged
sprites0 merged 1 commit into
node-builderfrom
fix/testnet-gap-69038325
Nov 10, 2026
Merged

sprites0 merged 1 commit into
node-builderfrom
fix/testnet-gap-69038325

Conversation

@sprites0

Copy link
Copy Markdown
Collaborator

Summary

Testnet blocks 69038325–69071492 (33,168 blocks) are missing from the hl-testnet-evm-blocks S3 bucket, so nodes syncing from S3 stall at 69038324. This embeds them next to the existing 50927214–50929512 gap (#129). BlockStore serves them the same way, so syncing works on any block source.

  • src/pseudo_peer/testnet_gaps_69038325.json.lz4: lz4-compressed JSON Vec<BlockAndReceipts>, the same format as testnet_gaps.json.lz4 (5.6 MB).
  • patch.rs: loads both embedded files (testnet only, as before).
  • Test embedded_testnet_gaps_chain: checks the range is complete, chains by parent hash, and links to the S3 blocks on both sides.

How the blocks were rebuilt

From another nanoreth node's RPC: eth_getBlockByNumber(full), eth_getBlockReceipts and eth_blockPrecompileData. That node runs in default mode, so system txs are listed. Each block is converted to its DB form and then to BlockAndReceipts::from_db, the same thing hl_syncGetBlock serves.

Checks per block:

  • Hashes: the header re-hashes to the RPC block hash, and every tx (system txs included) re-hashes to its RPC tx hash.
  • Receipts: regular-tx receipts reproduce the header's receipts root, logs bloom and gas used.
  • Chain: the parent-hash chain is pinned to S3 on both sides. It starts at the hash of 69038324 (0x718af9…9b25) and ends at the parent hash of 69071493 (0x3051f8…1f8b).
  • Precompile data: all 23 gap blocks that call read precompiles (found by tracing every block with txs) carry their recorded results.
  • Converter check: the same conversion run on 999 post-gap blocks that do exist in S3 reproduces them exactly.

Verification

  • Sync: synced a testnet archive from 68144874 through the gap to the head. Every embedded block was re-executed and passed post-execution validation.
  • Against QuickNode: balances, nonces and code of 150 random gap-touched accounts, at 69038324, 69055000 and 69071492, match the source node (0 mismatches).
  • Full state against hl-node: compared at 69237903 with a node initialized from hl-node state at 69224874 (via evm-init), covering every account, code and storage slot. The only differences are 79 empty accounts and 2 slots that already differ before the gap (unrelated, tracked separately), so none come from the gap blocks.

🤖 Generated with Claude Code

These 33168 blocks are missing from the upstream testnet S3 bucket. They are
embedded next to the existing 50927214–50929512 gap and served by BlockStore
the same way, so nodes sync through the range on any block source.

The blocks were rebuilt from another nanoreth node's RPC
(eth_getBlockByNumber / eth_getBlockReceipts / eth_blockPrecompileData) and
checked per block: header and tx hashes re-hash to the RPC values, receipts
reproduce the receipts root, logs bloom and gas used, and the parent-hash
chain links to the real S3 blocks on both sides (69038324 and 69071493).
All 23 blocks with read precompile calls carry their recorded results.

Verified by syncing a testnet archive through the range (every block
re-executed and validated) and comparing the full state at 69237903 against
a node initialized from hl-node state at 69224874: no differences caused by
the gap. Data is lz4-compressed JSON (5.6MB).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sprites0
sprites0 merged commit a3fddd1 into node-builder Nov 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant