EIP-7807 SSZ execution block (SSZ execution blocks)

Hegota Declined for Inclusion · EVM & execution · EIP status Draft · created 2024-10-28

Changes the serialization of execution blocks from RLP to SSZ, which the consensus layer uses, and computes the block hash as the SSZ hash_tree_root. Individual transactions, receipts, withdrawal entries and the state trie are left as they are.

Why it is needed

Today Ethereum represents blocks of the same chain in two ways. Execution layer blocks use RLP encoding and the Merkle Patricia trie, while the consensus layer uses SSZ (Simple Serialize). The differing representations carry a large cost. The consensus layer cannot verify by itself the execution block hash that a builder committed to, so it must communicate with the execution layer asynchronously, and the Engine API that connects the two layers is text-based JSON, so data is large and encoding and parsing are slow. Proving even one field of a block requires the whole block. Old fields from the proof-of-work (PoW) era and the inefficient logs bloom remain as well. This EIP proposes moving the execution block, the block hash and the block-level tries to SSZ, unifying the block representation across the two layers and cleaning up these problems.

What changes

Today

After EIP-7807

Who is affected

In depth

ExecutionPayload container

The new execution block is defined as ExecutionPayload, a ProgressiveContainer with 18 fields. It consists of parent_hash, miner, state_root, transactions, receipts_root, number, gas_limits, gas_used, timestamp, extra_data, mix_hash, base_fees_per_gas, withdrawals, excess_gas, parent_beacon_block_root, requests_hash, block_access_list, and slot_number. Regular gas and blob gas are grouped into the GasAmounts (GasAmount is uint64) and BlobFeesPerGas (FeePerGas is uint256) structures. Each element of the transaction and receipt lists keeps the existing EIP-2718 typed envelope representation, and the withdrawal elements and block_access_list keep the existing RLP representation. receipts_root is the hash_tree_root of the block receipt list (ProgressiveList[ProgressiveByteList]), and requests_hash equals the hash_tree_root of the ExecutionRequests used by the consensus layer.

The header is derived as an SSZ summary

No separate header format is kept. The execution block header is derived as an SSZ summary of the ExecutionPayload, with the transactions, withdrawals, and block_access_list fields each replaced by their hash_tree_root. The ExecutionPayload defined this way replaces the ExecutionPayload definition in the consensus layer spec (gloas) and is delivered inside the ExecutionPayloadEnvelope. Because both layers use the same data structure, it also becomes easier to share data structures in combined consensus and execution implementations.

Block hash is unified on hash_tree_root

The execution block hash is computed as ExecutionPayload.hash_tree_root() in every context. This covers the BLOCKHASH opcode, the blockHash field in JSON-RPC, devp2p networking, and the consensus layer's BeaconState.latest_block_hash and ExecutionPayloadBid.block_hash references. The SSZ block hash is SHA256-based and shares a namespace with the existing keccak256-based block hash, but the source explains that the two hash algorithms are fundamentally different, so there is no meaningful collision risk.

logsBloom is filled with all ones

The logsBloom field in JSON-RPC block responses MUST be set to a value with every bit set to 1 (256 bytes of 0xff). The inefficient logs bloom mechanism is being dropped with the SSZ transition as the occasion. An all-ones value means "the log you want might be here," so it pushes consumers to check every receipt directly. An all-zeros value, on the other hand, would signal that there are definitely no logs and could cause consumers to miss logs, so it is not used.

Gains for the Engine API and proofs

If all exchanged data supports SSZ, the Engine API can move from text JSON to binary SSZ encoding. The size of exchanged data drops by about 50%, and encoding and parsing efficiency improves significantly. Because the block hash is shared, the redundant block_hash field can be removed and ForkDigest context encoding can be adopted. For proofs, individual fields can be proven without the whole block. Because EIP-7495 ProgressiveContainer and EIP-7916 ProgressiveList are used, generalized indices stay stable even as the block definition evolves, and lists can grow without hardcoded limits. Proofs stay forward compatible as long as the meaning of a field does not change, which reduces the maintenance burden on smart contracts and verification applications.

Compatibility with existing contracts

This EIP breaks compatibility for smart contracts that depend on the existing binary block header format. This includes "generic" implementations that assume headers share a common prefix and linearly keccak256-hash the entire data. Meanwhile, consumers that read logsBloom over JSON-RPC will see an all-ones value on SSZ blocks, which is backward-compatible behavior, and the logsBloom in receipts is not affected by this EIP.

Status: Draft, excluded from Hegotá

This proposal is in Draft status, so its contents may still change. In core developer calls, EIP-7807, a reduced version that computes only the block hash the SSZ way, was accepted as a Proposed for Inclusion (PFI) candidate for Hegotá at ACDE #242 on 07.30. The reason was that the consensus layer can compute the block hash itself, which improves sync. However, at ACDE #245 on 09.10 it was decided to exclude it (DFI). The judgment was that all code that reads headers and blocks would have to change, so the blast radius is large while the user benefit is small.

Original EIP · ← EIPs