EIP-2935 Storing past block hashes in state (Serve historical block hashes from state)
Final · EVM & execution · EIP status Final · created 2020-09-03
The hashes of the most recent 8191 blocks are kept in the storage of a system contract as a ring buffer. Because block hashes are inside state, clients that hold only state can also look up and prove past hashes.
Why it is needed
Today's Ethereum Virtual Machine (EVM) assumes that clients always hold recent block hashes directly. However, stateless clients, which will appear in the future, do not store the full state and verify using only the witness data delivered with each block, so this assumption no longer holds. Putting block hashes inside state lets them be delivered in the witness, which solves the problem. Also, directly extending the range the BLOCKHASH opcode can look up (the most recent 256) would change the meaning of existing behavior, but keeping a separate contract storage lets a longer range be offered smoothly while leaving existing behavior untouched. Rollups can also query this contract directly to use a longer hash history, and there is a side benefit that proofs about the most recent 8191 ancestor blocks can be built or verified directly against the current state.
What changes
Today
- The BLOCKHASH opcode can look up only the hashes of the most recent 256 blocks
- Block hashes are outside state, so they are hard to include in the witness used for stateless verification
- Rollups have no good way to check old block hashes on chain
After EIP-2935
- The most recent 8191 block hashes are kept in the system contract storage
- Calling the contract lets you look up past hashes covering about one day
- The behavior and cost of the BLOCKHASH opcode stay exactly the same as before
Who is affected
- Client teams: Clients must implement a system call that records the parent block hash in the contract at the start of processing every block.
- Rollups (L2): They can query the contract directly and use a hash history much longer than 256.
- Contract and app developers: Apps that need to verify past block hashes can check a wider range on chain.
- Ordinary users: Block validation rules change, but nothing breaks in the existing user experience.
In depth
System call during block processing
Every block in which the EIP is active has SYSTEM_ADDRESS (0xfffffffffffffffffffffffffffffffffffffffe) call HISTORY_STORAGE_ADDRESS (0x0000F90827F1C53a10cb7A02335B175320002935) before processing transactions. The input is the 32-byte block.parent.hash, the gas limit is 30_000_000, and the value is 0. It follows the same system operation conventions as EIP-4788, so the call must run to completion, is not counted against the block gas limit, and does not follow the EIP-1559 burn rules. If there is no code at that address, it must fail silently. Clients may also write directly to state instead of making an EVM call, but the EVM call approach is recommended until the Verkle fork. This is to avoid the problem of a misconfigured network where the EIP is activated but the contract is not deployed.
get and set of the history contract
The contract has only two behaviors. The set operation runs only when the caller equals SYSTEM_ADDRESS, and every other call is a get. get takes the block number to look up as a 32-byte big-endian encoded value. It reverts if the calldata is not 32 bytes, and it also reverts if the requested block is outside the lookup range [block.number-HISTORY_SERVE_WINDOW, block.number-1]. set stores the block.parent.hash received as calldata in the slot block.number-1 % HISTORY_SERVE_WINDOW. The original text also specifies the exact EVM assembly bytecode that implements this logic.
Why the ring buffer size is 8191
The storage structure is a ring buffer of length HISTORY_SERVE_WINDOW (8191). Other system contracts use a prime size so that, even when slots are empty or the slot time changes, values are not overwritten before the whole buffer has been filled. In this EIP the operand of the modulo operation is the block number, which increases by exactly 1 each time, so the buffer always stays saturated and the size does not need to be prime. 8191 was kept for consistency with the other system contracts. On current mainnet, 8191 blocks is about one day, which gives enough time to build a transaction containing a verification of a specific hash and get it included on chain.
Deployment through a synthetic address
The contract is deployed to a special synthetic address that is computed backwards from the desired deployment transaction. The sender address 0x3462413Af4609098e1E27A490f554f260213D685 is derived from the signature values of the specified deployment transaction, and the address of the first contract this account deploys, rlp([sender, 0]), becomes HISTORY_STORAGE_ADDRESS. Unlike create2, it is not tied to a specific initcode, but the synthetic address is cryptographically bound to the transaction input data. As in EIP-4788, the deployed account has code and nonce 1, so it is excluded from the empty account cleanup of EIP-161.
Gas costs and the BLOCKHASH invariant
Under the EIP-2929 rules, the system update at the start of a block does not make the HISTORY_STORAGE_ADDRESS account or its storage slots warm. So whoever first calls this contract in that block pays the warm-up cost for the account and the slot. Other calls to this contract otherwise follow the normal EVM execution rules. Meanwhile, the semantics of the BLOCKHASH opcode do not change, so BLOCKHASH_SERVE_WINDOW (256) and its cost are not affected at all. If a longer range is needed, query this contract directly instead of using BLOCKHASH.
How the buffer fills after activation
Instead of storing the previous 8191 hashes all at once in the fork block, the design adds one hash per block after activation. The contract holds only the parent hash of the fork block onward, with nothing before it, and it takes HISTORY_SERVE_WINDOW blocks (about one day) for the buffer to fill completely. This was judged a favorable tradeoff because it simplifies the logic considerably, and because this is a new way of accessing history, so no contracts depend on it yet. If activated at genesis, the genesis hash is written to slot 0 at the start of block 1. If activated at block 32, the hash of block 31 is written to slot 31 and the remaining slots are 0.
Security considerations and compatibility
A contract with frequently updated storage paths can become the target of a branch poisoning attack, in which an attacker scatters small amounts of ether around that path (branch) to slow down state root updates. However, it was assessed that the attack cost would rise sharply for any meaningful slowdown. This EIP changes the block validation rules in a way that is not backward compatible, but nothing breaks in the activity or experience of current users. The status of this EIP is finalized as Final.