--- eip: 7709 title: Read BLOCKHASH from storage and update cost description: Read the `BLOCKHASH (0x40)` opcode from the EIP-2935 system contract storage and adjust its gas cost to reflect storage access. author: Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak), Guillaume Ballet (@gballet), Gajinder Singh (@g11tech), Tanishq Jasoria (@tanishqjasoria), Ignacio Hagopian (@jsign), Jochem Brouwer (@jochem-brouwer), Gabriel Rocheleau (@gabrocheleau) discussions-to: https://ethereum-magicians.org/t/eip-7709-read-blockhash-opcode-from-storage-and-adjust-gas-cost/20052 status: Stagnant type: Standards Track category: Core created: 2024-05-18 requires: 2935 --- ## Abstract 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 knowledge of the previous blocks, which in Verkle [EIP-6800](./eip-6800.md) would prevent stateless execution. However with [EIP-2935](./eip-2935.md) blockhashes can be retrieved and served from its system contract storage which allows Verkle blocks to include a storage access witness for stateless execution. ## Specification | Parameter | Value | | ------------------------- | ------ | | `FORK_TIMESTAMP` | TBD | | `HISTORY_STORAGE_ADDRESS` | `0x0000F90827F1C53a10cb7A02335B175320002935` | | `BLOCKHASH_SERVE_WINDOW` | `256` | 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: ```python def resolve_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 if arg >= block.number or (arg + BLOCKHASH_SERVE_WINDOW) < block.number return 0 # performs an sload on arg % HISTORY_SERVE_WINDOW including gas charges, # warming effects as well as execution accesses # # note that the `BLOCKHASH_SERVE_WINDOW` and the 2935 ring buffer window # `HISTORY_SERVE_WINDOW` for slot calculation are different return state.load_slot(HISTORY_STORAGE_ADDRESS, arg % HISTORY_SERVE_WINDOW) ``` ONLY if the `arg` is within the correct `BLOCKHASH` window, clients can choose to either * do a direct `SLOAD` from state, or * do a system call to [EIP-2935](./eip-2935.md) 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.) However the entire semantics and after effects of the `SLOAD` operation needs to be applied as per the current 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) * `SLOAD` accesses added to execution witnesses if Verkle ([EIP-6800](./eip-6800.md) and [EIP-4762](./eip-4762.md)) is activated ### Activation This EIP specifies the transition to the new logic assuming that [EIP-2935](./eip-2935.md) 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 The current proposal is to activate this EIP with Verkle to allow for stateless execution of the block. ### 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](./eip-2935.md) contract, the gas cost for the system code execution (and also the code witnesses if Verkle activated) is not applied. Only the effect of `SLOAD` is applied as described above. ## Rationale * The reason behind the updated gas cost is to match the real operation, which is equivalent to an `SLOAD`. * The [EIP-2935](./eip-2935.md) 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 though 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](./eip-2935.md) system contract which will lead to a normal contract execution (as well as charges and accesses) ## Backwards Compatibility This EIP introduces a significant increase in the cost of `BLOCKHASH`, 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](./eip-2935.md) fork and this EIP's fork (unless [EIP-2935](./eip-2935.md) is activated in genesis for e.g. in testnets/devnets) as the [EIP-2935](./eip-2935.md) system contract would not have saved the required history. ## Test Cases * If `BLOCKHASH` is not called or there is no [EIP-2935](./eip-2935.md) contract call by any transaction, only the [EIP-2935](./eip-2935.md) system update of the parent hash shows up in witnesses if Verkle is activated. * If `BLOCKHASH` is called, there MUST be a storage access gas charge (and corresponding access witness if Verkle is activated) for the storage slot but ONLY if the `BLOCKHASH` query is for the last `BLOCKHASH_SERVE_WINDOW` ancestors. This is irrespective of how the client chooses to resolve the `BLOCKHASH` (directly, via system contract or via memory) * 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](./eip-2935.md) contract is called directly (i.e. not through `BLOCKHASH`), then the witness and gas costs (including those related to contract code) 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](./eip-2935.md) ## Security Considerations No security considerations other than the ones contained in [EIP-2935](./eip-2935.md) are determined as of now. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).