Abstract
This EIP fundamentally changes the way an Ethereum block is validated by decoupling the execution validation from the consensus validation both logically as well as temporally. It does so by introducing a new in-protocol entity called builders and adding a new duty (submitting payload timeliness attestations) to Ethereum validators. The ExecutionPayload field of the BeaconBlockBody is removed and instead it is replaced by a signed commitment (a SignedExecutionPayloadBid object) from a builder to later reveal the corresponding execution payload. This commitment specifies in particular the blockhash of the execution block, the root of the execution requests it will produce, and a value to be paid to the beacon block proposer. When processing the BeaconBlock, the committed value is recorded as a pending payment from the builder's beacon chain balance. This payment is turned into a withdrawal to an address, of the proposer's choosing, in the execution layer, either when the corresponding execution payload is included on-chain or when enough attesters of that slot have voted for the beacon block. A subset of validators in the beacon committee is assigned to the Payload Timeliness Committee (PTC), these validators are tasked to attest (by broadcasting a PayloadAttestationMessage) to whether the corresponding builder has revealed the committed execution payload (with the right blockhash) in a timely fashion and whether the corresponding blob data was available according to their view. PTC members are not required to validate the execution payload, execution validation is thus deferred until the next beacon block validation. The consensus layer effects of the execution payload (its execution requests and the builder payment) are themselves applied when processing the next beacon block.
Builders are new staked entities that do not validate the beacon chain. They are tracked in a separate registry in the beacon state, they do not take part in any committee, they are not subject to the usual validator deposit and exit churn/queues and can be staked for as little as 1 ETH. Builders deposit, top up, and exit exclusively through two new system contracts deployed in the execution layer, whose requests are passed to the consensus layer as new EIP-7685 request types. While the in-protocol payment via withdrawals is trustlessly deducted from the builder's stake, the protocol also accommodates for trusted payments in the form of promises to be fulfilled elsewhere.
Motivation
This EIP solves a different set of unrelated important problems.
- An overwhelming majority of beacon block proposers outsource the construction of the execution payload within their blocks to a third party (henceforth called a builder). In order to do so, they request the hash tree root (HTR) of a promised execution payload and submit a
SignedBlindedBeaconBlockto a trusted party that is tasked with replacing the HTR with the full execution payload (received from the builder) before broadcasting. This EIP allows for a trust-free fair exchange between the beacon block proposer and the builder, guaranteeing that an honest beacon block proposer will receive payment from the builder regardless of the latter's actions and that the honest builder's payload will be the canonical head of the chain regardless of the proposer's action. - Currently, validators have the time between receiving the full beacon block (including an execution payload) and the attesting deadline (one third of the slot in Ethereum mainnet) to perform both consensus and execution state transition functions, check blob data availability and evaluate the new head of the blockchain. The remainder of the slot time is spent doing less CPU-intense and critical task. By separating the validation of the execution and consensus part of the block, validators are only tasked to perform the consensus state transition function in this critical time before attesting, while execution and data availability validation is deferred for most of the remainder of the slot (between the builder's reveal time and the next attestation deadline).
- By removing the full execution payload size from the consensus block, it allows for faster network propagation on the critical path.
- It removes the increased reorg likeliness of including blob transactions in blocks given the natural increase in timelines for data availability checks and the fact that the builder, who is now responsible for producing and broadcasting the blob data column sidecars, may broadcast them even before attestations for the beacon block have been released.
- It prevents validators from missing attestations and strengthens the weight properties of fork choice in the event that builders produce invalid payloads.
- It removes the need to use trusted middleware in order to delegate block construction to a builder.
Specification
Execution Layer
Two new system contracts are deployed in the execution layer:
- A builder deposit contract, through which a builder registers a new BLS public key or tops up an existing one. Each successful call produces a
BuilderDepositRequestwith fieldspubkey,withdrawal_credentials,amountandsignature, wheresignatureis a proof of possession of the BLS key over theDepositMessageunder the new domainDOMAIN_BUILDER_DEPOSIT. - A builder exit contract, through which a builder requests to exit. Each successful call produces a
BuilderExitRequestwith fieldssource_address(the sender of the transaction) andpubkey.
These requests are added to the EIP-7685 request list of the execution block with request types BUILDER_DEPOSIT_REQUEST_TYPE = 0x03 and BUILDER_EXIT_REQUEST_TYPE = 0x04 respectively. At most MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD = 64 builder deposit requests and MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD = 16 builder exit requests can be included per execution payload. The contract addresses, interface, and fee mechanisms are specified in a companion EIP.
No other changes are required.
Consensus Layer
The full consensus changes can be found in the consensus-specs Github repository. They are split between:
- Beacon Chain changes.
- Fork choice changes.
- P2P changes.
- Honest validator guide changes.
- A new honest builder guide.
- Fork logic changes.
A summary of the main changes is included below, the Rationale section contains an explanation for most of the design decisions around these changes. The same fork introduces forward compatible SSZ types; as such, several of the list types and containers below are defined in the consensus specs as progressive lists and progressive containers. This distinction is omitted in this summary.
Beacon chain changes
Types
| Name | SSZ equivalent | Description |
|---|---|---|
BuilderIndex | uint64 | Builder registry index |
Builders | ProgressiveList[Builder] | The builder registry |
BuilderPendingPayments | Vector[BuilderPendingPayment, 2 * SLOTS_PER_EPOCH] | Pending payments of the previous and current epoch |
BuilderPendingWithdrawals | ProgressiveList[BuilderPendingWithdrawal] | Queue of builder withdrawals |
BuilderDepositRequests | ProgressiveList[BuilderDepositRequest] | Builder deposit requests of an execution payload |
BuilderExitRequests | ProgressiveList[BuilderExitRequest] | Builder exit requests of an execution payload |
ExecutionPayloadAvailability | Bitvector[SLOTS_PER_HISTORICAL_ROOT] | Payload availability of recent slots |
PayloadAttestations | ProgressiveList[PayloadAttestation] | Payload attestations included in a beacon block |
PayloadTimelinessCommittee | Vector[ValidatorIndex, PTC_SIZE] | The PTC of a slot |
PayloadTimelinessCommitteeIndices | List[ValidatorIndex, PTC_SIZE] | Indices of participating PTC members |
PayloadTimelinessCommitteeBits | Bitvector[PTC_SIZE] | Participation bits of the PTC |
PayloadTimelinessCommitteeWindow | Vector[PayloadTimelinessCommittee, (MIN_SEED_LOOKAHEAD + 2) * SLOTS_PER_EPOCH] | Cached PTCs from the previous to the lookahead epoch |
Constants
Index flags
| Name | Value | Description |
|---|---|---|
BUILDER_INDEX_FLAG | uint64(2**40) | Bitwise flag which indicates that a ValidatorIndex should be treated as a BuilderIndex |
Domains
| Name | Value |
|---|---|
DOMAIN_BEACON_BUILDER | DomainType('0x0B000000') |
DOMAIN_PTC_ATTESTER | DomainType('0x0C000000') |
DOMAIN_PROPOSER_PREFERENCES | DomainType('0x0D000000') |
DOMAIN_BUILDER_DEPOSIT | DomainType('0x0E000000') |
Misc
| Name | Value | Description |
|---|---|---|
BUILDER_INDEX_SELF_BUILD | BuilderIndex(UINT64_MAX) | Value which indicates the proposer built the payload |
BUILDER_PAYMENT_THRESHOLD_NUMERATOR | uint64(6) | |
BUILDER_PAYMENT_THRESHOLD_DENOMINATOR | uint64(10) |
Withdrawal prefixes
| Name | Value | Description |
|---|---|---|
BUILDER_WITHDRAWAL_PREFIX | Bytes1('0xB0') | Withdrawal credential prefix for a builder |
Builder versions
| Name | Value | Description |
|---|---|---|
PAYLOAD_BUILDER_VERSION | uint8(0) | Version of builders allowed to submit payloads |
Execution layer triggered requests
| Name | Value |
|---|---|
BUILDER_DEPOSIT_REQUEST_TYPE | Bytes1('0x03') |
BUILDER_EXIT_REQUEST_TYPE | Bytes1('0x04') |
Preset
Misc
| Name | Value |
|---|---|
PTC_SIZE | uint64(2**9) (= 512) |
Max operations per block
| Name | Value |
|---|---|
MAX_PAYLOAD_ATTESTATIONS | 4 |
Execution
| Name | Value |
|---|---|
MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD | uint64(2**6) (= 64) |
MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD | uint64(2**4) (= 16) |
Withdrawals processing
| Name | Value |
|---|---|
MAX_BUILDERS_PER_WITHDRAWALS_SWEEP | 2**14 (= 16,384) |
Configuration
Time parameters
| Name | Value | Unit |
|---|---|---|
MIN_BUILDER_WITHDRAWABILITY_DELAY | uint64(2**6) (= 64) | epochs |
Containers
New containers
Builder
class Builder(Container):
pubkey: BLSPubkey
version: uint8
execution_address: ExecutionAddress
balance: Gwei
deposit_epoch: Epoch
withdrawable_epoch: EpochBuilderPendingPayment
class BuilderPendingPayment(Container):
weight: Gwei
withdrawal: BuilderPendingWithdrawal
proposer_index: ValidatorIndexBuilderPendingWithdrawal
class BuilderPendingWithdrawal(Container):
fee_recipient: ExecutionAddress
amount: Gwei
builder_index: BuilderIndexBuilderDepositRequest
class BuilderDepositRequest(Container):
pubkey: BLSPubkey
withdrawal_credentials: Bytes32
amount: Gwei
signature: BLSSignatureBuilderExitRequest
class BuilderExitRequest(Container):
source_address: ExecutionAddress
pubkey: BLSPubkeyPayloadAttestationData
class PayloadAttestationData(Container):
beacon_block_root: Root
slot: Slot
payload_present: boolean
blob_data_available: booleanPayloadAttestation
class PayloadAttestation(Container):
aggregation_bits: PayloadTimelinessCommitteeBits
data: PayloadAttestationData
signature: BLSSignaturePayloadAttestationMessage
class PayloadAttestationMessage(Container):
validator_index: ValidatorIndex
data: PayloadAttestationData
signature: BLSSignatureIndexedPayloadAttestation
class IndexedPayloadAttestation(Container):
attesting_indices: PayloadTimelinessCommitteeIndices
data: PayloadAttestationData
signature: BLSSignatureExecutionPayloadBid
class ExecutionPayloadBid(Container):
parent_block_hash: Hash32
parent_block_root: Root
block_hash: Hash32
prev_randao: Bytes32
fee_recipient: ExecutionAddress
gas_limit: uint64
builder_index: BuilderIndex
slot: Slot
value: Gwei
execution_payment: Gwei
blob_kzg_commitments: BlobKZGCommitments
execution_requests_root: RootSignedExecutionPayloadBid
class SignedExecutionPayloadBid(Container):
message: ExecutionPayloadBid
signature: BLSSignatureExecutionPayloadEnvelope
class ExecutionPayloadEnvelope(Container):
payload: ExecutionPayload
execution_requests: ExecutionRequests
builder_index: BuilderIndex
beacon_block_root: Root
parent_beacon_block_root: RootSignedExecutionPayloadEnvelope
class SignedExecutionPayloadEnvelope(Container):
message: ExecutionPayloadEnvelope
signature: BLSSignatureModified containers
ExecutionRequests
class ExecutionRequests(Container):
deposits: DepositRequests
withdrawals: WithdrawalRequests
consolidations: ConsolidationRequests
# [New in Gloas]
builder_deposits: BuilderDepositRequests
# [New in Gloas]
builder_exits: BuilderExitRequestsBeaconBlockBody
Note: The removed fields (execution_payload, blob_kzg_commitments, and
execution_requests) now exist in ExecutionPayloadEnvelope.
class BeaconBlockBody(Container):
randao_reveal: BLSSignature
eth1_data: Eth1Data
graffiti: Bytes32
proposer_slashings: ProposerSlashings
attester_slashings: AttesterSlashings
attestations: Attestations
deposits: Deposits
voluntary_exits: VoluntaryExits
sync_aggregate: SyncAggregate
# [Modified in Gloas:EIP7732]
# Removed `execution_payload`
bls_to_execution_changes: BLSToExecutionChanges
# [Modified in Gloas:EIP7732]
# Removed `blob_kzg_commitments`
# [Modified in Gloas:EIP7732]
# Removed `execution_requests`
# [New in Gloas:EIP7732]
signed_execution_payload_bid: SignedExecutionPayloadBid
# [New in Gloas:EIP7732]
payload_attestations: PayloadAttestations
# [New in Gloas:EIP7732]
parent_execution_requests: ExecutionRequestsBeaconState
class BeaconState(Container):
genesis_time: uint64
genesis_validators_root: Root
slot: Slot
fork: Fork
latest_block_header: BeaconBlockHeader
block_roots: BlockRoots
state_roots: StateRoots
historical_roots: HistoricalRoots
eth1_data: Eth1Data
eth1_data_votes: Eth1DataVotes
eth1_deposit_index: uint64
validators: Validators
balances: Balances
randao_mixes: RandaoMixes
slashings: Slashings
previous_epoch_participation: EpochParticipation
current_epoch_participation: EpochParticipation
justification_bits: JustificationBits
previous_justified_checkpoint: Checkpoint
current_justified_checkpoint: Checkpoint
finalized_checkpoint: Checkpoint
inactivity_scores: InactivityScores
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# [Modified in Gloas:EIP7732]
# Removed `latest_execution_payload_header`
# [New in Gloas:EIP7732]
latest_block_hash: Hash32
next_withdrawal_index: WithdrawalIndex
next_withdrawal_validator_index: ValidatorIndex
historical_summaries: HistoricalSummaries
deposit_requests_start_index: uint64
deposit_balance_to_consume: Gwei
exit_balance_to_consume: Gwei
earliest_exit_epoch: Epoch
consolidation_balance_to_consume: Gwei
earliest_consolidation_epoch: Epoch
pending_deposits: PendingDeposits
pending_partial_withdrawals: PendingPartialWithdrawals
pending_consolidations: PendingConsolidations
proposer_lookahead: ProposerLookahead
# [New in Gloas:EIP7732]
builders: Builders
# [New in Gloas:EIP7732]
next_withdrawal_builder_index: BuilderIndex
# [New in Gloas:EIP7732]
execution_payload_availability: ExecutionPayloadAvailability
# [New in Gloas:EIP7732]
builder_pending_payments: BuilderPendingPayments
# [New in Gloas:EIP7732]
builder_pending_withdrawals: BuilderPendingWithdrawals
# [New in Gloas:EIP7732]
latest_execution_payload_bid: ExecutionPayloadBid
# [New in Gloas:EIP7732]
payload_expected_withdrawals: Withdrawals
# [New in Gloas:EIP7732]
ptc_window: PayloadTimelinessCommitteeWindowThe BeaconState container is modified with the addition of:
latest_block_hash, of typeHash32, to track the blockhash of the last execution payload in the blockchain.builders, of typeBuildersto track the new in-protocol staked builders.next_withdrawal_builder_indexof typeBuilderIndexto help tracking builder withdrawals.execution_payload_availability, of typeExecutionPayloadAvailabilityto track the presence of execution payloads on the canonical chain.builder_pending_payments, of typeBuilderPendingPaymentsto track pending payments from builders to proposers before the execution payload has been processed.builder_pending_withdrawals, of typeBuilderPendingWithdrawalsto track pending withdrawals to the execution layer with the builders' payments.latest_execution_payload_bid, of typeExecutionPayloadBid, the bid committed by the latest beacon block.payload_expected_withdrawalsof typeWithdrawalsto track the latest withdrawals that were deducted in the consensus layer and need to still be honored in the Execution Layer.ptc_windowof typePayloadTimelinessCommitteeWindowcaching the PTC assignments for the previous, current and lookahead epochs.
The BeaconBlockBody is modified with the addition of:
signed_execution_payload_bidof typeSignedExecutionPayloadBidwith the builder's commitment.payload_attestationsof typePayloadAttestationsa list of PTC attestations from the previous slot.parent_execution_requestsof typeExecutionRequeststhe execution requests produced by the parent's execution payload, if the block builds on top of it.
The ExecutionPayloadHeader object is changed (and renamed to ExecutionPayloadBid) to only track the minimum information needed to commit to a builder's payload. In particular, it commits to the hash tree root of the execution requests that the payload will produce.
State transition
The state transition function is broken in two parts, first importing a signed beacon block and then importing a signed execution payload envelope. The beacon state is only modified by beacon blocks: the validity of an envelope against the post-state of its beacon block is checked by verify_execution_payload_envelope, a pure verification function, while the consensus layer effects of the payload are applied when processing the next beacon block.
Block processing is modified as follows:
- A new function
process_parent_execution_payloadis called first. If the block's bid builds on the parent's payload (that isbid.parent_block_hashmatches the parent's committedblock_hash) thenblock.body.parent_execution_requestsis verified against theexecution_requests_rootcommitted in the parent's bid, andapply_parent_execution_payloadis called. This function settles the parent's builder payment, processes the parent's deposit, withdrawal, consolidation, builder deposit and builder exit requests, sets the parent's bit inexecution_payload_availabilityand updateslatest_block_hash. If the parent's payload is not included,parent_execution_requestsmust be empty. process_withdrawalsis modified as follows. Withdrawals are obtained directly from the beacon state instead of the execution payload. They are immediately deducted from the beacon chain and the beacon statepayload_expected_withdrawalsis updated with this list. The next execution payload MUST include withdrawals matchingstate.payload_expected_withdrawals. If the parent beacon block's payload was not included, no new withdrawals are processed. Builder withdrawals are processed first, from thebuilder_pending_withdrawalsqueue, followed by pending partial withdrawals, a sweep of exited builders, and finally the usual validator sweep.process_execution_payloadis removed fromprocess_block. Instead a new functionprocess_execution_payload_bidis included, this function validates theSignedExecutionPayloadBidincluded in theBeaconBlockBody, ensures that the builder is active and that the payment from the builder's balance can be covered (the builder's balance must exceedMIN_DEPOSIT_AMOUNTplus all its pending payments and withdrawals, by at least the bid's value), and adds aBuilderPendingPaymentobject to the beacon state. Proposers that build their own payload useBUILDER_INDEX_SELF_BUILDas the builder index, a zero value and the point at infinity as signature.process_deposit_request,process_withdrawal_requestandprocess_consolidation_requestare removed fromprocess_operationsand deferred until the next beacon block viaprocess_parent_execution_payload.- New
process_builder_deposit_requestandprocess_builder_exit_requestprocess the new builder requests, also fromprocess_parent_execution_payload. A builder deposit request with a new public key and a valid proof of possession underDOMAIN_BUILDER_DEPOSITadds a new builder to the registry, possibly reusing the index of a builder that has exited and was fully withdrawn. A request for an existing public key tops up that builder's balance. A builder exit request is only effective if it was sent from the builder's execution address, the builder is active and the builder has no pending payments or withdrawals. A builder is active if its deposit epoch is finalized and it has not initiated an exit. - A new
process_payload_attestationis added toprocess_operations, this function validates the payload timeliness attestations broadcast by the PTC members. process_attestationis modified to track the weight of same-slot attestations towards theBuilderPendingPaymentof that slot.slash_validatoris modified to remove any pending builder payments for blocks proposed by the slashed validator.
A new beacon state getter get_ptc returns the PTC members for a given slot from the cached ptc_window. The PTC is computed by compute_ptc by sampling PTC_SIZE validators, weighted by effective balance, from the concatenation of the beacon committees of the slot. The same balance weighted selection helper is used for proposer selection.
Epoch processing is modified by addition of two new helper functions: process_builder_pending_payments, that processes the builder pending payments from those payloads that were not included in the canonical chain, but whose beacon block received at least BUILDER_PAYMENT_THRESHOLD_NUMERATOR / BUILDER_PAYMENT_THRESHOLD_DENOMINATOR of the per slot active balance in same-slot attestations; and process_ptc_window that rotates the cached PTC assignments.
Although there is no change in the AttestationData object, the index field which is unused since the Electra fork, is now repurposed to signal payload availability. The value of 0 is used when attesting to the current beacon block or a past beacon block without a payload present, and the value of 1 is used to attest to a past beacon block with a payload present. The timely head flag is only awarded if this value matches the payload availability recorded in the beacon state.
Fork transition
At the fork, the builder registry is initialized empty. Pending deposits in the beacon state with BUILDER_WITHDRAWAL_PREFIX withdrawal credentials, whose public key does not belong to an existing or pending validator, are converted to builders. Existing validators are never converted. After the fork, builders can only be registered via builder deposit requests.
Fork-Choice changes
Forkchoice is changed substantially to deal with the fact that forkchoice nodes can have different payload content.
- Fork choice nodes are now pairs
(root, payload_status)where the payload status is one ofPENDING,EMPTYorFULL. APENDINGnode has as children itsEMPTYvariant, and itsFULLvariant only if the execution payload has been received and verified locally. - Attestations vote for a payload status with the
indexfield,LatestMessagetracks the slot (instead of the epoch) and whether the payload was present. - The decision of whether the previous slot's payload is part of the canonical chain is not taken by attestation weight but by a tiebreaker: the
FULLnode is preferred if the payload was verified and either the PTC voted that the payload was timely and the blob data available (with more thanPAYLOAD_TIMELY_THRESHOLDandDATA_AVAILABILITY_TIMELY_THRESHOLDvotes respectively, both equal toPTC_SIZE // 2) or the current proposer boosted block builds on it. - A new handler
on_execution_payload_envelopeverifies the envelope and, in particular, it is now responsible for the blob data availability check, which is removed fromon_block. - A new handler
on_payload_attestation_messagetracks the PTC votes, either received through gossip or included in beacon blocks. - Block timeliness is tracked with respect to both the attestation deadline and the payload attestation deadline. Proposer boost is not applied to a block whose parent is from the previous slot, is weak, and whose proposer equivocated with another block that was timely.
The following deadlines are expressed in basis points of the slot duration.
| Name | Value |
|---|---|
ATTESTATION_DUE_BPS_GLOAS | 2500 |
AGGREGATE_DUE_BPS_GLOAS | 5000 |
SYNC_MESSAGE_DUE_BPS_GLOAS | 2500 |
CONTRIBUTION_DUE_BPS_GLOAS | 5000 |
PAYLOAD_DUE_BPS | 5000 |
PAYLOAD_ATTESTATION_DUE_BPS | 7500 |
P2P changes
- A new global topic
execution_payload_bidfor broadcastingSignedExecutionPayloadBidmessages (builder bids). Only the highest bid for a given slot and parent is forwarded, bids must match the proposer's preferences and must not carry anexecution_payment. - A new global topic
execution_payloadfor broadcastingSignedExecutionPayloadEnvelopeobjects. - A new global topic
payload_attestation_messagefor broadcastingPayloadAttestationMessageobjects. - A new global topic
proposer_preferencesfor broadcastingSignedProposerPreferencesobjects. Proposers use these messages to announce, for their upcoming proposal slots, the fee recipient and target gas limit that builders' bids should use. - Attestations with
indexlarger than1are rejected, attestations withindexequal to1are ignored until the corresponding execution payload has been seen. DataColumnSidecarno longer carries the KZG commitments, the signed beacon block header nor its inclusion proof. Instead it carries theslotandbeacon_block_root, and it is verified against the commitments in the beacon block's bid. Data column sidecars are produced and broadcast by the builder.- New request/response methods
ExecutionPayloadEnvelopesByRangeandExecutionPayloadEnvelopesByRoot.
class ProposerPreferences(Container):
dependent_root: Root
proposal_slot: Slot
validator_index: ValidatorIndex
fee_recipient: ExecutionAddress
target_gas_limit: uint64class SignedProposerPreferences(Container):
message: ProposerPreferences
signature: BLSSignatureclass DataColumnSidecar(Container):
index: ColumnIndex
column: DataColumn
kzg_proofs: KZGProofs
slot: Slot
beacon_block_root: RootHonest validator and builder changes
- Validators assigned to the PTC broadcast a
PayloadAttestationMessagebeforePAYLOAD_ATTESTATION_DUE_BPSinto the slot, signaling whether the payload envelope was seen beforePAYLOAD_DUE_BPSand whether the blob data is available. - Proposers may broadcast
SignedProposerPreferencesfor their upcoming proposal slots. Bids for slots without proposer preferences are not propagated in the P2P layer. - Proposers select a bid, either from the P2P network or by other means, or build the payload themselves. They decide whether to build on top of the parent's payload by their fork choice view and the PTC votes.
- Builders produce the signed bid, and later the
SignedExecutionPayloadEnvelopeand the data column sidecars. An honest builder may withhold its payload if the beacon block that included its bid was not timely and is not the head in the builder's view.
Engine API
No changes are needed beyond those required to support the new execution request types in the executionRequests lists of engine_newPayload and engine_getPayload.
Rationale
Staked builders
Being a builder is a new type of entity tracked in the beacon state. As such builders are staked in the beacon chain. This allows for in-protocol trustless enforcement of the builder's payment to the proposer. Alternatively, payment could be enforced in the Execution Layer (EL) at the cost of adding the corresponding EL consensus-changing logic. Payments in the EL have the advantage of not requiring the builder to periodically submit deposit transactions to replenish their balance. Both systems require availability of funds before the payload is revealed: in the Consensus Layer (CL) this is done by getting builders to stake. In the EL this is done with a balance check and a payment transaction. This transaction can be checked without executing the payload only if it the first transaction of the block.
Builders are not validators: they do not attest, propose, or take part in any committee, and their stake is not counted towards the total active balance. They are kept in their own registry with their own index space, and their withdrawals use the same Withdrawal container as validators by flagging the index with BUILDER_INDEX_FLAG. Since builder stake has no bearing on the security of the beacon chain, builders are not subject to the validator activation and exit churn. A builder becomes active as soon as its deposit is finalized, and it can withdraw MIN_BUILDER_WITHDRAWABILITY_DELAY epochs after initiating an exit.
Builders onboard through dedicated contracts in the EL instead of the validator deposit contract. This keeps builder deposits out of the validator pending deposits queue and its churn, and allows builders to top up their balances quickly, which is needed since every payment depletes their stake. The proof of possession is signed with a dedicated domain, DOMAIN_BUILDER_DEPOSIT, so that validator deposit signatures and builder deposit signatures cannot be replayed against the other contract. Builder exits are authorized by the builder's execution address, analogous to validator execution layer triggered exits, so that builders can exit without access to their BLS key. Exits are only allowed when the builder has no outstanding payments, so that a builder cannot avoid paying a proposer by exiting.
Builder indices are reused once a builder has exited and its balance has been fully withdrawn. This keeps the size of the builder registry bounded by the number of concurrently active builders. Deposits to an exited builder that is still in the registry are withdrawn to its execution address by the builders sweep, exited builders are never reactivated.
Delayed validation
The Payload Timeliness Committee members do not need to validate the execution payload before attesting to it. They perform basic checks such as verifying the builder's signature, and the correct blockhash is included. This takes away the full execution payload validation from the hot path of validation of an Ethereum block, giving the next proposer half a slot (from PAYLOAD_DUE_BPS to the end of the slot) to validate the payload and every other validator three quarters of a slot (until ATTESTATION_DUE_BPS_GLOAS into the next slot). From a user UX perspective, a transaction included in slot N by the builder is not widely validated until the proposer of slot N+1 releases their beacon block on top of block N first and the attesters of slot N+1 vote on this beacon block as the head of the chain.
Deferred processing of the execution payload
The execution payload envelope does not commit to a post-state root, and processing it does not modify the beacon state. Instead, the bid commits to the root of the execution requests that the payload produces, and the next beacon block, if it builds on top of the payload, includes these requests in parent_execution_requests. The consensus layer effects of the payload (settling the builder payment, processing its execution requests, and marking it as available) are applied at the beginning of the next block's processing.
This design keeps a single beacon state per beacon block, which greatly simplifies fork choice and state management in clients: there is no need to keep, regenerate or serve separate empty and full post-states for the same block. It also removes the need for the builder to compute the consensus layer state transition in order to produce the envelope. The cost of this simplicity is that execution requests are processed at the slot of the child block rather than the slot of the parent.
Builder payments
The proposer's payment is recorded in the beacon state as a BuilderPendingPayment when the beacon block is processed. It is turned into a withdrawal either when the payload is included on-chain or, if the payload is never included, at the epoch transition when the beacon block received same-slot attestations from at least 60% of the per-slot active balance. The latter guarantees the proposer's unconditional payment when the beacon block was timely, while the former protects the proposer when the payload was included regardless of attestation weight. Pending payments of a proposer that is slashed are removed. A proposer that equivocates by including different bids in different beacon blocks for the same slot cannot have all of them included on-chain, and its builders should not be charged for blocks that cannot contain their payloads.
Fork choice
The following features of fork choice are guaranteed under specified margins of security:
- Proposer unconditional payment.
- Builder reveal safety.
- Builder withhold safety.
Proposer unconditional payment refers to the following. An Ethereum slot can be either:
- Full: both the beacon block and the execution payload have been revealed and included on-chain.
- Skipped: No beacon block (and therefore no execution payload) has been included on-chain for this slot.
- Empty: The beacon block has been included on-chain, but the committed execution payload has not.
Proposer unconditional payment refers to the fact that in the third scenario the beacon block proposer received payment from the corresponding builder.
Builder reveal safety refers to the fact that if the builder acted honestly and revealed a payload in a timely fashion (as attested by the PTC) then the revealed payload will be included on-chain.
Builder withhold safety refers to the fact that if some beacon block containing a builder's commitment is withheld and revealed late, the builder will not be charged the value of the bid. In particular, the payload does not even need to be revealed in this case.
The precise method by which these safety mechanisms are enforced is by allowing attestations to also signal their view of the slot as in either of the above options Full, Skipped or Empty. For this, the index field in the AttestationData is used as explained above.
PTC equivocations
There is no penalty for PTC nor payload equivocation (that is revealing the right payload and also a withheld message at the same time). A collusion of a builder controlling network partition with a single malicious PTC member could cause a split view by achieving consensus both on payload withheld and a payload present. This could be mitigated by setting PAYLOAD_TIMELY_THRESHOLD to be 2/3 of the PTC, in which case the malicious operator would have to control at least 33% of the PTC.
Another mitigation mechanism is to add new slashing conditions for payload equivocation or PTC equivocations (both are signed messages by validators).
Since this attack results in a split view at a cost for the builder (the payload is revealed and may not be included) this EIP opted for simplicity of implementation.
Withdrawals
Withdrawals from the beacon chain are complex in nature, they involve removing funds from one layer and crediting them on another, with different trigger mechanisms that can start from either layer. Before applying the consensus layer state transition function to a given beacon state pre_state and processing a given signed beacon block block, the set of withdrawals that are expected to be deducted from the beacon chain are completely determined by pre_state. Previous to this EIP the set of withdrawals that are credited on the execution layer are included in block. The block is deemed invalid if these withdrawals do not match. With the separation included in this EIP, these operations of deducting and crediting become asynchronous:
- When processing the beacon block, the withdrawals are deducted from the beacon chain.
- The set of withdrawals just deducted is committed to the beacon state
post_state. - When processing any execution payload whose parent beacon state is
post_state, the payload is deemed invalid if it doesn't include precisely the list of withdrawals committed topost_state.
This asynchronous mechanism has some consequences as slots may be empty as defined above. In these cases, the consensus layer does not process any more withdrawals until an execution payload has fulfilled the outstanding ones. An alternative design would be to defer all of withdrawal processing to the execution payload validation phase. This has the advantage of not needing to track the fulfilled withdrawals on the beacon chain. The logic changes when several payloads are missing, in which case balances on the beacon chain change and therefore a withdrawal that would be possible with the former mechanism may be different, or even impossible with the latter.
Unlike execution requests, which are deferred to the next beacon block, withdrawals are deducted immediately. Deferring the deduction could break the total supply invariant: state transitions between the beacon block and its child (for example consolidations processed at an epoch boundary) could reduce a validator's balance below the committed withdrawal amount, while the execution layer would still mint the full committed amount.
Builder payments are processed as withdrawals before any other kind of withdrawal, so that proposers are paid promptly. Builders that have exited are swept, also before validators, with a bound of MAX_BUILDERS_PER_WITHDRAWALS_SWEEP builders per block.
Compatible designs
Inclusion lists
This EIP is fully compatible with forkchoice enforced inclusion lists or similar.
Slot auctions
A simple change to this EIP is to remove the blockhash commitment from the SignedExecutionPayloadBid. This allows the builder to commit any payload to the slot. A preliminary security analysis shows that payload equivocation does not weaken fork choice's FFG. Some advantages of Slot auctions include:
- Better user experience as any submitted transaction can be included in the next block (with block auctions a transaction sent in the first half of the slot can only be included in the following block).
- Longer continuous time to build blocks.
- Better compatibility designs with fork choice enforced inclusion list proposals.
The version field in the Builder container allows for different types of builders to coexist in the future.
Backwards Compatibility
This EIP introduces backward incompatible changes to the block validation rule set on the consensus layer and must be accompanied by a hard fork.
Security Considerations
Free option problem
Economically rational but malicious (as defined by the honest builder guide) builders may chose to withhold their payload in the event that it would be profitable for them. This could result in missed slots and degradation of user experience of Ethereum. Some preliminary data in the form of simulation suggests that the number of these occurrences may be noticeable. Some mitigations were proposed in the form of variable penalties for builders that fail to include their payload envelopes in the canonical chain.
Builder safety
- A colluding set of proposers and attesters controlling consecutive blocks and more than 20% of the total stake can reorg a builder's payload and force it to pay the bid's value.
- There is no possible unbundling of the builder's payload in the same slot, that is, if the builder reveals a payload for the head of the chain, no other payload is possible for the current slot.
Malicious PTC
The expected time for a malicious attacker, controlling 35% of the total stake, to have a majority control on the PTC is 205 000 years.
Builder index reuse
Builder indices are reusable, and a public key that appeared in the builder registry in the past may be registered again under a different index. Client implementations that cache builder public keys or indices must account for this behavior.
Copyright
Copyright and related rights waived via CC0.
