Update the BLOCKHASH (0x40) opcode to read and serve from the system contract storage and charge the additional (cold or warm) storage costs.
Motivation
The BLOCKHASH (0x40) opcode currently assumes that the client has access to recent block history. This makes it a protocol special case: it depends on historical chain data, but it is not modeled like other state-backed reads.
With EIP-2935, recent block hashes are stored in the system contract storage. This allows in-window BLOCKHASH lookups to be modeled as storage-backed accesses while preserving the existing BLOCKHASH return-value semantics.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Parameter
Value
FORK_TIMESTAMP
TBD
HISTORY_STORAGE_ADDRESS
0x0000F90827F1C53a10cb7A02335B175320002935
BLOCKHASH_SERVE_WINDOW
256
HISTORY_SERVE_WINDOW
8191
The BLOCKHASH opcode semantics remains the same as before. From the fork_block (defined as fork_block.timestamp >= FORK_TIMESTAMP and fork_block.parent.timestamp < FORK_TIMESTAMP), the BLOCKHASH instruction should be updated to resolve block hash in the following manner:
defresolve_blockhash(block:Block,state:State,arg:uint64):# note that outside the BLOCKHASH_SERVE_WINDOW we continue to return 0
# despite the 2935 history contract being able to serve more hashes
ifarg>=block.numberor(arg+BLOCKHASH_SERVE_WINDOW)<block.number:return0# performs an sload on arg % HISTORY_SERVE_WINDOW including gas charges,
# warming effects as well as state-access recording
#
# note that the `BLOCKHASH_SERVE_WINDOW` and the 2935 ring buffer window
# `HISTORY_SERVE_WINDOW` for slot calculation are different
returnstate.load_slot(HISTORY_STORAGE_ADDRESS,arg%HISTORY_SERVE_WINDOW)
If the arg is within the correct BLOCKHASH window, clients MAY choose to either
do a direct SLOAD from state, or
do a system call to EIP-2935 contract via its get mechanism (caller other than SYSTEM_ADDRESS) or
serve from memory or as per current designs if maintaining requisite history (full clients for e.g.)
Regardless of the chosen resolution method, clients MUST apply the entire semantics and effects of the SLOAD operation as defined by the active fork if the arg is within the correct BLOCKHASH window:
SLOAD gas costs (cold or warm) for the arg % HISTORY_SERVE_WINDOW slot.
SLOAD after effects on the slot (warming the slot)
Any state-access recording required by the active fork for the corresponding SLOAD.
Activation
This EIP specifies the transition to the new logic assuming that EIP-2935 has been activated:
sufficiently ahead of this EIP’s activation (>= BLOCKHASH_SERVE_WINDOW) or
at genesis for testnets/devnets where this EIP could also be activated at genesis.
Gas costs
As described above, if the arg to be resolved is within the correct window, the corresponding SLOAD charges and accesses are to be applied for the slot arg % HISTORY_SERVE_WINDOW. Note that the HISTORY_SERVE_WINDOW and BLOCKHASH_SERVE_WINDOW are different.
Reading from the System contract
Even if the clients choose to resolve BLOCKHASH through system call to EIP-2935 contract, the gas cost for the system code execution is not applied. Only the effect of SLOAD is applied as described above.
Rationale
The updated gas cost matches the accessed resource, which is equivalent to reading from storage.
Charging normal SLOAD-like cold and warm costs keeps BLOCKHASH aligned with existing state-access pricing instead of adding a separate gas rule for recent block hashes.
A client can alternatively prove recent block hashes through a chain of parent headers, but that keeps BLOCKHASH as a special case in witness construction rather than using the state access machinery provided by EIP-2935.
Always charging the warm SLOAD cost, or introducing a custom BLOCKHASH price, could reduce compatibility risk but would introduce a new special-case gas rule.
The EIP-2935 system contract execution charges and accesses are not applied to keep the gas low and to keep things simple for clients which choose to resolve BLOCKHASH in other ways (directly or through memory/maintained history).
Note that BLOCKHASH opcode only serves a limited BLOCKHASH_SERVE_WINDOW to be backward compatible (and to not extend the above exemptions). For deeper accesses one will need to directly call EIP-2935 system contract which will lead to a normal contract execution (as well as charges and accesses).
Backwards Compatibility
This EIP does not change the return-value semantics of BLOCKHASH.
This EIP introduces a significant increase in the cost of in-window BLOCKHASH queries, which could break use-cases that rely on the previous gas cost. Also, this EIP introduces a breaking change in the case where less than BLOCKHASH_SERVE_WINDOW elapse between the EIP-2935 fork and this EIP’s fork (unless EIP-2935 is activated in genesis for e.g. in testnets/devnets) as the EIP-2935 system contract would not have saved the required history.
Test Cases
If BLOCKHASH is called with an argument outside the last BLOCKHASH_SERVE_WINDOW ancestors, or with an argument greater than or equal to the current block number, it returns 0 without applying additional storage access effects.
If BLOCKHASH is called for an in-window ancestor and the corresponding storage slot is cold, the opcode charges the base BLOCKHASH cost plus the cold SLOAD cost.
If BLOCKHASH is called more than once in the same transaction for a block number that maps to the same storage slot, the later lookup charges the warm SLOAD cost.
The gas cost for each BLOCKHASH operation should still be charged, in addition to the SLOAD cost of each lookup (if performed).
If the EIP-2935 contract is called directly (i.e. not through BLOCKHASH), then the gas costs, state-access recording, and any other effects are applied as per normal contract execution of the current fork.
BLOCKHASH should be consistently resolved if this EIP is activated correctly >= BLOCKHASH_SERVE_WINDOW after EIP-2935.
Security Considerations
No security considerations other than the ones contained in EIP-2935 are determined as of now.