EIP.tools

EIP.tools

πŸ“’ Last CallStandards Track: Core

EIP-7732: Enshrined Proposer-Builder Separation

Separates the Ethereum block into consensus and execution parts and lets the consensus proposer choose the execution proposer

Authors
Created2024-06-28
Discussion Linkhttps://ethereum-magicians.org/t/eip-7732-enshrined-proposer-builder-separation-epbs/19634
Requires
Referenced by

Markdown

https://raw.githubusercontent.com/ethereum/EIPs/ma...

EIP-GPT summary

Contents
AbstractMotivationSpecificationExecution LayerConsensus LayerBeacon chain changesFork-Choice changesP2P changesHonest validator and builder changesEngine APIRationaleStaked buildersDelayed validationDeferred processing of the execution payloadBuilder paymentsFork choicePTC equivocationsWithdrawalsCompatible designsInclusion listsSlot auctionsBackwards CompatibilitySecurity ConsiderationsFree option problemBuilder safetyMalicious PTCBuilder index reuseCopyright

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 SignedBlindedBeaconBlock to 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 BuilderDepositRequest with fields pubkey, withdrawal_credentials, amount and signature, where signature is a proof of possession of the BLS key over the DepositMessage under the new domain DOMAIN_BUILDER_DEPOSIT.
  • A builder exit contract, through which a builder requests to exit. Each successful call produces a BuilderExitRequest with fields source_address (the sender of the transaction) and pubkey.

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:

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
NameSSZ equivalentDescription
BuilderIndexuint64Builder registry index
BuildersProgressiveList[Builder]The builder registry
BuilderPendingPaymentsVector[BuilderPendingPayment, 2 * SLOTS_PER_EPOCH]Pending payments of the previous and current epoch
BuilderPendingWithdrawalsProgressiveList[BuilderPendingWithdrawal]Queue of builder withdrawals
BuilderDepositRequestsProgressiveList[BuilderDepositRequest]Builder deposit requests of an execution payload
BuilderExitRequestsProgressiveList[BuilderExitRequest]Builder exit requests of an execution payload
ExecutionPayloadAvailabilityBitvector[SLOTS_PER_HISTORICAL_ROOT]Payload availability of recent slots
PayloadAttestationsProgressiveList[PayloadAttestation]Payload attestations included in a beacon block
PayloadTimelinessCommitteeVector[ValidatorIndex, PTC_SIZE]The PTC of a slot
PayloadTimelinessCommitteeIndicesList[ValidatorIndex, PTC_SIZE]Indices of participating PTC members
PayloadTimelinessCommitteeBitsBitvector[PTC_SIZE]Participation bits of the PTC
PayloadTimelinessCommitteeWindowVector[PayloadTimelinessCommittee, (MIN_SEED_LOOKAHEAD + 2) * SLOTS_PER_EPOCH]Cached PTCs from the previous to the lookahead epoch
Constants
Index flags
NameValueDescription
BUILDER_INDEX_FLAGuint64(2**40)Bitwise flag which indicates that a ValidatorIndex should be treated as a BuilderIndex
Domains
NameValue
DOMAIN_BEACON_BUILDERDomainType('0x0B000000')
DOMAIN_PTC_ATTESTERDomainType('0x0C000000')
DOMAIN_PROPOSER_PREFERENCESDomainType('0x0D000000')
DOMAIN_BUILDER_DEPOSITDomainType('0x0E000000')
Misc
NameValueDescription
BUILDER_INDEX_SELF_BUILDBuilderIndex(UINT64_MAX)Value which indicates the proposer built the payload
BUILDER_PAYMENT_THRESHOLD_NUMERATORuint64(6)
BUILDER_PAYMENT_THRESHOLD_DENOMINATORuint64(10)
Withdrawal prefixes
NameValueDescription
BUILDER_WITHDRAWAL_PREFIXBytes1('0xB0')Withdrawal credential prefix for a builder
Builder versions
NameValueDescription
PAYLOAD_BUILDER_VERSIONuint8(0)Version of builders allowed to submit payloads
Execution layer triggered requests
NameValue
BUILDER_DEPOSIT_REQUEST_TYPEBytes1('0x03')
BUILDER_EXIT_REQUEST_TYPEBytes1('0x04')
Preset
Misc
NameValue
PTC_SIZEuint64(2**9) (= 512)
Max operations per block
NameValue
MAX_PAYLOAD_ATTESTATIONS4
Execution
NameValue
MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOADuint64(2**6) (= 64)
MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOADuint64(2**4) (= 16)
Withdrawals processing
NameValue
MAX_BUILDERS_PER_WITHDRAWALS_SWEEP2**14 (= 16,384)
Configuration
Time parameters
NameValueUnit
MIN_BUILDER_WITHDRAWABILITY_DELAYuint64(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: Epoch
BuilderPendingPayment
class BuilderPendingPayment(Container):
    weight: Gwei
    withdrawal: BuilderPendingWithdrawal
    proposer_index: ValidatorIndex
BuilderPendingWithdrawal
class BuilderPendingWithdrawal(Container):
    fee_recipient: ExecutionAddress
    amount: Gwei
    builder_index: BuilderIndex
BuilderDepositRequest
class BuilderDepositRequest(Container):
    pubkey: BLSPubkey
    withdrawal_credentials: Bytes32
    amount: Gwei
    signature: BLSSignature
BuilderExitRequest
class BuilderExitRequest(Container):
    source_address: ExecutionAddress
    pubkey: BLSPubkey
PayloadAttestationData
class PayloadAttestationData(Container):
    beacon_block_root: Root
    slot: Slot
    payload_present: boolean
    blob_data_available: boolean
PayloadAttestation
class PayloadAttestation(Container):
    aggregation_bits: PayloadTimelinessCommitteeBits
    data: PayloadAttestationData
    signature: BLSSignature
PayloadAttestationMessage
class PayloadAttestationMessage(Container):
    validator_index: ValidatorIndex
    data: PayloadAttestationData
    signature: BLSSignature
IndexedPayloadAttestation
class IndexedPayloadAttestation(Container):
    attesting_indices: PayloadTimelinessCommitteeIndices
    data: PayloadAttestationData
    signature: BLSSignature
ExecutionPayloadBid
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: Root
SignedExecutionPayloadBid
class SignedExecutionPayloadBid(Container):
    message: ExecutionPayloadBid
    signature: BLSSignature
ExecutionPayloadEnvelope
class ExecutionPayloadEnvelope(Container):
    payload: ExecutionPayload
    execution_requests: ExecutionRequests
    builder_index: BuilderIndex
    beacon_block_root: Root
    parent_beacon_block_root: Root
SignedExecutionPayloadEnvelope
class SignedExecutionPayloadEnvelope(Container):
    message: ExecutionPayloadEnvelope
    signature: BLSSignature
Modified containers
ExecutionRequests
class ExecutionRequests(Container):
    deposits: DepositRequests
    withdrawals: WithdrawalRequests
    consolidations: ConsolidationRequests
    # [New in Gloas]
    builder_deposits: BuilderDepositRequests
    # [New in Gloas]
    builder_exits: BuilderExitRequests
BeaconBlockBody

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: ExecutionRequests
BeaconState
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: PayloadTimelinessCommitteeWindow

The BeaconState container is modified with the addition of:

  • latest_block_hash, of type Hash32, to track the blockhash of the last execution payload in the blockchain.
  • builders, of type Builders to track the new in-protocol staked builders.
  • next_withdrawal_builder_index of type BuilderIndex to help tracking builder withdrawals.
  • execution_payload_availability, of type ExecutionPayloadAvailability to track the presence of execution payloads on the canonical chain.
  • builder_pending_payments, of type BuilderPendingPayments to track pending payments from builders to proposers before the execution payload has been processed.
  • builder_pending_withdrawals, of type BuilderPendingWithdrawals to track pending withdrawals to the execution layer with the builders' payments.
  • latest_execution_payload_bid, of type ExecutionPayloadBid, the bid committed by the latest beacon block.
  • payload_expected_withdrawals of type Withdrawals to track the latest withdrawals that were deducted in the consensus layer and need to still be honored in the Execution Layer.
  • ptc_window of type PayloadTimelinessCommitteeWindow caching the PTC assignments for the previous, current and lookahead epochs.

The BeaconBlockBody is modified with the addition of:

  • signed_execution_payload_bid of type SignedExecutionPayloadBid with the builder's commitment.
  • payload_attestations of type PayloadAttestations a list of PTC attestations from the previous slot.
  • parent_execution_requests of type ExecutionRequests the 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_payload is called first. If the block's bid builds on the parent's payload (that is bid.parent_block_hash matches the parent's committed block_hash) then block.body.parent_execution_requests is verified against the execution_requests_root committed in the parent's bid, and apply_parent_execution_payload is 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 in execution_payload_availability and updates latest_block_hash. If the parent's payload is not included, parent_execution_requests must be empty.
  • process_withdrawals is 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 state payload_expected_withdrawals is updated with this list. The next execution payload MUST include withdrawals matching state.payload_expected_withdrawals. If the parent beacon block's payload was not included, no new withdrawals are processed. Builder withdrawals are processed first, from the builder_pending_withdrawals queue, followed by pending partial withdrawals, a sweep of exited builders, and finally the usual validator sweep.
  • process_execution_payload is removed from process_block. Instead a new function process_execution_payload_bid is included, this function validates the SignedExecutionPayloadBid included in the BeaconBlockBody, ensures that the builder is active and that the payment from the builder's balance can be covered (the builder's balance must exceed MIN_DEPOSIT_AMOUNT plus all its pending payments and withdrawals, by at least the bid's value), and adds a BuilderPendingPayment object to the beacon state. Proposers that build their own payload use BUILDER_INDEX_SELF_BUILD as the builder index, a zero value and the point at infinity as signature.
  • process_deposit_request, process_withdrawal_request and process_consolidation_request are removed from process_operations and deferred until the next beacon block via process_parent_execution_payload.
  • New process_builder_deposit_request and process_builder_exit_request process the new builder requests, also from process_parent_execution_payload. A builder deposit request with a new public key and a valid proof of possession under DOMAIN_BUILDER_DEPOSIT adds 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_attestation is added to process_operations, this function validates the payload timeliness attestations broadcast by the PTC members.
  • process_attestation is modified to track the weight of same-slot attestations towards the BuilderPendingPayment of that slot.
  • slash_validator is 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 of PENDING, EMPTY or FULL. A PENDING node has as children its EMPTY variant, and its FULL variant only if the execution payload has been received and verified locally.
  • Attestations vote for a payload status with the index field, LatestMessage tracks 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 FULL node is preferred if the payload was verified and either the PTC voted that the payload was timely and the blob data available (with more than PAYLOAD_TIMELY_THRESHOLD and DATA_AVAILABILITY_TIMELY_THRESHOLD votes respectively, both equal to PTC_SIZE // 2) or the current proposer boosted block builds on it.
  • A new handler on_execution_payload_envelope verifies the envelope and, in particular, it is now responsible for the blob data availability check, which is removed from on_block.
  • A new handler on_payload_attestation_message tracks 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.

NameValue
ATTESTATION_DUE_BPS_GLOAS2500
AGGREGATE_DUE_BPS_GLOAS5000
SYNC_MESSAGE_DUE_BPS_GLOAS2500
CONTRIBUTION_DUE_BPS_GLOAS5000
PAYLOAD_DUE_BPS5000
PAYLOAD_ATTESTATION_DUE_BPS7500

P2P changes

  • A new global topic execution_payload_bid for broadcasting SignedExecutionPayloadBid messages (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 an execution_payment.
  • A new global topic execution_payload for broadcasting SignedExecutionPayloadEnvelope objects.
  • A new global topic payload_attestation_message for broadcasting PayloadAttestationMessage objects.
  • A new global topic proposer_preferences for broadcasting SignedProposerPreferences objects. 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 index larger than 1 are rejected, attestations with index equal to 1 are ignored until the corresponding execution payload has been seen.
  • DataColumnSidecar no longer carries the KZG commitments, the signed beacon block header nor its inclusion proof. Instead it carries the slot and beacon_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 ExecutionPayloadEnvelopesByRange and ExecutionPayloadEnvelopesByRoot.
class ProposerPreferences(Container):
    dependent_root: Root
    proposal_slot: Slot
    validator_index: ValidatorIndex
    fee_recipient: ExecutionAddress
    target_gas_limit: uint64
class SignedProposerPreferences(Container):
    message: ProposerPreferences
    signature: BLSSignature
class DataColumnSidecar(Container):
    index: ColumnIndex
    column: DataColumn
    kzg_proofs: KZGProofs
    slot: Slot
    beacon_block_root: Root

Honest validator and builder changes

  • Validators assigned to the PTC broadcast a PayloadAttestationMessage before PAYLOAD_ATTESTATION_DUE_BPS into the slot, signaling whether the payload envelope was seen before PAYLOAD_DUE_BPS and whether the blob data is available.
  • Proposers may broadcast SignedProposerPreferences for 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 SignedExecutionPayloadEnvelope and 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 to post_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 and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth