Skip to content

perf(anvil): batch mine empty blocks to skip redundant state root computation#13432

Open
0xMars42 wants to merge 2 commits intofoundry-rs:masterfrom
0xMars42:perf-batch-mine-empty-blocks
Open

perf(anvil): batch mine empty blocks to skip redundant state root computation#13432
0xMars42 wants to merge 2 commits intofoundry-rs:masterfrom
0xMars42:perf-batch-mine-empty-blocks

Conversation

@0xMars42
Copy link
Contributor

@0xMars42 0xMars42 commented Feb 13, 2026

anvil_mine(n) calls do_mine_block() in a loop — each iteration spins up a TransactionExecutor, commits state, and recomputes the Merkle state_root, even when the pool is empty and there's nothing to execute. For anvil_mine(1000) that's 1000 redundant state root computations.

This adds a mine_empty_blocks() fast path that kicks in when the pool is empty and num_blocks > 1. It reuses the parent's state_root (no state transitions = same root) and builds block headers directly, skipping the EVM entirely. Everything else — base fee decay, blob gas, notifications, state history snapshots — is still computed per-block in the loop so downstream behavior doesn't change. anvil_mine(1) always goes through the existing path.

anvil_mine(1000) with an empty pool completes in ~1.5s (debug build, Windows). Before the optimization this took ~276s as reported in #5499.

Fixes #5499.

…pty_blocks

receipts_root used Default::default() (B256::ZERO) instead of EMPTY_ROOT_HASH
for empty blocks. transactions_root had the same issue but was masked by
create_block() overwriting it. Fix both for correctness.
@0xMars42 0xMars42 force-pushed the perf-batch-mine-empty-blocks branch from 827d549 to b5ec295 Compare February 13, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

perf: anvil_mine is much slower than hardhat_mine

1 participant