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 asuncle_hashes
),difficulty
,mixHash
andnonce
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 itbeneficiary
; EIP-1559 calls itauthor
. In any case, the proposer of the block sets thefee_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 theCOINBASE
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 thefee_recipient
Ethereum address.prev_randao
replacesdifficulty
. 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. Theprev_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.