Skip to content

Commit

Permalink
Update EIP-4788: send current slot from CL to avoid timestamp convers…
Browse files Browse the repository at this point in the history
…ions

Merged by EIP-Bot.
  • Loading branch information
ralexstokes authored May 17, 2023
1 parent f60afeb commit 14e48ab
Showing 1 changed file with 32 additions and 11 deletions.
43 changes: 32 additions & 11 deletions EIPS/eip-4788.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ Store each of these roots in a contract that lives in the execution state and ad

## Motivation

Roots of the beacon chain blocks are cryptographic accumulators that allow proofs of arbitrary consensus state. Exposing these roots inside the EVM allows for trust-minimized access to the consensus layer. This functionality supports a wide variety of use cases that improve trust assumptions of staking pools, restaking constructions, smart contract bridges, MEV mitigations and more.
Roots of the beacon chain blocks are cryptographic accumulators that allow proofs of arbitrary consensus state.
Exposing these roots inside the EVM allows for trust-minimized access to the consensus layer.
This functionality supports a wide variety of use cases that improve trust assumptions of staking pools,
restaking constructions, smart contract bridges, MEV mitigations and more.

## Specification

Expand All @@ -34,37 +37,55 @@ Roots of the beacon chain blocks are cryptographic accumulators that allow proof

The high-level idea is that each execution block contains the parent beacon block root. Even in the event of missed slots since the previous block root does not change,
we only need a constant amount of space to represent this "oracle" in each execution block. To improve the usability of this oracle, block roots are stored
in a canonical place in the execution state analogous to a `SSTORE` in given contract's storage for each update. Roots are stored keyed by the slot(s) they pertain to.
in a canonical place in the execution state analogous to a `SSTORE` in the given contract's storage for each update.
Roots are keyed by the slot(s) they pertain to.
To bound the amount of storage this construction consumes, a ring buffer is used that mirrors a block root accumulator on the consensus layer.
The method for exposing the root data via opcode is inspired by [EIP-2935](./eip-2935.md).

### Block structure and validity

Beginning at the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST**:

1. set 32 bytes of the execution block header after the `withdrawals_root` 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 `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.

*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`.
2. set the 8 bytes after the previous 32 byte extension to the slot number as a big-endian uint64 of the current slot.

*NOTE*: these fields are appended to the current block header structure with this EIP so that the size of the header grows after (and including) the `FORK_TIMESTAMP`.

### EVM changes

#### Block processing

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 slot number.
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`.

In pseudocode:
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.

```python
start_timestamp = get_block(block_header.parent_hash).header.timestamp
start_slot = convert_to_slot(start_timestamp)
*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`.

end_timestamp = block_header.timestamp
end_slot = convert_to_slot(end_timestamp)
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

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)
```

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.
Expand Down

0 comments on commit 14e48ab

Please sign in to comment.