158 lines
7.5 KiB
Markdown
158 lines
7.5 KiB
Markdown
---
|
|
eip: 4788
|
|
title: Beacon state root in the EVM
|
|
description: Expose beacon chain state roots in the EVM
|
|
author: Alex Stokes (@ralexstokes), Danny Ryan (@djrtwo)
|
|
discussions-to: https://ethereum-magicians.org/t/eip-4788-beacon-state-root-in-evm/8281
|
|
status: Stagnant
|
|
type: Standards Track
|
|
category: Core
|
|
created: 2022-02-10
|
|
---
|
|
|
|
## Abstract
|
|
|
|
Commit to the state root of the beacon chain in the `ommers` field in the post-merge execution block. Reflect the changes in the `ommersHash` field of the execution block header.
|
|
|
|
Store each beacon chain state root into a contract and add a new opcode that reads this contract.
|
|
|
|
## Motivation
|
|
|
|
Exposing the beacon chain state root allows for proofs about the beacon state to be verified inside the EVM. This functionality supports a wide variety of use cases in smart contracts involving validator status and finality produced by the consensus layer.
|
|
|
|
In particular, this functionality is required for beacon chain validator withdrawals to the EVM.
|
|
|
|
## Specification
|
|
|
|
| constants | value | units
|
|
|--- |--- |---
|
|
| `FORK_TIMESTAMP` | TBD |
|
|
| `FORK_EPOCH` | TBD |
|
|
| `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffd` |
|
|
| `OPCODE_VALUE` | `0x48` |
|
|
| `G_beacon_state_root` | 20 | gas
|
|
|
|
### Background
|
|
|
|
The method of injecting the beacon state root in this EIP follows the general strategy of [EIP-4399](./eip-4399.md) to make a post-merge change to the EVM integrating information from the beacon chain. This EIP along with [EIP-3675](./eip-3675.md) should be taken as relevant background to understand the particular approach of this EIP.
|
|
|
|
The method for exposing the state 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 the value of the `ommers` field in the block to an RLP list with one element: the 32 byte [hash tree root](https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization) of the [beacon state](https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#beaconstate) from the previous slot to this block.
|
|
|
|
2. set the value of the `ommersHash` field in the block header to the Keccak256 hash of the `ommers` field.
|
|
|
|
```python
|
|
beaconStateRoot = <32 byte value> # provided by consensus client
|
|
ommers = RLP([beaconStateRoot]) # in the block body
|
|
ommersHash = Keccak256(ommers) # in the block header
|
|
```
|
|
|
|
3. Add the block validation that the `ommersHash` does indeed match the expected commitment given the `ommers` value.
|
|
|
|
### 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 beacon state root provided in the block into the storage of the smart contract at `HISTORY_STORAGE_ADDRESS`. This data is keyed by the block number.
|
|
|
|
In pseudocode:
|
|
|
|
```python
|
|
beacon_state_root = block.ommers[0]
|
|
sstore(HISTORY_STORAGE_ADDRESS, block.number, beacon_state_root)
|
|
```
|
|
|
|
#### New opcode
|
|
|
|
Beginning at the execution timestamp `FORK_TIMESTAMP`, introduce a new opcode `BEACON_STATE_ROOT` at `OPCODE_VALUE`. This opcode consumes one word from the stack encoding the block number for the root. The opcode has a gas cost of `G_beacon_state_root`.
|
|
|
|
The result of executing this opcode leaves one word on the stack corresponding to a read of the history contract's storage; in pseudocode:
|
|
|
|
```python
|
|
block_number = evm.stack.pop()
|
|
sload(HISTORY_STORAGE_ADDRESS, block_number)
|
|
```
|
|
|
|
If there is no root stored at the requested block number, the opcode follows the existing EVM semantics of `sload` returning `0`.
|
|
|
|
## Rationale
|
|
|
|
### General strategy
|
|
|
|
See the rationale for EIP-4399 for discussion about this general strategy of reusing execution block elements for beacon chain data.
|
|
|
|
### Fork mechanics
|
|
|
|
This EIP requires the consensus layer and execution layer to execute a network upgrade in lockstep.
|
|
To carry out this task, a `FORK_EPOCH` (of the beacon chain) will be chosen and then used to compute a timestamp `FORK_TIMESTAMP`.
|
|
This `FORK_TIMESTAMP` can be used in the execution layer to identify when the protocol change should be deployed.
|
|
|
|
This technique works because the timestamps in post-merge execution blocks are aligned to beacon chain slots and thus serve as a proxy for the slot number.
|
|
|
|
Another option for the fork definition would be to pick a beacon chain epoch and an execution payload block number.
|
|
This design however is not reliable due to the presence of skipped slots on the beacon chain.
|
|
|
|
### Execution layer validations
|
|
|
|
By including the beacon state root in the execution block in the deprecated `ommers` field, execution clients can still verify the chain in a self-contained way without relying on an available consensus client.
|
|
This property is important during syncing (and likely other phases of execution node operation).
|
|
|
|
### Minimizing client code change
|
|
|
|
By including the `ommersHash` validation, clients can use existing code with only minimal changes (supplying the actual state root) during block production and verification.
|
|
Having the beacon state root value in the `ommers` field means that it is fairly straightforward to provide the value from the block data to the EVM execution context for client implementations as they stand today.
|
|
|
|
### Gas cost of opcode
|
|
|
|
The suggested gas cost is just using the value for the `BLOCKHASH` opcode as `BEACON_STATE_ROOT` is an analogous operation.
|
|
|
|
### Why not repurpose `BLOCKHASH`?
|
|
|
|
The `BLOCKHASH` opcode could be repurposed to provide a beacon state root instead of the current execution block hash.
|
|
To minimize code change and simplify deployment to mainnet, this EIP suggests leaving `BLOCKHASH` alone and adding a new opcode with the desired semantics.
|
|
|
|
### Why not bound history of state roots?
|
|
|
|
Marginal state growth; adding every single root results in an additional ~84MB of state growth per year compared to ~30 GB of state overall.
|
|
|
|
TODO: say something about statelessness
|
|
TODO: get latest numbers on state size, and compare against predicted growth
|
|
|
|
### Beacon state root instead of block root
|
|
|
|
Each slot on the beacon chain containing a block has both a block root and a state root (reflecting the state after applying said block).
|
|
The beacon block includes the state root so a proof about the state could also be authored against a block root at the cost of a few additional hashes.
|
|
Given that most use cases want to prove data encapsulated in a given state, rather than a given block, this EIP suggests exposing state roots over block roots.
|
|
|
|
### Block number in lieu of slot
|
|
|
|
The state roots are keyed by the `number` of the execution block.
|
|
Another option is to key roots by the beacon chain slot they belong to.
|
|
While at first pass this may seem more direct, the beacon chain can have "skipped" slots where a beacon proposer failed to produce a block that was included at a given slot.
|
|
Handling roots of skipped slots would complicate the EVM mechanism so this EIP suggests to use the execution block number where each distinct block number is guaranteed to have a distinct root.
|
|
|
|
## Backwards Compatibility
|
|
|
|
No issues.
|
|
|
|
## Test Cases
|
|
|
|
TODO
|
|
|
|
## Reference Implementation
|
|
|
|
TODO
|
|
|
|
## Security Considerations
|
|
|
|
TODO
|
|
|
|
## Copyright
|
|
|
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|