This EIP migrates the execution block to Simple Serialize (SSZ), including the block hash, request hash, and the per-block tries. Notably, individual transaction, receipt, and withdrawal list elements as well as the state trie are not affected by this EIP.
The execution block and its per-block tries use Merkle-Patricia and RLP encoding, separate from the SSZ representation used by the Consensus Layer. Migrating the block to SSZ unifies the block representation across both layers and enables:
Normalized block hash: The Consensus Layer can autonomously check the execution block hash against the builder's commitment, enabling early consistency checks that otherwise require asynchronous communication with the Execution Layer.
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.
Proving support: With SSZ, individual fields of the execution block become provable without requiring the full block to be present. With EIP-7495 ProgressiveContainer and EIP-7916 ProgressiveList, proofs are forward compatible as long as underlying semantics of individual fields are unchanged, reducing maintenance requirements for smart contracts and verifying client applications.
Cleanup opportunity: The conversion to SSZ allows dropping historical fields from the PoW era and the inefficient logs bloom mechanism.
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.
Definitions from existing specifications that are used throughout this document are replicated here for reference.
| Name | SSZ equivalent |
|---|---|
Root |
Bytes32 |
ExecutionAddress |
Bytes20 |
The different kinds of gas amounts and fees are combined into a single structure.
| Name | SSZ equivalent | Description |
|---|---|---|
GasAmount |
uint64 |
Amount in units of gas |
FeePerGas |
uint256 |
Fee per unit of gas |
class GasAmounts(ProgressiveContainer(active_fields=[1] * 2)):
regular: GasAmount
blob: GasAmount
class BlobFeesPerGas(ProgressiveContainer(active_fields=[1] * 2)):
regular: FeePerGas
blob: FeePerGas
New execution blocks use a normalized SSZ representation.
class ExecutionPayload(ProgressiveContainer(active_fields=[1] * 18)):
parent_hash: Root
miner: ExecutionAddress
state_root: Bytes32
transactions: ProgressiveList[ProgressiveByteList]
receipts_root: 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: ProgressiveList[ProgressiveByteList]
excess_gas: GasAmounts
parent_beacon_block_root: Root
requests_hash: Root
block_access_list: ProgressiveByteList
slot_number: uint64
receipts_root is receipts.hash_tree_root() over the block's ProgressiveList[ProgressiveByteList] of receiptsrequests_hash is ExecutionRequests.hash_tree_root(), matching the ExecutionRequests root used by the Consensus LayerAn execution block header can be derived as an SSZ summary of ExecutionPayload that replaces the transactions, withdrawals, and block_access_list fields with their corresponding hash_tree_root.
ExecutionPayload changesThe ExecutionPayload definition replaces the Consensus Layer ExecutionPayload, carried by the ExecutionPayloadEnvelope.
The execution block hash is computed as ExecutionPayload.hash_tree_root() in all contexts, including (1) the BLOCKHASH opcode, (2) JSON-RPC API interactions (blockHash field), (3) devp2p networking, and (4) Consensus Layer references in BeaconState.latest_block_hash / ExecutionPayloadBid.block_hash.
JSON-RPC block responses MUST set the logsBloom field to all 1 bits (256 bytes of 0xff).
ExecutionPayload uses ProgressiveContainer and ProgressiveList: generalized indices remain stable as the block definition evolves, and the lists can grow without a hardcoded limit.
Using an all 1 bits value forces consumers to fall back to inspecting every receipt; all 0 bits would signal a definite absence and may cause missed logs.
The shared SSZ block hash lets the engine API drop the redundant block_hash field and adopt binary ForkDigest-context encoding. This reduces encoding overhead and simplifies sharing data structures in combined Consensus Layer / Execution Layer implementations.
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.
JSON-RPC consumers that read logsBloom observe an all-1 value for SSZ blocks, which is backwards compatible. The receipt logsBloom is unaffected by this EIP.
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 and related rights waived via CC0.