Part 3: Annotated Specification
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.
ExecutionPayload is contained in the
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
noncewere not carried over from Eth1 blocks as they were specific to the proof of work mechanism.
fee_recipientis 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_recipientto specify where the appropriate transaction fees for the block are to be sent. Under proof of work this was the same address as the
COINBASEaddress 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
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_randaofield gives the execution layer access to the beacon chain's randomness. This is better, but still not of cryptographic quality.
block_numberin 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_hashis 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_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.
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.
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
The most recent
ExecutionPayloadHeader is stored in the beacon state.