Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions EIPS/eip-4788.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ restaking constructions, smart contract bridges, MEV mitigations and more.
| constants | value |
|--- |--- |
| `FORK_TIMESTAMP` | TBD |
| `HISTORICAL_ROOTS_MODULUS` | `98304` |
| `HISTORY_BUFFER_LENGTH` | `98304` |
| `SYSTEM_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffe` |
| `BEACON_ROOTS_ADDRESS` | `0xbEac00dDB15f3B6d645C48263dC93862413A222D` |

Expand Down Expand Up @@ -87,15 +87,15 @@ The beacon roots contract has two operations: `get` and `set`. The input itself

* Callers provide the `timestamp` they are querying encoded as 32 bytes in big-endian format.
* If the input is not exactly 32 bytes, the contract must revert.
* Given `timestamp`, the contract computes the storage index in which the timestamp is stored by computing the modulo `timestamp % HISTORICAL_ROOTS_MODULUS` and reads the value.
* Given `timestamp`, the contract computes the storage index in which the timestamp is stored by computing the modulo `timestamp % HISTORY_BUFFER_LENGTH` and reads the value.
* If the `timestamp` does not match, the contract must revert.
* Finally, the beacon root associated with the timestamp is returned to the user. It is stored at `timestamp % HISTORICAL_ROOTS_MODULUS + HISTORICAL_ROOTS_MODULUS`.
* Finally, the beacon root associated with the timestamp is returned to the user. It is stored at `timestamp % HISTORY_BUFFER_LENGTH + HISTORY_BUFFER_LENGTH`.

##### `set`

* Caller provides the parent beacon block root as calldata to the contract.
* Set the storage value at `header.timestamp % HISTORICAL_ROOTS_MODULUS` to be `header.timestamp`
* Set the storage value at `header.timestamp % HISTORICAL_ROOTS_MODULUS + HISTORICAL_ROOTS_MODULUS` to be `calldata[0:32]`
* Set the storage value at `header.timestamp % HISTORY_BUFFER_LENGTH` to be `header.timestamp`
* Set the storage value at `header.timestamp % HISTORY_BUFFER_LENGTH + HISTORY_BUFFER_LENGTH` to be `calldata[0:32]`

##### Pseudocode

Expand All @@ -109,20 +109,20 @@ def get():
if len(evm.calldata) != 32:
evm.revert()

timestamp_idx = to_uint256_be(evm.calldata) % HISTORICAL_ROOTS_MODULUS
timestamp_idx = to_uint256_be(evm.calldata) % HISTORY_BUFFER_LENGTH
timestamp = storage.get(timestamp_idx)

if timestamp != evm.calldata:
evm.revert()

root_idx = timestamp_idx + HISTORICAL_ROOTS_MODULUS
root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH
root = storage.get(root_idx)

evm.return(root)

def set():
timestamp_idx = to_uint256_be(evm.timestamp) % HISTORICAL_ROOTS_MODULUS
root_idx = timestamp_idx + HISTORICAL_ROOTS_MODULUS
timestamp_idx = to_uint256_be(evm.timestamp) % HISTORY_BUFFER_LENGTH
root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH

storage.set(timestamp_idx, evm.timestamp)
storage.set(root_idx, evm.calldata)
Expand Down Expand Up @@ -260,7 +260,7 @@ e.g. with a singleton state root contract that caches the proof per slot).

### Why two ring buffers?

The first ring buffer only tracks `HISTORICAL_ROOTS_MODULUS` worth of roots and so for all possible timestamp values would consume a constant amount of storage.
The first ring buffer only tracks `HISTORY_BUFFER_LENGTH` worth of roots and so for all possible timestamp values would consume a constant amount of storage.
However, this design opens the precompile to an attack where a skipped slot that has the same value modulo the ring buffer length would return an old root value,
rather than the most recent one.

Expand Down