diff --git a/EIPS/eip-4788.md b/EIPS/eip-4788.md index 7bcb51807f309b..74521bc1d2dba8 100644 --- a/EIPS/eip-4788.md +++ b/EIPS/eip-4788.md @@ -25,12 +25,11 @@ restaking constructions, smart contract bridges, MEV mitigations and more. ## Specification -| constants | value | units -|--- |--- |--- -| `FORK_TIMESTAMP` | TBD | -| `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffd` | -| `G_beacon_root` | 2100 | gas -| `SLOTS_PER_HISTORICAL_ROOT` | 8192 | slot(s) +| constants | value | units +|--- |--- |--- +| `FORK_TIMESTAMP` | TBD | +| `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffd` | +| `G_beacon_root` | 2100 | gas ### Background @@ -43,7 +42,7 @@ To bound the amount of storage this construction consumes, a ring buffer is used Beginning at the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST**: -1. set 32 bytes of the execution block header after the `excess_data_gas` to the 32 byte [hash tree root](https://github.com/ethereum/consensus-specs/blob/fa09d896484bbe240334fa21ffaa454bafe5842e/ssz/simple-serialize.md#merkleization) of the parent beacon block. +1. set 32 bytes of the execution block header after the last header field as of `FORK_TIMESTAMP` to the 32 byte [hash tree root](https://github.com/ethereum/consensus-specs/blob/fa09d896484bbe240334fa21ffaa454bafe5842e/ssz/simple-serialize.md#merkleization) of the parent beacon block. *NOTE*: this field is appended to the current block header structure with this EIP so that the size of the header grows after (and including) the `FORK_TIMESTAMP`. @@ -54,56 +53,37 @@ Beginning at the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST* At the start of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. before processing any transactions), write the parent beacon root provided in the block header into the storage of the contract at `HISTORY_STORAGE_ADDRESS`. -This data is keyed by the slots containing the parent beacon block root which in general could be multiple slots for the -same root. To find the starting slot of the range, read the storage slot `SLOTS_PER_HISTORICAL_ROOT` (interpreted as a -256-bit byte array) which contains the big-endian encoding of the last slot value written to. This encoding should be interpreted -as a 64-bit big-endian integer that yields the starting slot. -Clients then iterate from the start slot (inclusive) to the end slot (exclusive) found in the header -and write the beacon block root into each slot value. - -*NOTE*: if the slot stored at `SLOTS_PER_HISTORICAL_ROOT` is all zeros, clients should use set the start slot to one less than the -slot in the header, e.g. `header.beacon_block_root_slot - 1`. - -After writing the block root in the relevant slots, store the current slot for use during the next update in the same storage slot -`SLOTS_PER_HISTORICAL_ROOT`. +The root itself is used as a key into the contract's storage and the timestamp of the header is written as the key's value. +The timestamp (a 64-bit unsigned integer value) is encoded as 32 bytes in big-endian format. In Python pseudocode: ```python -start_slot = to_uint64(sload(HISTORY_STORAGE_ADDRESS, SLOTS_PER_HISTORICAL_ROOT)) -end_slot = block_header.beacon_slot -if start_slot == 0: - start_slot = max(end_slot - 1, 0) - parent_beacon_block_root = block_header.parent_beacon_block_root +timestamp = to_uint256_be(block_header.timestamp) -for slot in range(start_slot, end_slot): - sstore(HISTORY_STORAGE_ADDRESS, slot % SLOTS_PER_HISTORICAL_ROOT, parent_beacon_block_root) - -sstore(HISTORY_STORAGE_ADDRESS, SLOTS_PER_HISTORICAL_ROOT, end_slot) +sstore(HISTORY_STORAGE_ADDRESS, parent_beacon_block_root, timestamp) ``` -When using any slot value as a key to the storage, the value under consideration must be converted to 32 bytes with big-endian encoding. - #### New stateful precompile Beginning at the execution timestamp `FORK_TIMESTAMP`, the code and storage at `HISTORY_STORAGE_ADDRESS` constitute a "stateful" precompile. -Callers of the precompile should provide the `slot` they are querying encoding a 64-bit unsigned integer as 256-bit big-endian data. -Recall this `slot` number should be reduced modulo the `SLOTS_PER_HISTORICAL_ROOT` constant to derive the correct key into the ring buffer structure. +Callers of the precompile should provide the `root` they are querying encoded as 32 bytes. Alongside the existing gas for calling the precompile, there is an additional gas cost of `G_beacon_root` cost to reflect the implicit `SLOAD` from -the precompile's state. The root is returned as 32 bytes in the caller's provided return buffer. +the precompile's state. The timestamp of the corresponding root is returned as 32 bytes in the caller's provided return buffer and represents the +64-bit unsigned integer from the header in big-endian format. In pseudocode: ```python -slot = to_uint64(evm.calldata[:32]) -root = sload(HISTORY_STORAGE_ADDRESS, slot % SLOTS_PER_HISTORICAL_ROOT) -evm.returndata[:32].set(root) +root = evm.calldata[:32] +timestamp = sload(HISTORY_STORAGE_ADDRESS, root) +evm.returndata[:32].set(timestamp) ``` -If there is no root stored at the requested slot number, the opcode follows the existing EVM semantics of `sload` returning `0`. +If there is no timestamp stored at the given root, the opcode follows the existing EVM semantics of `sload` returning `0`. ## Rationale