EIP-7807 - SSZ execution blocks

Created 2024-10-28
Status Draft
Category Core
Type Standards Track
Authors
Requires

Abstract

This EIP defines a migration process of execution blocks to Simple Serialize (SSZ).

Motivation

With EIP-6404 SSZ transactions, EIP-6466 SSZ receipts, and EIP-6465 SSZ withdrawals, all Merkle-Patricia Trie (MPT) besides the state trie are converted to SSZ. This enables the surrounding data structure, in this case, the execution block itself, to also convert to SSZ, achieving a unified block representation across both Consensus Layer and Execution Layer.

  1. Normalized block hash: The Consensus Layer can compute the block hash autononomously, enabling it to process all consistency checks that currently require asynchronous communication with the Execution Layer (verify_and_notify_new_payload). This allows early rejection of inconsistent blocks and dropping the requirement to wait for engine API interactions while syncing.

  2. Optimized engine API: With all exchanged data supporting SSZ, the engine API can be changed from the textual JSON encoding to binary SSZ encoding, reducing exchanged data size by ~50% and significantly improving encoding/parsing efficiency.

  3. Proving support: With SSZ, individual fields of the execution block header become provable without requiring full block headers to be present. With EIP-7495 SSZ StableContainer, proofs are forward compatible as long as underlying semantics of individual fields are unchanged, reducing maintenance requirements for smart contracts and verifying client applications.

  4. Cleanup opportunity: The conversion to SSZ allows dropping historical fields from the PoW era and the inefficient logs bloom mechanism, and allows introducing the concept of EIP-7706 multi-dimensional gas.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

ExecutionBlockHeader container

Execution blocks are represented as a single, normalized SSZ container. The definition uses the StableContainer[N] SSZ type and Optional[T] as defined in EIP-7495.

Name Value Description
MAX_EXECUTION_BLOCK_FIELDS uint64(2**6) (= 64) Maximum number of fields to which StableExecutionBlock can ever grow in the future
class StableGasAmounts(StableContainer[MAX_FEES_PER_GAS_FIELDS]):
    regular: Optional[GasAmount]
    blob: Optional[GasAmount]

class GasAmounts(Profile[StableGasAmounts]):
    regular: GasAmount
    blob: GasAmount

class StableExecutionBlockHeader(StableContainer[MAX_EXECUTION_BLOCK_FIELDS]):
    parent_hash: Optional[Root]
    miner: Optional[ExecutionAddress]
    state_root: Optional[Bytes32]
    transactions_root: Optional[Root]
    receipts_root: Optional[Root]
    number: Optional[uint64]
    gas_limits: Optional[StableGasAmounts]
    gas_used: Optional[StableGasAmounts]
    timestamp: Optional[uint64]
    extra_data: Optional[ByteList[MAX_EXTRA_DATA_BYTES]]
    mix_hash: Optional[Bytes32]
    base_fees_per_gas: Optional[FeesPerGas]
    withdrawals_root: Optional[Root]
    excess_gas: Optional[StableGasAmounts]
    parent_beacon_block_root: Optional[Root]
    requests_hash: Optional[Bytes32]
    system_logs_root: Optional[Root]

class ExecutionBlockHeader(Profile[StableExecutionBlockHeader]):
    parent_hash: Root
    miner: ExecutionAddress
    state_root: Bytes32
    transactions_root: Root  # EIP-6404 transactions.hash_tree_root()
    receipts_root: Root  # EIP-6466 receipts.hash_tree_root()
    number: uint64
    gas_limits: GasAmounts
    gas_used: GasAmounts
    timestamp: uint64
    extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
    mix_hash: Bytes32
    base_fees_per_gas: BlobFeesPerGas
    withdrawals_root: Root  # EIP-6465 withdrawals.hash_tree_root()
    excess_gas: GasAmounts
    parent_beacon_block_root: Root
    requests_hash: Bytes32  # EIP-6110 `ExecutionRequests`.hash_tree_root()
    system_logs_root: Root

Requests hash computation

requests_hash is changed to ExecutionRequests.hash_tree_root() using the same structure as in the Consensus Layer BeaconBlockBody.

Execution block hash computation

The execution block hash is changed to be based on hash_tree_root in all contexts, including (1) the BLOCKHASH opcode, (2) engine API interactions (blockHash field), (3) JSON-RPC API interactions, (4) devp2p networking.

Rationale

In the initial draft, only the requests hash and block hash are changed to be SSZ hash_tree_root() based. No Consensus Layer changes are required.

Future

Backwards Compatibility

This breaks compatibility of smart contracts that depend on the previous block header binary format, including for "generic" implementations that assume a common prefix and run the entire data through a linear keccak256 hash.

Security Considerations

The SSZ block hash is based on SHA256 and shares the namespace with existing keccak256 based block hashes. As these hash algorithms are fundamentally different, no significant collision risk is expected.

Copyright

Copyright and related rights waived via CC0.