proto-eip: beacon state root in the EVM

Simple Summary

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
OPCODE_VALUE 0x48
G_beacon_state_root 20 gas
WINDOW_LENGTH 256 slots

Background

The method of injecting the beacon state root in this EIP follows the general strategy of EIP-4399 to make a post-merge change to the EVM integrating information from the beacon chain. This EIP along with EIP-3675 should be taken as relevant background to understand the particular approach of this EIP.

The method exposing the state root data is inspired by EIP-2935.

block structure

beginning with FORK_BLOCK_NUMBER, 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 of the beacon state 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.
beaconStateRoot = <32 byte value> # provided by consensus client
ommers = RLP([beaconStateRoot])   # in the block body
ommersHash = Keccak256(ommers)    # in the block header
  1. Re-instate the block validation that the ommersHash does indeed match the expected committment given the ommers value.

EVM changes

beginning with FORK_BLOCK_NUMBER, introduce a new opcode BEACON_STATE_ROOT at OPCODE_VALUE.

This opcode must return the value of the single element in the ommers field of the execution block. This value is set in the EVM execution context before processing a given execution block. The opcode consumes no words from the stack and places one word on the stack. The opcode has a gas cost of G_beacon_state_root.

For example, assume the beacon state root at slot 3,000,000 is 0x8ff077430957727dbace82d868a4f514d3720594727deaaafded1ee2e9e486f1. If processing a transaction that executes BEACON_STATE_ROOT at slot 3,000,001, the EVM should place on the stack this value of the state root 0x8ff0...86f1.

implementation notes and other considerations

The Engine API PayloadAttributes message will be updated with an additional field to send the relevant beacon state root from the consensus client to the execution client when preparing a block.

This field MUST be sent beginning with FORK_EPOCH at the consensus layer.

rationale, notes, etc.

general strategy

See the rationale for EIP-4399 for discussion about this general strategy of reusing execution block elements for beacon chain data.

consensus layer validations

The ExecutionPayload and ExecutionPayloadHeader will be updated with a 32 byte field beacon_state_root for the relevant beacon state root for the corresponding execution block. This way the beacon state root committed to in the block can be verified against the beacon state root recorded in the beacon chain (e.g. state.state_roots[state.slot-1]). A future PR to the consensus-specs repo with this change is forthcoming.

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.

acknowledgments

Thanks to Danny Ryan for helpful feedback and discussions.