fix(flashblocks): commit cached state to EVM in execute_with_cached_data#2653
fix(flashblocks): commit cached state to EVM in execute_with_cached_data#2653meyer9 wants to merge 1 commit into
Conversation
When reusing results from a previous flashblock, the cached EvmState was returned but never committed to the EVM database. Subsequent transactions executed freshly (not from cache) then ran against stale state, producing incorrect logs and receipts that diverged from what the final block-building executor produces, causing receipt root mismatches during block validation. Apply the same account pre-loading + commit pattern used by execute_with_evm.
🟡 Heimdall Review Status
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
| for address in state.keys() { | ||
| self.evm.db_mut().basic(*address).map_err(|err| { | ||
| StateProcessorError::Execution(ExecutionError::EvmEnv(err.to_string())) | ||
| })?; | ||
| } | ||
| self.evm.db_mut().commit(state.clone()); |
There was a problem hiding this comment.
execute_with_evm also updates self.state_overrides with balance, nonce, code, and storage diffs for every touched account (lines 293-306). The cached path skips this entirely.
state_overrides is consumed downstream by RPC endpoints (eth_call, eth_estimateGas, eth_simulateV1) via PendingBlocks::get_state_overrides() — see rpc/eth.rs:399, :440, :471. When transactions take the cached path, their state changes won't be reflected in state_overrides, causing RPC simulations against the pending block to run against stale state.
The EvmState has the account info but uses a different structure (revm::state::Account) than what state_overrides expects (AccountOverride with balance/nonce/code/state_diff). The conversion logic from execute_with_evm` (lines 293-306) should be replicated here, or better, extracted into a shared helper to avoid divergence.
Separately: the state.clone() on line 229 clones the entire HashMap<Address, Account> including all storage slots. The EvmState is already consumed by value into ExecutedPendingTransaction, so it cannot be moved here. This is fine for correctness but worth noting as a cost proportional to the number of touched accounts/slots per cached transaction.
Review SummaryThe core fix is correct: committing the cached Finding: Missing
|
When reusing results from a previous flashblock, the cached EvmState was returned but never committed to the EVM database. Subsequent transactions executed freshly (not from cache) then ran against stale state, producing incorrect logs and receipts that diverged from what the final block-building executor produces, causing receipt root mismatches during block validation.
Apply the same account pre-loading + commit pattern used by execute_with_evm.