Part 3: Annotated Specification

Containers

Execution

ExecutionPayload

class ExecutionPayload(Container):
    # Execution block header fields
    parent_hash: Hash32
    fee_recipient: ExecutionAddress  # 'beneficiary' in the yellow paper
    state_root: Bytes32
    receipts_root: Bytes32
    logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
    prev_randao: Bytes32  # 'difficulty' in the yellow paper
    block_number: uint64  # 'number' in the yellow paper
    gas_limit: uint64
    gas_used: uint64
    timestamp: uint64
    extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
    base_fee_per_gas: uint256
    # Extra payload fields
    block_hash: Hash32  # Hash of execution block
    transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
    withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]  # [New in Capella]

Since the Merge, blocks on the beacon chain contain Ethereum transaction data, formerly known as Eth1 blocks, and now called execution payloads.

This is a significant change, and is what led to the name "The Merge".

  • Pre-Merge, there were two types of block in the Ethereum system:
    • Eth1 blocks contained users' transactions and were gossiped between Eth1 nodes;
    • Eth2 blocks (beacon blocks) contained only consensus information and were gossiped between Eth2 nodes.
  • Post-Merge, there is only one kind of block, the merged beacon block:
    • Beacon blocks contain execution payloads that in turn contain users' transactions. These blocks are gossiped only between consensus (Eth2) nodes.

The ExecutionPayload is contained in the BeaconBlock structure.

The fields of ExecutionPayload mostly reflect the old structure of Eth1 blocks as described in Ethereum's Yellow Paper1, section 4.3. Differences from the Eth1 block structure are noted in the comments.

The execution payload differs from an old Eth1 block in the following respects:

  • ommersHash (also known as uncle_hashes), difficulty, mixHash and nonce were not carried over from Eth1 blocks as they were specific to the proof of work mechanism.
  • fee_recipient is the Ethereum account address that will receive the unburnt portion of the transaction fees (the priority fees). This has been called various things at various times: the original Yellow Paper calls it beneficiary; EIP-1559 calls it author. In any case, the proposer of the block sets the fee_recipient to specify where the appropriate transaction fees for the block are to be sent. Under proof of work this was the same address as the COINBASE address that received the block reward. Under proof of stake, the block reward is credited to the validator's beacon chain balance, and the transaction fees are credited to the fee_recipient Ethereum address.
  • prev_randao replaces difficulty. The Eth1 chain did not have access to good quality randomness. Sometimes the block hash or difficulty of the block were used to seed randomness, but these were low quality. The prev_randao field gives the execution layer access to the beacon chain's randomness. This is better, but still not of cryptographic quality.
  • block_number in the execution layer is the block height in that chain, picking up from the Eth1 block height at the Merge. It increments by one for every beacon block produced. The beacon chain itself does not track block height, only slot number, which can differ from block height due to empty slots.
  • The execution payload block_hash is included. The consensus layer does not know how to calculate the root hashes of execution blocks, but needs access to them when checking that the execution chain is unbroken during execution payload processing.
  • Despite being flagged in the comments as an "extra payload field", a list of transactions was always part of Eth1 blocks. However, the list of ommers/uncles is no longer present.

Individual transactions are represented by the Transaction custom type. There can be up to MAX_TRANSACTIONS_PER_PAYLOAD of them in a single execution payload. The values of MAX_BYTES_PER_TRANSACTION and MAX_TRANSACTIONS_PER_PAYLOAD are huge, and suggest that an execution payload could be up to a petabyte in size. These sizes are specified only because SSZ List types require them. They will occupy only the minimum necessary space in practice.

The withdrawals field was added in the Capella upgrade. Withdrawal transactions are unusual in that they affect state on both the consensus side and the execution side. Uniquely, withdrawal transactions are the only data that's generated by the consensus layer but only communicated between nodes in execution payloads.

ExecutionPayloadHeader

class ExecutionPayloadHeader(Container):
    # Execution block header fields
    parent_hash: Hash32
    fee_recipient: ExecutionAddress
    state_root: Bytes32
    receipts_root: Bytes32
    logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
    prev_randao: Bytes32
    block_number: uint64
    gas_limit: uint64
    gas_used: uint64
    timestamp: uint64
    extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
    base_fee_per_gas: uint256
    # Extra payload fields
    block_hash: Hash32  # Hash of execution block
    transactions_root: Root
    withdrawals_root: Root  # [New in Capella]

The same as ExecutionPayload but with the transactions represented only by their root. By the magic of Merkleization, the hash tree root of an ExecutionPayloadHeader will be the same as the hash tree root of its corresponding ExecutionPayload.

The most recent ExecutionPayloadHeader is stored in the beacon state.


  1. This is intended to be a permalink to the Yellow Paper's "Berlin" edition, a pre-Merge version of the YP. At the time of writing, the YP has not been updated for the "London" upgrade and is therefore missing the EIP-1559 field base_fee_per_gas.

Created by Ben Edgington. Licensed under CC BY-SA 4.0. Published 2023-09-29 14:16 UTC. Commit ebfcf50.