Alert Source Discuss
⚠️ Draft Standards Track: Core

EIP-4788: Beacon block root in the EVM

Expose beacon chain roots in the EVM

Authors Alex Stokes (@ralexstokes), Ansgar Dietrichs (@adietrichs), Danny Ryan (@djrtwo)
Created 2022-02-10
Discussion Link https://ethereum-magicians.org/t/eip-4788-beacon-root-in-evm/8281

Abstract

Commit to the hash tree root of each beacon chain block in the corresponding execution payload header.

Store each of these roots in a stateful precompile.

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.

Specification

constants value units
FORK_TIMESTAMP TBD  
HISTORY_STORAGE_ADDRESS 0xfffffffffffffffffffffffffffffffffffffffd  
G_beacon_root 2100 gas

Background

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, a small history of block roots are stored in a stateful precompile. To bound the amount of storage this construction consumes, a ring buffer is used that mirrors a block root accumulator on the consensus layer.

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 last header field as of FORK_TIMESTAMP to the 32 byte hash tree root 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.

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.

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:

parent_beacon_block_root = block_header.parent_beacon_block_root
timestamp = to_uint256_be(block_header.timestamp)

sstore(HISTORY_STORAGE_ADDRESS, parent_beacon_block_root, timestamp)

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 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 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:

root = evm.calldata[:32]
timestamp = sload(HISTORY_STORAGE_ADDRESS, root)
evm.returndata[:32].set(timestamp)

If there is no timestamp stored at the given root, the opcode follows the existing EVM semantics of sload returning 0.

Rationale

Gas cost of precompile

The suggested gas cost reflects a cold SLOAD analogous to the operation performed while executing the precompile’s logic.

Why not repurpose BLOCKHASH?

The BLOCKHASH opcode could be repurposed to provide the beacon root instead of some execution block hash. To minimize code change, avoid breaking changes to smart contracts, and simplify deployment to mainnet, this EIP suggests leaving BLOCKHASH alone and adding a new opcode with the desired semantics.

Beacon block root instead of state root

Block roots are preferred over state roots so there is a constant amount of work to do with each new execution block. Otherwise, skipped slots would require a linear amount of work with each new payload. While skipped slots are quite rare on mainnet, it is best to not add additional load under what would already be nonfavorable conditions.

Use of block root over state root does mean proofs will require a few additional nodes but this cost is negligible (and could be amortized across all consumers, e.g. with a singleton state root contract that caches the proof per slot).

Backwards Compatibility

No issues.

Test Cases

TODO

Reference Implementation

TODO

Security Considerations

TODO

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Alex Stokes (@ralexstokes), Ansgar Dietrichs (@adietrichs), Danny Ryan (@djrtwo), "EIP-4788: Beacon block root in the EVM [DRAFT]," Ethereum Improvement Proposals, no. 4788, February 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-4788.