From 74b53c1fd4477b1aaca8e936126cb0eab44e662c Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 17 Dec 2025 19:11:47 +0530 Subject: [PATCH 01/59] initial builder-api for gloas --- apis/builder/beacon_block.yaml | 50 ++++++++++++++ apis/builder/execution_payload_bid.yaml | 89 +++++++++++++++++++++++++ builder-oapi.yaml | 6 +- specs/gloas/builder.md | 34 ++++++++++ specs/gloas/validator.md | 38 +++++++++++ 5 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 apis/builder/beacon_block.yaml create mode 100644 apis/builder/execution_payload_bid.yaml create mode 100644 specs/gloas/builder.md create mode 100644 specs/gloas/validator.md diff --git a/apis/builder/beacon_block.yaml b/apis/builder/beacon_block.yaml new file mode 100644 index 00000000..1a86500b --- /dev/null +++ b/apis/builder/beacon_block.yaml @@ -0,0 +1,50 @@ +post: + operationId: "submitSignedBeaconBlock" + summary: Submit a signed beacon block with the execution payload bid. + description: | + Submits a `SignedBeaconBlock` to the builder, binding the proposer to the block. + + A success response (200) indicates that the signed beacon block was + valid. If the signed beacon block was invalid, then the builder + must return an error response (400) with a description of the validation + failure. + tags: + - Builder + parameters: + - in: header + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ConsensusVersion" + required: false + name: Eth-Consensus-Version + description: "The active consensus version to which the block being submitted belongs. Required if request is SSZ encoded." + requestBody: + description: A `SignedBeaconBlock`. + required: true + content: + application/json: + schema: + type: object + required: [data] + properties: + data: + $ref: "../../beacon-apis/types/gloas/block.yaml#/Gloas/SignedBeaconBlock" + description: "The signed beacon block." + application/octet-stream: + schema: + description: "SSZ serialized `SignedBeaconBlock` bytes. Use content type header to indicate that SSZ data is contained in the request body." + responses: + "202": + description: Success response. + "400": + description: Error response. + content: + application/json: + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" + example: + code: 400 + message: "Invalid signed beacon block: missing signature" + "415": + $ref: "../../builder-oapi.yaml#/components/responses/UnsupportedMediaType" + "500": + $ref: "../../builder-oapi.yaml#/components/responses/InternalError" diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml new file mode 100644 index 00000000..cd243287 --- /dev/null +++ b/apis/builder/execution_payload_bid.yaml @@ -0,0 +1,89 @@ +get: + operationId: "getExecutionPayloadBid" + summary: Get an execution payload bid. + description: | + Requests a builder node to produce a valid execution payload bid, which + can be integrated into a blinded beacon block and signed. + + If the builder is unable to produce a valid execution payload bid, then + the builder MUST return a 204 response. If the request is invalid, then the + builder MUST return an error response (400) with a description of the + validation failure. + + This API is applicable from Glamsterdam fork onwards. + tags: + - Builder + parameters: + - name: Date-Milliseconds + in: header + required: false + description: | + Optional header containing a Unix timestamp in milliseconds representing + the point-in-time the request was sent. This header can be used to measure + latency. + schema: + type: integer + format: int64 + example: 1710338135000 + - name: X-Timeout-Ms + in: header + required: false + description: | + Optional header containing the proposer's timeout for the request in milliseconds. + Relays should use this header to adjust the amount of time by which they delay getBid + requests to maximise block rewards. Otherwise, getBid requests will timeout and the proposer + will not receive the header in time. + schema: + type: integer + format: int64 + example: 10000 + - name: X-Fee-Recipient + in: header + required: true + description: | + Required header containing the fee recipient address to which the proposer wants to receive + the payment for the bid. + schema: + type: string + format: address + example: "0x0000000000000000000000000000000000000000" + responses: + "200": + description: Success response. + headers: + Eth-Consensus-Version: + $ref: "../../builder-oapi.yaml#/components/headers/Eth-Consensus-Version" + required: false + content: + application/json: + schema: + title: GetExecutionPayloadBidResponse + type: object + required: [version, data] + properties: + version: + type: string + enum: [ gloas ] + example: "gloas" + data: + $ref: "../../beacon-apis/types/gloas/execution_payload_bid.yaml#/Gloas/ExecutionPayloadBid" + application/octet-stream: + schema: + description: "SSZ serialized `ExecutionPayloadBid` bytes. Use Accept header to choose this response type" + "204": + description: No header is available. + "400": + description: Error response. + content: + application/json: + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" + examples: + InvalidRequest: + value: + code: 400 + message: "Unknown hash: missing parent hash" + "406": + $ref: "../../builder-oapi.yaml#/components/responses/NotAcceptable" + "500": + $ref: "../../builder-oapi.yaml#/components/responses/InternalError" diff --git a/builder-oapi.yaml b/builder-oapi.yaml index a9c05f06..0962a15c 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -53,6 +53,10 @@ paths: $ref: "./apis/builder/validators.yaml" /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}: $ref: "./apis/builder/header.yaml" + /eth/v1/builder/execution_payload_bid: + $ref: "./apis/builder/execution_payload_bid.yaml" + /eth/v1/builder/beacon_block: + $ref: "./apis/builder/beacon_block.yaml" /eth/v1/builder/blinded_blocks: $ref: "./apis/builder/blinded_blocks.yaml" /eth/v2/builder/blinded_blocks: @@ -72,7 +76,7 @@ components: $ref: "./beacon-apis/types/http.yaml#/ErrorMessage" ConsensusVersion: $ref: "./beacon-apis/beacon-node-oapi.yaml#/components/schemas/ConsensusVersion" - enum: [bellatrix, capella, deneb, electra, fulu] + enum: [bellatrix, capella, deneb, electra, fulu, gloas] example: "bellatrix" Bellatrix.ExecutionPayload: $ref: "./beacon-apis/types/bellatrix/execution_payload.yaml#/Bellatrix/ExecutionPayload" diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md new file mode 100644 index 00000000..793f15cc --- /dev/null +++ b/specs/gloas/builder.md @@ -0,0 +1,34 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Gloas - Builder Specification](#gloas---builder-specification) + - [Introduction](#introduction) + - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) + - [Constructing a `SignedExecutionPayloadEnvelope`](#constructing-a-signedexecutionpayloadenvelope) + + + +# Gloas - Builder Specification + +## Introduction + +This document documents the builder behaviour with the Builder-API. + +### `ValidatorRegistrationV1` are deprecated + +With Gloas, `ValidatorRegistrations` are deprecated. Pre-Gloas, Validators used `ValidatorRegistrationV1` to signal their +preferred `fee_receipient` and `gas_limit` to the builder. + +Now, A proposer can indicate the `fee_receipient` to which they want the builder to pay as a header while requesting the +`SignedExecutionPayloadBid`. + +### Constructing a `SignedExecutionPayloadBid` + +The specification for a block builder to construct a `SignedExecutionPayloadBid` is documented in the +gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. + +### Constructing a `SignedExecutionPayloadEnvelope` + +The specification for a block builder to construct a `SignedExecutionPayloadEnvelope` is documented in the +gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md new file mode 100644 index 00000000..a8d48103 --- /dev/null +++ b/specs/gloas/validator.md @@ -0,0 +1,38 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Gloas - Honest Validator](#gloas---honest-validator) + - [Introduction](#introduction) + - [Block proposal](#block-proposal) + - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [ExecutionPayloadBid](#executionpayloadbid) + + + +# Gloas - Honest Validator + + +## Introduction + +This document explains how a beacon-chain validator can participate in the external block building market post ePBS. + +Validators request an `ExecutionPayloadBid` from the external builder network to put it in their `SignedBeaconBlock`. +The external builder network broadcasts the `SignedExecutionPayloadEnvelope` corresponding to the bid to the PTC commitee. + +### Block proposal + +#### Constructing the `BeaconBlockBody` + +##### ExecutionPayloadBid + +To obtain an execution payload, a block proposer building a block on top of a beacon `state` in a given `slot` must take +the following actions: + +1. Call upstream builder software to get an `ExecutionPayloadBid`. +2. Assemble a `SignedBeaconBlock` according to the process outlined in the [Gloas specs][https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal] but with + the `ExecutionPayloadBid` from the prior step. +3. The proposer returns the `SignedBeaconBlock` back to the upstream block + building software. +5. The upstream block building software constructs the `SignedExecutionPayloadEnvelope` from the + `SignedBlindedExecutionPayloadEnvelope` and broadcasts it to the PTC commitee. \ No newline at end of file From 0c5564696672f606cc7508d984a3724b74b9a747 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 12 Dec 2025 15:25:26 +0530 Subject: [PATCH 02/59] updates --- apis/builder/execution_payload_bid.yaml | 2 +- specs/gloas/builder.md | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index cd243287..c716ed6d 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -30,7 +30,7 @@ get: required: false description: | Optional header containing the proposer's timeout for the request in milliseconds. - Relays should use this header to adjust the amount of time by which they delay getBid + Builders should use this header to adjust the amount of time by which they delay getBid requests to maximise block rewards. Otherwise, getBid requests will timeout and the proposer will not receive the header in time. schema: diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 793f15cc..7c6f8197 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -4,8 +4,10 @@ - [Gloas - Builder Specification](#gloas---builder-specification) - [Introduction](#introduction) + - [`ValidatorRegistrationV1` are deprecated](#validatorregistrationv1-are-deprecated) - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) - [Constructing a `SignedExecutionPayloadEnvelope`](#constructing-a-signedexecutionpayloadenvelope) + - [Sealing the Payload with `fee_recipient`](#sealing-the-payload-with-fee_recipient) @@ -18,9 +20,9 @@ This document documents the builder behaviour with the Builder-API. ### `ValidatorRegistrationV1` are deprecated With Gloas, `ValidatorRegistrations` are deprecated. Pre-Gloas, Validators used `ValidatorRegistrationV1` to signal their -preferred `fee_receipient` and `gas_limit` to the builder. +preferred `fee_recipient` and `gas_limit` to the builder. -Now, A proposer can indicate the `fee_receipient` to which they want the builder to pay as a header while requesting the +Now, A proposer can indicate the `fee_recipient` to which they want the builder to pay as a header while requesting the `SignedExecutionPayloadBid`. ### Constructing a `SignedExecutionPayloadBid` @@ -32,3 +34,9 @@ gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/ The specification for a block builder to construct a `SignedExecutionPayloadEnvelope` is documented in the gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. + +### Sealing the Payload with `fee_recipient` + +The builder receives the `fee_recipient` as a header to the call to get the `SignedExecutionPayloadBid`. The builder +is required to seal the block to be sent with the payment transaction to the `fee_recipient` with the amount specified in the +`execution_payment` field in the `ExecutionPayloadBid`. \ No newline at end of file From 278df30f5aef18375bd891c9d87e28cd6cc664a9 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 12 Dec 2025 15:45:12 +0530 Subject: [PATCH 03/59] pass in params while querying execution payload bid --- apis/builder/execution_payload_bid.yaml | 24 ++++++++++++++++++++++++ builder-oapi.yaml | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index c716ed6d..8f28ee4a 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -14,6 +14,30 @@ get: tags: - Builder parameters: + - name: slot + in: path + required: true + description: The slot for which the block should be proposed. + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/Uint64" + - name: parent_hash + in: path + required: true + description: Hash of execution layer block the proposer will build on. + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/Root" + - name: parent_root + in: path + required: true + description: Root of the execution layer block the proposer will build on. + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/Root" + - name: proposer_index + in: path + required: true + description: Index of the proposer. + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/Uint64" - name: Date-Milliseconds in: header required: false diff --git a/builder-oapi.yaml b/builder-oapi.yaml index 0962a15c..59387505 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -53,7 +53,7 @@ paths: $ref: "./apis/builder/validators.yaml" /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}: $ref: "./apis/builder/header.yaml" - /eth/v1/builder/execution_payload_bid: + /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_index}: $ref: "./apis/builder/execution_payload_bid.yaml" /eth/v1/builder/beacon_block: $ref: "./apis/builder/beacon_block.yaml" From 1577ba9538f52fe4367ad62838de0e7f61846d42 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 16 Dec 2025 15:27:35 +0530 Subject: [PATCH 04/59] introduce validator registrations v2 --- apis/builder/validators_v2.yaml | 45 ++++++++++++++ builder-oapi.yaml | 2 + specs/gloas/builder.md | 102 +++++++++++++++++++++++++++----- specs/gloas/validator.md | 36 ++++++++++- 4 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 apis/builder/validators_v2.yaml diff --git a/apis/builder/validators_v2.yaml b/apis/builder/validators_v2.yaml new file mode 100644 index 00000000..eeff67bf --- /dev/null +++ b/apis/builder/validators_v2.yaml @@ -0,0 +1,45 @@ +post: + operationId: "registerValidatorV2" + summary: Register or update a validator's block building preferences for Gloas. + description: | + Registers a validator's preferred fee recipient, gas limit and preferences. + + A success response (200) indicates that the registration was valid. If the + registration passes validation, then the builder MUST integrate the + registration into its state, such that future blocks built for the + validator conform to the preferences expressed in the registration. If the + registration is invalid, then the builder MUST return an error response + (400) with a description of the validation failure. + tags: + - Builder + requestBody: + description: | + A signed declaration of a validator's block building preferences. + required: true + content: + application/json: + schema: + type: array + items: + $ref: "../../builder-oapi.yaml#/components/schemas/SignedValidatorRegistrationV2" + example: + $ref: "../../builder-oapi.yaml#/components/examples/SignedValidatorRegistrations/value" + application/octet-stream: + schema: + description: "SSZ serialized `List[SignedValidatorRegistrationV2, VALIDATOR_REGISTRY_LIMIT]` bytes. Use content type header to indicate that SSZ data is contained in the request body." + responses: + "200": + description: Success response. + "400": + description: Error response. + content: + application/json: + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" + example: + code: 400 + message: "unknown validator" + "415": + $ref: "../../builder-oapi.yaml#/components/responses/UnsupportedMediaType" + "500": + $ref: "../../builder-oapi.yaml#/components/responses/InternalError" diff --git a/builder-oapi.yaml b/builder-oapi.yaml index 59387505..7a65c7c2 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -98,6 +98,8 @@ components: $ref: "./types/deneb/execution_payload_and_blobs_bundle.yaml#/Deneb/ExecutionPayloadAndBlobsBundle" SignedValidatorRegistration: $ref: "./beacon-apis/types/registration.yaml#/SignedValidatorRegistration" + SignedValidatorRegistrationV2: + $ref: "./beacon-apis/types/registration.yaml#/SignedValidatorRegistrationV2" Electra.SignedBlindedBeaconBlock: $ref: "./beacon-apis/types/electra/block.yaml#/Electra/SignedBlindedBeaconBlock" Electra.SignedBuilderBid: diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 7c6f8197..163ba32e 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -17,26 +17,100 @@ This document documents the builder behaviour with the Builder-API. -### `ValidatorRegistrationV1` are deprecated +## Custom types -With Gloas, `ValidatorRegistrations` are deprecated. Pre-Gloas, Validators used `ValidatorRegistrationV1` to signal their -preferred `fee_recipient` and `gas_limit` to the builder. +| Name | SSZ equivalent | Description | +| -------------- | -------------- | ---------------------- | +| `BuilderIndex` | `uint64` | Builder registry index | -Now, A proposer can indicate the `fee_recipient` to which they want the builder to pay as a header while requesting the -`SignedExecutionPayloadBid`. +## Containers -### Constructing a `SignedExecutionPayloadBid` +### New Containers -The specification for a block builder to construct a `SignedExecutionPayloadBid` is documented in the -gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. +#### `ValidatorRegistrationV2` -### Constructing a `SignedExecutionPayloadEnvelope` +```python +class ValidatorRegistrationV2(Container): + builder_pubkey: BLSPubkey ## is this needed? + fee_recipient: ExecutionAddress + gas_limit: uint64 + timestamp: uint64 + pubkey: BLSPubkey + can_accept_trusted_payment: bool + proposal_epoch: Epoch +``` -The specification for a block builder to construct a `SignedExecutionPayloadEnvelope` is documented in the +#### `SignedValidatorRegistrationV2` + +```python +class SignedValidatorRegistrationV2(Container): + message: ValidatorRegistrationV2 + signature: BLSSignature +``` + +### `verify_registration_signature` + +```python +def verify_registration_signature(state: BeaconState, signed_registration: SignedValidatorRegistrationV2) -> bool: + pubkey = signed_registration.message.pubkey + domain = compute_domain(DOMAIN_APPLICATION_BUILDER) + signing_root = compute_signing_root(signed_registration.message, domain) + return bls.Verify(pubkey, signing_root, signed_registration.signature) +``` + +## Validator Registration V2 + +The second version of ValidatorRegistrations adds the following new fields: +* `builder_pubkey`: The pubkey of the builder to which this registration is being sent. +* `can_accept_trusted_payment`: This is a boolean which indicates that the validator is willing accept a trusted + execution layer payment from the builder to which it is sending the registrations. +* `proposal_epoch`: The epoch at which this validator is proposing. + +### `process_registration` + +```python +def process_registration(state: BeaconState, + registration: SignedValidatorRegistrationV2, + registrations: Dict[BLSPubkey, ValidatorRegistrationV2], + current_timestamp: uint64): + signature = registration.signature + registration = registration.message + pubkey = registration.pubkey + builder_pubkey = registration.builder_pubkey + + # Verify BLS public key corresponds to a registered validator + validator_pubkeys = [v.pubkey for v in state.validators] + assert pubkey in validator_pubkeys + + index = ValidatorIndex(validator_pubkeys.index(pubkey)) + validator = state.validators[index] + + # [New in Gloas] + builder_pubkeys = [b.pubkey for v in state.builders] + assert builder_pubkey in builder_pubkeys + + # Verify validator registration elibility + assert is_eligible_for_registration(state, validator) + + # Verify timestamp is not too far in the future + assert registration.timestamp <= current_timestamp + MAX_REGISTRATION_LOOKAHEAD + + # Verify timestamp is not less than the timestamp of the previous registration (if it exists) + if registration.pubkey in registrations: + prev_registration = registrations[registration.pubkey] + assert registration.timestamp >= prev_registration.timestamp + + # Verify registration signature + assert verify_registration_signature(state, registration) +``` + + +## Constructing a `SignedExecutionPayloadBid` + +The specification for a block builder to construct a `SignedExecutionPayloadBid` is documented in the gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. -### Sealing the Payload with `fee_recipient` +## Constructing a `SignedExecutionPayloadEnvelope` -The builder receives the `fee_recipient` as a header to the call to get the `SignedExecutionPayloadBid`. The builder -is required to seal the block to be sent with the payment transaction to the `fee_recipient` with the amount specified in the -`execution_payment` field in the `ExecutionPayloadBid`. \ No newline at end of file +The specification for a block builder to construct a `SignedExecutionPayloadEnvelope` is documented in the +gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. \ No newline at end of file diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index a8d48103..24a3bc04 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -20,7 +20,41 @@ This document explains how a beacon-chain validator can participate in the exter Validators request an `ExecutionPayloadBid` from the external builder network to put it in their `SignedBeaconBlock`. The external builder network broadcasts the `SignedExecutionPayloadEnvelope` corresponding to the bid to the PTC commitee. -### Block proposal +## Validator Registrations + +### Constructing the `ValidatorRegistrationV2` + +To do this, the validator client assembles a [`ValidatorRegistrationV2`][validator-registration-v2] with the following +information: + +* `fee_recipient`: an execution layer address where fees for the validator should go. +* `builder_pubkey`: the pubkey of the builder to which this registration is being sent to. +* `gas_limit`: the value a validator prefers for the execution block gas limit. +* `timestamp`: a recent timestamp later than any previously constructed `ValidatorRegistrationV1`. + Builders use this timestamp as a form of anti-DoS and to sequence registrations. +* `pubkey`: the validator's public key. Used to identify the beacon chain validator and verify the wrapping signature. +* `can_accept_trusted_payment`: whether the proposer is willing to accept a trusted payment from the builder with pubkey + `builder_pubkey`. +* `proposal_epoch`: This is set to `get_current_epoch(state) + 1`. + + +### Validator Registration dissemination + +This specification suggests validators re-submit registrations only if they will be proposing in the upcoming epoch(E+1). +This is to avoid sending a lot of `ValidatorRegistrations` every epoch. This can potentially help reduce the load +Validators are expected to perform this check at every epoch boundary. Validators can send their registrations even though +they won't be proposing in the upcoming epoch. + +```python +def is_next_epoch_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool: + """ + Check if ``validator_index`` is scheduled to propose in the next epoch. + """ + next_epoch_proposers = state.proposer_lookahead[SLOTS_PER_EPOCH:] + return validator_index in next_epoch_proposers +``` + +## Block proposal #### Constructing the `BeaconBlockBody` From a3db7f7311f3e2d5c053c9871bf56e575931a081 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 17 Dec 2025 19:12:07 +0530 Subject: [PATCH 05/59] run doctoc --- specs/gloas/builder.md | 14 ++++++++++---- specs/gloas/validator.md | 5 ++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 163ba32e..018aa6f7 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -4,10 +4,16 @@ - [Gloas - Builder Specification](#gloas---builder-specification) - [Introduction](#introduction) - - [`ValidatorRegistrationV1` are deprecated](#validatorregistrationv1-are-deprecated) - - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) - - [Constructing a `SignedExecutionPayloadEnvelope`](#constructing-a-signedexecutionpayloadenvelope) - - [Sealing the Payload with `fee_recipient`](#sealing-the-payload-with-fee_recipient) + - [Custom types](#custom-types) + - [Containers](#containers) + - [New Containers](#new-containers) + - [`ValidatorRegistrationV2`](#validatorregistrationv2) + - [`SignedValidatorRegistrationV2`](#signedvalidatorregistrationv2) + - [`verify_registration_signature`](#verify_registration_signature) + - [Validator Registration V2](#validator-registration-v2) + - [`process_registration`](#process_registration) + - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) + - [Constructing a `SignedExecutionPayloadEnvelope`](#constructing-a-signedexecutionpayloadenvelope) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 24a3bc04..e9872df3 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -4,7 +4,10 @@ - [Gloas - Honest Validator](#gloas---honest-validator) - [Introduction](#introduction) - - [Block proposal](#block-proposal) + - [Validator Registrations](#validator-registrations) + - [Constructing the `ValidatorRegistrationV2`](#constructing-the-validatorregistrationv2) + - [Validator Registration dissemination](#validator-registration-dissemination) + - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [ExecutionPayloadBid](#executionpayloadbid) From df52176d1a5ff20cddd4dcf04782892763451946 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 17 Dec 2025 15:05:25 +0530 Subject: [PATCH 06/59] update validator registration v2 --- specs/gloas/builder.md | 81 +++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 018aa6f7..75ee6dad 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -29,6 +29,30 @@ This document documents the builder behaviour with the Builder-API. | -------------- | -------------- | ---------------------- | | `BuilderIndex` | `uint64` | Builder registry index | +## Predicates + +### `is_active_builder` + +```python +def is_active_builder(builder: Builder) -> bool: + """ + Check if ``builder`` is active. + """ + return builder.exit_epoch == FAR_FUTURE_EPOCH +``` + +## Helper Functions + +#### `compute_epoch_at_slot` + +```python +def compute_epoch_at_slot(slot: Slot) -> Epoch: + """ + Return the epoch number at ``slot``. + """ + return Epoch(slot // SLOTS_PER_EPOCH) +``` + ## Containers ### New Containers @@ -37,13 +61,12 @@ This document documents the builder behaviour with the Builder-API. ```python class ValidatorRegistrationV2(Container): - builder_pubkey: BLSPubkey ## is this needed? + builder_index: BuilderIndex + validator_index: ValidatorIndex fee_recipient: ExecutionAddress + proposal_slot: Slot gas_limit: uint64 - timestamp: uint64 - pubkey: BLSPubkey - can_accept_trusted_payment: bool - proposal_epoch: Epoch + execution_payment_accepted: boolean ``` #### `SignedValidatorRegistrationV2` @@ -58,7 +81,8 @@ class SignedValidatorRegistrationV2(Container): ```python def verify_registration_signature(state: BeaconState, signed_registration: SignedValidatorRegistrationV2) -> bool: - pubkey = signed_registration.message.pubkey + validator = state.validators[signed_registration.message.validator_index] + pubkey = validator.pubkey domain = compute_domain(DOMAIN_APPLICATION_BUILDER) signing_root = compute_signing_root(signed_registration.message, domain) return bls.Verify(pubkey, signing_root, signed_registration.signature) @@ -67,50 +91,51 @@ def verify_registration_signature(state: BeaconState, signed_registration: Signe ## Validator Registration V2 The second version of ValidatorRegistrations adds the following new fields: -* `builder_pubkey`: The pubkey of the builder to which this registration is being sent. -* `can_accept_trusted_payment`: This is a boolean which indicates that the validator is willing accept a trusted +* `builder_index`: The index of the builder to which this registration is being sent. +* `validator_index`: The index of the validator selected to propose a block at slot `proposal_slot` +* `execution_payment_accepted`: This is a boolean which indicates that the validator is willing accept a trusted execution layer payment from the builder to which it is sending the registrations. -* `proposal_epoch`: The epoch at which this validator is proposing. +* `proposal_slot`: The slot at which this validator is proposing. -### `process_registration` +The following fields are removed: +* `pubkey`: This is the pubkey of the validator which has now been replaced with `validator_index`. +* `timestamp`: A new validator registration will be sent by the validator to the builder in + the epoch prior to one where + +### `process_registration_v2` ```python -def process_registration(state: BeaconState, +def process_registration_v2(state: BeaconState, registration: SignedValidatorRegistrationV2, registrations: Dict[BLSPubkey, ValidatorRegistrationV2], current_timestamp: uint64): signature = registration.signature registration = registration.message - pubkey = registration.pubkey - builder_pubkey = registration.builder_pubkey + validator_index = registration.validator_index + builder_index = registration.builder_index + proposal_slot = registration.proposal_slot - # Verify BLS public key corresponds to a registered validator - validator_pubkeys = [v.pubkey for v in state.validators] - assert pubkey in validator_pubkeys + assert validator_index < len(state.validators) + assert builder_index < len(state.builders) - index = ValidatorIndex(validator_pubkeys.index(pubkey)) - validator = state.validators[index] + validator = state.validators[validator_index] + builder = state.builders[builder_index] - # [New in Gloas] - builder_pubkeys = [b.pubkey for v in state.builders] - assert builder_pubkey in builder_pubkeys + assert is_active_validator(validator, compute_epoch_at_slot(proposal_slot)) + assert is_active_builder(builder) # Verify validator registration elibility assert is_eligible_for_registration(state, validator) - # Verify timestamp is not too far in the future - assert registration.timestamp <= current_timestamp + MAX_REGISTRATION_LOOKAHEAD - - # Verify timestamp is not less than the timestamp of the previous registration (if it exists) + # Verify that the old registration's slot is earlier than the new registration's slot if registration.pubkey in registrations: - prev_registration = registrations[registration.pubkey] - assert registration.timestamp >= prev_registration.timestamp + prev_registration = registrations[validator_index] + assert registration.proposal_slot >= prev_registration.proposal_slot # Verify registration signature assert verify_registration_signature(state, registration) ``` - ## Constructing a `SignedExecutionPayloadBid` The specification for a block builder to construct a `SignedExecutionPayloadBid` is documented in the From 6e1a77744dcf7a59531288a9e7be4cffd58d4716 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 17 Dec 2025 19:03:46 +0530 Subject: [PATCH 07/59] add more specs --- builder-oapi.yaml | 2 ++ specs/gloas/validator.md | 64 +++++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/builder-oapi.yaml b/builder-oapi.yaml index 7a65c7c2..ca26f88b 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -51,6 +51,8 @@ tags: paths: /eth/v1/builder/validators: $ref: "./apis/builder/validators.yaml" + /eth/v2/builder/validators: + $ref: "./apis/builder/validators_2.yaml" /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}: $ref: "./apis/builder/header.yaml" /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_index}: diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index e9872df3..d0eda580 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -23,6 +23,30 @@ This document explains how a beacon-chain validator can participate in the exter Validators request an `ExecutionPayloadBid` from the external builder network to put it in their `SignedBeaconBlock`. The external builder network broadcasts the `SignedExecutionPayloadEnvelope` corresponding to the bid to the PTC commitee. +## Helper + +### `get_proposer_slots_in_upcoming_epoch` + +```python +def get_proposer_slots_in_upcoming_epoch( + state: BeaconState, + validator_index: ValidatorIndex +) -> List[Slot]: + """ + Return all slots where validator_index is the proposer within the lookahead window in the next epoch. + """ + proposer_slots = [] + current_epoch_start_slot = compute_start_slot_at_epoch(get_current_epoch(state)) + next_epoch_proposer_lookahead = state.proposer_lookahead[SLOTS_PER_EPOCH:] + + for i, proposer_index in enumerate(next_epoch_proposer_lookahead): + if proposer_index == validator_index: + slot = current_epoch_start_slot + SLOTS_PER_EPOCH + i + proposer_slots.append(slot) + + return proposer_slots +``` + ## Validator Registrations ### Constructing the `ValidatorRegistrationV2` @@ -31,32 +55,38 @@ To do this, the validator client assembles a [`ValidatorRegistrationV2`][validat information: * `fee_recipient`: an execution layer address where fees for the validator should go. -* `builder_pubkey`: the pubkey of the builder to which this registration is being sent to. +* `builder_index`: the index of the builder to which this registration is being sent to. * `gas_limit`: the value a validator prefers for the execution block gas limit. -* `timestamp`: a recent timestamp later than any previously constructed `ValidatorRegistrationV1`. - Builders use this timestamp as a form of anti-DoS and to sequence registrations. -* `pubkey`: the validator's public key. Used to identify the beacon chain validator and verify the wrapping signature. -* `can_accept_trusted_payment`: whether the proposer is willing to accept a trusted payment from the builder with pubkey - `builder_pubkey`. -* `proposal_epoch`: This is set to `get_current_epoch(state) + 1`. +* `validator_index`: the validator's index. Used to identify the beacon chain validator and verify the wrapping signature. +* `execution_payment_accepted`: whether the proposer is willing to accept a trusted payment from the builder with index + `builder_index`. +* `proposal_slot`: This is set to the slot in which the validator will be proposing. This can be looked up in the `proposal_lookahead`. ### Validator Registration dissemination This specification suggests validators re-submit registrations only if they will be proposing in the upcoming epoch(E+1). -This is to avoid sending a lot of `ValidatorRegistrations` every epoch. This can potentially help reduce the load -Validators are expected to perform this check at every epoch boundary. Validators can send their registrations even though -they won't be proposing in the upcoming epoch. +This is such that we don't send too many validator registrations all at once to builders. +Validators run `create_validator_registrations` at every epoch boundary to create validator registrations for all the slots +they will be proposing in the upcoming epoch. ```python -def is_next_epoch_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool: - """ - Check if ``validator_index`` is scheduled to propose in the next epoch. - """ - next_epoch_proposers = state.proposer_lookahead[SLOTS_PER_EPOCH:] - return validator_index in next_epoch_proposers +def create_validator_registrations(state: BeaconState, validator_index: ValidatorIndex, gas_limit: uint64, builder_index: BuilderIndex, execution_payment_accepted: bool) -> List[ValidatorRegistrationV2]: + slots = get_proposer_slots_in_lookahead(state, validator_index) + registrations: List[ValidatorRegistrationsV2] = [] + + for slot in slots: + registrations.append(ValidatorRegistrationV2( + fee_recipient=fee_recipient, + builder_index=builder_index, + gas_limit=gas_limit, + validator_index=validator_index + execution_payment_accepted=execution_payment_accepted, + proposal_slot=slot + )) + + return registrations ``` - ## Block proposal #### Constructing the `BeaconBlockBody` From 80974bc10fc31639533fe5ae41ff5a08d2e8c7fb Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 17 Dec 2025 19:18:49 +0530 Subject: [PATCH 08/59] run lint --- specs/gloas/builder.md | 40 ++++++++++++++--------- specs/gloas/validator.md | 68 ++++++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 42 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 75ee6dad..e91f40f0 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -1,6 +1,9 @@ + -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +**Table of Contents** *generated with +[DocToc](https://github.com/thlorenz/doctoc)* - [Gloas - Builder Specification](#gloas---builder-specification) - [Introduction](#introduction) @@ -25,9 +28,8 @@ This document documents the builder behaviour with the Builder-API. ## Custom types -| Name | SSZ equivalent | Description | -| -------------- | -------------- | ---------------------- | -| `BuilderIndex` | `uint64` | Builder registry index | +| Name | SSZ equivalent | Description | | -------------- | -------------- | +---------------------- | | `BuilderIndex` | `uint64` | Builder registry index | ## Predicates @@ -91,16 +93,22 @@ def verify_registration_signature(state: BeaconState, signed_registration: Signe ## Validator Registration V2 The second version of ValidatorRegistrations adds the following new fields: -* `builder_index`: The index of the builder to which this registration is being sent. -* `validator_index`: The index of the validator selected to propose a block at slot `proposal_slot` -* `execution_payment_accepted`: This is a boolean which indicates that the validator is willing accept a trusted - execution layer payment from the builder to which it is sending the registrations. -* `proposal_slot`: The slot at which this validator is proposing. + +- `builder_index`: The index of the builder to which this registration is being + sent. +- `validator_index`: The index of the validator selected to propose a block at + slot `proposal_slot` +- `execution_payment_accepted`: This is a boolean which indicates that the + validator is willing accept a trusted execution layer payment from the builder + to which it is sending the registrations. +- `proposal_slot`: The slot at which this validator is proposing. The following fields are removed: -* `pubkey`: This is the pubkey of the validator which has now been replaced with `validator_index`. -* `timestamp`: A new validator registration will be sent by the validator to the builder in - the epoch prior to one where + +- `pubkey`: This is the pubkey of the validator which has now been replaced with + `validator_index`. +- `timestamp`: A new validator registration will be sent by the validator to the + builder in the epoch prior to one where ### `process_registration_v2` @@ -138,10 +146,12 @@ def process_registration_v2(state: BeaconState, ## Constructing a `SignedExecutionPayloadBid` -The specification for a block builder to construct a `SignedExecutionPayloadBid` is documented in the +The specification for a block builder to construct a `SignedExecutionPayloadBid` +is documented in the gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. ## Constructing a `SignedExecutionPayloadEnvelope` -The specification for a block builder to construct a `SignedExecutionPayloadEnvelope` is documented in the -gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. \ No newline at end of file +The specification for a block builder to construct a +`SignedExecutionPayloadEnvelope` is documented in the +gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index d0eda580..1376e3c3 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -1,6 +1,9 @@ + -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +**Table of Contents** *generated with +[DocToc](https://github.com/thlorenz/doctoc)* - [Gloas - Honest Validator](#gloas---honest-validator) - [Introduction](#introduction) @@ -8,20 +11,21 @@ - [Constructing the `ValidatorRegistrationV2`](#constructing-the-validatorregistrationv2) - [Validator Registration dissemination](#validator-registration-dissemination) - [Block proposal](#block-proposal) - - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - - [ExecutionPayloadBid](#executionpayloadbid) + - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [ExecutionPayloadBid](#executionpayloadbid) # Gloas - Honest Validator - ## Introduction -This document explains how a beacon-chain validator can participate in the external block building market post ePBS. +This document explains how a beacon-chain validator can participate in the +external block building market post ePBS. -Validators request an `ExecutionPayloadBid` from the external builder network to put it in their `SignedBeaconBlock`. -The external builder network broadcasts the `SignedExecutionPayloadEnvelope` corresponding to the bid to the PTC commitee. +Validators request an `ExecutionPayloadBid` from the external builder network to +put it in their `SignedBeaconBlock`. The external builder network broadcasts the +`SignedExecutionPayloadEnvelope` corresponding to the bid to the PTC committee. ## Helper @@ -47,28 +51,33 @@ def get_proposer_slots_in_upcoming_epoch( return proposer_slots ``` -## Validator Registrations +## Validator Registrations ### Constructing the `ValidatorRegistrationV2` -To do this, the validator client assembles a [`ValidatorRegistrationV2`][validator-registration-v2] with the following +To do this, the validator client assembles a +\[`ValidatorRegistrationV2`\][validator-registration-v2] with the following information: -* `fee_recipient`: an execution layer address where fees for the validator should go. -* `builder_index`: the index of the builder to which this registration is being sent to. -* `gas_limit`: the value a validator prefers for the execution block gas limit. -* `validator_index`: the validator's index. Used to identify the beacon chain validator and verify the wrapping signature. -* `execution_payment_accepted`: whether the proposer is willing to accept a trusted payment from the builder with index - `builder_index`. -* `proposal_slot`: This is set to the slot in which the validator will be proposing. This can be looked up in the `proposal_lookahead`. - +- `fee_recipient`: an execution layer address where fees for the validator + should go. +- `builder_index`: the index of the builder to which this registration is being + sent to. +- `gas_limit`: the value a validator prefers for the execution block gas limit. +- `validator_index`: the validator's index. Used to identify the beacon chain + validator and verify the wrapping signature. +- `execution_payment_accepted`: whether the proposer is willing to accept a + trusted payment from the builder with index `builder_index`. +- `proposal_slot`: This is set to the slot in which the validator will be + proposing. This can be looked up in the `proposal_lookahead`. ### Validator Registration dissemination -This specification suggests validators re-submit registrations only if they will be proposing in the upcoming epoch(E+1). -This is such that we don't send too many validator registrations all at once to builders. -Validators run `create_validator_registrations` at every epoch boundary to create validator registrations for all the slots -they will be proposing in the upcoming epoch. +This specification suggests validators re-submit registrations only if they will +be proposing in the upcoming epoch(E+1). This is such that we don't send too +many validator registrations all at once to builders. Validators run +`create_validator_registrations` at every epoch boundary to create validator +registrations for all the slots they will be proposing in the upcoming epoch. ```python def create_validator_registrations(state: BeaconState, validator_index: ValidatorIndex, gas_limit: uint64, builder_index: BuilderIndex, execution_payment_accepted: bool) -> List[ValidatorRegistrationV2]: @@ -87,19 +96,24 @@ def create_validator_registrations(state: BeaconState, validator_index: Validato return registrations ``` + ## Block proposal #### Constructing the `BeaconBlockBody` ##### ExecutionPayloadBid -To obtain an execution payload, a block proposer building a block on top of a beacon `state` in a given `slot` must take -the following actions: +To obtain an execution payload, a block proposer building a block on top of a +beacon `state` in a given `slot` must take the following actions: 1. Call upstream builder software to get an `ExecutionPayloadBid`. -2. Assemble a `SignedBeaconBlock` according to the process outlined in the [Gloas specs][https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal] but with - the `ExecutionPayloadBid` from the prior step. +2. Assemble a `SignedBeaconBlock` according to the process outlined in the + \[Gloas + specs\][https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal] + but with the `ExecutionPayloadBid` from the prior step. 3. The proposer returns the `SignedBeaconBlock` back to the upstream block building software. -5. The upstream block building software constructs the `SignedExecutionPayloadEnvelope` from the - `SignedBlindedExecutionPayloadEnvelope` and broadcasts it to the PTC commitee. \ No newline at end of file +4. The upstream block building software constructs the + `SignedExecutionPayloadEnvelope` from the + `SignedBlindedExecutionPayloadEnvelope` and broadcasts it to the PTC + committee. From a242d3b3774427bbd88bbb5b2ffe39755867a4d0 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 17 Dec 2025 22:24:09 +0530 Subject: [PATCH 09/59] updates --- specs/gloas/builder.md | 14 +++++++++----- specs/gloas/validator.md | 41 ++++++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index e91f40f0..57dea977 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -1,20 +1,21 @@ - - -**Table of Contents** *generated with -[DocToc](https://github.com/thlorenz/doctoc)* + - [Gloas - Builder Specification](#gloas---builder-specification) - [Introduction](#introduction) - [Custom types](#custom-types) + - [Predicates](#predicates) + - [`is_active_builder`](#is_active_builder) + - [Helper Functions](#helper-functions) + - [`compute_epoch_at_slot`](#compute_epoch_at_slot) - [Containers](#containers) - [New Containers](#new-containers) - [`ValidatorRegistrationV2`](#validatorregistrationv2) - [`SignedValidatorRegistrationV2`](#signedvalidatorregistrationv2) - [`verify_registration_signature`](#verify_registration_signature) - [Validator Registration V2](#validator-registration-v2) - - [`process_registration`](#process_registration) + - [`process_registration_v2`](#process_registration_v2) - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) - [Constructing a `SignedExecutionPayloadEnvelope`](#constructing-a-signedexecutionpayloadenvelope) @@ -112,6 +113,9 @@ The following fields are removed: ### `process_registration_v2` +A `validator_registration_v2` is considered valid if the following function +completes without raising any assertions: + ```python def process_registration_v2(state: BeaconState, registration: SignedValidatorRegistrationV2, diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 1376e3c3..1a8ca1db 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -1,18 +1,18 @@ - - -**Table of Contents** *generated with -[DocToc](https://github.com/thlorenz/doctoc)* + - [Gloas - Honest Validator](#gloas---honest-validator) - [Introduction](#introduction) + - [Helper](#helper) + - [`get_proposer_slots_in_upcoming_epoch`](#get_proposer_slots_in_upcoming_epoch) - [Validator Registrations](#validator-registrations) - [Constructing the `ValidatorRegistrationV2`](#constructing-the-validatorregistrationv2) - [Validator Registration dissemination](#validator-registration-dissemination) + - [Validating a `SignedExecutionPayloadBid`](#validating-a-signedexecutionpayloadbid) - [Block proposal](#block-proposal) - - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - - [ExecutionPayloadBid](#executionpayloadbid) + - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [ExecutionPayloadBid](#executionpayloadbid) @@ -97,11 +97,36 @@ def create_validator_registrations(state: BeaconState, validator_index: Validato return registrations ``` +## Validating a `SignedExecutionPayloadBid` + +When the proposer receives a `SignedExecutionPayloadBid` from a builder, it can +validate the bid using `validate_bid`. It can discard the bid if the conditions +are not satisfied. + +```python +def validate_bid( + state: BeaconState, signed_bid: SignedExecutionPayloadBid, fee_recipient: ExecutionAddress +) -> bool: + builder = state.builders[signed_bid.builder_index] + + assert signed_bid.slot == state.slot + assert signed_bid.fee_recipient == fee_recipient + assert signed_bid.parent_block_hash == state.latest_block_hash + assert signed_bid.parent_block_root == hash_tree_root(state.latest_block_header) + assert signed_bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) + assert is_builder(state, builder.pubkey) + + if signed_bid.value > 0: + assert can_builder_cover_bid(state, signed_bid.builder_index, signed_bid.value) + + return verify_execution_payload_bid_signature(state, signed_bid) +``` + ## Block proposal -#### Constructing the `BeaconBlockBody` +### Constructing the `BeaconBlockBody` -##### ExecutionPayloadBid +#### Recieving ExecutionPayloadBid To obtain an execution payload, a block proposer building a block on top of a beacon `state` in a given `slot` must take the following actions: From 4ab5a0f084143cec71d3adb08990314db47169fc Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 18 Dec 2025 10:50:18 +0530 Subject: [PATCH 10/59] abstract execution_payment_accepted to a BuilderPreferences struct --- specs/gloas/builder.md | 27 ++++++++++++++++++++++----- specs/gloas/validator.md | 21 ++++++++++++++------- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 57dea977..62f5e2ba 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -1,5 +1,7 @@ + + - [Gloas - Builder Specification](#gloas---builder-specification) @@ -8,7 +10,7 @@ - [Predicates](#predicates) - [`is_active_builder`](#is_active_builder) - [Helper Functions](#helper-functions) - - [`compute_epoch_at_slot`](#compute_epoch_at_slot) + - [`compute_epoch_at_slot`](#compute_epoch_at_slot) - [Containers](#containers) - [New Containers](#new-containers) - [`ValidatorRegistrationV2`](#validatorregistrationv2) @@ -60,6 +62,13 @@ def compute_epoch_at_slot(slot: Slot) -> Epoch: ### New Containers +#### `BuilderPreferences` + +```python +class BuilderPreferences(Container): + execution_payment_accepted: boolean +``` + #### `ValidatorRegistrationV2` ```python @@ -69,7 +78,7 @@ class ValidatorRegistrationV2(Container): fee_recipient: ExecutionAddress proposal_slot: Slot gas_limit: uint64 - execution_payment_accepted: boolean + builder_preferences: BuilderPreferences ``` #### `SignedValidatorRegistrationV2` @@ -91,6 +100,15 @@ def verify_registration_signature(state: BeaconState, signed_registration: Signe return bls.Verify(pubkey, signing_root, signed_registration.signature) ``` +## Builder Preferences + +Using validator registrations, a proposer can express the preferences it has for +a builder. Currently, the only preference that is supported is: + +- `execution_payment_accepted`: This is a boolean which indicates that the + proposer is willing to accept a trusted execution layer payment from the + builder. + ## Validator Registration V2 The second version of ValidatorRegistrations adds the following new fields: @@ -99,9 +117,8 @@ The second version of ValidatorRegistrations adds the following new fields: sent. - `validator_index`: The index of the validator selected to propose a block at slot `proposal_slot` -- `execution_payment_accepted`: This is a boolean which indicates that the - validator is willing accept a trusted execution layer payment from the builder - to which it is sending the registrations. +- `builder_preferences`: This is a struct which contains the per builder + preferences the proposer has. - `proposal_slot`: The slot at which this validator is proposing. The following fields are removed: diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 1a8ca1db..02163f5b 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -1,5 +1,7 @@ + + - [Gloas - Honest Validator](#gloas---honest-validator) @@ -11,8 +13,8 @@ - [Validator Registration dissemination](#validator-registration-dissemination) - [Validating a `SignedExecutionPayloadBid`](#validating-a-signedexecutionpayloadbid) - [Block proposal](#block-proposal) - - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - - [ExecutionPayloadBid](#executionpayloadbid) + - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [ExecutionPayloadBid](#executionpayloadbid) @@ -43,9 +45,9 @@ def get_proposer_slots_in_upcoming_epoch( current_epoch_start_slot = compute_start_slot_at_epoch(get_current_epoch(state)) next_epoch_proposer_lookahead = state.proposer_lookahead[SLOTS_PER_EPOCH:] - for i, proposer_index in enumerate(next_epoch_proposer_lookahead): + for offset, proposer_index in enumerate(next_epoch_proposer_lookahead): if proposer_index == validator_index: - slot = current_epoch_start_slot + SLOTS_PER_EPOCH + i + slot = current_epoch_start_slot + SLOTS_PER_EPOCH + offset proposer_slots.append(slot) return proposer_slots @@ -80,17 +82,22 @@ many validator registrations all at once to builders. Validators run registrations for all the slots they will be proposing in the upcoming epoch. ```python -def create_validator_registrations(state: BeaconState, validator_index: ValidatorIndex, gas_limit: uint64, builder_index: BuilderIndex, execution_payment_accepted: bool) -> List[ValidatorRegistrationV2]: +def create_validator_registrations_for_builder(state: BeaconState, validator_index: ValidatorIndex, gas_limit: uint64, builder_index: BuilderIndex, builder_preferences: BuilderPreferences) -> List[ValidatorRegistrationV2]: slots = get_proposer_slots_in_lookahead(state, validator_index) registrations: List[ValidatorRegistrationsV2] = [] + assert is_builder(state, builder_index) + + builder = state.builders[builder_index] + assert builder.exit_epoch == FAR_FUTURE_EPOCH + for slot in slots: registrations.append(ValidatorRegistrationV2( fee_recipient=fee_recipient, builder_index=builder_index, gas_limit=gas_limit, validator_index=validator_index - execution_payment_accepted=execution_payment_accepted, + builder_preferences=builder_preferences, proposal_slot=slot )) @@ -126,7 +133,7 @@ def validate_bid( ### Constructing the `BeaconBlockBody` -#### Recieving ExecutionPayloadBid +#### Receiving ExecutionPayloadBid To obtain an execution payload, a block proposer building a block on top of a beacon `state` in a given `slot` must take the following actions: From 9901fdc5403acafb41b6dd1301509adba260499d Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 18 Dec 2025 11:39:31 +0530 Subject: [PATCH 11/59] minor fixes --- builder-oapi.yaml | 2 +- specs/gloas/builder.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builder-oapi.yaml b/builder-oapi.yaml index ca26f88b..26c99165 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -52,7 +52,7 @@ paths: /eth/v1/builder/validators: $ref: "./apis/builder/validators.yaml" /eth/v2/builder/validators: - $ref: "./apis/builder/validators_2.yaml" + $ref: "./apis/builder/validators_v2.yaml" /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}: $ref: "./apis/builder/header.yaml" /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_index}: diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 62f5e2ba..2c902551 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -126,7 +126,7 @@ The following fields are removed: - `pubkey`: This is the pubkey of the validator which has now been replaced with `validator_index`. - `timestamp`: A new validator registration will be sent by the validator to the - builder in the epoch prior to one where + builder in the epoch prior to one where they will be proposing. ### `process_registration_v2` From 6959fda232b1d66592edc5384a5eb55ac6a7e25e Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 18 Dec 2025 11:42:41 +0530 Subject: [PATCH 12/59] run doctoc --- specs/gloas/builder.md | 6 +++--- specs/gloas/validator.md | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 2c902551..876fb36a 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -1,7 +1,5 @@ - - - [Gloas - Builder Specification](#gloas---builder-specification) @@ -10,12 +8,14 @@ - [Predicates](#predicates) - [`is_active_builder`](#is_active_builder) - [Helper Functions](#helper-functions) - - [`compute_epoch_at_slot`](#compute_epoch_at_slot) + - [`compute_epoch_at_slot`](#compute_epoch_at_slot) - [Containers](#containers) - [New Containers](#new-containers) + - [`BuilderPreferences`](#builderpreferences) - [`ValidatorRegistrationV2`](#validatorregistrationv2) - [`SignedValidatorRegistrationV2`](#signedvalidatorregistrationv2) - [`verify_registration_signature`](#verify_registration_signature) + - [Builder Preferences](#builder-preferences) - [Validator Registration V2](#validator-registration-v2) - [`process_registration_v2`](#process_registration_v2) - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 02163f5b..a40cc19f 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -1,7 +1,5 @@ - - - [Gloas - Honest Validator](#gloas---honest-validator) @@ -14,7 +12,7 @@ - [Validating a `SignedExecutionPayloadBid`](#validating-a-signedexecutionpayloadbid) - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - - [ExecutionPayloadBid](#executionpayloadbid) + - [Receiving ExecutionPayloadBid](#receiving-executionpayloadbid) From e752694b985b30be9a700090fece38a274419f6f Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 19 Dec 2025 15:53:00 +0530 Subject: [PATCH 13/59] use correct method to check if builder is active --- specs/gloas/validator.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index a40cc19f..737f6293 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -84,10 +84,7 @@ def create_validator_registrations_for_builder(state: BeaconState, validator_ind slots = get_proposer_slots_in_lookahead(state, validator_index) registrations: List[ValidatorRegistrationsV2] = [] - assert is_builder(state, builder_index) - - builder = state.builders[builder_index] - assert builder.exit_epoch == FAR_FUTURE_EPOCH + assert is_active_builder(state, builder_index) for slot in slots: registrations.append(ValidatorRegistrationV2( From d5773ac7c56c2185b81cabd5c44a6bf02a97f874 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 23 Dec 2025 19:39:08 +0530 Subject: [PATCH 14/59] save --- apis/builder/execution_payload_bid.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 8f28ee4a..6a5bdd63 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -29,7 +29,7 @@ get: - name: parent_root in: path required: true - description: Root of the execution layer block the proposer will build on. + description: Root of the beacon block the proposer will build on. schema: $ref: "../../builder-oapi.yaml#/components/schemas/Root" - name: proposer_index From 68c1f21c91c8ed01e7e12a0edfdd7d293debd0ec Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 23 Dec 2025 22:49:33 +0530 Subject: [PATCH 15/59] updates --- specs/gloas/builder.md | 30 ++++++++++++++++++++---------- specs/gloas/validator.md | 6 ++++-- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 876fb36a..6f9f9386 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -1,5 +1,7 @@ + + - [Gloas - Builder Specification](#gloas---builder-specification) @@ -8,7 +10,7 @@ - [Predicates](#predicates) - [`is_active_builder`](#is_active_builder) - [Helper Functions](#helper-functions) - - [`compute_epoch_at_slot`](#compute_epoch_at_slot) + - [`compute_epoch_at_slot`](#compute_epoch_at_slot) - [Containers](#containers) - [New Containers](#new-containers) - [`BuilderPreferences`](#builderpreferences) @@ -39,11 +41,17 @@ This document documents the builder behaviour with the Builder-API. ### `is_active_builder` ```python -def is_active_builder(builder: Builder) -> bool: +def is_active_builder(state: BeaconState, builder_index: BuilderIndex) -> bool: """ - Check if ``builder`` is active. + Check if the builder at ``builder_index`` is active for the given ``state``. """ - return builder.exit_epoch == FAR_FUTURE_EPOCH + builder = state.builders[builder_index] + return ( + # Placement in builder list is finalized + builder.deposit_epoch < state.finalized_checkpoint.epoch + # Has not initiated exit + and builder.withdrawable_epoch == FAR_FUTURE_EPOCH + ) ``` ## Helper Functions @@ -144,19 +152,15 @@ def process_registration_v2(state: BeaconState, builder_index = registration.builder_index proposal_slot = registration.proposal_slot - assert validator_index < len(state.validators) - assert builder_index < len(state.builders) - validator = state.validators[validator_index] builder = state.builders[builder_index] - assert is_active_validator(validator, compute_epoch_at_slot(proposal_slot)) - assert is_active_builder(builder) + assert is_active_builder(state, builder) # Verify validator registration elibility assert is_eligible_for_registration(state, validator) - # Verify that the old registration's slot is earlier than the new registration's slot + # Verify that the old registration's proposal slot is earlier than the new registration's proposal slot if registration.pubkey in registrations: prev_registration = registrations[validator_index] assert registration.proposal_slot >= prev_registration.proposal_slot @@ -173,6 +177,12 @@ gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/ ## Constructing a `SignedExecutionPayloadEnvelope` +If the builder's `SignedExecutionPayloadBid` has been accepted by the proposer +and it has been included in it's `SignedBeaconBlock`, then the builder has to +construct a `SignedExecutionPayloadEnvelope` corresponding to the +`SignedExecutionPayloadBid` and it has to broadcast it to the PTC committee via +the `execution_payload_envelope` gossip topic. + The specification for a block builder to construct a `SignedExecutionPayloadEnvelope` is documented in the gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 737f6293..5b4eb328 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -1,5 +1,7 @@ + + - [Gloas - Honest Validator](#gloas---honest-validator) @@ -110,13 +112,13 @@ def validate_bid( state: BeaconState, signed_bid: SignedExecutionPayloadBid, fee_recipient: ExecutionAddress ) -> bool: builder = state.builders[signed_bid.builder_index] - + + assert is_active_builder(state, builder) assert signed_bid.slot == state.slot assert signed_bid.fee_recipient == fee_recipient assert signed_bid.parent_block_hash == state.latest_block_hash assert signed_bid.parent_block_root == hash_tree_root(state.latest_block_header) assert signed_bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) - assert is_builder(state, builder.pubkey) if signed_bid.value > 0: assert can_builder_cover_bid(state, signed_bid.builder_index, signed_bid.value) From 373493a6f4e6005b119c8aab31ea2905bdb47bd4 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 23 Dec 2025 23:02:46 +0530 Subject: [PATCH 16/59] add information for bidding --- specs/gloas/builder.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 6f9f9386..3ab317ca 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -108,6 +108,48 @@ def verify_registration_signature(state: BeaconState, signed_registration: Signe return bls.Verify(pubkey, signing_root, signed_registration.signature) ``` +## Bidding + +In Gloas, Execution payloads are built for a specific `slot`, `parent_hash`, +`pubkey` along with the `parent_root` tuple corresponding to a unique beacon +block serving as the parent. + +This is because in Gloas with EIP-7732, the execution payload and beacon blocks +are decoupled. The `parent_hash` could refer to a beacon block which is an +ancestor of the parent beacon block corresponding to the current beacon block +for which we are building the execution payload. + +We update `is_eligible_for_bid` below: + +```python +def is_eligible_for_bid(state: BeaconState, + registrations: Dict[BLSPubkey, ValidatorRegistrationV2], + slot: Slot, + parent_hash: Hash32, + # [New in Gloas] + parent_root: Root, + pubkey: BLSPubkey): + # Verify slot + if slot != state.slot: + return False + + # Verify BLS public key corresponds to a registered validator + if pubkey not in registrations: + return False + + # Verify BLS public key corresponds to the proposer for the slot + proposer_index = get_beacon_proposer_index(state) + if pubkey != state.validators[proposer_index].pubkey: + return False + + # Verify parent hash + # [Modified in Gloas:EIP7732] + assert parent_hash == state.latest_block_hash + + # Verify parent root + assert parent_root == hash_tree_root(state.latest_block_header) +``` + ## Builder Preferences Using validator registrations, a proposer can express the preferences it has for From b0d954291b71131629ecbfeab39bee578d4e645b Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 23 Dec 2025 23:17:30 +0530 Subject: [PATCH 17/59] update --- apis/builder/execution_payload_bid.yaml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 6a5bdd63..da04a93c 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -61,16 +61,6 @@ get: type: integer format: int64 example: 10000 - - name: X-Fee-Recipient - in: header - required: true - description: | - Required header containing the fee recipient address to which the proposer wants to receive - the payment for the bid. - schema: - type: string - format: address - example: "0x0000000000000000000000000000000000000000" responses: "200": description: Success response. From 77be52104b2866901d38975611f34c84408ec4a5 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 6 Jan 2026 22:18:35 +0530 Subject: [PATCH 18/59] add bid request auth --- apis/builder/execution_payload_bid.yaml | 34 ++++++++++--- builder-oapi.yaml | 5 ++ specs/gloas/builder.md | 39 ++------------- specs/gloas/validator.md | 65 ++++++++++++++++++++----- types/gloas/bid_request_auth.yaml | 22 +++++++++ 5 files changed, 114 insertions(+), 51 deletions(-) create mode 100644 types/gloas/bid_request_auth.yaml diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index da04a93c..0578d64c 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -1,14 +1,23 @@ -get: +post: operationId: "getExecutionPayloadBid" summary: Get an execution payload bid. description: | Requests a builder node to produce a valid execution payload bid, which - can be integrated into a blinded beacon block and signed. + can be integrated into a blinded beacon block and signed. + + The proposer sends a POST request to the builder with the following information: + - The slot for which the block should be proposed. + - The hash of the execution layer block the proposer will build on. + - The root of the beacon block the proposer will build on. + - The index of the proposer. + - A signed bid request auth to authenticate the request to a specific builder index. + + The builder responds with a 200 response containing an execution payload bid if it can provide one. If the builder is unable to produce a valid execution payload bid, then the builder MUST return a 204 response. If the request is invalid, then the builder MUST return an error response (400) with a description of the - validation failure. + validation failure. If the SignedBidRequestAuth is invalid, the builder MUST return a 400 response. This API is applicable from Glamsterdam fork onwards. tags: @@ -55,12 +64,21 @@ get: description: | Optional header containing the proposer's timeout for the request in milliseconds. Builders should use this header to adjust the amount of time by which they delay getBid - requests to maximise block rewards. Otherwise, getBid requests will timeout and the proposer + requests to maximise block rewards. Otherwise, getExecutionPayloadBid requests will timeout and the proposer will not receive the header in time. schema: type: integer format: int64 example: 10000 + requestBody: + required: true + content: + application/json: + schema: + $ref: "../../types/gloas/bid_request_auth.yaml#/Gloas/SignedBidRequestAuth" + application/octet-stream: + schema: + description: "SSZ serialized `SignedBidRequestAuth` bytes." responses: "200": description: Success response. @@ -85,7 +103,7 @@ get: schema: description: "SSZ serialized `ExecutionPayloadBid` bytes. Use Accept header to choose this response type" "204": - description: No header is available. + description: No bid is available. "400": description: Error response. content: @@ -93,10 +111,14 @@ get: schema: $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" examples: - InvalidRequest: + InvalidHash: value: code: 400 message: "Unknown hash: missing parent hash" + InvalidAuth: + value: + code: 400 + message: "Invalid SignedBidRequestAuth: signature verification failed" "406": $ref: "../../builder-oapi.yaml#/components/responses/NotAcceptable" "500": diff --git a/builder-oapi.yaml b/builder-oapi.yaml index 26c99165..3e3915e5 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -110,6 +110,11 @@ components: $ref: "./types/fulu/blobs_bundle.yaml#/Fulu/BlobsBundle" Fulu.ExecutionPayloadAndBlobsBundle: $ref: "./types/fulu/execution_payload_and_blobs_bundle.yaml#/Fulu/ExecutionPayloadAndBlobsBundle" + Gloas.BidRequestAuth: + $ref: "./types/gloas/bid_request_auth.yaml#/Gloas/BidRequestAuth" + Gloas.SignedBidRequestAuth: + $ref: "./types/gloas/bid_request_auth.yaml#/Gloas/SignedBidRequestAuth" + responses: InternalError: diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 3ab317ca..1bf14db6 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -29,43 +29,13 @@ ## Introduction -This document documents the builder behaviour with the Builder-API. +This document documents the builder behaviour with the Builder-API post ePBS. ## Custom types | Name | SSZ equivalent | Description | | -------------- | -------------- | ---------------------- | | `BuilderIndex` | `uint64` | Builder registry index | -## Predicates - -### `is_active_builder` - -```python -def is_active_builder(state: BeaconState, builder_index: BuilderIndex) -> bool: - """ - Check if the builder at ``builder_index`` is active for the given ``state``. - """ - builder = state.builders[builder_index] - return ( - # Placement in builder list is finalized - builder.deposit_epoch < state.finalized_checkpoint.epoch - # Has not initiated exit - and builder.withdrawable_epoch == FAR_FUTURE_EPOCH - ) -``` - -## Helper Functions - -#### `compute_epoch_at_slot` - -```python -def compute_epoch_at_slot(slot: Slot) -> Epoch: - """ - Return the epoch number at ``slot``. - """ - return Epoch(slot // SLOTS_PER_EPOCH) -``` - ## Containers ### New Containers @@ -81,8 +51,8 @@ class BuilderPreferences(Container): ```python class ValidatorRegistrationV2(Container): - builder_index: BuilderIndex validator_index: ValidatorIndex + builder_index: BuilderIndex fee_recipient: ExecutionAddress proposal_slot: Slot gas_limit: uint64 @@ -147,6 +117,7 @@ def is_eligible_for_bid(state: BeaconState, assert parent_hash == state.latest_block_hash # Verify parent root + # [Modified in Gloas:EIP7732] assert parent_root == hash_tree_root(state.latest_block_header) ``` @@ -163,8 +134,8 @@ a builder. Currently, the only preference that is supported is: The second version of ValidatorRegistrations adds the following new fields: -- `builder_index`: The index of the builder to which this registration is being - sent. +- `builder_index`: The index of the builder to which the validator is sending + the registration. - `validator_index`: The index of the validator selected to propose a block at slot `proposal_slot` - `builder_preferences`: This is a struct which contains the per builder diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 5b4eb328..430174a6 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -23,12 +23,37 @@ ## Introduction This document explains how a beacon-chain validator can participate in the -external block building market post ePBS. +external block building market with the Builder-API post ePBS. Validators request an `ExecutionPayloadBid` from the external builder network to put it in their `SignedBeaconBlock`. The external builder network broadcasts the `SignedExecutionPayloadEnvelope` corresponding to the bid to the PTC committee. +## Containers + +### New Containers + +#### `BidRequestAuth` + +`BidRequestAuth` is used to authenticate requests to get the bid from a builder. +This is useful so that other builders don't DDOS the builder to get their latest +bid. + +```python +class BidRequestAuth(Container): + builder_index: BuilderIndex + validator_index: ValidatorIndex + proposer_slot: Slot +``` + +#### `SignedBidRequestAuth` + +```python +class SignedBidRequestAuth(Container): + message: BidRequestAuth + signature: BLSSignature +``` + ## Helper ### `get_proposer_slots_in_upcoming_epoch` @@ -53,6 +78,21 @@ def get_proposer_slots_in_upcoming_epoch( return proposer_slots ``` +## Bid Authentication + +### Constructing the `BidRequestAuth` + +To construct the `BidRequestAuth`, we need to fill the following information: + +- `builder_index`: This builder index for which the validator is sending a + request to get the bid. +- `validator_index`: The proposer's validator index. +- `proposal_slot`: The slot at which the proposer is building a block. + +The validator constructs the `SignedBidRequestAuth` by signing the +`BidRequestAuth`. It sends the `SignedBidRequestAuth` as a header along with the +request to get the bid. + ## Validator Registrations ### Constructing the `ValidatorRegistrationV2` @@ -61,17 +101,17 @@ To do this, the validator client assembles a \[`ValidatorRegistrationV2`\][validator-registration-v2] with the following information: -- `fee_recipient`: an execution layer address where fees for the validator +- `builder_index`: The index of the builder to which the validator is submitting + the registration. +- `fee_recipient`: An execution layer address where fees for the validator should go. -- `builder_index`: the index of the builder to which this registration is being - sent to. -- `gas_limit`: the value a validator prefers for the execution block gas limit. -- `validator_index`: the validator's index. Used to identify the beacon chain +- `gas_limit`: The value a validator prefers for the execution block gas limit. +- `validator_index`: The validator's index. Used to identify the beacon chain validator and verify the wrapping signature. -- `execution_payment_accepted`: whether the proposer is willing to accept a +- `execution_payment_accepted`: Whether the proposer is willing to accept a trusted payment from the builder with index `builder_index`. - `proposal_slot`: This is set to the slot in which the validator will be - proposing. This can be looked up in the `proposal_lookahead`. + proposing. This can be looked up in `state.proposal_lookahead`. ### Validator Registration dissemination @@ -91,8 +131,8 @@ def create_validator_registrations_for_builder(state: BeaconState, validator_ind for slot in slots: registrations.append(ValidatorRegistrationV2( fee_recipient=fee_recipient, - builder_index=builder_index, gas_limit=gas_limit, + builder_index=builder_index, validator_index=validator_index builder_preferences=builder_preferences, proposal_slot=slot @@ -135,11 +175,14 @@ def validate_bid( To obtain an execution payload, a block proposer building a block on top of a beacon `state` in a given `slot` must take the following actions: -1. Call upstream builder software to get an `ExecutionPayloadBid`. +1. Call upstream builder software to get an `ExecutionPayloadBid`. The validator + is required to send the `SignedBidRequestAuth` in the request body in order to + authenticate the request to the builder. If a builder has multiple builder indices associated with + them, the validator will have to call the upstream builder software each time for each builder index. 2. Assemble a `SignedBeaconBlock` according to the process outlined in the \[Gloas specs\][https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal] - but with the `ExecutionPayloadBid` from the prior step. + but with the best `ExecutionPayloadBid` from the prior step. 3. The proposer returns the `SignedBeaconBlock` back to the upstream block building software. 4. The upstream block building software constructs the diff --git a/types/gloas/bid_request_auth.yaml b/types/gloas/bid_request_auth.yaml new file mode 100644 index 00000000..6fa4c2d8 --- /dev/null +++ b/types/gloas/bid_request_auth.yaml @@ -0,0 +1,22 @@ +Gloas: + BidRequestAuth: + type: object + required: [builder_index, validator_index, proposal_slot] + properties: + builder_index: + $ref: "../../builder-oapi.yaml#/components/schemas/Uint64" + description: "The index of the builder to which the validator is sending the request." + validator_index: + $ref: "../../beacon-apis/types/primitive.yaml#/ValidatorIndex" + description: "The index of the validator that is sending the request." + proposal_slot: + $ref: "../../beacon-apis/types/primitive.yaml#/Slot" + description: "The slot at which the proposer is building a block." + SignedBidRequestAuth: + type: object + required: [message, signature] + properties: + message: + $ref: "#/Gloas/BidRequestAuth" + signature: + $ref: "../../beacon-apis/types/primitive.yaml#/BLSSignature" \ No newline at end of file From 56a5ec9f9a4aad13d959ca2b8bf9e6e61bc6ee6f Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 6 Jan 2026 22:28:11 +0530 Subject: [PATCH 19/59] remove builder index from validator registration --- specs/gloas/builder.md | 7 ------- specs/gloas/validator.md | 14 +++++--------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 1bf14db6..054e8d29 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -52,7 +52,6 @@ class BuilderPreferences(Container): ```python class ValidatorRegistrationV2(Container): validator_index: ValidatorIndex - builder_index: BuilderIndex fee_recipient: ExecutionAddress proposal_slot: Slot gas_limit: uint64 @@ -134,8 +133,6 @@ a builder. Currently, the only preference that is supported is: The second version of ValidatorRegistrations adds the following new fields: -- `builder_index`: The index of the builder to which the validator is sending - the registration. - `validator_index`: The index of the validator selected to propose a block at slot `proposal_slot` - `builder_preferences`: This is a struct which contains the per builder @@ -162,13 +159,9 @@ def process_registration_v2(state: BeaconState, signature = registration.signature registration = registration.message validator_index = registration.validator_index - builder_index = registration.builder_index proposal_slot = registration.proposal_slot validator = state.validators[validator_index] - builder = state.builders[builder_index] - - assert is_active_builder(state, builder) # Verify validator registration elibility assert is_eligible_for_registration(state, validator) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 430174a6..d7d4ad6e 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -101,8 +101,6 @@ To do this, the validator client assembles a \[`ValidatorRegistrationV2`\][validator-registration-v2] with the following information: -- `builder_index`: The index of the builder to which the validator is submitting - the registration. - `fee_recipient`: An execution layer address where fees for the validator should go. - `gas_limit`: The value a validator prefers for the execution block gas limit. @@ -122,17 +120,14 @@ many validator registrations all at once to builders. Validators run registrations for all the slots they will be proposing in the upcoming epoch. ```python -def create_validator_registrations_for_builder(state: BeaconState, validator_index: ValidatorIndex, gas_limit: uint64, builder_index: BuilderIndex, builder_preferences: BuilderPreferences) -> List[ValidatorRegistrationV2]: +def create_validator_registrations(state: BeaconState, validator_index: ValidatorIndex, gas_limit: uint64, builder_preferences: BuilderPreferences) -> List[ValidatorRegistrationV2]: slots = get_proposer_slots_in_lookahead(state, validator_index) registrations: List[ValidatorRegistrationsV2] = [] - assert is_active_builder(state, builder_index) - for slot in slots: registrations.append(ValidatorRegistrationV2( fee_recipient=fee_recipient, gas_limit=gas_limit, - builder_index=builder_index, validator_index=validator_index builder_preferences=builder_preferences, proposal_slot=slot @@ -176,9 +171,10 @@ To obtain an execution payload, a block proposer building a block on top of a beacon `state` in a given `slot` must take the following actions: 1. Call upstream builder software to get an `ExecutionPayloadBid`. The validator - is required to send the `SignedBidRequestAuth` in the request body in order to - authenticate the request to the builder. If a builder has multiple builder indices associated with - them, the validator will have to call the upstream builder software each time for each builder index. + is required to send the `SignedBidRequestAuth` in the request body in order + to authenticate the request to the builder. If a builder has multiple builder + indices associated with them, the validator will have to call the upstream + builder software each time for each builder index. 2. Assemble a `SignedBeaconBlock` according to the process outlined in the \[Gloas specs\][https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal] From dd644e5d3f1297802cd057fc254067b5e89f3f91 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 6 Jan 2026 22:50:22 +0530 Subject: [PATCH 20/59] add validator registration type --- apis/builder/validators_v2.yaml | 2 +- beacon-apis | 2 +- builder-oapi.yaml | 11 +++++---- specs/gloas/validator.md | 2 +- types/gloas/bid_request_auth.yaml | 6 ++--- types/gloas/registration.yaml | 38 +++++++++++++++++++++++++++++++ 6 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 types/gloas/registration.yaml diff --git a/apis/builder/validators_v2.yaml b/apis/builder/validators_v2.yaml index eeff67bf..813ae98d 100644 --- a/apis/builder/validators_v2.yaml +++ b/apis/builder/validators_v2.yaml @@ -21,7 +21,7 @@ post: schema: type: array items: - $ref: "../../builder-oapi.yaml#/components/schemas/SignedValidatorRegistrationV2" + $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedValidatorRegistrationV2" example: $ref: "../../builder-oapi.yaml#/components/examples/SignedValidatorRegistrations/value" application/octet-stream: diff --git a/beacon-apis b/beacon-apis index 339eea96..fe362694 160000 --- a/beacon-apis +++ b/beacon-apis @@ -1 +1 @@ -Subproject commit 339eea96b41c787dad47765fc781303fb40aa886 +Subproject commit fe362694bd70685fe7d24cc40ada99178a354d7d diff --git a/builder-oapi.yaml b/builder-oapi.yaml index 3e3915e5..99e75455 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -100,8 +100,6 @@ components: $ref: "./types/deneb/execution_payload_and_blobs_bundle.yaml#/Deneb/ExecutionPayloadAndBlobsBundle" SignedValidatorRegistration: $ref: "./beacon-apis/types/registration.yaml#/SignedValidatorRegistration" - SignedValidatorRegistrationV2: - $ref: "./beacon-apis/types/registration.yaml#/SignedValidatorRegistrationV2" Electra.SignedBlindedBeaconBlock: $ref: "./beacon-apis/types/electra/block.yaml#/Electra/SignedBlindedBeaconBlock" Electra.SignedBuilderBid: @@ -114,8 +112,13 @@ components: $ref: "./types/gloas/bid_request_auth.yaml#/Gloas/BidRequestAuth" Gloas.SignedBidRequestAuth: $ref: "./types/gloas/bid_request_auth.yaml#/Gloas/SignedBidRequestAuth" - - + Gloas.BuilderPreferences: + $ref: "./types/gloas/registration.yaml#/Gloas/BuilderPreferences" + Gloas.ValidatorRegistrationV2: + $ref: "./types/gloas/registration.yaml#/Gloas/ValidatorRegistrationV2" + Gloas.SignedValidatorRegistrationV2: + $ref: "./types/gloas/registration.yaml#/Gloas/SignedValidatorRegistrationV2" + responses: InternalError: $ref: "./types/http.yaml#/InternalError" diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index d7d4ad6e..323e0b8a 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -121,7 +121,7 @@ registrations for all the slots they will be proposing in the upcoming epoch. ```python def create_validator_registrations(state: BeaconState, validator_index: ValidatorIndex, gas_limit: uint64, builder_preferences: BuilderPreferences) -> List[ValidatorRegistrationV2]: - slots = get_proposer_slots_in_lookahead(state, validator_index) + slots = get_proposer_slots_in_upcoming_epoch(state, validator_index) registrations: List[ValidatorRegistrationsV2] = [] for slot in slots: diff --git a/types/gloas/bid_request_auth.yaml b/types/gloas/bid_request_auth.yaml index 6fa4c2d8..4053307e 100644 --- a/types/gloas/bid_request_auth.yaml +++ b/types/gloas/bid_request_auth.yaml @@ -7,10 +7,10 @@ Gloas: $ref: "../../builder-oapi.yaml#/components/schemas/Uint64" description: "The index of the builder to which the validator is sending the request." validator_index: - $ref: "../../beacon-apis/types/primitive.yaml#/ValidatorIndex" + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" description: "The index of the validator that is sending the request." proposal_slot: - $ref: "../../beacon-apis/types/primitive.yaml#/Slot" + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" description: "The slot at which the proposer is building a block." SignedBidRequestAuth: type: object @@ -19,4 +19,4 @@ Gloas: message: $ref: "#/Gloas/BidRequestAuth" signature: - $ref: "../../beacon-apis/types/primitive.yaml#/BLSSignature" \ No newline at end of file + $ref: "../../beacon-apis/types/primitive.yaml#/Signature" \ No newline at end of file diff --git a/types/gloas/registration.yaml b/types/gloas/registration.yaml new file mode 100644 index 00000000..11359751 --- /dev/null +++ b/types/gloas/registration.yaml @@ -0,0 +1,38 @@ +Gloas: + BuilderPreferences: + type: object + description: "Per-builder preferences that a validator can express." + required: [execution_payment_accepted] + properties: + execution_payment_accepted: + type: boolean + description: "Indicates that the proposer is willing to accept a trusted execution layer payment from the builder." + ValidatorRegistrationV2: + type: object + description: "The `ValidatorRegistrationV2` object for Gloas fork, replacing pubkey and timestamp with validator_index, proposal_slot, and builder_preferences." + required: [validator_index, fee_recipient, proposal_slot, gas_limit, builder_preferences] + properties: + validator_index: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + description: "Validator index. Used to identify the beacon chain validator and verify the signature." + fee_recipient: + $ref: "../../beacon-apis/types/primitive.yaml#/ExecutionAddress" + description: "Address to receive fees from the block." + proposal_slot: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + description: "The slot at which this validator is proposing." + gas_limit: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + description: "Preferred gas limit of validator." + builder_preferences: + $ref: "#/Gloas/BuilderPreferences" + description: "Per-builder preferences." + SignedValidatorRegistrationV2: + type: object + description: "The `SignedValidatorRegistrationV2` object for Gloas fork." + required: [message, signature] + properties: + message: + $ref: "#/Gloas/ValidatorRegistrationV2" + signature: + $ref: "../../beacon-apis/types/primitive.yaml#/Signature" \ No newline at end of file From 80dbeb05736c990c0c7726c6aa42ea929e3be0a9 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 7 Jan 2026 10:36:49 +0530 Subject: [PATCH 21/59] link to api calls --- specs/gloas/builder.md | 96 ++++++++++++++++++++++----------------- specs/gloas/validator.md | 97 +++++++++++++++++++++++++++------------- wordlist.txt | 7 ++- 3 files changed, 126 insertions(+), 74 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 054e8d29..0267d27a 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -6,17 +6,13 @@ - [Gloas - Builder Specification](#gloas---builder-specification) - [Introduction](#introduction) - - [Custom types](#custom-types) - - [Predicates](#predicates) - - [`is_active_builder`](#is_active_builder) - - [Helper Functions](#helper-functions) - - [`compute_epoch_at_slot`](#compute_epoch_at_slot) - [Containers](#containers) - [New Containers](#new-containers) - [`BuilderPreferences`](#builderpreferences) - [`ValidatorRegistrationV2`](#validatorregistrationv2) - [`SignedValidatorRegistrationV2`](#signedvalidatorregistrationv2) - - [`verify_registration_signature`](#verify_registration_signature) + - [`verify_registration_v2_signature`](#verify_registration_v2_signature) + - [Bidding](#bidding) - [Builder Preferences](#builder-preferences) - [Validator Registration V2](#validator-registration-v2) - [`process_registration_v2`](#process_registration_v2) @@ -29,12 +25,11 @@ ## Introduction -This document documents the builder behaviour with the Builder-API post ePBS. - -## Custom types - -| Name | SSZ equivalent | Description | | -------------- | -------------- | ----------------------- | | `BuilderIndex` | `uint64` | Builder registry index | +This document documents the builder behaviour with the Builder-API post ePBS. It +describes how builders interact with validators through +[`ValidatorRegistrationV2`][validator-registration-v2] and construct +[`SignedExecutionPayloadBid`][signed-execution-payload-bid] and +[`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] objects. ## Containers @@ -66,10 +61,13 @@ class SignedValidatorRegistrationV2(Container): signature: BLSSignature ``` -### `verify_registration_signature` +### `verify_registration_v2_signature` + +*Note*: `compute_domain` and `compute_signing_root` are defined in the +[Gloas consensus specs][gloas-consensus-specs]. ```python -def verify_registration_signature(state: BeaconState, signed_registration: SignedValidatorRegistrationV2) -> bool: +def verify_registration_v2_signature(state: BeaconState, signed_registration: SignedValidatorRegistrationV2) -> bool: validator = state.validators[signed_registration.message.validator_index] pubkey = validator.pubkey domain = compute_domain(DOMAIN_APPLICATION_BUILDER) @@ -80,35 +78,34 @@ def verify_registration_signature(state: BeaconState, signed_registration: Signe ## Bidding In Gloas, Execution payloads are built for a specific `slot`, `parent_hash`, -`pubkey` along with the `parent_root` tuple corresponding to a unique beacon -block serving as the parent. +`validator_index` along with the `parent_root` tuple corresponding to a unique +beacon block serving as the parent. -This is because in Gloas with EIP-7732, the execution payload and beacon blocks -are decoupled. The `parent_hash` could refer to a beacon block which is an -ancestor of the parent beacon block corresponding to the current beacon block -for which we are building the execution payload. +This is because in Gloas with [EIP-7732][eip-7732], the execution payload and +beacon blocks are decoupled. The `parent_hash` could refer to a beacon block +which is an ancestor of the parent beacon block corresponding to the current +beacon block for which we are building the execution payload. -We update `is_eligible_for_bid` below: +We update `is_eligible_for_bid` below. *Note*: `hash_tree_root` is defined in +the [Gloas consensus specs][gloas-consensus-specs]. ```python def is_eligible_for_bid(state: BeaconState, - registrations: Dict[BLSPubkey, ValidatorRegistrationV2], + registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], slot: Slot, parent_hash: Hash32, # [New in Gloas] parent_root: Root, - pubkey: BLSPubkey): + # [New in Gloas] + validator_index: ValidatorIndex): # Verify slot if slot != state.slot: return False - # Verify BLS public key corresponds to a registered validator - if pubkey not in registrations: + if validator_index not in state.validators.keys(): return False - # Verify BLS public key corresponds to the proposer for the slot - proposer_index = get_beacon_proposer_index(state) - if pubkey != state.validators[proposer_index].pubkey: + if validator_index not in registrations: return False # Verify parent hash @@ -149,12 +146,16 @@ The following fields are removed: ### `process_registration_v2` A `validator_registration_v2` is considered valid if the following function -completes without raising any assertions: +completes without raising any assertions. + +*Note*: [`is_eligible_for_registration`][is-eligible-for-registration] and +[`verify_registration_signature`][verify-registration-signature] are defined in +the [Gloas consensus specs][gloas-consensus-specs]. ```python def process_registration_v2(state: BeaconState, registration: SignedValidatorRegistrationV2, - registrations: Dict[BLSPubkey, ValidatorRegistrationV2], + registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], current_timestamp: uint64): signature = registration.signature registration = registration.message @@ -163,11 +164,11 @@ def process_registration_v2(state: BeaconState, validator = state.validators[validator_index] - # Verify validator registration elibility + # Verify validator registration eligibility assert is_eligible_for_registration(state, validator) # Verify that the old registration's proposal slot is earlier than the new registration's proposal slot - if registration.pubkey in registrations: + if validator_index in registrations: prev_registration = registrations[validator_index] assert registration.proposal_slot >= prev_registration.proposal_slot @@ -177,18 +178,29 @@ def process_registration_v2(state: BeaconState, ## Constructing a `SignedExecutionPayloadBid` -The specification for a block builder to construct a `SignedExecutionPayloadBid` -is documented in the -gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. +The specification for a block builder to construct a +[`SignedExecutionPayloadBid`][signed-execution-payload-bid] is documented in the +[Gloas consensus specs][gloas-builder-specs]. ## Constructing a `SignedExecutionPayloadEnvelope` -If the builder's `SignedExecutionPayloadBid` has been accepted by the proposer -and it has been included in it's `SignedBeaconBlock`, then the builder has to -construct a `SignedExecutionPayloadEnvelope` corresponding to the -`SignedExecutionPayloadBid` and it has to broadcast it to the PTC committee via -the `execution_payload_envelope` gossip topic. +If the builder's [`SignedExecutionPayloadBid`][signed-execution-payload-bid] has +been accepted by the proposer and it has been included in it's +`SignedBeaconBlock`, then the builder has to construct a +[`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] +corresponding to the [`SignedExecutionPayloadBid`][signed-execution-payload-bid] +and it has to broadcast it to the PTC committee via the +`execution_payload_envelope` gossip topic. The specification for a block builder to construct a -`SignedExecutionPayloadEnvelope` is documented in the -gloas-specs[https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md]. +[`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] is +documented in the [Gloas consensus specs][gloas-builder-specs]. + +[eip-7732]: https://eips.ethereum.org/EIPS/eip-7732 +[gloas-builder-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md +[gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas +[is-eligible-for-registration]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#is_eligible_for_registration +[signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid +[signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope +[validator-registration-v2]: #validatorregistrationv2 +[verify-registration-signature]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#verify_registration_signature diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 323e0b8a..7e41ece9 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -6,8 +6,14 @@ - [Gloas - Honest Validator](#gloas---honest-validator) - [Introduction](#introduction) + - [Containers](#containers) + - [New Containers](#new-containers) + - [`BidRequestAuth`](#bidrequestauth) + - [`SignedBidRequestAuth`](#signedbidrequestauth) - [Helper](#helper) - [`get_proposer_slots_in_upcoming_epoch`](#get_proposer_slots_in_upcoming_epoch) + - [Bid Authentication](#bid-authentication) + - [Constructing the `BidRequestAuth`](#constructing-the-bidrequestauth) - [Validator Registrations](#validator-registrations) - [Constructing the `ValidatorRegistrationV2`](#constructing-the-validatorregistrationv2) - [Validator Registration dissemination](#validator-registration-dissemination) @@ -25,9 +31,11 @@ This document explains how a beacon-chain validator can participate in the external block building market with the Builder-API post ePBS. -Validators request an `ExecutionPayloadBid` from the external builder network to -put it in their `SignedBeaconBlock`. The external builder network broadcasts the -`SignedExecutionPayloadEnvelope` corresponding to the bid to the PTC committee. +Validators request a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] +from the external builder network to put it in their `SignedBeaconBlock`. The +external builder network broadcasts the +[`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] +corresponding to the bid to the PTC committee. ## Containers @@ -58,6 +66,9 @@ class SignedBidRequestAuth(Container): ### `get_proposer_slots_in_upcoming_epoch` +*Note*: `compute_start_slot_at_epoch` and `get_current_epoch` are defined in the +[Gloas consensus specs][gloas-consensus-specs]. + ```python def get_proposer_slots_in_upcoming_epoch( state: BeaconState, @@ -90,15 +101,16 @@ To construct the `BidRequestAuth`, we need to fill the following information: - `proposal_slot`: The slot at which the proposer is building a block. The validator constructs the `SignedBidRequestAuth` by signing the -`BidRequestAuth`. It sends the `SignedBidRequestAuth` as a header along with the -request to get the bid. +`BidRequestAuth`. It sends the `SignedBidRequestAuth` in the request body along +with the request to get the bid in the +[`getExecutionPayloadBid`][get-execution-payload-bid-api] API call. ## Validator Registrations ### Constructing the `ValidatorRegistrationV2` To do this, the validator client assembles a -\[`ValidatorRegistrationV2`\][validator-registration-v2] with the following +[`ValidatorRegistrationV2`][validator-registration-v2] with the following information: - `fee_recipient`: An execution layer address where fees for the validator @@ -122,25 +134,33 @@ registrations for all the slots they will be proposing in the upcoming epoch. ```python def create_validator_registrations(state: BeaconState, validator_index: ValidatorIndex, gas_limit: uint64, builder_preferences: BuilderPreferences) -> List[ValidatorRegistrationV2]: slots = get_proposer_slots_in_upcoming_epoch(state, validator_index) - registrations: List[ValidatorRegistrationsV2] = [] + registrations: List[ValidatorRegistrationV2] = [] for slot in slots: - registrations.append(ValidatorRegistrationV2( - fee_recipient=fee_recipient, - gas_limit=gas_limit, - validator_index=validator_index - builder_preferences=builder_preferences, - proposal_slot=slot - )) - - return registrations + registrations.append(ValidatorRegistrationV2( + fee_recipient=fee_recipient, + gas_limit=gas_limit, + validator_index=validator_index, + builder_preferences=builder_preferences, + proposal_slot=slot + )) + + return registrations ``` ## Validating a `SignedExecutionPayloadBid` -When the proposer receives a `SignedExecutionPayloadBid` from a builder, it can -validate the bid using `validate_bid`. It can discard the bid if the conditions -are not satisfied. +When the proposer receives a +[`SignedExecutionPayloadBid`][signed-execution-payload-bid] from a builder, it +can validate the bid using `validate_bid`. It can discard the bid if the +conditions are not satisfied. + +*Note*: `hash_tree_root`, `get_randao_mix`, and `get_current_epoch` are defined +in the [Gloas consensus specs][gloas-consensus-specs]. The predicates +[`is_active_builder`][is-active-builder], +[`can_builder_cover_bid`][can-builder-cover-bid], and +[`verify_execution_payload_bid_signature`][verify-execution-payload-bid-signature] +are also defined in the consensus specs. ```python def validate_bid( @@ -170,18 +190,33 @@ def validate_bid( To obtain an execution payload, a block proposer building a block on top of a beacon `state` in a given `slot` must take the following actions: -1. Call upstream builder software to get an `ExecutionPayloadBid`. The validator - is required to send the `SignedBidRequestAuth` in the request body in order - to authenticate the request to the builder. If a builder has multiple builder - indices associated with them, the validator will have to call the upstream - builder software each time for each builder index. +1. Call upstream builder software to get a + [`SignedExecutionPayloadBid`][signed-execution-payload-bid] using the + [`getExecutionPayloadBid`][get-execution-payload-bid-api] API call. The + validator is required to send the `SignedBidRequestAuth` in the request body + in order to authenticate the request to the builder. If a builder has + multiple builder indices associated with them, the validator will have to + call the upstream builder software each time for each builder index. 2. Assemble a `SignedBeaconBlock` according to the process outlined in the - \[Gloas - specs\][https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal] - but with the best `ExecutionPayloadBid` from the prior step. + [Gloas validator specs][gloas-validator-specs] but with the best + [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the prior + step. 3. The proposer returns the `SignedBeaconBlock` back to the upstream block - building software. + building software via + [`submitSignedBeaconBlock`][submit-signed-beacon-block] API call. 4. The upstream block building software constructs the - `SignedExecutionPayloadEnvelope` from the - `SignedBlindedExecutionPayloadEnvelope` and broadcasts it to the PTC - committee. + [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] + corresponding to the + [`SignedExecutionPayloadBid`][signed-execution-payload-bid] and broadcasts it + to the PTC committee. + +[can-builder-cover-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#can_builder_cover_bid +[get-execution-payload-bid-api]: ./../../apis/builder/execution_payload_bid.yaml +[gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas +[gloas-validator-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal +[is-active-builder]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#is_active_builder +[submit-signed-beacon-block]: ./../../apis/builder/beacon_block.yaml +[signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid +[signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope +[validator-registration-v2]: ./builder.md#validatorregistrationv2 +[verify-execution-payload-bid-signature]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#verify_execution_payload_bid_signature diff --git a/wordlist.txt b/wordlist.txt index 970e86ca..56973e49 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -28,6 +28,11 @@ validator's vc wei EIP +ePBS Fulu fulu -submitBlindedBlockV \ No newline at end of file +Gloas +gloas +PTC +submitBlindedBlockV +ValidatorRegistrationsV \ No newline at end of file From 3a9640ac8355b532368d6d75ea172513b1c0546e Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 7 Jan 2026 18:12:49 +0530 Subject: [PATCH 22/59] add some notes --- specs/gloas/builder.md | 28 +++++++++----------------- specs/gloas/validator.md | 43 ++++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 0267d27a..2a28628c 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -27,7 +27,7 @@ This document documents the builder behaviour with the Builder-API post ePBS. It describes how builders interact with validators through -[`ValidatorRegistrationV2`][validator-registration-v2] and construct +\[`ValidatorRegistrationV2`\][validator-registration-v2] and construct [`SignedExecutionPayloadBid`][signed-execution-payload-bid] and [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] objects. @@ -81,10 +81,10 @@ In Gloas, Execution payloads are built for a specific `slot`, `parent_hash`, `validator_index` along with the `parent_root` tuple corresponding to a unique beacon block serving as the parent. -This is because in Gloas with [EIP-7732][eip-7732], the execution payload and -beacon blocks are decoupled. The `parent_hash` could refer to a beacon block -which is an ancestor of the parent beacon block corresponding to the current -beacon block for which we are building the execution payload. +This is because in Gloas with [EIP-7732], the execution payload and beacon +blocks are decoupled. The `parent_hash` could refer to a beacon block which is +an ancestor of the parent beacon block corresponding to the current beacon block +for which we are building the execution payload. We update `is_eligible_for_bid` below. *Note*: `hash_tree_root` is defined in the [Gloas consensus specs][gloas-consensus-specs]. @@ -99,14 +99,11 @@ def is_eligible_for_bid(state: BeaconState, # [New in Gloas] validator_index: ValidatorIndex): # Verify slot - if slot != state.slot: - return False + assert slot == state.slot - if validator_index not in state.validators.keys(): - return False + assert validator_index in state.validator.keys() - if validator_index not in registrations: - return False + assert validator_index in registrations.keys() # Verify parent hash # [Modified in Gloas:EIP7732] @@ -148,10 +145,6 @@ The following fields are removed: A `validator_registration_v2` is considered valid if the following function completes without raising any assertions. -*Note*: [`is_eligible_for_registration`][is-eligible-for-registration] and -[`verify_registration_signature`][verify-registration-signature] are defined in -the [Gloas consensus specs][gloas-consensus-specs]. - ```python def process_registration_v2(state: BeaconState, registration: SignedValidatorRegistrationV2, @@ -173,7 +166,7 @@ def process_registration_v2(state: BeaconState, assert registration.proposal_slot >= prev_registration.proposal_slot # Verify registration signature - assert verify_registration_signature(state, registration) + assert verify_registration_v2_signature(state, registration) ``` ## Constructing a `SignedExecutionPayloadBid` @@ -199,8 +192,5 @@ documented in the [Gloas consensus specs][gloas-builder-specs]. [eip-7732]: https://eips.ethereum.org/EIPS/eip-7732 [gloas-builder-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md [gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas -[is-eligible-for-registration]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#is_eligible_for_registration [signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid [signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope -[validator-registration-v2]: #validatorregistrationv2 -[verify-registration-signature]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#verify_registration_signature diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 7e41ece9..e0cc63b8 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -164,23 +164,40 @@ are also defined in the consensus specs. ```python def validate_bid( - state: BeaconState, signed_bid: SignedExecutionPayloadBid, fee_recipient: ExecutionAddress + state: BeaconState, reg: SignedValidatorRegistrationV2, bid: SignedExecutionPayloadBid, bid_request_auth: SignedBidRequestAuth, fee_recipient: ExecutionAddress ) -> bool: - builder = state.builders[signed_bid.builder_index] + bid = bid.message + + assert bid.builder_index == bid_request_auth.message.builder_index + + builder = state.builders[bid.builder_index] assert is_active_builder(state, builder) - assert signed_bid.slot == state.slot - assert signed_bid.fee_recipient == fee_recipient - assert signed_bid.parent_block_hash == state.latest_block_hash - assert signed_bid.parent_block_root == hash_tree_root(state.latest_block_header) - assert signed_bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) + assert bid.slot == state.slot + assert bid.fee_recipient == fee_recipient + assert bid.parent_block_hash == state.latest_block_hash + assert bid.parent_block_root == hash_tree_root(state.latest_block_header) + assert bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) - if signed_bid.value > 0: - assert can_builder_cover_bid(state, signed_bid.builder_index, signed_bid.value) + if not reg.message.builder_preferences.execution_payment_accepted: + assert bid.execution_payment == 0 - return verify_execution_payload_bid_signature(state, signed_bid) + if bid.value > 0: + assert can_builder_cover_bid(state, bid.builder_index, signed_bid.value) + + return verify_execution_payload_bid_signature(state, bid) ``` +Note that, the fee recipient specified in `bid.fee_recipient` does not +necessarily correspond to the fee recipient of the execution payload. Even if a +builder pays the validator via execution layer payments, we require that the +bid's fee recipient matches the validators expected fee recipient and not the +builder's fee recipient. + +To express per-builder preferences we need validators to remember which +registration they have sent to the builder, so that they can validate whether +the bid conforms to the preferences expressed by the validators. + ## Block proposal ### Constructing the `BeaconBlockBody` @@ -202,8 +219,8 @@ beacon `state` in a given `slot` must take the following actions: [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the prior step. 3. The proposer returns the `SignedBeaconBlock` back to the upstream block - building software via - [`submitSignedBeaconBlock`][submit-signed-beacon-block] API call. + building software via [`submitSignedBeaconBlock`][submit-signed-beacon-block] + API call. 4. The upstream block building software constructs the [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] corresponding to the @@ -215,8 +232,8 @@ beacon `state` in a given `slot` must take the following actions: [gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas [gloas-validator-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal [is-active-builder]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#is_active_builder -[submit-signed-beacon-block]: ./../../apis/builder/beacon_block.yaml [signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid [signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope +[submit-signed-beacon-block]: ./../../apis/builder/beacon_block.yaml [validator-registration-v2]: ./builder.md#validatorregistrationv2 [verify-execution-payload-bid-signature]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#verify_execution_payload_bid_signature From 8962cc336e1fb06c41b32551359a8eb1b3c1b3ad Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 7 Jan 2026 22:19:15 +0530 Subject: [PATCH 23/59] fix return type of execution_payload_bid.yaml --- apis/builder/execution_payload_bid.yaml | 4 ++-- specs/gloas/builder.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 0578d64c..2b9441e4 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -98,10 +98,10 @@ post: enum: [ gloas ] example: "gloas" data: - $ref: "../../beacon-apis/types/gloas/execution_payload_bid.yaml#/Gloas/ExecutionPayloadBid" + $ref: "../../beacon-apis/types/gloas/execution_payload_bid.yaml#/Gloas/SignedExecutionPayloadBid" application/octet-stream: schema: - description: "SSZ serialized `ExecutionPayloadBid` bytes. Use Accept header to choose this response type" + description: "SSZ serialized `SignedExecutionPayloadBid` bytes. Use Accept header to choose this response type" "204": description: No bid is available. "400": diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 2a28628c..6aa1c80c 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -161,7 +161,7 @@ def process_registration_v2(state: BeaconState, assert is_eligible_for_registration(state, validator) # Verify that the old registration's proposal slot is earlier than the new registration's proposal slot - if validator_index in registrations: + if validator_index in registrations.keys(): prev_registration = registrations[validator_index] assert registration.proposal_slot >= prev_registration.proposal_slot From 2cdc29c734e6c0d49a7a84f53922e7d29858c038 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 7 Jan 2026 23:12:18 +0530 Subject: [PATCH 24/59] use max_trusted_bid in builder preferences --- specs/gloas/builder.md | 12 ++++++++---- specs/gloas/validator.md | 7 +++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 6aa1c80c..178cda30 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -39,7 +39,7 @@ describes how builders interact with validators through ```python class BuilderPreferences(Container): - execution_payment_accepted: boolean + max_trusted_bid: uint64 ``` #### `ValidatorRegistrationV2` @@ -119,9 +119,13 @@ def is_eligible_for_bid(state: BeaconState, Using validator registrations, a proposer can express the preferences it has for a builder. Currently, the only preference that is supported is: -- `execution_payment_accepted`: This is a boolean which indicates that the - proposer is willing to accept a trusted execution layer payment from the - builder. +- `max_trusted_bid`: Specifies the maximum value (in Gwei) that a proposer is + willing to accept as a trusted execution layer payment from the builder. A + value of `0` indicates that the proposer does not accept any trusted payments + from the builder, requiring all payments to be cryptographically verifiable + on-chain. A value of `UINT64_MAX` indicates that the proposer will accept any + trusted payment amount from the builder. Proposers may adjust this parameter + based on their level of trust in the builder's reliability and reputation. ## Validator Registration V2 diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index e0cc63b8..f8b3e1b2 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -118,8 +118,8 @@ information: - `gas_limit`: The value a validator prefers for the execution block gas limit. - `validator_index`: The validator's index. Used to identify the beacon chain validator and verify the wrapping signature. -- `execution_payment_accepted`: Whether the proposer is willing to accept a - trusted payment from the builder with index `builder_index`. +- `max_trusted_bid`: The amount(in Gwei) the proposer is willing to accept as a + trusted execution layer payment from the builder. - `proposal_slot`: This is set to the slot in which the validator will be proposing. This can be looked up in `state.proposal_lookahead`. @@ -179,8 +179,7 @@ def validate_bid( assert bid.parent_block_root == hash_tree_root(state.latest_block_header) assert bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) - if not reg.message.builder_preferences.execution_payment_accepted: - assert bid.execution_payment == 0 + assert bid.execution_payment <= reg.message.builder_preferences.max_trusted_bid if bid.value > 0: assert can_builder_cover_bid(state, bid.builder_index, signed_bid.value) From 5ab4d6de21641b4112e68593f8e150574605e65e Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 8 Jan 2026 10:48:55 +0530 Subject: [PATCH 25/59] run linter --- specs/gloas/builder.md | 36 ++++++++++++++------------ specs/gloas/validator.md | 56 +++++++++++++++++++++++----------------- 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 178cda30..5b4acaff 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -2,8 +2,6 @@ - - - [Gloas - Builder Specification](#gloas---builder-specification) - [Introduction](#introduction) - [Containers](#containers) @@ -67,7 +65,9 @@ class SignedValidatorRegistrationV2(Container): [Gloas consensus specs][gloas-consensus-specs]. ```python -def verify_registration_v2_signature(state: BeaconState, signed_registration: SignedValidatorRegistrationV2) -> bool: +def verify_registration_v2_signature( + state: BeaconState, signed_registration: SignedValidatorRegistrationV2 +) -> bool: validator = state.validators[signed_registration.message.validator_index] pubkey = validator.pubkey domain = compute_domain(DOMAIN_APPLICATION_BUILDER) @@ -90,14 +90,16 @@ We update `is_eligible_for_bid` below. *Note*: `hash_tree_root` is defined in the [Gloas consensus specs][gloas-consensus-specs]. ```python -def is_eligible_for_bid(state: BeaconState, - registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], - slot: Slot, - parent_hash: Hash32, - # [New in Gloas] - parent_root: Root, - # [New in Gloas] - validator_index: ValidatorIndex): +def is_eligible_for_bid( + state: BeaconState, + registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], + slot: Slot, + parent_hash: Hash32, + # [New in Gloas] + parent_root: Root, + # [New in Gloas] + validator_index: ValidatorIndex, +): # Verify slot assert slot == state.slot @@ -150,10 +152,12 @@ A `validator_registration_v2` is considered valid if the following function completes without raising any assertions. ```python -def process_registration_v2(state: BeaconState, - registration: SignedValidatorRegistrationV2, - registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], - current_timestamp: uint64): +def process_registration_v2( + state: BeaconState, + registration: SignedValidatorRegistrationV2, + registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], + current_timestamp: uint64, +): signature = registration.signature registration = registration.message validator_index = registration.validator_index @@ -182,7 +186,7 @@ The specification for a block builder to construct a ## Constructing a `SignedExecutionPayloadEnvelope` If the builder's [`SignedExecutionPayloadBid`][signed-execution-payload-bid] has -been accepted by the proposer and it has been included in it's +been accepted by the proposer and it has been included in the `SignedBeaconBlock`, then the builder has to construct a [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] corresponding to the [`SignedExecutionPayloadBid`][signed-execution-payload-bid] diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index f8b3e1b2..df0217f8 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -2,8 +2,6 @@ - - - [Gloas - Honest Validator](#gloas---honest-validator) - [Introduction](#introduction) - [Containers](#containers) @@ -44,22 +42,22 @@ corresponding to the bid to the PTC committee. #### `BidRequestAuth` `BidRequestAuth` is used to authenticate requests to get the bid from a builder. -This is useful so that other builders don't DDOS the builder to get their latest -bid. +This is useful so that other builders do not DDOS the builder to get their +latest bid. ```python class BidRequestAuth(Container): - builder_index: BuilderIndex - validator_index: ValidatorIndex - proposer_slot: Slot + builder_index: BuilderIndex + validator_index: ValidatorIndex + proposer_slot: Slot ``` #### `SignedBidRequestAuth` ```python class SignedBidRequestAuth(Container): - message: BidRequestAuth - signature: BLSSignature + message: BidRequestAuth + signature: BLSSignature ``` ## Helper @@ -71,8 +69,7 @@ class SignedBidRequestAuth(Container): ```python def get_proposer_slots_in_upcoming_epoch( - state: BeaconState, - validator_index: ValidatorIndex + state: BeaconState, validator_index: ValidatorIndex ) -> List[Slot]: """ Return all slots where validator_index is the proposer within the lookahead window in the next epoch. @@ -80,12 +77,12 @@ def get_proposer_slots_in_upcoming_epoch( proposer_slots = [] current_epoch_start_slot = compute_start_slot_at_epoch(get_current_epoch(state)) next_epoch_proposer_lookahead = state.proposer_lookahead[SLOTS_PER_EPOCH:] - + for offset, proposer_index in enumerate(next_epoch_proposer_lookahead): if proposer_index == validator_index: slot = current_epoch_start_slot + SLOTS_PER_EPOCH + offset proposer_slots.append(slot) - + return proposer_slots ``` @@ -126,24 +123,31 @@ information: ### Validator Registration dissemination This specification suggests validators re-submit registrations only if they will -be proposing in the upcoming epoch(E+1). This is such that we don't send too +be proposing in the upcoming epoch(E+1). This is such that we do not send too many validator registrations all at once to builders. Validators run `create_validator_registrations` at every epoch boundary to create validator registrations for all the slots they will be proposing in the upcoming epoch. ```python -def create_validator_registrations(state: BeaconState, validator_index: ValidatorIndex, gas_limit: uint64, builder_preferences: BuilderPreferences) -> List[ValidatorRegistrationV2]: +def create_validator_registrations( + state: BeaconState, + validator_index: ValidatorIndex, + gas_limit: uint64, + builder_preferences: BuilderPreferences, +) -> List[ValidatorRegistrationV2]: slots = get_proposer_slots_in_upcoming_epoch(state, validator_index) registrations: List[ValidatorRegistrationV2] = [] for slot in slots: - registrations.append(ValidatorRegistrationV2( - fee_recipient=fee_recipient, - gas_limit=gas_limit, - validator_index=validator_index, - builder_preferences=builder_preferences, - proposal_slot=slot - )) + registrations.append( + ValidatorRegistrationV2( + fee_recipient=fee_recipient, + gas_limit=gas_limit, + validator_index=validator_index, + builder_preferences=builder_preferences, + proposal_slot=slot, + ) + ) return registrations ``` @@ -164,14 +168,18 @@ are also defined in the consensus specs. ```python def validate_bid( - state: BeaconState, reg: SignedValidatorRegistrationV2, bid: SignedExecutionPayloadBid, bid_request_auth: SignedBidRequestAuth, fee_recipient: ExecutionAddress + state: BeaconState, + reg: SignedValidatorRegistrationV2, + bid: SignedExecutionPayloadBid, + bid_request_auth: SignedBidRequestAuth, + fee_recipient: ExecutionAddress, ) -> bool: bid = bid.message assert bid.builder_index == bid_request_auth.message.builder_index builder = state.builders[bid.builder_index] - + assert is_active_builder(state, builder) assert bid.slot == state.slot assert bid.fee_recipient == fee_recipient From f4a9e0480e897162e46b680e20003c6f81518532 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 8 Jan 2026 10:51:34 +0530 Subject: [PATCH 26/59] fix spellchecker --- wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/wordlist.txt b/wordlist.txt index 56973e49..353ab834 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -33,6 +33,7 @@ Fulu fulu Gloas gloas +Gwei PTC submitBlindedBlockV ValidatorRegistrationsV \ No newline at end of file From 35fb5a7c503923a5d36e1f60a2a5191c8cb67753 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 8 Jan 2026 12:36:34 +0530 Subject: [PATCH 27/59] update bid request auth according to feedback --- specs/gloas/builder.md | 13 ++++++++++--- specs/gloas/validator.md | 14 +++++++++----- types/gloas/bid_request_auth.yaml | 15 +++++---------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 5b4acaff..7dfef928 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -4,6 +4,7 @@ - [Gloas - Builder Specification](#gloas---builder-specification) - [Introduction](#introduction) + - [Constants](#constants) - [Containers](#containers) - [New Containers](#new-containers) - [`BuilderPreferences`](#builderpreferences) @@ -29,6 +30,11 @@ describes how builders interact with validators through [`SignedExecutionPayloadBid`][signed-execution-payload-bid] and [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] objects. +## Constants + +| Name | Value | | ---------------------------- | -------------------| | +`MAX_TRUSTED_BID` | `uint64('2**64-1')`| + ## Containers ### New Containers @@ -125,9 +131,10 @@ a builder. Currently, the only preference that is supported is: willing to accept as a trusted execution layer payment from the builder. A value of `0` indicates that the proposer does not accept any trusted payments from the builder, requiring all payments to be cryptographically verifiable - on-chain. A value of `UINT64_MAX` indicates that the proposer will accept any - trusted payment amount from the builder. Proposers may adjust this parameter - based on their level of trust in the builder's reliability and reputation. + on-chain. A value of `MAX_TRUSTED_BID` indicates that the proposer will accept + any trusted payment amount from the builder. Proposers may adjust this + parameter based on their level of trust in the builder's reliability and + reputation. ## Validator Registration V2 diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index df0217f8..d0e2174e 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -4,6 +4,7 @@ - [Gloas - Honest Validator](#gloas---honest-validator) - [Introduction](#introduction) + - [Constants](#constants) - [Containers](#containers) - [New Containers](#new-containers) - [`BidRequestAuth`](#bidrequestauth) @@ -35,6 +36,11 @@ external builder network broadcasts the [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] corresponding to the bid to the PTC committee. +## Constants + +| Name | Value | | -------------------------------- | -------------------| | +`MAX_BUILDER_INDICES` | `uint64('2**64-1')`| + ## Containers ### New Containers @@ -47,9 +53,7 @@ latest bid. ```python class BidRequestAuth(Container): - builder_index: BuilderIndex - validator_index: ValidatorIndex - proposer_slot: Slot + builder_indices: List[BuilderIndex, MAX_BUILDER_INDICES] ``` #### `SignedBidRequestAuth` @@ -176,7 +180,7 @@ def validate_bid( ) -> bool: bid = bid.message - assert bid.builder_index == bid_request_auth.message.builder_index + assert bid.builder_index in bid_request_auth.message.builder_indices builder = state.builders[bid.builder_index] @@ -190,7 +194,7 @@ def validate_bid( assert bid.execution_payment <= reg.message.builder_preferences.max_trusted_bid if bid.value > 0: - assert can_builder_cover_bid(state, bid.builder_index, signed_bid.value) + assert can_builder_cover_bid(state, bid.builder_index, bid.value) return verify_execution_payload_bid_signature(state, bid) ``` diff --git a/types/gloas/bid_request_auth.yaml b/types/gloas/bid_request_auth.yaml index 4053307e..a6401137 100644 --- a/types/gloas/bid_request_auth.yaml +++ b/types/gloas/bid_request_auth.yaml @@ -1,17 +1,12 @@ Gloas: BidRequestAuth: type: object - required: [builder_index, validator_index, proposal_slot] + required: [builder_indices] properties: - builder_index: - $ref: "../../builder-oapi.yaml#/components/schemas/Uint64" - description: "The index of the builder to which the validator is sending the request." - validator_index: - $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" - description: "The index of the validator that is sending the request." - proposal_slot: - $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" - description: "The slot at which the proposer is building a block." + builder_indices: + type: array + items: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" SignedBidRequestAuth: type: object required: [message, signature] From e33dfed824c99c963aad012a86acb4018561a165 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 8 Jan 2026 21:15:31 +0530 Subject: [PATCH 28/59] minor updates --- apis/builder/execution_payload_bid.yaml | 4 ++-- specs/gloas/builder.md | 17 +++++++++-------- specs/gloas/validator.md | 24 +++++++++++------------- types/gloas/registration.yaml | 8 ++++---- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 2b9441e4..c14a6878 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -63,8 +63,8 @@ post: required: false description: | Optional header containing the proposer's timeout for the request in milliseconds. - Builders should use this header to adjust the amount of time by which they delay getBid - requests to maximise block rewards. Otherwise, getExecutionPayloadBid requests will timeout and the proposer + Builders should use this header to adjust the amount of time by which they delay the + requests to maximise block rewards. Otherwise, requests will timeout and the proposer will not receive the header in time. schema: type: integer diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 7dfef928..8641a4f6 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -26,14 +26,14 @@ This document documents the builder behaviour with the Builder-API post ePBS. It describes how builders interact with validators through -\[`ValidatorRegistrationV2`\][validator-registration-v2] and construct +[`ValidatorRegistrationV2`][validator-registration-v2] and construct [`SignedExecutionPayloadBid`][signed-execution-payload-bid] and [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] objects. ## Constants | Name | Value | | ---------------------------- | -------------------| | -`MAX_TRUSTED_BID` | `uint64('2**64-1')`| +`MAX_TRUSTED_BID` | `2**64 - 1`| ## Containers @@ -109,7 +109,7 @@ def is_eligible_for_bid( # Verify slot assert slot == state.slot - assert validator_index in state.validator.keys() + assert validator_index in state.validators.keys() assert validator_index in registrations.keys() @@ -161,12 +161,12 @@ completes without raising any assertions. ```python def process_registration_v2( state: BeaconState, - registration: SignedValidatorRegistrationV2, + signed_registration: SignedValidatorRegistrationV2, registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], current_timestamp: uint64, ): - signature = registration.signature - registration = registration.message + signature = signed_registration.signature + registration = signed_registration.message validator_index = registration.validator_index proposal_slot = registration.proposal_slot @@ -181,7 +181,7 @@ def process_registration_v2( assert registration.proposal_slot >= prev_registration.proposal_slot # Verify registration signature - assert verify_registration_v2_signature(state, registration) + assert verify_registration_v2_signature(state, signed_registration) ``` ## Constructing a `SignedExecutionPayloadBid` @@ -193,7 +193,7 @@ The specification for a block builder to construct a ## Constructing a `SignedExecutionPayloadEnvelope` If the builder's [`SignedExecutionPayloadBid`][signed-execution-payload-bid] has -been accepted by the proposer and it has been included in the +been accepted by the proposer and it has been included in its `SignedBeaconBlock`, then the builder has to construct a [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] corresponding to the [`SignedExecutionPayloadBid`][signed-execution-payload-bid] @@ -209,3 +209,4 @@ documented in the [Gloas consensus specs][gloas-builder-specs]. [gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas [signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid [signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope +[validator-registration-v2]: #validatorregistrationv2 diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index d0e2174e..6d38f99a 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -39,7 +39,7 @@ corresponding to the bid to the PTC committee. ## Constants | Name | Value | | -------------------------------- | -------------------| | -`MAX_BUILDER_INDICES` | `uint64('2**64-1')`| +`MAX_BUILDER_INDICES` | `2**64 - 1`| ## Containers @@ -96,10 +96,9 @@ def get_proposer_slots_in_upcoming_epoch( To construct the `BidRequestAuth`, we need to fill the following information: -- `builder_index`: This builder index for which the validator is sending a - request to get the bid. -- `validator_index`: The proposer's validator index. -- `proposal_slot`: The slot at which the proposer is building a block. +- `builder_indices`: These are the builder indices corresponding to the builder + from whom we are fetching the bid. These indices will be known by the + validators when they whitelist the builder. The validator constructs the `SignedBidRequestAuth` by signing the `BidRequestAuth`. It sends the `SignedBidRequestAuth` in the request body along @@ -138,6 +137,7 @@ def create_validator_registrations( validator_index: ValidatorIndex, gas_limit: uint64, builder_preferences: BuilderPreferences, + fee_recipient: ExecutionAddress, ) -> List[ValidatorRegistrationV2]: slots = get_proposer_slots_in_upcoming_epoch(state, validator_index) registrations: List[ValidatorRegistrationV2] = [] @@ -174,11 +174,11 @@ are also defined in the consensus specs. def validate_bid( state: BeaconState, reg: SignedValidatorRegistrationV2, - bid: SignedExecutionPayloadBid, + signed_bid: SignedExecutionPayloadBid, bid_request_auth: SignedBidRequestAuth, fee_recipient: ExecutionAddress, ) -> bool: - bid = bid.message + bid = signed_bid.message assert bid.builder_index in bid_request_auth.message.builder_indices @@ -196,7 +196,7 @@ def validate_bid( if bid.value > 0: assert can_builder_cover_bid(state, bid.builder_index, bid.value) - return verify_execution_payload_bid_signature(state, bid) + return verify_execution_payload_bid_signature(state, signed_bid) ``` Note that, the fee recipient specified in `bid.fee_recipient` does not @@ -215,16 +215,14 @@ the bid conforms to the preferences expressed by the validators. #### Receiving ExecutionPayloadBid -To obtain an execution payload, a block proposer building a block on top of a -beacon `state` in a given `slot` must take the following actions: +To obtain execution payloads for a given `slot`, a block proposer building a +block on top of a beacon `state` must take the following actions: 1. Call upstream builder software to get a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] using the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API call. The validator is required to send the `SignedBidRequestAuth` in the request body - in order to authenticate the request to the builder. If a builder has - multiple builder indices associated with them, the validator will have to - call the upstream builder software each time for each builder index. + in order to authenticate the request to the builder. 2. Assemble a `SignedBeaconBlock` according to the process outlined in the [Gloas validator specs][gloas-validator-specs] but with the best [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the prior diff --git a/types/gloas/registration.yaml b/types/gloas/registration.yaml index 11359751..b0d46f4d 100644 --- a/types/gloas/registration.yaml +++ b/types/gloas/registration.yaml @@ -2,11 +2,11 @@ Gloas: BuilderPreferences: type: object description: "Per-builder preferences that a validator can express." - required: [execution_payment_accepted] + required: [max_trusted_bid] properties: - execution_payment_accepted: - type: boolean - description: "Indicates that the proposer is willing to accept a trusted execution layer payment from the builder." + max_trusted_bid: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + description: "Indicates the maximum amount that a proposer is willing to accept as a trusted payment amount from the builder." ValidatorRegistrationV2: type: object description: "The `ValidatorRegistrationV2` object for Gloas fork, replacing pubkey and timestamp with validator_index, proposal_slot, and builder_preferences." From 8aaa3b594e6f48fac33d0b0dbec4d1bd38abd66b Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 9 Jan 2026 13:36:58 +0530 Subject: [PATCH 29/59] use a salt in the bid request auth --- apis/builder/execution_payload_bid.yaml | 3 ++- specs/gloas/builder.md | 3 +-- specs/gloas/validator.md | 13 +++++-------- types/gloas/bid_request_auth.yaml | 10 +++++----- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index c14a6878..15dd90ea 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -10,7 +10,8 @@ post: - The hash of the execution layer block the proposer will build on. - The root of the beacon block the proposer will build on. - The index of the proposer. - - A signed bid request auth to authenticate the request to a specific builder index. + - A signed bid request auth using a builder-specific salt to authenticate the request. Proposers are required + to set the salt to the URL provided by the whitelisted builder. The builder responds with a 200 response containing an execution payload bid if it can provide one. diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 8641a4f6..a8b8bdaa 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -33,7 +33,7 @@ describes how builders interact with validators through ## Constants | Name | Value | | ---------------------------- | -------------------| | -`MAX_TRUSTED_BID` | `2**64 - 1`| +`MAX_TRUSTED_BID` | `2**64 - 1` | ## Containers @@ -163,7 +163,6 @@ def process_registration_v2( state: BeaconState, signed_registration: SignedValidatorRegistrationV2, registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], - current_timestamp: uint64, ): signature = signed_registration.signature registration = signed_registration.message diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 6d38f99a..34e9f2b6 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -39,7 +39,7 @@ corresponding to the bid to the PTC committee. ## Constants | Name | Value | | -------------------------------- | -------------------| | -`MAX_BUILDER_INDICES` | `2**64 - 1`| +`MAX_SALT_BYTES` | `4096` | ## Containers @@ -53,7 +53,7 @@ latest bid. ```python class BidRequestAuth(Container): - builder_indices: List[BuilderIndex, MAX_BUILDER_INDICES] + salt: ByteList[MAX_SALT_BYTES] ``` #### `SignedBidRequestAuth` @@ -96,9 +96,9 @@ def get_proposer_slots_in_upcoming_epoch( To construct the `BidRequestAuth`, we need to fill the following information: -- `builder_indices`: These are the builder indices corresponding to the builder - from whom we are fetching the bid. These indices will be known by the - validators when they whitelist the builder. +- `salt`: This is a 4kB salt which has to be specific to each whitelisted + builder. The spec requires the proposer to set it to the URL provided by the + whitelisted builder. The validator constructs the `SignedBidRequestAuth` by signing the `BidRequestAuth`. It sends the `SignedBidRequestAuth` in the request body along @@ -175,13 +175,10 @@ def validate_bid( state: BeaconState, reg: SignedValidatorRegistrationV2, signed_bid: SignedExecutionPayloadBid, - bid_request_auth: SignedBidRequestAuth, fee_recipient: ExecutionAddress, ) -> bool: bid = signed_bid.message - assert bid.builder_index in bid_request_auth.message.builder_indices - builder = state.builders[bid.builder_index] assert is_active_builder(state, builder) diff --git a/types/gloas/bid_request_auth.yaml b/types/gloas/bid_request_auth.yaml index a6401137..0393bcd9 100644 --- a/types/gloas/bid_request_auth.yaml +++ b/types/gloas/bid_request_auth.yaml @@ -1,12 +1,12 @@ Gloas: BidRequestAuth: type: object - required: [builder_indices] + required: [salt] properties: - builder_indices: - type: array - items: - $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + salt: + type: string + maxLength: 4096 + description: "A builder-specific salt (max 4096 bytes) used to authenticate bid requests. Must be set to the URL provided by the whitelisted builder." SignedBidRequestAuth: type: object required: [message, signature] From 3625f5477b4463dd35023c485f0ae78606d0b856 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 9 Jan 2026 13:56:05 +0530 Subject: [PATCH 30/59] correctly render constants table --- specs/gloas/builder.md | 6 +++--- specs/gloas/validator.md | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index a8b8bdaa..da013da4 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -1,5 +1,4 @@ - - [Gloas - Builder Specification](#gloas---builder-specification) @@ -32,8 +31,9 @@ describes how builders interact with validators through ## Constants -| Name | Value | | ---------------------------- | -------------------| | -`MAX_TRUSTED_BID` | `2**64 - 1` | +| Name | Value | +| ----------------------------------------- | ------------------ | +| `MAX_TRUSTED_BID` | `2**64 - 1` | ## Containers diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 34e9f2b6..0461cf2c 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -38,8 +38,9 @@ corresponding to the bid to the PTC committee. ## Constants -| Name | Value | | -------------------------------- | -------------------| | -`MAX_SALT_BYTES` | `4096` | +| Name | Value | +| ----------------------------------------- | ------------------ | +| `MAX_SALT_BYTES` | `4096` | ## Containers From 66c15dcf26cf8736ca71f72179904fe1180d1fec Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 9 Jan 2026 22:40:20 +0530 Subject: [PATCH 31/59] address feedback --- specs/gloas/validator.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 0461cf2c..57e0ee01 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -122,7 +122,7 @@ information: - `max_trusted_bid`: The amount(in Gwei) the proposer is willing to accept as a trusted execution layer payment from the builder. - `proposal_slot`: This is set to the slot in which the validator will be - proposing. This can be looked up in `state.proposal_lookahead`. + proposing. This can be looked up in `state.proposer_lookahead`. ### Validator Registration dissemination @@ -180,14 +180,13 @@ def validate_bid( ) -> bool: bid = signed_bid.message - builder = state.builders[bid.builder_index] - - assert is_active_builder(state, builder) + assert is_active_builder(state, bid.builder_index) assert bid.slot == state.slot assert bid.fee_recipient == fee_recipient assert bid.parent_block_hash == state.latest_block_hash assert bid.parent_block_root == hash_tree_root(state.latest_block_header) assert bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) + assert bid.gas_limit <= reg.message.gas_limit assert bid.execution_payment <= reg.message.builder_preferences.max_trusted_bid From 31dc1d410c002db3811be9841b8f4b4f216371e5 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Wed, 14 Jan 2026 11:20:49 +0530 Subject: [PATCH 32/59] update bid request auth to request auth --- apis/builder/execution_payload_bid.yaml | 10 ++-- apis/builder/validators_v2.yaml | 20 ++++--- builder-oapi.yaml | 12 +++-- examples/gloas/signed_request_auth.json | 8 +++ .../signed_validator_registrations_v2.json | 24 +++++++++ specs/gloas/builder.md | 6 +-- specs/gloas/validator.md | 52 +++++++++++-------- ...id_request_auth.yaml => request_auth.yaml} | 8 +-- 8 files changed, 96 insertions(+), 44 deletions(-) create mode 100644 examples/gloas/signed_request_auth.json create mode 100644 examples/gloas/signed_validator_registrations_v2.json rename types/gloas/{bid_request_auth.yaml => request_auth.yaml} (66%) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 15dd90ea..c720738e 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -18,9 +18,9 @@ post: If the builder is unable to produce a valid execution payload bid, then the builder MUST return a 204 response. If the request is invalid, then the builder MUST return an error response (400) with a description of the - validation failure. If the SignedBidRequestAuth is invalid, the builder MUST return a 400 response. + validation failure. If the SignedRequestAuth is invalid, the builder MUST return a 400 response. - This API is applicable from Glamsterdam fork onwards. + This API is applicable from Gloas fork onwards. tags: - Builder parameters: @@ -76,10 +76,10 @@ post: content: application/json: schema: - $ref: "../../types/gloas/bid_request_auth.yaml#/Gloas/SignedBidRequestAuth" + $ref: "../../types/gloas/request_auth.yaml#/Gloas/SignedRequestAuth" application/octet-stream: schema: - description: "SSZ serialized `SignedBidRequestAuth` bytes." + description: "SSZ serialized `SignedRequestAuth` bytes." responses: "200": description: Success response. @@ -119,7 +119,7 @@ post: InvalidAuth: value: code: 400 - message: "Invalid SignedBidRequestAuth: signature verification failed" + message: "Invalid SignedRequestAuth: signature verification failed" "406": $ref: "../../builder-oapi.yaml#/components/responses/NotAcceptable" "500": diff --git a/apis/builder/validators_v2.yaml b/apis/builder/validators_v2.yaml index 813ae98d..562f8ca4 100644 --- a/apis/builder/validators_v2.yaml +++ b/apis/builder/validators_v2.yaml @@ -14,19 +14,27 @@ post: - Builder requestBody: description: | - A signed declaration of a validator's block building preferences. + A signed declaration of a validator's block building preferences, along with signed request authentication. required: true content: application/json: schema: - type: array - items: - $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedValidatorRegistrationV2" + type: object + required: + - signed_request_auth + - registrations + properties: + signed_request_auth: + $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedRequestAuth" + registrations: + type: array + items: + $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedValidatorRegistrationV2" example: - $ref: "../../builder-oapi.yaml#/components/examples/SignedValidatorRegistrations/value" + $ref: "../../builder-oapi.yaml#/components/examples/Gloas.SignedValidatorRegistrationsV2" application/octet-stream: schema: - description: "SSZ serialized `List[SignedValidatorRegistrationV2, VALIDATOR_REGISTRY_LIMIT]` bytes. Use content type header to indicate that SSZ data is contained in the request body." + description: "SSZ serialized `Container[SignedRequestAuth, List[SignedValidatorRegistrationV2, VALIDATOR_REGISTRY_LIMIT]]` bytes. Use content type header to indicate that SSZ data is contained in the request body." responses: "200": description: Success response. diff --git a/builder-oapi.yaml b/builder-oapi.yaml index 99e75455..79b8b1ab 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -108,10 +108,10 @@ components: $ref: "./types/fulu/blobs_bundle.yaml#/Fulu/BlobsBundle" Fulu.ExecutionPayloadAndBlobsBundle: $ref: "./types/fulu/execution_payload_and_blobs_bundle.yaml#/Fulu/ExecutionPayloadAndBlobsBundle" - Gloas.BidRequestAuth: - $ref: "./types/gloas/bid_request_auth.yaml#/Gloas/BidRequestAuth" - Gloas.SignedBidRequestAuth: - $ref: "./types/gloas/bid_request_auth.yaml#/Gloas/SignedBidRequestAuth" + Gloas.RequestAuth: + $ref: "./types/gloas/request_auth.yaml#/Gloas/RequestAuth" + Gloas.SignedRequestAuth: + $ref: "./types/gloas/request_auth.yaml#/Gloas/SignedRequestAuth" Gloas.BuilderPreferences: $ref: "./types/gloas/registration.yaml#/Gloas/BuilderPreferences" Gloas.ValidatorRegistrationV2: @@ -167,3 +167,7 @@ components: $ref: "./examples/fulu/signed_blinded_beacon_block.json" Fulu.SignedBuilderBid: $ref: "./examples/fulu/signed_builder_bid.json" + Gloas.SignedRequestAuth: + $ref: "./examples/gloas/signed_request_auth.json" + Gloas.SignedValidatorRegistrationsV2: + $ref: "./examples/gloas/signed_validator_registrations_v2.json" \ No newline at end of file diff --git a/examples/gloas/signed_request_auth.json b/examples/gloas/signed_request_auth.json new file mode 100644 index 00000000..c985d92a --- /dev/null +++ b/examples/gloas/signed_request_auth.json @@ -0,0 +1,8 @@ +{ + "value": { + "message": { + "salt": "https://builder.example.com/api/v1" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + } +} diff --git a/examples/gloas/signed_validator_registrations_v2.json b/examples/gloas/signed_validator_registrations_v2.json new file mode 100644 index 00000000..bc41bba4 --- /dev/null +++ b/examples/gloas/signed_validator_registrations_v2.json @@ -0,0 +1,24 @@ +{ + "value": { + "signed_request_auth": { + "message": { + "salt": "https://builder.example.com/api/v1" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }, + "registrations": [ + { + "message": { + "validator_index": "12345", + "fee_recipient": "0xabcf8e0d4e9587369b2301d0790347320302cc09", + "proposal_slot": "100000", + "gas_limit": "30000000", + "builder_preferences": { + "max_trusted_bid": "1000000000000000000" + } + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + } + ] + } + } \ No newline at end of file diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index da013da4..7c613f1a 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -1,4 +1,5 @@ + - [Gloas - Builder Specification](#gloas---builder-specification) @@ -31,9 +32,8 @@ describes how builders interact with validators through ## Constants -| Name | Value | -| ----------------------------------------- | ------------------ | -| `MAX_TRUSTED_BID` | `2**64 - 1` | +| Name | Value | | ----------------------------------------- | +------------------ | | `MAX_TRUSTED_BID` | `2**64 - 1` | ## Containers diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 57e0ee01..8149f38b 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -7,12 +7,12 @@ - [Constants](#constants) - [Containers](#containers) - [New Containers](#new-containers) - - [`BidRequestAuth`](#bidrequestauth) - - [`SignedBidRequestAuth`](#signedbidrequestauth) + - [`RequestAuth`](#requestauth) + - [`SignedRequestAuth`](#signedrequestauth) - [Helper](#helper) - [`get_proposer_slots_in_upcoming_epoch`](#get_proposer_slots_in_upcoming_epoch) - [Bid Authentication](#bid-authentication) - - [Constructing the `BidRequestAuth`](#constructing-the-bidrequestauth) + - [Constructing the `RequestAuth`](#constructing-the-requestauth) - [Validator Registrations](#validator-registrations) - [Constructing the `ValidatorRegistrationV2`](#constructing-the-validatorregistrationv2) - [Validator Registration dissemination](#validator-registration-dissemination) @@ -20,6 +20,7 @@ - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [Receiving ExecutionPayloadBid](#receiving-executionpayloadbid) + - [Liveness failsafe](#liveness-failsafe) @@ -38,30 +39,28 @@ corresponding to the bid to the PTC committee. ## Constants -| Name | Value | -| ----------------------------------------- | ------------------ | -| `MAX_SALT_BYTES` | `4096` | +| Name | Value | | ----------------------------------------- | +------------------ | | `MAX_SALT_BYTES` | `4096` | ## Containers ### New Containers -#### `BidRequestAuth` +#### `RequestAuth` -`BidRequestAuth` is used to authenticate requests to get the bid from a builder. -This is useful so that other builders do not DDOS the builder to get their -latest bid. +`RequestAuth` is used to authenticate requests to a builder. This is useful so +that other builders do not DDOS or run replay attacks on the builder. ```python -class BidRequestAuth(Container): +class RequestAuth(Container): salt: ByteList[MAX_SALT_BYTES] ``` -#### `SignedBidRequestAuth` +#### `SignedRequestAuth` ```python -class SignedBidRequestAuth(Container): - message: BidRequestAuth +class SignedRequestAuth(Container): + message: RequestAuth signature: BLSSignature ``` @@ -93,18 +92,21 @@ def get_proposer_slots_in_upcoming_epoch( ## Bid Authentication -### Constructing the `BidRequestAuth` +### Constructing the `RequestAuth` -To construct the `BidRequestAuth`, we need to fill the following information: +To construct the `RequestAuth`, we need to fill the following information: - `salt`: This is a 4kB salt which has to be specific to each whitelisted builder. The spec requires the proposer to set it to the URL provided by the whitelisted builder. -The validator constructs the `SignedBidRequestAuth` by signing the -`BidRequestAuth`. It sends the `SignedBidRequestAuth` in the request body along -with the request to get the bid in the -[`getExecutionPayloadBid`][get-execution-payload-bid-api] API call. +The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`. +It sends the `SignedRequestAuth` in the request body along with the request to +get the bid in the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API +call. It also sends the `SignedRequestAuth` in the +[`registerValidatorV2`][register-validator-v2-api] to avoid replay attacks. A +builder could send the registration to another builder and make them do +unnecessary work. ## Validator Registrations @@ -218,8 +220,8 @@ block on top of a beacon `state` must take the following actions: 1. Call upstream builder software to get a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] using the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API call. The - validator is required to send the `SignedBidRequestAuth` in the request body - in order to authenticate the request to the builder. + validator is required to send the `SignedRequestAuth` in the request body in + order to authenticate the request to the builder. 2. Assemble a `SignedBeaconBlock` according to the process outlined in the [Gloas validator specs][gloas-validator-specs] but with the best [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the prior @@ -233,11 +235,17 @@ block on top of a beacon `state` must take the following actions: [`SignedExecutionPayloadBid`][signed-execution-payload-bid] and broadcasts it to the PTC committee. +## Liveness failsafe + +When the circuit breaker condition is triggered for nodes, they *MUST* fallback +to p2p bidding and can also build blocks locally. + [can-builder-cover-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#can_builder_cover_bid [get-execution-payload-bid-api]: ./../../apis/builder/execution_payload_bid.yaml [gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas [gloas-validator-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal [is-active-builder]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#is_active_builder +[register-validator-v2-api]: ./../../apis/builder/validators_v2.yaml [signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid [signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope [submit-signed-beacon-block]: ./../../apis/builder/beacon_block.yaml diff --git a/types/gloas/bid_request_auth.yaml b/types/gloas/request_auth.yaml similarity index 66% rename from types/gloas/bid_request_auth.yaml rename to types/gloas/request_auth.yaml index 0393bcd9..9f85f67f 100644 --- a/types/gloas/bid_request_auth.yaml +++ b/types/gloas/request_auth.yaml @@ -1,17 +1,17 @@ Gloas: - BidRequestAuth: + RequestAuth: type: object required: [salt] properties: salt: type: string maxLength: 4096 - description: "A builder-specific salt (max 4096 bytes) used to authenticate bid requests. Must be set to the URL provided by the whitelisted builder." - SignedBidRequestAuth: + description: "A builder-specific salt (max 4096 bytes) used to authenticate requests to a builder. Must be set to the URL provided by the whitelisted builder." + SignedRequestAuth: type: object required: [message, signature] properties: message: - $ref: "#/Gloas/BidRequestAuth" + $ref: "#/Gloas/RequestAuth" signature: $ref: "../../beacon-apis/types/primitive.yaml#/Signature" \ No newline at end of file From 0644e07981e30a0806fb72c02d8880d04da432bc Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 20 Jan 2026 20:25:14 +0530 Subject: [PATCH 33/59] fix typo --- apis/builder/execution_payload_bid.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index c720738e..55427217 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -3,7 +3,7 @@ post: summary: Get an execution payload bid. description: | Requests a builder node to produce a valid execution payload bid, which - can be integrated into a blinded beacon block and signed. + can be integrated into a beacon block and signed. The proposer sends a POST request to the builder with the following information: - The slot for which the block should be proposed. From b02106fd4f18a5f0ed919808b943a024f55e4485 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 23 Jan 2026 11:24:11 +0530 Subject: [PATCH 34/59] use salt instead of url --- specs/gloas/validator.md | 6 +++--- types/gloas/request_auth.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 8149f38b..5a8cab98 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -40,7 +40,7 @@ corresponding to the bid to the PTC committee. ## Constants | Name | Value | | ----------------------------------------- | ------------------- | | `MAX_SALT_BYTES` | `4096` | +------------------ | | `MAX_URL_BYTES` | `4096` | ## Containers @@ -53,7 +53,7 @@ that other builders do not DDOS or run replay attacks on the builder. ```python class RequestAuth(Container): - salt: ByteList[MAX_SALT_BYTES] + url: ByteList[MAX_URL_BYTES] ``` #### `SignedRequestAuth` @@ -96,7 +96,7 @@ def get_proposer_slots_in_upcoming_epoch( To construct the `RequestAuth`, we need to fill the following information: -- `salt`: This is a 4kB salt which has to be specific to each whitelisted +- `url`: This is a 4kB salt which has to be specific to each whitelisted builder. The spec requires the proposer to set it to the URL provided by the whitelisted builder. diff --git a/types/gloas/request_auth.yaml b/types/gloas/request_auth.yaml index 9f85f67f..e8860696 100644 --- a/types/gloas/request_auth.yaml +++ b/types/gloas/request_auth.yaml @@ -3,10 +3,10 @@ Gloas: type: object required: [salt] properties: - salt: + url: type: string maxLength: 4096 - description: "A builder-specific salt (max 4096 bytes) used to authenticate requests to a builder. Must be set to the URL provided by the whitelisted builder." + description: "A builder-specific string (max 4096 bytes) used to authenticate requests to a builder. Must be set to the URL provided by the whitelisted builder." SignedRequestAuth: type: object required: [message, signature] From dbce4d642ffe6d909d61000c961aadd983bfeac3 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 27 Jan 2026 13:24:45 +0530 Subject: [PATCH 35/59] use get-upcoming-proposal-slots --- specs/gloas/validator.md | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 5a8cab98..2874d8d1 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -9,8 +9,6 @@ - [New Containers](#new-containers) - [`RequestAuth`](#requestauth) - [`SignedRequestAuth`](#signedrequestauth) - - [Helper](#helper) - - [`get_proposer_slots_in_upcoming_epoch`](#get_proposer_slots_in_upcoming_epoch) - [Bid Authentication](#bid-authentication) - [Constructing the `RequestAuth`](#constructing-the-requestauth) - [Validator Registrations](#validator-registrations) @@ -64,39 +62,13 @@ class SignedRequestAuth(Container): signature: BLSSignature ``` -## Helper - -### `get_proposer_slots_in_upcoming_epoch` - -*Note*: `compute_start_slot_at_epoch` and `get_current_epoch` are defined in the -[Gloas consensus specs][gloas-consensus-specs]. - -```python -def get_proposer_slots_in_upcoming_epoch( - state: BeaconState, validator_index: ValidatorIndex -) -> List[Slot]: - """ - Return all slots where validator_index is the proposer within the lookahead window in the next epoch. - """ - proposer_slots = [] - current_epoch_start_slot = compute_start_slot_at_epoch(get_current_epoch(state)) - next_epoch_proposer_lookahead = state.proposer_lookahead[SLOTS_PER_EPOCH:] - - for offset, proposer_index in enumerate(next_epoch_proposer_lookahead): - if proposer_index == validator_index: - slot = current_epoch_start_slot + SLOTS_PER_EPOCH + offset - proposer_slots.append(slot) - - return proposer_slots -``` - ## Bid Authentication ### Constructing the `RequestAuth` To construct the `RequestAuth`, we need to fill the following information: -- `url`: This is a 4kB salt which has to be specific to each whitelisted +- `salt`: This is a 4kB salt which has to be specific to each whitelisted builder. The spec requires the proposer to set it to the URL provided by the whitelisted builder. @@ -142,7 +114,7 @@ def create_validator_registrations( builder_preferences: BuilderPreferences, fee_recipient: ExecutionAddress, ) -> List[ValidatorRegistrationV2]: - slots = get_proposer_slots_in_upcoming_epoch(state, validator_index) + slots = get_upcoming_proposal_slots(state, validator_index) registrations: List[ValidatorRegistrationV2] = [] for slot in slots: From b0e3d0c0cae3febc2f4c668e006a1ac30e713e90 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 6 Feb 2026 17:44:57 +0530 Subject: [PATCH 36/59] Update liveness failsafe section --- specs/gloas/validator.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 2874d8d1..30399325 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -4,7 +4,6 @@ - [Gloas - Honest Validator](#gloas---honest-validator) - [Introduction](#introduction) - - [Constants](#constants) - [Containers](#containers) - [New Containers](#new-containers) - [`RequestAuth`](#requestauth) @@ -35,11 +34,6 @@ external builder network broadcasts the [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] corresponding to the bid to the PTC committee. -## Constants - -| Name | Value | | ----------------------------------------- | ------------------- | | `MAX_URL_BYTES` | `4096` | - ## Containers ### New Containers @@ -51,7 +45,7 @@ that other builders do not DDOS or run replay attacks on the builder. ```python class RequestAuth(Container): - url: ByteList[MAX_URL_BYTES] + builder_index: BuilderIndex ``` #### `SignedRequestAuth` @@ -68,9 +62,7 @@ class SignedRequestAuth(Container): To construct the `RequestAuth`, we need to fill the following information: -- `salt`: This is a 4kB salt which has to be specific to each whitelisted - builder. The spec requires the proposer to set it to the URL provided by the - whitelisted builder. +- `builder_index`: This is the on-chain index associated with the builder. The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`. It sends the `SignedRequestAuth` in the request body along with the request to @@ -210,9 +202,11 @@ block on top of a beacon `state` must take the following actions: ## Liveness failsafe When the circuit breaker condition is triggered for nodes, they *MUST* fallback -to p2p bidding and can also build blocks locally. +to receiving bids from the P2P [`execution_payload_bid`][execution-payload-bid] +topic and can also build blocks locally. [can-builder-cover-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#can_builder_cover_bid +[execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md?plain=1#L321 [get-execution-payload-bid-api]: ./../../apis/builder/execution_payload_bid.yaml [gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas [gloas-validator-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal From e7d4321d3389d9e0d1a7027f36e41ac245ee2484 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 6 Feb 2026 18:11:36 +0530 Subject: [PATCH 37/59] update RequestAuth types --- apis/builder/execution_payload_bid.yaml | 3 +-- beacon-apis | 2 +- examples/gloas/signed_request_auth.json | 2 +- types/gloas/request_auth.yaml | 8 ++++---- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 55427217..ca319efd 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -10,8 +10,7 @@ post: - The hash of the execution layer block the proposer will build on. - The root of the beacon block the proposer will build on. - The index of the proposer. - - A signed bid request auth using a builder-specific salt to authenticate the request. Proposers are required - to set the salt to the URL provided by the whitelisted builder. + - A signed bid request auth using the builder's on-chain index to authenticate the request. The builder responds with a 200 response containing an execution payload bid if it can provide one. diff --git a/beacon-apis b/beacon-apis index fe362694..8da01b0f 160000 --- a/beacon-apis +++ b/beacon-apis @@ -1 +1 @@ -Subproject commit fe362694bd70685fe7d24cc40ada99178a354d7d +Subproject commit 8da01b0febff59ec2ffdf40f0a2f0d8217d4b364 diff --git a/examples/gloas/signed_request_auth.json b/examples/gloas/signed_request_auth.json index c985d92a..3ea97505 100644 --- a/examples/gloas/signed_request_auth.json +++ b/examples/gloas/signed_request_auth.json @@ -1,7 +1,7 @@ { "value": { "message": { - "salt": "https://builder.example.com/api/v1" + "builder_index": "0x2a" }, "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" } diff --git a/types/gloas/request_auth.yaml b/types/gloas/request_auth.yaml index e8860696..bcc6cc7f 100644 --- a/types/gloas/request_auth.yaml +++ b/types/gloas/request_auth.yaml @@ -1,12 +1,12 @@ Gloas: RequestAuth: type: object - required: [salt] + required: [builder_index] properties: - url: + builder_index: type: string - maxLength: 4096 - description: "A builder-specific string (max 4096 bytes) used to authenticate requests to a builder. Must be set to the URL provided by the whitelisted builder." + pattern: "^0x[a-fA-F0-9]{1,16}$" + description: "The on-chain index associated with the builder, represented as a hex string." SignedRequestAuth: type: object required: [message, signature] From 37c8e873ab170f665ab73c7c9b2ae83f402edb2e Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 9 Mar 2026 11:18:41 +0530 Subject: [PATCH 38/59] deprecate validator registrations in favour of proposer preferences --- apis/builder/preferences.yaml | 60 +++++++++ apis/builder/validators_v2.yaml | 53 -------- builder-oapi.yaml | 16 +-- ...2.json => signed_builder_preferences.json} | 15 +-- specs/gloas/builder.md | 121 +++++++++--------- specs/gloas/validator.md | 109 ++++++++-------- types/gloas/preferences.yaml | 21 +++ types/gloas/registration.yaml | 38 ------ 8 files changed, 212 insertions(+), 221 deletions(-) create mode 100644 apis/builder/preferences.yaml delete mode 100644 apis/builder/validators_v2.yaml rename examples/gloas/{signed_validator_registrations_v2.json => signed_builder_preferences.json} (61%) create mode 100644 types/gloas/preferences.yaml delete mode 100644 types/gloas/registration.yaml diff --git a/apis/builder/preferences.yaml b/apis/builder/preferences.yaml new file mode 100644 index 00000000..e8a24d24 --- /dev/null +++ b/apis/builder/preferences.yaml @@ -0,0 +1,60 @@ +post: + operationId: "submitBuilderPreferences" + summary: Submit per-builder preferences for Gloas. + description: | + Submits a validator's per-builder preferences (e.g. max_trusted_bid) to a + specific builder. + + Note: General validator preferences (fee_recipient, gas_limit, + proposal_slot, validator_index) are now communicated via the + `proposer_preferences` gossip topic defined in the consensus specs. This + endpoint is only used for preferences that are specific to a particular + builder. + + A success response (200) indicates that the preferences were valid. If the + preferences pass validation, then the builder MUST integrate the + preferences into its state, such that future bids built for the + validator conform to the preferences. If the preferences are invalid, + then the builder MUST return an error response (400) with a description + of the validation failure. + tags: + - Builder + requestBody: + description: | + Signed per-builder preferences, along with signed request authentication. + required: true + content: + application/json: + schema: + type: object + required: + - signed_request_auth + - preferences + properties: + signed_request_auth: + $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedRequestAuth" + preferences: + type: array + items: + $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedBuilderPreferences" + example: + $ref: "../../builder-oapi.yaml#/components/examples/Gloas.SignedBuilderPreferences" + application/octet-stream: + schema: + description: "SSZ serialized `Container[SignedRequestAuth, List[SignedBuilderPreferences, VALIDATOR_REGISTRY_LIMIT]]` bytes. Use content type header to indicate that SSZ data is contained in the request body." + responses: + "200": + description: Success response. + "400": + description: Error response. + content: + application/json: + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" + example: + code: 400 + message: "invalid preferences" + "415": + $ref: "../../builder-oapi.yaml#/components/responses/UnsupportedMediaType" + "500": + $ref: "../../builder-oapi.yaml#/components/responses/InternalError" diff --git a/apis/builder/validators_v2.yaml b/apis/builder/validators_v2.yaml deleted file mode 100644 index 562f8ca4..00000000 --- a/apis/builder/validators_v2.yaml +++ /dev/null @@ -1,53 +0,0 @@ -post: - operationId: "registerValidatorV2" - summary: Register or update a validator's block building preferences for Gloas. - description: | - Registers a validator's preferred fee recipient, gas limit and preferences. - - A success response (200) indicates that the registration was valid. If the - registration passes validation, then the builder MUST integrate the - registration into its state, such that future blocks built for the - validator conform to the preferences expressed in the registration. If the - registration is invalid, then the builder MUST return an error response - (400) with a description of the validation failure. - tags: - - Builder - requestBody: - description: | - A signed declaration of a validator's block building preferences, along with signed request authentication. - required: true - content: - application/json: - schema: - type: object - required: - - signed_request_auth - - registrations - properties: - signed_request_auth: - $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedRequestAuth" - registrations: - type: array - items: - $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedValidatorRegistrationV2" - example: - $ref: "../../builder-oapi.yaml#/components/examples/Gloas.SignedValidatorRegistrationsV2" - application/octet-stream: - schema: - description: "SSZ serialized `Container[SignedRequestAuth, List[SignedValidatorRegistrationV2, VALIDATOR_REGISTRY_LIMIT]]` bytes. Use content type header to indicate that SSZ data is contained in the request body." - responses: - "200": - description: Success response. - "400": - description: Error response. - content: - application/json: - schema: - $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" - example: - code: 400 - message: "unknown validator" - "415": - $ref: "../../builder-oapi.yaml#/components/responses/UnsupportedMediaType" - "500": - $ref: "../../builder-oapi.yaml#/components/responses/InternalError" diff --git a/builder-oapi.yaml b/builder-oapi.yaml index 79b8b1ab..1d9ab090 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -51,8 +51,8 @@ tags: paths: /eth/v1/builder/validators: $ref: "./apis/builder/validators.yaml" - /eth/v2/builder/validators: - $ref: "./apis/builder/validators_v2.yaml" + /eth/v1/builder/preferences: + $ref: "./apis/builder/preferences.yaml" /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}: $ref: "./apis/builder/header.yaml" /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_index}: @@ -113,11 +113,9 @@ components: Gloas.SignedRequestAuth: $ref: "./types/gloas/request_auth.yaml#/Gloas/SignedRequestAuth" Gloas.BuilderPreferences: - $ref: "./types/gloas/registration.yaml#/Gloas/BuilderPreferences" - Gloas.ValidatorRegistrationV2: - $ref: "./types/gloas/registration.yaml#/Gloas/ValidatorRegistrationV2" - Gloas.SignedValidatorRegistrationV2: - $ref: "./types/gloas/registration.yaml#/Gloas/SignedValidatorRegistrationV2" + $ref: "./types/gloas/preferences.yaml#/Gloas/BuilderPreferences" + Gloas.SignedBuilderPreferences: + $ref: "./types/gloas/preferences.yaml#/Gloas/SignedBuilderPreferences" responses: InternalError: @@ -169,5 +167,5 @@ components: $ref: "./examples/fulu/signed_builder_bid.json" Gloas.SignedRequestAuth: $ref: "./examples/gloas/signed_request_auth.json" - Gloas.SignedValidatorRegistrationsV2: - $ref: "./examples/gloas/signed_validator_registrations_v2.json" \ No newline at end of file + Gloas.SignedBuilderPreferences: + $ref: "./examples/gloas/signed_builder_preferences.json" \ No newline at end of file diff --git a/examples/gloas/signed_validator_registrations_v2.json b/examples/gloas/signed_builder_preferences.json similarity index 61% rename from examples/gloas/signed_validator_registrations_v2.json rename to examples/gloas/signed_builder_preferences.json index bc41bba4..410459ea 100644 --- a/examples/gloas/signed_validator_registrations_v2.json +++ b/examples/gloas/signed_builder_preferences.json @@ -2,23 +2,18 @@ "value": { "signed_request_auth": { "message": { - "salt": "https://builder.example.com/api/v1" + "builder_index": "0x2a" }, "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" }, - "registrations": [ + "preferences": [ { "message": { - "validator_index": "12345", - "fee_recipient": "0xabcf8e0d4e9587369b2301d0790347320302cc09", - "proposal_slot": "100000", - "gas_limit": "30000000", - "builder_preferences": { - "max_trusted_bid": "1000000000000000000" - } + "builder_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", + "max_trusted_bid": "1000000000000000000" }, "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" } ] } - } \ No newline at end of file + } diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 7c613f1a..99257a3e 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -8,13 +8,12 @@ - [Containers](#containers) - [New Containers](#new-containers) - [`BuilderPreferences`](#builderpreferences) - - [`ValidatorRegistrationV2`](#validatorregistrationv2) - - [`SignedValidatorRegistrationV2`](#signedvalidatorregistrationv2) - - [`verify_registration_v2_signature`](#verify_registration_v2_signature) + - [`SignedBuilderPreferences`](#signedbuilderpreferences) + - [`verify_builder_preferences_signature`](#verify_builder_preferences_signature) - [Bidding](#bidding) - [Builder Preferences](#builder-preferences) - - [Validator Registration V2](#validator-registration-v2) - - [`process_registration_v2`](#process_registration_v2) + - [Proposer Preferences (Deprecation of Validator Registrations)](#proposer-preferences-deprecation-of-validator-registrations) + - [`process_builder_preferences`](#process_builder_preferences) - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) - [Constructing a `SignedExecutionPayloadEnvelope`](#constructing-a-signedexecutionpayloadenvelope) @@ -26,14 +25,15 @@ This document documents the builder behaviour with the Builder-API post ePBS. It describes how builders interact with validators through -[`ValidatorRegistrationV2`][validator-registration-v2] and construct +[`BuilderPreferences`](#builderpreferences) and construct [`SignedExecutionPayloadBid`][signed-execution-payload-bid] and [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] objects. ## Constants -| Name | Value | | ----------------------------------------- | ------------------- | | `MAX_TRUSTED_BID` | `2**64 - 1` | +| Name | Value | +| ----------------------------------------- | ------------------ | +| `MAX_TRUSTED_BID` | `2**64 - 1` | ## Containers @@ -43,42 +43,32 @@ describes how builders interact with validators through ```python class BuilderPreferences(Container): + builder_pubkey: BLSPubkey max_trusted_bid: uint64 ``` -#### `ValidatorRegistrationV2` +#### `SignedBuilderPreferences` ```python -class ValidatorRegistrationV2(Container): - validator_index: ValidatorIndex - fee_recipient: ExecutionAddress - proposal_slot: Slot - gas_limit: uint64 - builder_preferences: BuilderPreferences -``` - -#### `SignedValidatorRegistrationV2` - -```python -class SignedValidatorRegistrationV2(Container): - message: ValidatorRegistrationV2 +class SignedBuilderPreferences(Container): + message: BuilderPreferences signature: BLSSignature ``` -### `verify_registration_v2_signature` +### `verify_builder_preferences_signature` *Note*: `compute_domain` and `compute_signing_root` are defined in the [Gloas consensus specs][gloas-consensus-specs]. ```python -def verify_registration_v2_signature( - state: BeaconState, signed_registration: SignedValidatorRegistrationV2 +def verify_builder_preferences_signature( + state: BeaconState, signed_preferences: SignedBuilderPreferences, validator_index: ValidatorIndex ) -> bool: - validator = state.validators[signed_registration.message.validator_index] + validator = state.validators[validator_index] pubkey = validator.pubkey domain = compute_domain(DOMAIN_APPLICATION_BUILDER) - signing_root = compute_signing_root(signed_registration.message, domain) - return bls.Verify(pubkey, signing_root, signed_registration.signature) + signing_root = compute_signing_root(signed_preferences.message, domain) + return bls.Verify(pubkey, signing_root, signed_preferences.signature) ``` ## Bidding @@ -98,7 +88,7 @@ the [Gloas consensus specs][gloas-consensus-specs]. ```python def is_eligible_for_bid( state: BeaconState, - registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], + proposer_preferences: Dict[ValidatorIndex, ProposerPreferences], slot: Slot, parent_hash: Hash32, # [New in Gloas] @@ -111,7 +101,8 @@ def is_eligible_for_bid( assert validator_index in state.validators.keys() - assert validator_index in registrations.keys() + # Verify that proposer preferences have been received via the gossip topic + assert validator_index in proposer_preferences.keys() # Verify parent hash # [Modified in Gloas:EIP7732] @@ -124,8 +115,10 @@ def is_eligible_for_bid( ## Builder Preferences -Using validator registrations, a proposer can express the preferences it has for -a builder. Currently, the only preference that is supported is: +Validators send per-builder preferences directly to the builder via the +[`submitBuilderPreferences`][submit-builder-preferences-api] API call. This +allows a proposer to express trust preferences for a specific builder. Currently, +the only preference that is supported is: - `max_trusted_bid`: Specifies the maximum value (in Gwei) that a proposer is willing to accept as a trusted execution layer payment from the builder. A @@ -136,51 +129,55 @@ a builder. Currently, the only preference that is supported is: parameter based on their level of trust in the builder's reliability and reputation. -## Validator Registration V2 +The `builder_pubkey` field identifies which builder the preferences are intended +for. + +## Proposer Preferences (Deprecation of Validator Registrations) -The second version of ValidatorRegistrations adds the following new fields: +*Note*: `ValidatorRegistrationV2` is **deprecated** in favour of +[`ProposerPreferences`][proposer-preferences] from the consensus specs. -- `validator_index`: The index of the validator selected to propose a block at - slot `proposal_slot` -- `builder_preferences`: This is a struct which contains the per builder - preferences the proposer has. -- `proposal_slot`: The slot at which this validator is proposing. +Builders SHOULD subscribe to the [`proposer_preferences`][proposer-preferences-topic] +gossip topic to learn about a validator's general preferences for upcoming +proposal slots. The `ProposerPreferences` message contains: -The following fields are removed: +- `validator_index`: The index of the validator proposing. +- `fee_recipient`: The execution layer address where fees should go. +- `gas_limit`: The preferred gas limit. +- `proposal_slot`: The slot in which the validator will be proposing. -- `pubkey`: This is the pubkey of the validator which has now been replaced with - `validator_index`. -- `timestamp`: A new validator registration will be sent by the validator to the - builder in the epoch prior to one where they will be proposing. +For per-builder preferences (such as `max_trusted_bid`), validators send +[`SignedBuilderPreferences`](#signedbuilderpreferences) directly to the builder +via the [`submitBuilderPreferences`][submit-builder-preferences-api] API call. -### `process_registration_v2` +### `process_builder_preferences` -A `validator_registration_v2` is considered valid if the following function +A `BuilderPreferences` message is considered valid if the following function completes without raising any assertions. ```python -def process_registration_v2( +def process_builder_preferences( state: BeaconState, - signed_registration: SignedValidatorRegistrationV2, - registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], + proposer_preferences: ProposerPreferences, + signed_preferences: SignedBuilderPreferences, + validator_index: ValidatorIndex, + builder_preferences: Dict[ValidatorIndex, BuilderPreferences], ): - signature = signed_registration.signature - registration = signed_registration.message - validator_index = registration.validator_index - proposal_slot = registration.proposal_slot + preferences = signed_preferences.message validator = state.validators[validator_index] - # Verify validator registration eligibility + # Verify validator is eligible assert is_eligible_for_registration(state, validator) - # Verify that the old registration's proposal slot is earlier than the new registration's proposal slot - if validator_index in registrations.keys(): - prev_registration = registrations[validator_index] - assert registration.proposal_slot >= prev_registration.proposal_slot + # Verify that proposer preferences have been received via the gossip topic + assert proposer_preferences.validator_index == validator_index + + # Verify the builder_pubkey matches the builder receiving the preferences + # (implementation specific check) - # Verify registration signature - assert verify_registration_v2_signature(state, signed_registration) + # Verify builder preferences signature + assert verify_builder_preferences_signature(state, signed_preferences, validator_index) ``` ## Constructing a `SignedExecutionPayloadBid` @@ -206,6 +203,8 @@ documented in the [Gloas consensus specs][gloas-builder-specs]. [eip-7732]: https://eips.ethereum.org/EIPS/eip-7732 [gloas-builder-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md [gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas +[proposer-preferences]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md +[proposer-preferences-topic]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md [signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid [signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope -[validator-registration-v2]: #validatorregistrationv2 +[submit-builder-preferences-api]: ./../../apis/builder/preferences.yaml diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 30399325..673536d8 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -10,9 +10,10 @@ - [`SignedRequestAuth`](#signedrequestauth) - [Bid Authentication](#bid-authentication) - [Constructing the `RequestAuth`](#constructing-the-requestauth) - - [Validator Registrations](#validator-registrations) - - [Constructing the `ValidatorRegistrationV2`](#constructing-the-validatorregistrationv2) - - [Validator Registration dissemination](#validator-registration-dissemination) + - [Proposer Preferences](#proposer-preferences) + - [Builder Preferences](#builder-preferences) + - [Constructing the `BuilderPreferences`](#constructing-the-builderpreferences) + - [Builder Preferences dissemination](#builder-preferences-dissemination) - [Validating a `SignedExecutionPayloadBid`](#validating-a-signedexecutionpayloadbid) - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) @@ -68,59 +69,64 @@ The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`. It sends the `SignedRequestAuth` in the request body along with the request to get the bid in the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API call. It also sends the `SignedRequestAuth` in the -[`registerValidatorV2`][register-validator-v2-api] to avoid replay attacks. A -builder could send the registration to another builder and make them do +[`submitBuilderPreferences`][submit-builder-preferences-api] to avoid replay +attacks. A builder could send the preferences to another builder and make them do unnecessary work. -## Validator Registrations +## Proposer Preferences -### Constructing the `ValidatorRegistrationV2` +*Note*: Validator registrations (`ValidatorRegistrationV2`) are **deprecated** in +favour of [`ProposerPreferences`][proposer-preferences] from the consensus specs. -To do this, the validator client assembles a -[`ValidatorRegistrationV2`][validator-registration-v2] with the following -information: +General validator preferences are now communicated via the +[`proposer_preferences`][proposer-preferences-topic] gossip topic defined in the +[Gloas consensus specs][gloas-consensus-specs]. Validators broadcast +[`SignedProposerPreferences`][proposer-preferences] messages at the beginning of +each epoch containing: - `fee_recipient`: An execution layer address where fees for the validator should go. - `gas_limit`: The value a validator prefers for the execution block gas limit. -- `validator_index`: The validator's index. Used to identify the beacon chain - validator and verify the wrapping signature. -- `max_trusted_bid`: The amount(in Gwei) the proposer is willing to accept as a +- `validator_index`: The validator's index. +- `proposal_slot`: The slot in which the validator will be proposing. This can be + looked up in `state.proposer_lookahead`. + +Builders SHOULD subscribe to this gossip topic to learn about proposer +preferences for upcoming slots. + +## Builder Preferences + +For per-builder preferences that cannot be communicated via a global gossip +topic, validators send [`SignedBuilderPreferences`][builder-preferences] directly +to the builder via the [`submitBuilderPreferences`][submit-builder-preferences-api] +API call. + +### Constructing the `BuilderPreferences` + +To construct the `BuilderPreferences`, the validator client assembles a +[`BuilderPreferences`][builder-preferences] with the following information: + +- `builder_pubkey`: The BLS public key of the builder that these preferences are + intended for. +- `max_trusted_bid`: The amount (in Gwei) the proposer is willing to accept as a trusted execution layer payment from the builder. -- `proposal_slot`: This is set to the slot in which the validator will be - proposing. This can be looked up in `state.proposer_lookahead`. -### Validator Registration dissemination +### Builder Preferences dissemination -This specification suggests validators re-submit registrations only if they will -be proposing in the upcoming epoch(E+1). This is such that we do not send too -many validator registrations all at once to builders. Validators run -`create_validator_registrations` at every epoch boundary to create validator -registrations for all the slots they will be proposing in the upcoming epoch. +Validators send builder preferences to each builder they wish to interact with +for their upcoming proposal slots. Validators run +`create_builder_preferences` at every epoch boundary to create builder +preferences for all the builders they trust. ```python -def create_validator_registrations( - state: BeaconState, - validator_index: ValidatorIndex, - gas_limit: uint64, - builder_preferences: BuilderPreferences, - fee_recipient: ExecutionAddress, -) -> List[ValidatorRegistrationV2]: - slots = get_upcoming_proposal_slots(state, validator_index) - registrations: List[ValidatorRegistrationV2] = [] - - for slot in slots: - registrations.append( - ValidatorRegistrationV2( - fee_recipient=fee_recipient, - gas_limit=gas_limit, - validator_index=validator_index, - builder_preferences=builder_preferences, - proposal_slot=slot, - ) - ) - - return registrations +def create_builder_preferences( + builder_pubkey: BLSPubkey, + max_trusted_bid: uint64, +) -> BuilderPreferences: + return BuilderPreferences( + builder_pubkey=builder_pubkey, + max_trusted_bid=max_trusted_bid, + ) ``` ## Validating a `SignedExecutionPayloadBid` @@ -140,7 +146,8 @@ are also defined in the consensus specs. ```python def validate_bid( state: BeaconState, - reg: SignedValidatorRegistrationV2, + proposer_preferences: ProposerPreferences, + builder_preferences: BuilderPreferences, signed_bid: SignedExecutionPayloadBid, fee_recipient: ExecutionAddress, ) -> bool: @@ -152,9 +159,9 @@ def validate_bid( assert bid.parent_block_hash == state.latest_block_hash assert bid.parent_block_root == hash_tree_root(state.latest_block_header) assert bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) - assert bid.gas_limit <= reg.message.gas_limit + assert bid.gas_limit <= proposer_preferences.gas_limit - assert bid.execution_payment <= reg.message.builder_preferences.max_trusted_bid + assert bid.execution_payment <= builder_preferences.max_trusted_bid if bid.value > 0: assert can_builder_cover_bid(state, bid.builder_index, bid.value) @@ -169,8 +176,8 @@ bid's fee recipient matches the validators expected fee recipient and not the builder's fee recipient. To express per-builder preferences we need validators to remember which -registration they have sent to the builder, so that they can validate whether -the bid conforms to the preferences expressed by the validators. +builder preferences they have sent to each builder, so that they can validate +whether the bid conforms to the preferences expressed by the validators. ## Block proposal @@ -205,15 +212,17 @@ When the circuit breaker condition is triggered for nodes, they *MUST* fallback to receiving bids from the P2P [`execution_payload_bid`][execution-payload-bid] topic and can also build blocks locally. +[builder-preferences]: ./builder.md#builderpreferences [can-builder-cover-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#can_builder_cover_bid [execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md?plain=1#L321 [get-execution-payload-bid-api]: ./../../apis/builder/execution_payload_bid.yaml [gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas [gloas-validator-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal [is-active-builder]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#is_active_builder -[register-validator-v2-api]: ./../../apis/builder/validators_v2.yaml +[proposer-preferences]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md +[proposer-preferences-topic]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md [signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid [signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope +[submit-builder-preferences-api]: ./../../apis/builder/preferences.yaml [submit-signed-beacon-block]: ./../../apis/builder/beacon_block.yaml -[validator-registration-v2]: ./builder.md#validatorregistrationv2 [verify-execution-payload-bid-signature]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#verify_execution_payload_bid_signature diff --git a/types/gloas/preferences.yaml b/types/gloas/preferences.yaml new file mode 100644 index 00000000..c62c2240 --- /dev/null +++ b/types/gloas/preferences.yaml @@ -0,0 +1,21 @@ +Gloas: + BuilderPreferences: + type: object + description: "Per-builder preferences that a validator can express to a specific builder." + required: [builder_pubkey, max_trusted_bid] + properties: + builder_pubkey: + $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" + description: "The BLS public key of the builder that these preferences are intended for." + max_trusted_bid: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + description: "Indicates the maximum amount that a proposer is willing to accept as a trusted payment amount from the builder." + SignedBuilderPreferences: + type: object + description: "The `SignedBuilderPreferences` object for Gloas fork." + required: [message, signature] + properties: + message: + $ref: "#/Gloas/BuilderPreferences" + signature: + $ref: "../../beacon-apis/types/primitive.yaml#/Signature" diff --git a/types/gloas/registration.yaml b/types/gloas/registration.yaml deleted file mode 100644 index b0d46f4d..00000000 --- a/types/gloas/registration.yaml +++ /dev/null @@ -1,38 +0,0 @@ -Gloas: - BuilderPreferences: - type: object - description: "Per-builder preferences that a validator can express." - required: [max_trusted_bid] - properties: - max_trusted_bid: - $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" - description: "Indicates the maximum amount that a proposer is willing to accept as a trusted payment amount from the builder." - ValidatorRegistrationV2: - type: object - description: "The `ValidatorRegistrationV2` object for Gloas fork, replacing pubkey and timestamp with validator_index, proposal_slot, and builder_preferences." - required: [validator_index, fee_recipient, proposal_slot, gas_limit, builder_preferences] - properties: - validator_index: - $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" - description: "Validator index. Used to identify the beacon chain validator and verify the signature." - fee_recipient: - $ref: "../../beacon-apis/types/primitive.yaml#/ExecutionAddress" - description: "Address to receive fees from the block." - proposal_slot: - $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" - description: "The slot at which this validator is proposing." - gas_limit: - $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" - description: "Preferred gas limit of validator." - builder_preferences: - $ref: "#/Gloas/BuilderPreferences" - description: "Per-builder preferences." - SignedValidatorRegistrationV2: - type: object - description: "The `SignedValidatorRegistrationV2` object for Gloas fork." - required: [message, signature] - properties: - message: - $ref: "#/Gloas/ValidatorRegistrationV2" - signature: - $ref: "../../beacon-apis/types/primitive.yaml#/Signature" \ No newline at end of file From 3b5735b81bfc4aecfbc84d05c1b297a0e2abf6f9 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 12 Mar 2026 13:37:54 +0530 Subject: [PATCH 39/59] updates to api --- apis/builder/execution_payload_bid.yaml | 2 +- apis/builder/preferences.yaml | 7 +-- .../gloas/signed_builder_preferences.json | 7 +-- examples/gloas/signed_request_auth.json | 3 +- specs/gloas/builder.md | 31 ++++++---- specs/gloas/validator.md | 57 ++++++++++--------- types/gloas/preferences.yaml | 5 +- types/gloas/request_auth.yaml | 12 ++-- 8 files changed, 66 insertions(+), 58 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index ca319efd..45aafeb5 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -10,7 +10,7 @@ post: - The hash of the execution layer block the proposer will build on. - The root of the beacon block the proposer will build on. - The index of the proposer. - - A signed bid request auth using the builder's on-chain index to authenticate the request. + - A signed request auth containing the builder's public key and slot to authenticate the request. The builder responds with a 200 response containing an execution payload bid if it can provide one. diff --git a/apis/builder/preferences.yaml b/apis/builder/preferences.yaml index e8a24d24..a2e9d4a1 100644 --- a/apis/builder/preferences.yaml +++ b/apis/builder/preferences.yaml @@ -21,18 +21,15 @@ post: - Builder requestBody: description: | - Signed per-builder preferences, along with signed request authentication. + Signed per-builder preferences. required: true content: application/json: schema: type: object required: - - signed_request_auth - preferences properties: - signed_request_auth: - $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedRequestAuth" preferences: type: array items: @@ -41,7 +38,7 @@ post: $ref: "../../builder-oapi.yaml#/components/examples/Gloas.SignedBuilderPreferences" application/octet-stream: schema: - description: "SSZ serialized `Container[SignedRequestAuth, List[SignedBuilderPreferences, VALIDATOR_REGISTRY_LIMIT]]` bytes. Use content type header to indicate that SSZ data is contained in the request body." + description: "SSZ serialized `List[SignedBuilderPreferences, VALIDATOR_REGISTRY_LIMIT]` bytes. Use content type header to indicate that SSZ data is contained in the request body." responses: "200": description: Success response. diff --git a/examples/gloas/signed_builder_preferences.json b/examples/gloas/signed_builder_preferences.json index 410459ea..5c45b50e 100644 --- a/examples/gloas/signed_builder_preferences.json +++ b/examples/gloas/signed_builder_preferences.json @@ -1,15 +1,10 @@ { "value": { - "signed_request_auth": { - "message": { - "builder_index": "0x2a" - }, - "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" - }, "preferences": [ { "message": { "builder_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", + "slot": "32", "max_trusted_bid": "1000000000000000000" }, "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" diff --git a/examples/gloas/signed_request_auth.json b/examples/gloas/signed_request_auth.json index 3ea97505..eb5bfe95 100644 --- a/examples/gloas/signed_request_auth.json +++ b/examples/gloas/signed_request_auth.json @@ -1,7 +1,8 @@ { "value": { "message": { - "builder_index": "0x2a" + "builder_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", + "slot": "1" }, "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" } diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 99257a3e..162ddedd 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -31,8 +31,8 @@ describes how builders interact with validators through ## Constants -| Name | Value | -| ----------------------------------------- | ------------------ | +| Name | Value | +| ----------------- | ----------- | | `MAX_TRUSTED_BID` | `2**64 - 1` | ## Containers @@ -44,6 +44,7 @@ describes how builders interact with validators through ```python class BuilderPreferences(Container): builder_pubkey: BLSPubkey + slot: Slot max_trusted_bid: uint64 ``` @@ -62,7 +63,9 @@ class SignedBuilderPreferences(Container): ```python def verify_builder_preferences_signature( - state: BeaconState, signed_preferences: SignedBuilderPreferences, validator_index: ValidatorIndex + state: BeaconState, + signed_preferences: SignedBuilderPreferences, + validator_index: ValidatorIndex, ) -> bool: validator = state.validators[validator_index] pubkey = validator.pubkey @@ -117,14 +120,14 @@ def is_eligible_for_bid( Validators send per-builder preferences directly to the builder via the [`submitBuilderPreferences`][submit-builder-preferences-api] API call. This -allows a proposer to express trust preferences for a specific builder. Currently, -the only preference that is supported is: +allows a proposer to express trust preferences for a specific builder. +Currently, the only preference that is supported is: - `max_trusted_bid`: Specifies the maximum value (in Gwei) that a proposer is willing to accept as a trusted execution layer payment from the builder. A value of `0` indicates that the proposer does not accept any trusted payments - from the builder, requiring all payments to be cryptographically verifiable - on-chain. A value of `MAX_TRUSTED_BID` indicates that the proposer will accept + from the builder, requiring all payments to use the on-chain trustless payments mechanism. + A value of `MAX_TRUSTED_BID` indicates that the proposer will accept any trusted payment amount from the builder. Proposers may adjust this parameter based on their level of trust in the builder's reliability and reputation. @@ -134,12 +137,14 @@ for. ## Proposer Preferences (Deprecation of Validator Registrations) -*Note*: `ValidatorRegistrationV2` is **deprecated** in favour of +*Note*: `ValidatorRegistrationV1` is **deprecated** in favour of [`ProposerPreferences`][proposer-preferences] from the consensus specs. -Builders SHOULD subscribe to the [`proposer_preferences`][proposer-preferences-topic] -gossip topic to learn about a validator's general preferences for upcoming -proposal slots. The `ProposerPreferences` message contains: +Builders SHOULD subscribe to the +[`proposer_preferences`][proposer-preferences-topic] gossip topic to learn about +a validator's general preferences. Validators broadcast these messages at the +beginning of each epoch for their proposal slots in the next epoch. The +`ProposerPreferences` message contains: - `validator_index`: The index of the validator proposing. - `fee_recipient`: The execution layer address where fees should go. @@ -177,7 +182,9 @@ def process_builder_preferences( # (implementation specific check) # Verify builder preferences signature - assert verify_builder_preferences_signature(state, signed_preferences, validator_index) + assert verify_builder_preferences_signature( + state, signed_preferences, validator_index + ) ``` ## Constructing a `SignedExecutionPayloadBid` diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 673536d8..71f66c2d 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -33,7 +33,7 @@ Validators request a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the external builder network to put it in their `SignedBeaconBlock`. The external builder network broadcasts the [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] -corresponding to the bid to the PTC committee. +corresponding to the included bid to the PTC committee. ## Containers @@ -46,7 +46,8 @@ that other builders do not DDOS or run replay attacks on the builder. ```python class RequestAuth(Container): - builder_index: BuilderIndex + builder_pubkey: BLSPubkey + slot: Slot ``` #### `SignedRequestAuth` @@ -63,33 +64,32 @@ class SignedRequestAuth(Container): To construct the `RequestAuth`, we need to fill the following information: -- `builder_index`: This is the on-chain index associated with the builder. +- `builder_pubkey`: The BLS public key of the builder the request is intended for. +- `slot`: The slot for which the bid is being requested. The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`. It sends the `SignedRequestAuth` in the request body along with the request to get the bid in the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API -call. It also sends the `SignedRequestAuth` in the -[`submitBuilderPreferences`][submit-builder-preferences-api] to avoid replay -attacks. A builder could send the preferences to another builder and make them do -unnecessary work. +call. ## Proposer Preferences -*Note*: Validator registrations (`ValidatorRegistrationV2`) are **deprecated** in -favour of [`ProposerPreferences`][proposer-preferences] from the consensus specs. +*Note*: Validator registrations (`ValidatorRegistrationV1`) are **deprecated** +in favour of [`ProposerPreferences`][proposer-preferences] from the consensus +specs. General validator preferences are now communicated via the [`proposer_preferences`][proposer-preferences-topic] gossip topic defined in the -[Gloas consensus specs][gloas-consensus-specs]. Validators broadcast -[`SignedProposerPreferences`][proposer-preferences] messages at the beginning of -each epoch containing: +[Gloas consensus specs][gloas-consensus-specs]. At the beginning of each epoch, +validators broadcast [`SignedProposerPreferences`][proposer-preferences] messages +for their proposal slots in the next epoch containing: - `fee_recipient`: An execution layer address where fees for the validator should go. - `gas_limit`: The value a validator prefers for the execution block gas limit. - `validator_index`: The validator's index. -- `proposal_slot`: The slot in which the validator will be proposing. This can be - looked up in `state.proposer_lookahead`. +- `proposal_slot`: The slot in which the validator will be proposing. This can + be looked up in `state.proposer_lookahead`. Builders SHOULD subscribe to this gossip topic to learn about proposer preferences for upcoming slots. @@ -97,9 +97,9 @@ preferences for upcoming slots. ## Builder Preferences For per-builder preferences that cannot be communicated via a global gossip -topic, validators send [`SignedBuilderPreferences`][builder-preferences] directly -to the builder via the [`submitBuilderPreferences`][submit-builder-preferences-api] -API call. +topic, validators send [`SignedBuilderPreferences`][builder-preferences] +directly to the builder via the +[`submitBuilderPreferences`][submit-builder-preferences-api] API call. ### Constructing the `BuilderPreferences` @@ -108,23 +108,28 @@ To construct the `BuilderPreferences`, the validator client assembles a - `builder_pubkey`: The BLS public key of the builder that these preferences are intended for. +- `slot`: The proposal slot of the validator. This can be looked up in + `state.proposer_lookahead`. - `max_trusted_bid`: The amount (in Gwei) the proposer is willing to accept as a trusted execution layer payment from the builder. ### Builder Preferences dissemination Validators send builder preferences to each builder they wish to interact with -for their upcoming proposal slots. Validators run -`create_builder_preferences` at every epoch boundary to create builder -preferences for all the builders they trust. +for their upcoming proposal slots. Validators run `create_builder_preferences` +in the epoch prior to the epoch in which the validator will become a proposer, +using the `proposer_lookahead` in the beacon state to determine their proposal +slots. ```python def create_builder_preferences( builder_pubkey: BLSPubkey, + slot: Slot, max_trusted_bid: uint64, ) -> BuilderPreferences: return BuilderPreferences( builder_pubkey=builder_pubkey, + slot=slot, max_trusted_bid=max_trusted_bid, ) ``` @@ -175,9 +180,9 @@ builder pays the validator via execution layer payments, we require that the bid's fee recipient matches the validators expected fee recipient and not the builder's fee recipient. -To express per-builder preferences we need validators to remember which -builder preferences they have sent to each builder, so that they can validate -whether the bid conforms to the preferences expressed by the validators. +To express per-builder preferences we need validators to remember which builder +preferences they have sent to each builder, so that they can validate whether +the bid conforms to the preferences expressed by the validators. ## Block proposal @@ -200,10 +205,8 @@ block on top of a beacon `state` must take the following actions: 3. The proposer returns the `SignedBeaconBlock` back to the upstream block building software via [`submitSignedBeaconBlock`][submit-signed-beacon-block] API call. -4. The upstream block building software constructs the - [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] - corresponding to the - [`SignedExecutionPayloadBid`][signed-execution-payload-bid] and broadcasts it +4. The upstream block building software constructs the corresponding + [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] and broadcasts it to the PTC committee. ## Liveness failsafe diff --git a/types/gloas/preferences.yaml b/types/gloas/preferences.yaml index c62c2240..907ebca5 100644 --- a/types/gloas/preferences.yaml +++ b/types/gloas/preferences.yaml @@ -2,11 +2,14 @@ Gloas: BuilderPreferences: type: object description: "Per-builder preferences that a validator can express to a specific builder." - required: [builder_pubkey, max_trusted_bid] + required: [builder_pubkey, slot, max_trusted_bid] properties: builder_pubkey: $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" description: "The BLS public key of the builder that these preferences are intended for." + slot: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + description: "The proposal slot of the validator." max_trusted_bid: $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" description: "Indicates the maximum amount that a proposer is willing to accept as a trusted payment amount from the builder." diff --git a/types/gloas/request_auth.yaml b/types/gloas/request_auth.yaml index bcc6cc7f..b8742949 100644 --- a/types/gloas/request_auth.yaml +++ b/types/gloas/request_auth.yaml @@ -1,12 +1,14 @@ Gloas: RequestAuth: type: object - required: [builder_index] + required: [builder_pubkey, slot] properties: - builder_index: - type: string - pattern: "^0x[a-fA-F0-9]{1,16}$" - description: "The on-chain index associated with the builder, represented as a hex string." + builder_pubkey: + $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" + description: "The BLS public key of the builder the request is intended for." + slot: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + description: "The slot for which the bid is being requested." SignedRequestAuth: type: object required: [message, signature] From 19c272f412623aeae7d1035fb3a323a938ef0185 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 12 Mar 2026 14:15:57 +0530 Subject: [PATCH 40/59] run lint --- specs/gloas/builder.md | 8 ++++---- specs/gloas/validator.md | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 162ddedd..97944b89 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -126,10 +126,10 @@ Currently, the only preference that is supported is: - `max_trusted_bid`: Specifies the maximum value (in Gwei) that a proposer is willing to accept as a trusted execution layer payment from the builder. A value of `0` indicates that the proposer does not accept any trusted payments - from the builder, requiring all payments to use the on-chain trustless payments mechanism. - A value of `MAX_TRUSTED_BID` indicates that the proposer will accept - any trusted payment amount from the builder. Proposers may adjust this - parameter based on their level of trust in the builder's reliability and + from the builder, requiring all payments to use the on-chain trustless + payments mechanism. A value of `MAX_TRUSTED_BID` indicates that the proposer + will accept any trusted payment amount from the builder. Proposers may adjust + this parameter based on their level of trust in the builder's reliability and reputation. The `builder_pubkey` field identifies which builder the preferences are intended diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 71f66c2d..3295651d 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -64,7 +64,8 @@ class SignedRequestAuth(Container): To construct the `RequestAuth`, we need to fill the following information: -- `builder_pubkey`: The BLS public key of the builder the request is intended for. +- `builder_pubkey`: The BLS public key of the builder the request is intended + for. - `slot`: The slot for which the bid is being requested. The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`. @@ -81,8 +82,8 @@ specs. General validator preferences are now communicated via the [`proposer_preferences`][proposer-preferences-topic] gossip topic defined in the [Gloas consensus specs][gloas-consensus-specs]. At the beginning of each epoch, -validators broadcast [`SignedProposerPreferences`][proposer-preferences] messages -for their proposal slots in the next epoch containing: +validators broadcast [`SignedProposerPreferences`][proposer-preferences] +messages for their proposal slots in the next epoch containing: - `fee_recipient`: An execution layer address where fees for the validator should go. @@ -206,8 +207,8 @@ block on top of a beacon `state` must take the following actions: building software via [`submitSignedBeaconBlock`][submit-signed-beacon-block] API call. 4. The upstream block building software constructs the corresponding - [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] and broadcasts it - to the PTC committee. + [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] and + broadcasts it to the PTC committee. ## Liveness failsafe From 18ca2ed00bd9aa8c18b1b244f0f0b9ddd0549bb9 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 16 Mar 2026 14:31:30 +0530 Subject: [PATCH 41/59] remove description of proposer preferences --- specs/gloas/builder.md | 11 ++--------- specs/gloas/validator.md | 9 +-------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 97944b89..a96ade6f 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -143,13 +143,7 @@ for. Builders SHOULD subscribe to the [`proposer_preferences`][proposer-preferences-topic] gossip topic to learn about a validator's general preferences. Validators broadcast these messages at the -beginning of each epoch for their proposal slots in the next epoch. The -`ProposerPreferences` message contains: - -- `validator_index`: The index of the validator proposing. -- `fee_recipient`: The execution layer address where fees should go. -- `gas_limit`: The preferred gas limit. -- `proposal_slot`: The slot in which the validator will be proposing. +beginning of each epoch for their proposal slots in the next epoch. For per-builder preferences (such as `max_trusted_bid`), validators send [`SignedBuilderPreferences`](#signedbuilderpreferences) directly to the builder @@ -200,8 +194,7 @@ been accepted by the proposer and it has been included in its `SignedBeaconBlock`, then the builder has to construct a [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] corresponding to the [`SignedExecutionPayloadBid`][signed-execution-payload-bid] -and it has to broadcast it to the PTC committee via the -`execution_payload_envelope` gossip topic. +and it has to broadcast via the `execution_payload_envelope` gossip topic. The specification for a block builder to construct a [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] is diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 3295651d..3bd2174e 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -83,14 +83,7 @@ General validator preferences are now communicated via the [`proposer_preferences`][proposer-preferences-topic] gossip topic defined in the [Gloas consensus specs][gloas-consensus-specs]. At the beginning of each epoch, validators broadcast [`SignedProposerPreferences`][proposer-preferences] -messages for their proposal slots in the next epoch containing: - -- `fee_recipient`: An execution layer address where fees for the validator - should go. -- `gas_limit`: The value a validator prefers for the execution block gas limit. -- `validator_index`: The validator's index. -- `proposal_slot`: The slot in which the validator will be proposing. This can - be looked up in `state.proposer_lookahead`. +messages for their proposal slots in the next epoch. Builders SHOULD subscribe to this gossip topic to learn about proposer preferences for upcoming slots. From c1f2af3aa6c6112075ae326b732245bdce93f1bb Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 30 Mar 2026 13:35:08 +0530 Subject: [PATCH 42/59] add validator pubkey in builder preferences and request auth --- apis/builder/execution_payload_bid.yaml | 6 +++--- specs/gloas/builder.md | 12 +++++------- specs/gloas/validator.md | 12 +++++++++--- types/gloas/preferences.yaml | 5 ++++- types/gloas/request_auth.yaml | 5 ++++- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 45aafeb5..dfa71a6d 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -10,14 +10,14 @@ post: - The hash of the execution layer block the proposer will build on. - The root of the beacon block the proposer will build on. - The index of the proposer. - - A signed request auth containing the builder's public key and slot to authenticate the request. + - An optional signed request auth containing the builder's public key, validator's public key, and slot to authenticate the request. The builder responds with a 200 response containing an execution payload bid if it can provide one. If the builder is unable to produce a valid execution payload bid, then the builder MUST return a 204 response. If the request is invalid, then the builder MUST return an error response (400) with a description of the - validation failure. If the SignedRequestAuth is invalid, the builder MUST return a 400 response. + validation failure. If the `SignedRequestAuth` is provided and invalid, the builder MUST return a 400 response. This API is applicable from Gloas fork onwards. tags: @@ -71,7 +71,7 @@ post: format: int64 example: 10000 requestBody: - required: true + required: false content: application/json: schema: diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index a96ade6f..60aab303 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -44,7 +44,8 @@ describes how builders interact with validators through ```python class BuilderPreferences(Container): builder_pubkey: BLSPubkey - slot: Slot + validator_pubkey: BLSPubkey + proposal_slot: Slot max_trusted_bid: uint64 ``` @@ -63,12 +64,9 @@ class SignedBuilderPreferences(Container): ```python def verify_builder_preferences_signature( - state: BeaconState, signed_preferences: SignedBuilderPreferences, - validator_index: ValidatorIndex, ) -> bool: - validator = state.validators[validator_index] - pubkey = validator.pubkey + pubkey = signed_preferences.message.validator_pubkey domain = compute_domain(DOMAIN_APPLICATION_BUILDER) signing_root = compute_signing_root(signed_preferences.message, domain) return bls.Verify(pubkey, signing_root, signed_preferences.signature) @@ -159,11 +157,11 @@ def process_builder_preferences( state: BeaconState, proposer_preferences: ProposerPreferences, signed_preferences: SignedBuilderPreferences, - validator_index: ValidatorIndex, builder_preferences: Dict[ValidatorIndex, BuilderPreferences], ): preferences = signed_preferences.message + validator_index = ValidatorIndex(state.validators.index(preferences.validator_pubkey)) validator = state.validators[validator_index] # Verify validator is eligible @@ -177,7 +175,7 @@ def process_builder_preferences( # Verify builder preferences signature assert verify_builder_preferences_signature( - state, signed_preferences, validator_index + signed_preferences, ) ``` diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 3bd2174e..e497d1b8 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -47,6 +47,7 @@ that other builders do not DDOS or run replay attacks on the builder. ```python class RequestAuth(Container): builder_pubkey: BLSPubkey + validator_pubkey: BLSPubkey slot: Slot ``` @@ -66,12 +67,14 @@ To construct the `RequestAuth`, we need to fill the following information: - `builder_pubkey`: The BLS public key of the builder the request is intended for. +- `validator_pubkey`: The BLS public key of the validator making the request. - `slot`: The slot for which the bid is being requested. The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`. -It sends the `SignedRequestAuth` in the request body along with the request to +It MAY send the `SignedRequestAuth` in the request body along with the request to get the bid in the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API -call. +call. Sending the `SignedRequestAuth` is optional; builders MAY require it for +authentication purposes. ## Proposer Preferences @@ -102,6 +105,7 @@ To construct the `BuilderPreferences`, the validator client assembles a - `builder_pubkey`: The BLS public key of the builder that these preferences are intended for. +- `validator_pubkey`: The BLS public key of the validator sending the preferences. - `slot`: The proposal slot of the validator. This can be looked up in `state.proposer_lookahead`. - `max_trusted_bid`: The amount (in Gwei) the proposer is willing to accept as a @@ -118,11 +122,13 @@ slots. ```python def create_builder_preferences( builder_pubkey: BLSPubkey, + validator_pubkey: BLSPubkey, slot: Slot, max_trusted_bid: uint64, ) -> BuilderPreferences: return BuilderPreferences( builder_pubkey=builder_pubkey, + validator_pubkey=validator_pubkey, slot=slot, max_trusted_bid=max_trusted_bid, ) @@ -190,7 +196,7 @@ block on top of a beacon `state` must take the following actions: 1. Call upstream builder software to get a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] using the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API call. The - validator is required to send the `SignedRequestAuth` in the request body in + validator MAY send the `SignedRequestAuth` in the request body in order to authenticate the request to the builder. 2. Assemble a `SignedBeaconBlock` according to the process outlined in the [Gloas validator specs][gloas-validator-specs] but with the best diff --git a/types/gloas/preferences.yaml b/types/gloas/preferences.yaml index 907ebca5..e69e0eec 100644 --- a/types/gloas/preferences.yaml +++ b/types/gloas/preferences.yaml @@ -2,11 +2,14 @@ Gloas: BuilderPreferences: type: object description: "Per-builder preferences that a validator can express to a specific builder." - required: [builder_pubkey, slot, max_trusted_bid] + required: [builder_pubkey, validator_pubkey, slot, max_trusted_bid] properties: builder_pubkey: $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" description: "The BLS public key of the builder that these preferences are intended for." + validator_pubkey: + $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" + description: "The BLS public key of the validator sending the preferences." slot: $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" description: "The proposal slot of the validator." diff --git a/types/gloas/request_auth.yaml b/types/gloas/request_auth.yaml index b8742949..4919c0e1 100644 --- a/types/gloas/request_auth.yaml +++ b/types/gloas/request_auth.yaml @@ -1,11 +1,14 @@ Gloas: RequestAuth: type: object - required: [builder_pubkey, slot] + required: [builder_pubkey, validator_pubkey, slot] properties: builder_pubkey: $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" description: "The BLS public key of the builder the request is intended for." + validator_pubkey: + $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" + description: "The BLS public key of the validator making the request." slot: $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" description: "The slot for which the bid is being requested." From 9280a1b3c3709524570da477992b7be0b93875a8 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 30 Mar 2026 14:17:31 +0530 Subject: [PATCH 43/59] use get-upcoming-proposal-slots --- specs/gloas/validator.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index e497d1b8..c3f54d6e 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -65,10 +65,16 @@ class SignedRequestAuth(Container): To construct the `RequestAuth`, we need to fill the following information: +<<<<<<< HEAD - `builder_pubkey`: The BLS public key of the builder the request is intended for. - `validator_pubkey`: The BLS public key of the validator making the request. - `slot`: The slot for which the bid is being requested. +======= +- `salt`: This is a 4kB salt which has to be specific to each whitelisted + builder. The spec requires the proposer to set it to the URL provided by the + whitelisted builder. +>>>>>>> 53c7d9f (use get-upcoming-proposal-slots) The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`. It MAY send the `SignedRequestAuth` in the request body along with the request to From 52ccbab685e857b275eaf441fbce6d9bc9ea7523 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 30 Mar 2026 14:17:48 +0530 Subject: [PATCH 44/59] Update liveness failsafe section --- specs/gloas/validator.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index c3f54d6e..2ef603ff 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -33,7 +33,11 @@ Validators request a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the external builder network to put it in their `SignedBeaconBlock`. The external builder network broadcasts the [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] +<<<<<<< HEAD corresponding to the included bid to the PTC committee. +======= +corresponding to the bid to the PTC committee. +>>>>>>> b522a99 (Update liveness failsafe section) ## Containers @@ -46,9 +50,13 @@ that other builders do not DDOS or run replay attacks on the builder. ```python class RequestAuth(Container): +<<<<<<< HEAD builder_pubkey: BLSPubkey validator_pubkey: BLSPubkey slot: Slot +======= + builder_index: BuilderIndex +>>>>>>> b522a99 (Update liveness failsafe section) ``` #### `SignedRequestAuth` @@ -65,6 +73,7 @@ class SignedRequestAuth(Container): To construct the `RequestAuth`, we need to fill the following information: +<<<<<<< HEAD <<<<<<< HEAD - `builder_pubkey`: The BLS public key of the builder the request is intended for. @@ -75,6 +84,9 @@ To construct the `RequestAuth`, we need to fill the following information: builder. The spec requires the proposer to set it to the URL provided by the whitelisted builder. >>>>>>> 53c7d9f (use get-upcoming-proposal-slots) +======= +- `builder_index`: This is the on-chain index associated with the builder. +>>>>>>> b522a99 (Update liveness failsafe section) The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`. It MAY send the `SignedRequestAuth` in the request body along with the request to From 3d78f1223844882d81bb09349e88c42a7418b0a8 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 30 Mar 2026 14:19:06 +0530 Subject: [PATCH 45/59] define builder config spec --- specs/gloas/validator.md | 117 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 2ef603ff..6f9b5cff 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -8,6 +8,11 @@ - [New Containers](#new-containers) - [`RequestAuth`](#requestauth) - [`SignedRequestAuth`](#signedrequestauth) + - [`BuilderConfig`](#builderconfig) + - [`GlobalPreferences`](#globalpreferences) + - [`BuilderWhitelist`](#builderwhitelist) + - [Helper](#helper) + - [`get_proposer_slots_in_upcoming_epoch`](#get_proposer_slots_in_upcoming_epoch) - [Bid Authentication](#bid-authentication) - [Constructing the `RequestAuth`](#constructing-the-requestauth) - [Proposer Preferences](#proposer-preferences) @@ -19,6 +24,10 @@ - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [Receiving ExecutionPayloadBid](#receiving-executionpayloadbid) - [Liveness failsafe](#liveness-failsafe) + - [Connecting with upstream block building](#connecting-with-upstream-block-building) + - [Builder Config](#builder-config) + - [Deadline Enforcement](#deadline-enforcement) + - [Bid Selection Strategy](#bid-selection-strategy) @@ -67,6 +76,63 @@ class SignedRequestAuth(Container): signature: BLSSignature ``` +### `BuilderConfig` + +```python +class BuilderConfig(Container): + url: ByteList[MAX_URL_BYTES] + max_trusted_bid: uint64 + min_bid: uint64 + bid_boost: uint64 +``` + +### `GlobalPreferences` + +The GlobalPreferences container contains validator preferences across all +builders. This includes: + +- `bid_selection_strategy`: Strategy with which the proposer selects the winning + bids given a list of bids from whitelisted builders. + +```python +class GlobalPreferences(Container): + bid_selection_strategy: ByteList[MAX_STRATEGY_DESC_BYTES] +``` + +### `BuilderWhitelist` + +```python +class BuilderWhitelist(Container): + builders: List[BuilderConfig, MAX_WHITELISTED_BUILDERS] + global_preferences: GlobalPreferences +``` + +## Helper + +### `get_proposer_slots_in_upcoming_epoch` + +*Note*: `compute_start_slot_at_epoch` and `get_current_epoch` are defined in the +[Gloas consensus specs][gloas-consensus-specs]. + +```python +def get_proposer_slots_in_upcoming_epoch( + state: BeaconState, validator_index: ValidatorIndex +) -> List[Slot]: + """ + Return all slots where validator_index is the proposer within the lookahead window in the next epoch. + """ + proposer_slots = [] + current_epoch_start_slot = compute_start_slot_at_epoch(get_current_epoch(state)) + next_epoch_proposer_lookahead = state.proposer_lookahead[SLOTS_PER_EPOCH:] + + for offset, proposer_index in enumerate(next_epoch_proposer_lookahead): + if proposer_index == validator_index: + slot = current_epoch_start_slot + SLOTS_PER_EPOCH + offset + proposer_slots.append(slot) + + return proposer_slots +``` + ## Bid Authentication ### Constructing the `RequestAuth` @@ -233,7 +299,58 @@ When the circuit breaker condition is triggered for nodes, they *MUST* fallback to receiving bids from the P2P [`execution_payload_bid`][execution-payload-bid] topic and can also build blocks locally. +<<<<<<< HEAD [builder-preferences]: ./builder.md#builderpreferences +======= +## Connecting with upstream block building + +### Builder Config + +The Builder Config above specifies how the client can maintain builder +configurations. The Builder Config is manually maintained by an operator. It +contains the information on how to call a builder and preferences for the +specific builder. It is left up to the client on how the config is passed and +parsed. + +The Builder Whitelist includes the per builder configs along with the global +preferences. + +The following are the fields in the Builder Config: + +- `url`: The URL of the whitelisted builder where we can fetch bids from. +- `max_trusted_bid`: The maximum amount which the proposer will accept in a + trusted payment. This will be sent in the validator registration to the + corresponding builder. +- `min_bid`: The minimum amount of acceptable bid from the builder. +- `bid_boost`: A multiplier factor (in basis points, where 10000 = 100%) applied + to the builder's bid value when comparing against other bids or the local + block value. + +### Deadline Enforcement + +Clients need to configure a timeout `BUILDER_DEADLINE_MS` for all builders. + +```python +def get_builder_deadline(slot_start_time: uint64) -> uint64: + """ + Calculate deadline for builder responses. + Must leave time for local fallback if needed. + """ + + return slot_start_time + BUILDER_DEADLINE_MS +``` + +### Bid Selection Strategy + +A bid selection strategy defines how a client will pick the winning bid out of +the many bids it receives from it's whitelisted builders. + +We can define different types of bid selection strategies but for now we only +define one: + +- `max_profit`: The proposer picks the bid which maximizes profit. + +>>>>>>> fc0ded5 (define builder config spec) [can-builder-cover-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#can_builder_cover_bid [execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md?plain=1#L321 [get-execution-payload-bid-api]: ./../../apis/builder/execution_payload_bid.yaml From b5ba48870833cef12eb7b68782c5bf5e72e0aba6 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 6 Feb 2026 17:47:01 +0530 Subject: [PATCH 46/59] update validator.md --- specs/gloas/validator.md | 50 ++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 6f9b5cff..3c504c34 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -11,8 +11,6 @@ - [`BuilderConfig`](#builderconfig) - [`GlobalPreferences`](#globalpreferences) - [`BuilderWhitelist`](#builderwhitelist) - - [Helper](#helper) - - [`get_proposer_slots_in_upcoming_epoch`](#get_proposer_slots_in_upcoming_epoch) - [Bid Authentication](#bid-authentication) - [Constructing the `RequestAuth`](#constructing-the-requestauth) - [Proposer Preferences](#proposer-preferences) @@ -48,6 +46,15 @@ corresponding to the included bid to the PTC committee. corresponding to the bid to the PTC committee. >>>>>>> b522a99 (Update liveness failsafe section) +<<<<<<< HEAD +======= +## Constants + +| Name | Value | | ---- | ----- | | `MAX_SALT_BYTES` | `4096` | | +`MAX_URL_BYTES` | `4096` | | `MAX_WHITELISTED_BUILDERS` | `10000` | | +`BUILDER_DEADLINE_MS` | `1000` | | `MAX_STRATEGY_DESC_BYTES` | `4096` | + +>>>>>>> 2cf061b (update validator.md) ## Containers ### New Containers @@ -93,10 +100,20 @@ builders. This includes: - `bid_selection_strategy`: Strategy with which the proposer selects the winning bids given a list of bids from whitelisted builders. +- `local_block_boost`: A multiplier factor (in basis points, where 10000 = 100%) + applied to the locally built block value when comparing against bids from the + p2p topic or bids from other builders. This gives priority to the local block + in bid selection. +- `p2p_bid_block_boost`: A multiplier factor (in basis points, where 10000 = + 100%) applied to best bid received from the p2p topic when comparing against + the local block or bids from other builders. This gives priority to bids + received via p2p in bid selection. ```python class GlobalPreferences(Container): bid_selection_strategy: ByteList[MAX_STRATEGY_DESC_BYTES] + local_block_boost: uint64 + p2p_bid_block_boost: uint64 ``` ### `BuilderWhitelist` @@ -107,32 +124,6 @@ class BuilderWhitelist(Container): global_preferences: GlobalPreferences ``` -## Helper - -### `get_proposer_slots_in_upcoming_epoch` - -*Note*: `compute_start_slot_at_epoch` and `get_current_epoch` are defined in the -[Gloas consensus specs][gloas-consensus-specs]. - -```python -def get_proposer_slots_in_upcoming_epoch( - state: BeaconState, validator_index: ValidatorIndex -) -> List[Slot]: - """ - Return all slots where validator_index is the proposer within the lookahead window in the next epoch. - """ - proposer_slots = [] - current_epoch_start_slot = compute_start_slot_at_epoch(get_current_epoch(state)) - next_epoch_proposer_lookahead = state.proposer_lookahead[SLOTS_PER_EPOCH:] - - for offset, proposer_index in enumerate(next_epoch_proposer_lookahead): - if proposer_index == validator_index: - slot = current_epoch_start_slot + SLOTS_PER_EPOCH + offset - proposer_slots.append(slot) - - return proposer_slots -``` - ## Bid Authentication ### Constructing the `RequestAuth` @@ -323,8 +314,7 @@ The following are the fields in the Builder Config: corresponding builder. - `min_bid`: The minimum amount of acceptable bid from the builder. - `bid_boost`: A multiplier factor (in basis points, where 10000 = 100%) applied - to the builder's bid value when comparing against other bids or the local - block value. + to the builder's bid value when comparing against other builder bids. ### Deadline Enforcement From d243794690ebb229cd7c94433565674dd982edb9 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 6 Feb 2026 17:47:46 +0530 Subject: [PATCH 47/59] fix conflict --- specs/gloas/validator.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 3c504c34..33f37943 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -46,15 +46,6 @@ corresponding to the included bid to the PTC committee. corresponding to the bid to the PTC committee. >>>>>>> b522a99 (Update liveness failsafe section) -<<<<<<< HEAD -======= -## Constants - -| Name | Value | | ---- | ----- | | `MAX_SALT_BYTES` | `4096` | | -`MAX_URL_BYTES` | `4096` | | `MAX_WHITELISTED_BUILDERS` | `10000` | | -`BUILDER_DEADLINE_MS` | `1000` | | `MAX_STRATEGY_DESC_BYTES` | `4096` | - ->>>>>>> 2cf061b (update validator.md) ## Containers ### New Containers From 1ce08f2256f73da686ce754f151fceb2e7f07ec2 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 13 Feb 2026 00:14:16 +0530 Subject: [PATCH 48/59] updates --- specs/gloas/validator.md | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 33f37943..d32818d8 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -79,9 +79,10 @@ class SignedRequestAuth(Container): ```python class BuilderConfig(Container): url: ByteList[MAX_URL_BYTES] + builder_pubkey: BLSPubkey max_trusted_bid: uint64 - min_bid: uint64 bid_boost: uint64 + excluded_validators: List[BLSPubkey, MAX_EXCLUDED_VALIDATORS] ``` ### `GlobalPreferences` @@ -89,22 +90,21 @@ class BuilderConfig(Container): The GlobalPreferences container contains validator preferences across all builders. This includes: +- `min_bid`: The minimum bid value (in Gwei) from any builder for the proposer + to consider using a builder bid. If no builder bid meets this threshold, the + proposer falls back to the locally built block. A value of `0` means no + minimum. - `bid_selection_strategy`: Strategy with which the proposer selects the winning bids given a list of bids from whitelisted builders. - `local_block_boost`: A multiplier factor (in basis points, where 10000 = 100%) - applied to the locally built block value when comparing against bids from the - p2p topic or bids from other builders. This gives priority to the local block - in bid selection. -- `p2p_bid_block_boost`: A multiplier factor (in basis points, where 10000 = - 100%) applied to best bid received from the p2p topic when comparing against - the local block or bids from other builders. This gives priority to bids - received via p2p in bid selection. + applied to the locally built block value when comparing against bids from + builders. This gives priority to the local block in bid selection. ```python class GlobalPreferences(Container): + min_bid: uint64 bid_selection_strategy: ByteList[MAX_STRATEGY_DESC_BYTES] local_block_boost: uint64 - p2p_bid_block_boost: uint64 ``` ### `BuilderWhitelist` @@ -300,12 +300,19 @@ preferences. The following are the fields in the Builder Config: - `url`: The URL of the whitelisted builder where we can fetch bids from. -- `max_trusted_bid`: The maximum amount which the proposer will accept in a - trusted payment. This will be sent in the validator registration to the - corresponding builder. -- `min_bid`: The minimum amount of acceptable bid from the builder. +- `builder_pubkey`: The advertised public key of the builder. This is configured + alongside the URL and forms the builder's off-chain identity. It is used to + bind registrations and request auth to a specific builder, preventing + cross-builder replay attacks. +- `max_trusted_bid`: The maximum amount (in Gwei) which the proposer will accept + as a trusted execution layer payment from the builder. This will be sent in + the validator registration to the corresponding builder. - `bid_boost`: A multiplier factor (in basis points, where 10000 = 100%) applied to the builder's bid value when comparing against other builder bids. +- `excluded_validators`: A list of validator public keys that should NOT interact + with this builder when proposing. By default all validators use all + whitelisted builders; this field allows operators to exclude specific + validators from specific builders. ### Deadline Enforcement From 91048a4c90fc47697996d74df6724329ec711c06 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 16 Feb 2026 11:15:55 +0530 Subject: [PATCH 49/59] add builder pubkey --- specs/gloas/validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index d32818d8..f96ad7a0 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -309,8 +309,8 @@ The following are the fields in the Builder Config: the validator registration to the corresponding builder. - `bid_boost`: A multiplier factor (in basis points, where 10000 = 100%) applied to the builder's bid value when comparing against other builder bids. -- `excluded_validators`: A list of validator public keys that should NOT interact - with this builder when proposing. By default all validators use all +- `excluded_validators`: A list of validator public keys that should NOT + interact with this builder when proposing. By default all validators use all whitelisted builders; this field allows operators to exclude specific validators from specific builders. From 1edcb92773a55e7513ebad1a147d8a9901c54c03 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 30 Mar 2026 15:09:18 +0530 Subject: [PATCH 50/59] dont specify impl specific info --- specs/gloas/validator.md | 59 ++++++++++------------------------------ 1 file changed, 15 insertions(+), 44 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index f96ad7a0..2a575adf 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -24,8 +24,6 @@ - [Liveness failsafe](#liveness-failsafe) - [Connecting with upstream block building](#connecting-with-upstream-block-building) - [Builder Config](#builder-config) - - [Deadline Enforcement](#deadline-enforcement) - - [Bid Selection Strategy](#bid-selection-strategy) @@ -40,11 +38,7 @@ Validators request a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the external builder network to put it in their `SignedBeaconBlock`. The external builder network broadcasts the [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] -<<<<<<< HEAD corresponding to the included bid to the PTC committee. -======= -corresponding to the bid to the PTC committee. ->>>>>>> b522a99 (Update liveness failsafe section) ## Containers @@ -57,13 +51,9 @@ that other builders do not DDOS or run replay attacks on the builder. ```python class RequestAuth(Container): -<<<<<<< HEAD builder_pubkey: BLSPubkey validator_pubkey: BLSPubkey slot: Slot -======= - builder_index: BuilderIndex ->>>>>>> b522a99 (Update liveness failsafe section) ``` #### `SignedRequestAuth` @@ -87,15 +77,13 @@ class BuilderConfig(Container): ### `GlobalPreferences` -The GlobalPreferences container contains validator preferences across all +The `GlobalPreferences` container contains validator preferences across all builders. This includes: - `min_bid`: The minimum bid value (in Gwei) from any builder for the proposer to consider using a builder bid. If no builder bid meets this threshold, the proposer falls back to the locally built block. A value of `0` means no minimum. -- `bid_selection_strategy`: Strategy with which the proposer selects the winning - bids given a list of bids from whitelisted builders. - `local_block_boost`: A multiplier factor (in basis points, where 10000 = 100%) applied to the locally built block value when comparing against bids from builders. This gives priority to the local block in bid selection. @@ -103,7 +91,6 @@ builders. This includes: ```python class GlobalPreferences(Container): min_bid: uint64 - bid_selection_strategy: ByteList[MAX_STRATEGY_DESC_BYTES] local_block_boost: uint64 ``` @@ -281,23 +268,20 @@ When the circuit breaker condition is triggered for nodes, they *MUST* fallback to receiving bids from the P2P [`execution_payload_bid`][execution-payload-bid] topic and can also build blocks locally. -<<<<<<< HEAD -[builder-preferences]: ./builder.md#builderpreferences -======= ## Connecting with upstream block building ### Builder Config -The Builder Config above specifies how the client can maintain builder -configurations. The Builder Config is manually maintained by an operator. It +The `BuilderConfig` specifies how the client can maintain builder +configurations. It is manually maintained by an operator and contains the information on how to call a builder and preferences for the specific builder. It is left up to the client on how the config is passed and parsed. -The Builder Whitelist includes the per builder configs along with the global +The `BuilderWhitelist` includes the per-builder configs along with the global preferences. -The following are the fields in the Builder Config: +The following are the fields in the `BuilderConfig`: - `url`: The URL of the whitelisted builder where we can fetch bids from. - `builder_pubkey`: The advertised public key of the builder. This is configured @@ -306,7 +290,7 @@ The following are the fields in the Builder Config: cross-builder replay attacks. - `max_trusted_bid`: The maximum amount (in Gwei) which the proposer will accept as a trusted execution layer payment from the builder. This will be sent in - the validator registration to the corresponding builder. + the builder preferences to the corresponding builder. - `bid_boost`: A multiplier factor (in basis points, where 10000 = 100%) applied to the builder's bid value when comparing against other builder bids. - `excluded_validators`: A list of validator public keys that should NOT @@ -314,31 +298,18 @@ The following are the fields in the Builder Config: whitelisted builders; this field allows operators to exclude specific validators from specific builders. -### Deadline Enforcement - -Clients need to configure a timeout `BUILDER_DEADLINE_MS` for all builders. +The `GlobalPreferences` contains cross-builder parameters: -```python -def get_builder_deadline(slot_start_time: uint64) -> uint64: - """ - Calculate deadline for builder responses. - Must leave time for local fallback if needed. - """ - - return slot_start_time + BUILDER_DEADLINE_MS -``` - -### Bid Selection Strategy - -A bid selection strategy defines how a client will pick the winning bid out of -the many bids it receives from it's whitelisted builders. - -We can define different types of bid selection strategies but for now we only -define one: +- `min_bid`: The minimum bid value (in Gwei) from any builder for the proposer + to consider. Below this threshold, the proposer falls back to the local block. +- `local_block_boost`: A multiplier factor (in basis points, where 10000 = 100%) + applied to the locally built block value when comparing against bids from + builders. -- `max_profit`: The proposer picks the bid which maximizes profit. +Aspects such as deadline enforcement and bid selection strategy are left up to +the client implementation. ->>>>>>> fc0ded5 (define builder config spec) +[builder-preferences]: ./builder.md#builderpreferences [can-builder-cover-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#can_builder_cover_bid [execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md?plain=1#L321 [get-execution-payload-bid-api]: ./../../apis/builder/execution_payload_bid.yaml From 48c6ad8ecc9adcc49a53fa11fbdd608314403f8c Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Sat, 25 Apr 2026 14:31:23 +0200 Subject: [PATCH 51/59] specify setting fee reicpient in execution payload bid --- specs/gloas/builder.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 60aab303..f7e8653c 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -185,6 +185,18 @@ The specification for a block builder to construct a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] is documented in the [Gloas consensus specs][gloas-builder-specs]. +`bid.fee_recipient` MUST be set to the fee recipient from the proposer's +[`ProposerPreferences`][proposer-preferences], regardless of whether the builder +pays via `bid.execution_payment` or `bid.value`. + +If the builder intends to pay the proposer via their staked collateral, they +MUST set `bid.value` to the amount they are committing to pay. + +If the builder intends to pay the proposer via an execution layer payment, they +MUST set `bid.execution_payment`. This value MUST NOT exceed the +`max_trusted_bid` from the proposer's +[`SignedBuilderPreferences`](#signedbuilderpreferences). + ## Constructing a `SignedExecutionPayloadEnvelope` If the builder's [`SignedExecutionPayloadBid`][signed-execution-payload-bid] has From 3d53d8d0ae31d40da127f3296a227f68b91b4d4c Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 30 Apr 2026 10:20:57 +0200 Subject: [PATCH 52/59] updates to epbs --- apis/builder/execution_payload_bid.yaml | 60 ++++++-- apis/builder/preferences.yaml | 57 -------- builder-oapi.yaml | 10 +- .../gloas/signed_builder_preferences.json | 14 -- specs/gloas/builder.md | 138 +++++------------- specs/gloas/validator.md | 115 +++++++-------- types/gloas/preferences.yaml | 27 ---- 7 files changed, 135 insertions(+), 286 deletions(-) delete mode 100644 apis/builder/preferences.yaml delete mode 100644 examples/gloas/signed_builder_preferences.json delete mode 100644 types/gloas/preferences.yaml diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index dfa71a6d..14f359b8 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -3,21 +3,26 @@ post: summary: Get an execution payload bid. description: | Requests a builder node to produce a valid execution payload bid, which - can be integrated into a beacon block and signed. + can be integrated into a beacon block and signed. The proposer sends a POST request to the builder with the following information: - The slot for which the block should be proposed. - The hash of the execution layer block the proposer will build on. - The root of the beacon block the proposer will build on. - The index of the proposer. - - An optional signed request auth containing the builder's public key, validator's public key, and slot to authenticate the request. + - The `X-Eth-Request-Auth` header containing a JSON-encoded `SignedRequestAuth` that authenticates the request. + - The `X-Eth-Max-Trusted-Bid` header carrying the proposer's `max_trusted_bid` (decimal `uint64`, in Gwei) for this request. + + Both headers are required. If either is missing, malformed, or fails + validation, the builder MUST NOT serve a bid for the proposer and MUST + return a 400 response. The builder responds with a 200 response containing an execution payload bid if it can provide one. If the builder is unable to produce a valid execution payload bid, then the builder MUST return a 204 response. If the request is invalid, then the builder MUST return an error response (400) with a description of the - validation failure. If the `SignedRequestAuth` is provided and invalid, the builder MUST return a 400 response. + validation failure. This API is applicable from Gloas fork onwards. tags: @@ -62,23 +67,40 @@ post: in: header required: false description: | - Optional header containing the proposer's timeout for the request in milliseconds. - Builders should use this header to adjust the amount of time by which they delay the - requests to maximise block rewards. Otherwise, requests will timeout and the proposer + Optional header containing the proposer's timeout for the request in milliseconds. + Builders should use this header to adjust the amount of time by which they delay the + requests to maximise block rewards. Otherwise, requests will timeout and the proposer will not receive the header in time. schema: type: integer format: int64 example: 10000 - requestBody: - required: false - content: - application/json: - schema: - $ref: "../../types/gloas/request_auth.yaml#/Gloas/SignedRequestAuth" - application/octet-stream: - schema: - description: "SSZ serialized `SignedRequestAuth` bytes." + - name: X-Eth-Request-Auth + in: header + required: true + description: | + JSON-encoded `SignedRequestAuth` authenticating the request. The + builder MUST verify the BLS signature against `validator_pubkey` and + check that `builder_pubkey` matches its own identity and that `slot` + matches the requested slot. If the header is missing or invalid, the + builder MUST return a 400 response. + schema: + $ref: "../../types/gloas/request_auth.yaml#/Gloas/SignedRequestAuth" + - name: X-Eth-Max-Trusted-Bid + in: header + required: true + description: | + Decimal `uint64` (in Gwei) carrying the proposer's `max_trusted_bid` + for this request. `bid.execution_payment` MUST NOT exceed this value. + A value of `0` indicates that the proposer does not accept any + trusted payments from this builder. A value of `2**64 - 1` + (`MAX_TRUSTED_BID`) indicates that the proposer accepts any trusted + payment amount. If the header is missing or malformed, the builder + MUST return a 400 response. + schema: + type: integer + format: uint64 + example: 1000000000 responses: "200": description: Success response. @@ -115,10 +137,18 @@ post: value: code: 400 message: "Unknown hash: missing parent hash" + MissingRequestAuth: + value: + code: 400 + message: "Missing X-Eth-Request-Auth header" InvalidAuth: value: code: 400 message: "Invalid SignedRequestAuth: signature verification failed" + MissingMaxTrustedBid: + value: + code: 400 + message: "Missing X-Eth-Max-Trusted-Bid header" "406": $ref: "../../builder-oapi.yaml#/components/responses/NotAcceptable" "500": diff --git a/apis/builder/preferences.yaml b/apis/builder/preferences.yaml deleted file mode 100644 index a2e9d4a1..00000000 --- a/apis/builder/preferences.yaml +++ /dev/null @@ -1,57 +0,0 @@ -post: - operationId: "submitBuilderPreferences" - summary: Submit per-builder preferences for Gloas. - description: | - Submits a validator's per-builder preferences (e.g. max_trusted_bid) to a - specific builder. - - Note: General validator preferences (fee_recipient, gas_limit, - proposal_slot, validator_index) are now communicated via the - `proposer_preferences` gossip topic defined in the consensus specs. This - endpoint is only used for preferences that are specific to a particular - builder. - - A success response (200) indicates that the preferences were valid. If the - preferences pass validation, then the builder MUST integrate the - preferences into its state, such that future bids built for the - validator conform to the preferences. If the preferences are invalid, - then the builder MUST return an error response (400) with a description - of the validation failure. - tags: - - Builder - requestBody: - description: | - Signed per-builder preferences. - required: true - content: - application/json: - schema: - type: object - required: - - preferences - properties: - preferences: - type: array - items: - $ref: "../../builder-oapi.yaml#/components/schemas/Gloas.SignedBuilderPreferences" - example: - $ref: "../../builder-oapi.yaml#/components/examples/Gloas.SignedBuilderPreferences" - application/octet-stream: - schema: - description: "SSZ serialized `List[SignedBuilderPreferences, VALIDATOR_REGISTRY_LIMIT]` bytes. Use content type header to indicate that SSZ data is contained in the request body." - responses: - "200": - description: Success response. - "400": - description: Error response. - content: - application/json: - schema: - $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" - example: - code: 400 - message: "invalid preferences" - "415": - $ref: "../../builder-oapi.yaml#/components/responses/UnsupportedMediaType" - "500": - $ref: "../../builder-oapi.yaml#/components/responses/InternalError" diff --git a/builder-oapi.yaml b/builder-oapi.yaml index 1d9ab090..bfcb6d9f 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -51,8 +51,6 @@ tags: paths: /eth/v1/builder/validators: $ref: "./apis/builder/validators.yaml" - /eth/v1/builder/preferences: - $ref: "./apis/builder/preferences.yaml" /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}: $ref: "./apis/builder/header.yaml" /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_index}: @@ -112,10 +110,6 @@ components: $ref: "./types/gloas/request_auth.yaml#/Gloas/RequestAuth" Gloas.SignedRequestAuth: $ref: "./types/gloas/request_auth.yaml#/Gloas/SignedRequestAuth" - Gloas.BuilderPreferences: - $ref: "./types/gloas/preferences.yaml#/Gloas/BuilderPreferences" - Gloas.SignedBuilderPreferences: - $ref: "./types/gloas/preferences.yaml#/Gloas/SignedBuilderPreferences" responses: InternalError: @@ -166,6 +160,4 @@ components: Fulu.SignedBuilderBid: $ref: "./examples/fulu/signed_builder_bid.json" Gloas.SignedRequestAuth: - $ref: "./examples/gloas/signed_request_auth.json" - Gloas.SignedBuilderPreferences: - $ref: "./examples/gloas/signed_builder_preferences.json" \ No newline at end of file + $ref: "./examples/gloas/signed_request_auth.json" \ No newline at end of file diff --git a/examples/gloas/signed_builder_preferences.json b/examples/gloas/signed_builder_preferences.json deleted file mode 100644 index 5c45b50e..00000000 --- a/examples/gloas/signed_builder_preferences.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "value": { - "preferences": [ - { - "message": { - "builder_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", - "slot": "32", - "max_trusted_bid": "1000000000000000000" - }, - "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" - } - ] - } - } diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index f7e8653c..4f9a8ea9 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -5,15 +5,10 @@ - [Gloas - Builder Specification](#gloas---builder-specification) - [Introduction](#introduction) - [Constants](#constants) - - [Containers](#containers) - - [New Containers](#new-containers) - - [`BuilderPreferences`](#builderpreferences) - - [`SignedBuilderPreferences`](#signedbuilderpreferences) - - [`verify_builder_preferences_signature`](#verify_builder_preferences_signature) - [Bidding](#bidding) - - [Builder Preferences](#builder-preferences) + - [Per-request Validator Inputs](#per-request-validator-inputs) + - [`max_trusted_bid`](#max_trusted_bid) - [Proposer Preferences (Deprecation of Validator Registrations)](#proposer-preferences-deprecation-of-validator-registrations) - - [`process_builder_preferences`](#process_builder_preferences) - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) - [Constructing a `SignedExecutionPayloadEnvelope`](#constructing-a-signedexecutionpayloadenvelope) @@ -23,10 +18,9 @@ ## Introduction -This document documents the builder behaviour with the Builder-API post ePBS. It -describes how builders interact with validators through -[`BuilderPreferences`](#builderpreferences) and construct -[`SignedExecutionPayloadBid`][signed-execution-payload-bid] and +This document documents the builder behaviour with the Builder-API post ePBS. +It describes how builders consume per-request inputs from validators and +construct [`SignedExecutionPayloadBid`][signed-execution-payload-bid] and [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] objects. ## Constants @@ -35,43 +29,6 @@ describes how builders interact with validators through | ----------------- | ----------- | | `MAX_TRUSTED_BID` | `2**64 - 1` | -## Containers - -### New Containers - -#### `BuilderPreferences` - -```python -class BuilderPreferences(Container): - builder_pubkey: BLSPubkey - validator_pubkey: BLSPubkey - proposal_slot: Slot - max_trusted_bid: uint64 -``` - -#### `SignedBuilderPreferences` - -```python -class SignedBuilderPreferences(Container): - message: BuilderPreferences - signature: BLSSignature -``` - -### `verify_builder_preferences_signature` - -*Note*: `compute_domain` and `compute_signing_root` are defined in the -[Gloas consensus specs][gloas-consensus-specs]. - -```python -def verify_builder_preferences_signature( - signed_preferences: SignedBuilderPreferences, -) -> bool: - pubkey = signed_preferences.message.validator_pubkey - domain = compute_domain(DOMAIN_APPLICATION_BUILDER) - signing_root = compute_signing_root(signed_preferences.message, domain) - return bls.Verify(pubkey, signing_root, signed_preferences.signature) -``` - ## Bidding In Gloas, Execution payloads are built for a specific `slot`, `parent_hash`, @@ -114,24 +71,35 @@ def is_eligible_for_bid( assert parent_root == hash_tree_root(state.latest_block_header) ``` -## Builder Preferences +## Per-request Validator Inputs + +Validators communicate per-request inputs to a builder via HTTP headers on +each [`getExecutionPayloadBid`][get-execution-payload-bid-api] call: -Validators send per-builder preferences directly to the builder via the -[`submitBuilderPreferences`][submit-builder-preferences-api] API call. This -allows a proposer to express trust preferences for a specific builder. -Currently, the only preference that is supported is: +- `X-Eth-Request-Auth`: a JSON-encoded + [`SignedRequestAuth`][signed-request-auth] used to authenticate the + requesting validator. Builders MUST verify the signature against + `validator_pubkey` and check that `builder_pubkey` matches their own + identity and that `slot` matches the requested slot. +- `X-Eth-Max-Trusted-Bid`: a decimal `uint64` (in Gwei) carrying the + proposer's `max_trusted_bid` for this request. -- `max_trusted_bid`: Specifies the maximum value (in Gwei) that a proposer is - willing to accept as a trusted execution layer payment from the builder. A - value of `0` indicates that the proposer does not accept any trusted payments - from the builder, requiring all payments to use the on-chain trustless - payments mechanism. A value of `MAX_TRUSTED_BID` indicates that the proposer - will accept any trusted payment amount from the builder. Proposers may adjust - this parameter based on their level of trust in the builder's reliability and - reputation. +If either header is missing, malformed, or fails validation, the builder MUST +NOT serve a bid for the proposer (return a 400 response). -The `builder_pubkey` field identifies which builder the preferences are intended -for. +### `max_trusted_bid` + +`max_trusted_bid` is the maximum value (in Gwei) that a proposer is willing +to accept as a trusted execution layer payment from this builder for this +request. A value of `0` indicates that the proposer does not accept any +trusted payments from the builder, requiring all payments to use the on-chain +trustless payments mechanism. A value of `MAX_TRUSTED_BID` indicates that the +proposer will accept any trusted payment amount from the builder. Proposers +may adjust this parameter based on their level of trust in the builder's +reliability and reputation. + +`max_trusted_bid` is sent in the clear in the `X-Eth-Max-Trusted-Bid` header +and is **not** covered by the `RequestAuth` signature. ## Proposer Preferences (Deprecation of Validator Registrations) @@ -143,42 +111,6 @@ Builders SHOULD subscribe to the a validator's general preferences. Validators broadcast these messages at the beginning of each epoch for their proposal slots in the next epoch. -For per-builder preferences (such as `max_trusted_bid`), validators send -[`SignedBuilderPreferences`](#signedbuilderpreferences) directly to the builder -via the [`submitBuilderPreferences`][submit-builder-preferences-api] API call. - -### `process_builder_preferences` - -A `BuilderPreferences` message is considered valid if the following function -completes without raising any assertions. - -```python -def process_builder_preferences( - state: BeaconState, - proposer_preferences: ProposerPreferences, - signed_preferences: SignedBuilderPreferences, - builder_preferences: Dict[ValidatorIndex, BuilderPreferences], -): - preferences = signed_preferences.message - - validator_index = ValidatorIndex(state.validators.index(preferences.validator_pubkey)) - validator = state.validators[validator_index] - - # Verify validator is eligible - assert is_eligible_for_registration(state, validator) - - # Verify that proposer preferences have been received via the gossip topic - assert proposer_preferences.validator_index == validator_index - - # Verify the builder_pubkey matches the builder receiving the preferences - # (implementation specific check) - - # Verify builder preferences signature - assert verify_builder_preferences_signature( - signed_preferences, - ) -``` - ## Constructing a `SignedExecutionPayloadBid` The specification for a block builder to construct a @@ -194,8 +126,9 @@ MUST set `bid.value` to the amount they are committing to pay. If the builder intends to pay the proposer via an execution layer payment, they MUST set `bid.execution_payment`. This value MUST NOT exceed the -`max_trusted_bid` from the proposer's -[`SignedBuilderPreferences`](#signedbuilderpreferences). +`max_trusted_bid` received in the `X-Eth-Max-Trusted-Bid` header of the +corresponding [`getExecutionPayloadBid`][get-execution-payload-bid-api] +request. ## Constructing a `SignedExecutionPayloadEnvelope` @@ -211,10 +144,11 @@ The specification for a block builder to construct a documented in the [Gloas consensus specs][gloas-builder-specs]. [eip-7732]: https://eips.ethereum.org/EIPS/eip-7732 +[get-execution-payload-bid-api]: ./../../apis/builder/execution_payload_bid.yaml [gloas-builder-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/builder.md [gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas [proposer-preferences]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md [proposer-preferences-topic]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md [signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid [signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope -[submit-builder-preferences-api]: ./../../apis/builder/preferences.yaml +[signed-request-auth]: ./validator.md#signedrequestauth diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index e497d1b8..e35f7a30 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -8,12 +8,10 @@ - [New Containers](#new-containers) - [`RequestAuth`](#requestauth) - [`SignedRequestAuth`](#signedrequestauth) - - [Bid Authentication](#bid-authentication) + - [Bid Request](#bid-request) - [Constructing the `RequestAuth`](#constructing-the-requestauth) + - [`max_trusted_bid`](#max_trusted_bid) - [Proposer Preferences](#proposer-preferences) - - [Builder Preferences](#builder-preferences) - - [Constructing the `BuilderPreferences`](#constructing-the-builderpreferences) - - [Builder Preferences dissemination](#builder-preferences-dissemination) - [Validating a `SignedExecutionPayloadBid`](#validating-a-signedexecutionpayloadbid) - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) @@ -59,7 +57,19 @@ class SignedRequestAuth(Container): signature: BLSSignature ``` -## Bid Authentication +## Bid Request + +When calling [`getExecutionPayloadBid`][get-execution-payload-bid-api], the +validator MUST send two HTTP headers: + +- `X-Eth-Request-Auth`: a JSON-encoded + [`SignedRequestAuth`](#signedrequestauth) that authenticates the request. +- `X-Eth-Max-Trusted-Bid`: a decimal `uint64` (in Gwei) expressing the + per-builder `max_trusted_bid` for this request. See + [`max_trusted_bid`](#max_trusted_bid). + +If either header is missing, the builder will not serve a bid for the +proposer. ### Constructing the `RequestAuth` @@ -70,11 +80,29 @@ To construct the `RequestAuth`, we need to fill the following information: - `validator_pubkey`: The BLS public key of the validator making the request. - `slot`: The slot for which the bid is being requested. -The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`. -It MAY send the `SignedRequestAuth` in the request body along with the request to -get the bid in the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API -call. Sending the `SignedRequestAuth` is optional; builders MAY require it for -authentication purposes. +The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`, +JSON-encodes it, and sends it in the `X-Eth-Request-Auth` header of the +[`getExecutionPayloadBid`][get-execution-payload-bid-api] request. The +signature lets builders authenticate the requesting validator and discard +requests from other parties (e.g. DDOS or replay attempts from competing +builders). + +### `max_trusted_bid` + +`max_trusted_bid` is the maximum value (in Gwei) that the proposer is willing +to accept as a trusted execution layer payment from this builder for this +request. A value of `0` means the proposer does not accept any trusted +payments from this builder, requiring all payments to go through the on-chain +trustless payments mechanism. A value of `MAX_TRUSTED_BID` means the proposer +will accept any trusted payment amount from the builder. Proposers may adjust +this parameter based on their level of trust in the builder's reliability and +reputation. + +The validator sends `max_trusted_bid` as a decimal `uint64` in the +`X-Eth-Max-Trusted-Bid` header. Note that `max_trusted_bid` is **not** +covered by the `RequestAuth` signature. The validator MUST remember the +`max_trusted_bid` value it sent for each request so it can validate the +resulting bid against the same value. ## Proposer Preferences @@ -91,48 +119,10 @@ messages for their proposal slots in the next epoch. Builders SHOULD subscribe to this gossip topic to learn about proposer preferences for upcoming slots. -## Builder Preferences - -For per-builder preferences that cannot be communicated via a global gossip -topic, validators send [`SignedBuilderPreferences`][builder-preferences] -directly to the builder via the -[`submitBuilderPreferences`][submit-builder-preferences-api] API call. - -### Constructing the `BuilderPreferences` - -To construct the `BuilderPreferences`, the validator client assembles a -[`BuilderPreferences`][builder-preferences] with the following information: - -- `builder_pubkey`: The BLS public key of the builder that these preferences are - intended for. -- `validator_pubkey`: The BLS public key of the validator sending the preferences. -- `slot`: The proposal slot of the validator. This can be looked up in - `state.proposer_lookahead`. -- `max_trusted_bid`: The amount (in Gwei) the proposer is willing to accept as a - trusted execution layer payment from the builder. - -### Builder Preferences dissemination - -Validators send builder preferences to each builder they wish to interact with -for their upcoming proposal slots. Validators run `create_builder_preferences` -in the epoch prior to the epoch in which the validator will become a proposer, -using the `proposer_lookahead` in the beacon state to determine their proposal -slots. - -```python -def create_builder_preferences( - builder_pubkey: BLSPubkey, - validator_pubkey: BLSPubkey, - slot: Slot, - max_trusted_bid: uint64, -) -> BuilderPreferences: - return BuilderPreferences( - builder_pubkey=builder_pubkey, - validator_pubkey=validator_pubkey, - slot=slot, - max_trusted_bid=max_trusted_bid, - ) -``` +Per-builder preferences (currently just `max_trusted_bid`) are not gossiped; +they are sent directly to the builder as a header on each +[`getExecutionPayloadBid`][get-execution-payload-bid-api] request. See +[`max_trusted_bid`](#max_trusted_bid). ## Validating a `SignedExecutionPayloadBid` @@ -152,7 +142,7 @@ are also defined in the consensus specs. def validate_bid( state: BeaconState, proposer_preferences: ProposerPreferences, - builder_preferences: BuilderPreferences, + max_trusted_bid: uint64, signed_bid: SignedExecutionPayloadBid, fee_recipient: ExecutionAddress, ) -> bool: @@ -166,7 +156,7 @@ def validate_bid( assert bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) assert bid.gas_limit <= proposer_preferences.gas_limit - assert bid.execution_payment <= builder_preferences.max_trusted_bid + assert bid.execution_payment <= max_trusted_bid if bid.value > 0: assert can_builder_cover_bid(state, bid.builder_index, bid.value) @@ -174,16 +164,18 @@ def validate_bid( return verify_execution_payload_bid_signature(state, signed_bid) ``` +`max_trusted_bid` is the value the validator sent in the +`X-Eth-Max-Trusted-Bid` header of the corresponding +[`getExecutionPayloadBid`][get-execution-payload-bid-api] request. Validators +MUST validate each bid against the `max_trusted_bid` they sent for that +request. + Note that, the fee recipient specified in `bid.fee_recipient` does not necessarily correspond to the fee recipient of the execution payload. Even if a builder pays the validator via execution layer payments, we require that the bid's fee recipient matches the validators expected fee recipient and not the builder's fee recipient. -To express per-builder preferences we need validators to remember which builder -preferences they have sent to each builder, so that they can validate whether -the bid conforms to the preferences expressed by the validators. - ## Block proposal ### Constructing the `BeaconBlockBody` @@ -196,8 +188,9 @@ block on top of a beacon `state` must take the following actions: 1. Call upstream builder software to get a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] using the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API call. The - validator MAY send the `SignedRequestAuth` in the request body in - order to authenticate the request to the builder. + validator MUST include the `X-Eth-Request-Auth` and + `X-Eth-Max-Trusted-Bid` headers on the request; otherwise the builder will + not serve a bid. 2. Assemble a `SignedBeaconBlock` according to the process outlined in the [Gloas validator specs][gloas-validator-specs] but with the best [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the prior @@ -215,7 +208,6 @@ When the circuit breaker condition is triggered for nodes, they *MUST* fallback to receiving bids from the P2P [`execution_payload_bid`][execution-payload-bid] topic and can also build blocks locally. -[builder-preferences]: ./builder.md#builderpreferences [can-builder-cover-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#can_builder_cover_bid [execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md?plain=1#L321 [get-execution-payload-bid-api]: ./../../apis/builder/execution_payload_bid.yaml @@ -226,6 +218,5 @@ topic and can also build blocks locally. [proposer-preferences-topic]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md [signed-execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadbid [signed-execution-payload-envelope]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#signedexecutionpayloadenvelope -[submit-builder-preferences-api]: ./../../apis/builder/preferences.yaml [submit-signed-beacon-block]: ./../../apis/builder/beacon_block.yaml [verify-execution-payload-bid-signature]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#verify_execution_payload_bid_signature diff --git a/types/gloas/preferences.yaml b/types/gloas/preferences.yaml deleted file mode 100644 index e69e0eec..00000000 --- a/types/gloas/preferences.yaml +++ /dev/null @@ -1,27 +0,0 @@ -Gloas: - BuilderPreferences: - type: object - description: "Per-builder preferences that a validator can express to a specific builder." - required: [builder_pubkey, validator_pubkey, slot, max_trusted_bid] - properties: - builder_pubkey: - $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" - description: "The BLS public key of the builder that these preferences are intended for." - validator_pubkey: - $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" - description: "The BLS public key of the validator sending the preferences." - slot: - $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" - description: "The proposal slot of the validator." - max_trusted_bid: - $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" - description: "Indicates the maximum amount that a proposer is willing to accept as a trusted payment amount from the builder." - SignedBuilderPreferences: - type: object - description: "The `SignedBuilderPreferences` object for Gloas fork." - required: [message, signature] - properties: - message: - $ref: "#/Gloas/BuilderPreferences" - signature: - $ref: "../../beacon-apis/types/primitive.yaml#/Signature" From d125dea1e67453903dd27fcc41062dc38cc08d2b Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 8 May 2026 12:02:02 +0530 Subject: [PATCH 53/59] updates --- apis/builder/execution_payload_bid.yaml | 53 +++++++++++----- specs/gloas/builder.md | 67 +++++++++++--------- specs/gloas/validator.md | 81 ++++++++++++------------- 3 files changed, 115 insertions(+), 86 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 14f359b8..aeacdb13 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -10,13 +10,24 @@ post: - The hash of the execution layer block the proposer will build on. - The root of the beacon block the proposer will build on. - The index of the proposer. - - The `X-Eth-Request-Auth` header containing a JSON-encoded `SignedRequestAuth` that authenticates the request. - - The `X-Eth-Max-Trusted-Bid` header carrying the proposer's `max_trusted_bid` (decimal `uint64`, in Gwei) for this request. + - The `X-Eth-Max-Trusted-Bid` header carrying the proposer's + `max_trusted_bid` (decimal `uint64`, in Gwei) for this request. + - Optionally, a `SignedRequestAuth` in the request body that + authenticates the request. The body MAY be encoded as JSON + (`Content-Type: application/json`) or SSZ + (`Content-Type: application/octet-stream`). - Both headers are required. If either is missing, malformed, or fails - validation, the builder MUST NOT serve a bid for the proposer and MUST + The `X-Eth-Max-Trusted-Bid` header is required. If it is missing or + malformed, the builder MUST NOT serve a bid for the proposer and MUST return a 400 response. + The `SignedRequestAuth` body is optional. If it is present but malformed + or fails signature verification, the builder MUST return a 400 response. + If it is absent, the builder MAY still serve a bid, but builders SHOULD + use the presence and validity of the `SignedRequestAuth` to apply + per-validator policy (e.g. rate-limiting, prioritisation, or refusing + unauthenticated requests). + The builder responds with a 200 response containing an execution payload bid if it can provide one. If the builder is unable to produce a valid execution payload bid, then @@ -75,17 +86,14 @@ post: type: integer format: int64 example: 10000 - - name: X-Eth-Request-Auth + - name: Eth-Consensus-Version in: header - required: true + required: false description: | - JSON-encoded `SignedRequestAuth` authenticating the request. The - builder MUST verify the BLS signature against `validator_pubkey` and - check that `builder_pubkey` matches its own identity and that `slot` - matches the requested slot. If the header is missing or invalid, the - builder MUST return a 400 response. + The active consensus version to which the `SignedRequestAuth` in the + request body belongs. Required if the request body is SSZ encoded. schema: - $ref: "../../types/gloas/request_auth.yaml#/Gloas/SignedRequestAuth" + $ref: "../../builder-oapi.yaml#/components/schemas/ConsensusVersion" - name: X-Eth-Max-Trusted-Bid in: header required: true @@ -101,6 +109,21 @@ post: type: integer format: uint64 example: 1000000000 + requestBody: + description: | + Optional `SignedRequestAuth` authenticating the request. If provided, + the builder MUST verify the BLS signature against `validator_pubkey` + and check that `builder_pubkey` matches its own identity and that + `slot` matches the requested slot. If absent, the builder MAY still + serve a bid subject to its own policy. + required: false + content: + application/json: + schema: + $ref: "../../types/gloas/request_auth.yaml#/Gloas/SignedRequestAuth" + application/octet-stream: + schema: + description: "SSZ serialized `SignedRequestAuth` bytes. Use Content-Type header to indicate that SSZ data is contained in the request body. The `Eth-Consensus-Version` header MUST also be set." responses: "200": description: Success response. @@ -137,10 +160,6 @@ post: value: code: 400 message: "Unknown hash: missing parent hash" - MissingRequestAuth: - value: - code: 400 - message: "Missing X-Eth-Request-Auth header" InvalidAuth: value: code: 400 @@ -151,5 +170,7 @@ post: message: "Missing X-Eth-Max-Trusted-Bid header" "406": $ref: "../../builder-oapi.yaml#/components/responses/NotAcceptable" + "415": + $ref: "../../builder-oapi.yaml#/components/responses/UnsupportedMediaType" "500": $ref: "../../builder-oapi.yaml#/components/responses/InternalError" diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 4f9a8ea9..2d9887ea 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -18,9 +18,9 @@ ## Introduction -This document documents the builder behaviour with the Builder-API post ePBS. -It describes how builders consume per-request inputs from validators and -construct [`SignedExecutionPayloadBid`][signed-execution-payload-bid] and +This document documents the builder behaviour with the Builder-API post ePBS. It +describes how builders consume per-request inputs from validators and construct +[`SignedExecutionPayloadBid`][signed-execution-payload-bid] and [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] objects. ## Constants @@ -62,9 +62,13 @@ def is_eligible_for_bid( # Verify that proposer preferences have been received via the gossip topic assert validator_index in proposer_preferences.keys() - # Verify parent hash + # Verify parent hash. The proposer could build on the FULL parent block or on the EMPTY parent block based on + # their view of the chain. # [Modified in Gloas:EIP7732] - assert parent_hash == state.latest_block_hash + assert ( + parent_hash == state.latest_execution_payload_bid.block_hash + or parent_hash == state.latest_block_hash + ) # Verify parent root # [Modified in Gloas:EIP7732] @@ -73,33 +77,43 @@ def is_eligible_for_bid( ## Per-request Validator Inputs -Validators communicate per-request inputs to a builder via HTTP headers on -each [`getExecutionPayloadBid`][get-execution-payload-bid-api] call: +Validators communicate per-request inputs to a builder on each +[`getExecutionPayloadBid`][get-execution-payload-bid-api] call: -- `X-Eth-Request-Auth`: a JSON-encoded - [`SignedRequestAuth`][signed-request-auth] used to authenticate the - requesting validator. Builders MUST verify the signature against - `validator_pubkey` and check that `builder_pubkey` matches their own - identity and that `slot` matches the requested slot. -- `X-Eth-Max-Trusted-Bid`: a decimal `uint64` (in Gwei) carrying the - proposer's `max_trusted_bid` for this request. +- The `X-Eth-Max-Trusted-Bid` header carrying a decimal `uint64` (in Gwei) with + the proposer's `max_trusted_bid` for this request. This header is + **required**. +- Optionally, a [`SignedRequestAuth`][signed-request-auth] in the request body + used to authenticate the requesting validator. The body MAY be encoded as JSON + (`Content-Type: application/json`) or SSZ + (`Content-Type: application/octet-stream`); when SSZ is used, the + `Eth-Consensus-Version` header MUST also be set. -If either header is missing, malformed, or fails validation, the builder MUST +If the `X-Eth-Max-Trusted-Bid` header is missing or malformed, the builder MUST NOT serve a bid for the proposer (return a 400 response). +If the request body is present, builders MUST verify the `SignedRequestAuth` +signature against `validator_pubkey` and check that `builder_pubkey` matches +their own identity and that `slot` matches the requested slot. If verification +fails, the builder MUST return a 400 response. + +If the request body is absent, the builder MAY still serve a bid, but SHOULD use +the presence and validity of the `SignedRequestAuth` to inform per-validator +policy (e.g. rate-limiting, prioritisation, or refusing unauthenticated +requests). + ### `max_trusted_bid` -`max_trusted_bid` is the maximum value (in Gwei) that a proposer is willing -to accept as a trusted execution layer payment from this builder for this -request. A value of `0` indicates that the proposer does not accept any -trusted payments from the builder, requiring all payments to use the on-chain -trustless payments mechanism. A value of `MAX_TRUSTED_BID` indicates that the -proposer will accept any trusted payment amount from the builder. Proposers -may adjust this parameter based on their level of trust in the builder's -reliability and reputation. +`max_trusted_bid` is the maximum value (in Gwei) that a proposer is willing to +accept as a trusted execution layer payment from this builder for this request. +A value of `0` indicates that the proposer does not accept any trusted payments +from the builder, requiring all payments to use the on-chain trustless payments +mechanism. A value of `MAX_TRUSTED_BID` indicates that the proposer will accept +any trusted payment amount from the builder. Proposers may adjust this parameter +based on their level of trust in the builder's reliability and reputation. -`max_trusted_bid` is sent in the clear in the `X-Eth-Max-Trusted-Bid` header -and is **not** covered by the `RequestAuth` signature. +`max_trusted_bid` is sent in the clear in the `X-Eth-Max-Trusted-Bid` header and +is **not** covered by the `RequestAuth` signature. ## Proposer Preferences (Deprecation of Validator Registrations) @@ -127,8 +141,7 @@ MUST set `bid.value` to the amount they are committing to pay. If the builder intends to pay the proposer via an execution layer payment, they MUST set `bid.execution_payment`. This value MUST NOT exceed the `max_trusted_bid` received in the `X-Eth-Max-Trusted-Bid` header of the -corresponding [`getExecutionPayloadBid`][get-execution-payload-bid-api] -request. +corresponding [`getExecutionPayloadBid`][get-execution-payload-bid-api] request. ## Constructing a `SignedExecutionPayloadEnvelope` diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index e35f7a30..60a629eb 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -16,7 +16,6 @@ - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [Receiving ExecutionPayloadBid](#receiving-executionpayloadbid) - - [Liveness failsafe](#liveness-failsafe) @@ -60,47 +59,47 @@ class SignedRequestAuth(Container): ## Bid Request When calling [`getExecutionPayloadBid`][get-execution-payload-bid-api], the -validator MUST send two HTTP headers: - -- `X-Eth-Request-Auth`: a JSON-encoded - [`SignedRequestAuth`](#signedrequestauth) that authenticates the request. -- `X-Eth-Max-Trusted-Bid`: a decimal `uint64` (in Gwei) expressing the - per-builder `max_trusted_bid` for this request. See - [`max_trusted_bid`](#max_trusted_bid). - -If either header is missing, the builder will not serve a bid for the -proposer. +validator MUST send the `X-Eth-Max-Trusted-Bid` header carrying a decimal +`uint64` (in Gwei) expressing the per-builder `max_trusted_bid` for this +request. See [`max_trusted_bid`](#max_trusted_bid). If the header is missing, +the builder will not serve a bid for the proposer. + +The validator MAY additionally send a [`SignedRequestAuth`](#signedrequestauth) +as the request body to authenticate the request. The body MAY be encoded as JSON +(`Content-Type: application/json`) or SSZ +(`Content-Type: application/octet-stream`); when SSZ is used, the validator MUST +also send the `Eth-Consensus-Version` header. If the body is omitted, the +builder MAY still serve a bid. ### Constructing the `RequestAuth` -To construct the `RequestAuth`, we need to fill the following information: +If the validator chooses to authenticate its request, it constructs a +`RequestAuth` with the following fields: - `builder_pubkey`: The BLS public key of the builder the request is intended for. - `validator_pubkey`: The BLS public key of the validator making the request. - `slot`: The slot for which the bid is being requested. -The validator constructs the `SignedRequestAuth` by signing the `RequestAuth`, -JSON-encodes it, and sends it in the `X-Eth-Request-Auth` header of the -[`getExecutionPayloadBid`][get-execution-payload-bid-api] request. The -signature lets builders authenticate the requesting validator and discard -requests from other parties (e.g. DDOS or replay attempts from competing -builders). +The validator then constructs the `SignedRequestAuth` by signing the +`RequestAuth`, and sends it in the body of the +[`getExecutionPayloadBid`][get-execution-payload-bid-api] request. The signature +lets builders authenticate the requesting validator and discard requests from +other parties (e.g. DDOS or replay attempts from competing builders). ### `max_trusted_bid` -`max_trusted_bid` is the maximum value (in Gwei) that the proposer is willing -to accept as a trusted execution layer payment from this builder for this -request. A value of `0` means the proposer does not accept any trusted -payments from this builder, requiring all payments to go through the on-chain -trustless payments mechanism. A value of `MAX_TRUSTED_BID` means the proposer -will accept any trusted payment amount from the builder. Proposers may adjust -this parameter based on their level of trust in the builder's reliability and -reputation. +`max_trusted_bid` is the maximum value (in Gwei) that the proposer is willing to +accept as a trusted execution layer payment from this builder for this request. +A value of `0` means the proposer does not accept any trusted payments from this +builder, requiring all payments to go through the on-chain trustless payments +mechanism. A value of `MAX_TRUSTED_BID` means the proposer will accept any +trusted payment amount from the builder. Proposers may adjust this parameter +based on their level of trust in the builder's reliability and reputation. The validator sends `max_trusted_bid` as a decimal `uint64` in the -`X-Eth-Max-Trusted-Bid` header. Note that `max_trusted_bid` is **not** -covered by the `RequestAuth` signature. The validator MUST remember the +`X-Eth-Max-Trusted-Bid` header. Note that `max_trusted_bid` is **not** covered +by the `RequestAuth` signature. The validator MUST remember the `max_trusted_bid` value it sent for each request so it can validate the resulting bid against the same value. @@ -151,7 +150,11 @@ def validate_bid( assert is_active_builder(state, bid.builder_index) assert bid.slot == state.slot assert bid.fee_recipient == fee_recipient - assert bid.parent_block_hash == state.latest_block_hash + # Bid can choose to extend on FULL or EMPTY. + assert ( + bid.parent_block_hash == state.latest_execution_payload_bid.block_hash + or bid.parent_block_hash == state.latest_block_hash + ) assert bid.parent_block_root == hash_tree_root(state.latest_block_header) assert bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) assert bid.gas_limit <= proposer_preferences.gas_limit @@ -164,11 +167,10 @@ def validate_bid( return verify_execution_payload_bid_signature(state, signed_bid) ``` -`max_trusted_bid` is the value the validator sent in the -`X-Eth-Max-Trusted-Bid` header of the corresponding +`max_trusted_bid` is the value the validator sent in the `X-Eth-Max-Trusted-Bid` +header of the corresponding [`getExecutionPayloadBid`][get-execution-payload-bid-api] request. Validators -MUST validate each bid against the `max_trusted_bid` they sent for that -request. +MUST validate each bid against the `max_trusted_bid` they sent for that request. Note that, the fee recipient specified in `bid.fee_recipient` does not necessarily correspond to the fee recipient of the execution payload. Even if a @@ -188,9 +190,9 @@ block on top of a beacon `state` must take the following actions: 1. Call upstream builder software to get a [`SignedExecutionPayloadBid`][signed-execution-payload-bid] using the [`getExecutionPayloadBid`][get-execution-payload-bid-api] API call. The - validator MUST include the `X-Eth-Request-Auth` and - `X-Eth-Max-Trusted-Bid` headers on the request; otherwise the builder will - not serve a bid. + validator MUST include the `X-Eth-Max-Trusted-Bid` header on the request; + otherwise the builder will not serve a bid. The validator MAY additionally + send a `SignedRequestAuth` in the request body to authenticate the request. 2. Assemble a `SignedBeaconBlock` according to the process outlined in the [Gloas validator specs][gloas-validator-specs] but with the best [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the prior @@ -202,14 +204,7 @@ block on top of a beacon `state` must take the following actions: [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] and broadcasts it to the PTC committee. -## Liveness failsafe - -When the circuit breaker condition is triggered for nodes, they *MUST* fallback -to receiving bids from the P2P [`execution_payload_bid`][execution-payload-bid] -topic and can also build blocks locally. - [can-builder-cover-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#can_builder_cover_bid -[execution-payload-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md?plain=1#L321 [get-execution-payload-bid-api]: ./../../apis/builder/execution_payload_bid.yaml [gloas-consensus-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas [gloas-validator-specs]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/validator.md#block-proposal From 5a618ae42403f30baf92efa1ab67e91b4702917d Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 8 May 2026 12:58:06 +0530 Subject: [PATCH 54/59] minor updates --- apis/builder/execution_payload_bid.yaml | 2 +- specs/gloas/builder.md | 7 ++----- specs/gloas/validator.md | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index aeacdb13..0a47872a 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -25,7 +25,7 @@ post: or fails signature verification, the builder MUST return a 400 response. If it is absent, the builder MAY still serve a bid, but builders SHOULD use the presence and validity of the `SignedRequestAuth` to apply - per-validator policy (e.g. rate-limiting, prioritisation, or refusing + per-validator policy (e.g. rate-limiting, prioritization, or refusing unauthenticated requests). The builder responds with a 200 response containing an execution payload bid if it can provide one. diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 2d9887ea..00ffc2b9 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -97,10 +97,7 @@ signature against `validator_pubkey` and check that `builder_pubkey` matches their own identity and that `slot` matches the requested slot. If verification fails, the builder MUST return a 400 response. -If the request body is absent, the builder MAY still serve a bid, but SHOULD use -the presence and validity of the `SignedRequestAuth` to inform per-validator -policy (e.g. rate-limiting, prioritisation, or refusing unauthenticated -requests). +If the request body is absent, the builder MAY still serve a bid. ### `max_trusted_bid` @@ -117,7 +114,7 @@ is **not** covered by the `RequestAuth` signature. ## Proposer Preferences (Deprecation of Validator Registrations) -*Note*: `ValidatorRegistrationV1` is **deprecated** in favour of +*Note*: `ValidatorRegistrationV1` is **deprecated** in favor of [`ProposerPreferences`][proposer-preferences] from the consensus specs. Builders SHOULD subscribe to the diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 60a629eb..87dfdc61 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -106,7 +106,7 @@ resulting bid against the same value. ## Proposer Preferences *Note*: Validator registrations (`ValidatorRegistrationV1`) are **deprecated** -in favour of [`ProposerPreferences`][proposer-preferences] from the consensus +in favor of [`ProposerPreferences`][proposer-preferences] from the consensus specs. General validator preferences are now communicated via the From 6429ef9f4b44dc32d2adaa1af6e987da514b1874 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 8 May 2026 19:42:19 +0530 Subject: [PATCH 55/59] address feedback --- apis/builder/execution_payload_bid.yaml | 13 +++++++------ specs/gloas/builder.md | 15 +++++++++++---- specs/gloas/validator.md | 12 ++++++++++-- types/gloas/request_auth.yaml | 5 +---- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 0a47872a..bb89fd7d 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -22,8 +22,8 @@ post: return a 400 response. The `SignedRequestAuth` body is optional. If it is present but malformed - or fails signature verification, the builder MUST return a 400 response. - If it is absent, the builder MAY still serve a bid, but builders SHOULD + or fails signature verification, the builder MAY return a 400 response. + If it is absent, the builder MAY still serve a bid, but builders MAY use the presence and validity of the `SignedRequestAuth` to apply per-validator policy (e.g. rate-limiting, prioritization, or refusing unauthenticated requests). @@ -112,10 +112,11 @@ post: requestBody: description: | Optional `SignedRequestAuth` authenticating the request. If provided, - the builder MUST verify the BLS signature against `validator_pubkey` - and check that `builder_pubkey` matches its own identity and that - `slot` matches the requested slot. If absent, the builder MAY still - serve a bid subject to its own policy. + the builder MAY verify the BLS signature against the validator pubkey + resolved from the `proposer_index` path parameter, and check that + `builder_pubkey` matches its own identity and that `slot` matches the + requested slot. If absent, the builder MAY still serve a bid subject + to its own policy. required: false content: application/json: diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 00ffc2b9..7aedf458 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -92,10 +92,11 @@ Validators communicate per-request inputs to a builder on each If the `X-Eth-Max-Trusted-Bid` header is missing or malformed, the builder MUST NOT serve a bid for the proposer (return a 400 response). -If the request body is present, builders MUST verify the `SignedRequestAuth` -signature against `validator_pubkey` and check that `builder_pubkey` matches -their own identity and that `slot` matches the requested slot. If verification -fails, the builder MUST return a 400 response. +If the request body is present, builders MAY verify the `SignedRequestAuth` +signature against the validator pubkey resolved from the `proposer_index` path +parameter, and check that `builder_pubkey` matches their own identity and that +`slot` matches the requested slot. If verification fails, the builder MAY return +a 400 response. If the request body is absent, the builder MAY still serve a bid. @@ -140,6 +141,12 @@ MUST set `bid.execution_payment`. This value MUST NOT exceed the `max_trusted_bid` received in the `X-Eth-Max-Trusted-Bid` header of the corresponding [`getExecutionPayloadBid`][get-execution-payload-bid-api] request. +*Note*: `bid.value` and `bid.execution_payment` are not mutually exclusive. +A builder MAY set both fields on a single bid; in that case the builder is +committing to pay the proposer the sum of the two. `bid.value` is deducted +from the builder's staked collateral on-chain even when +`bid.execution_payment` is also set. + ## Constructing a `SignedExecutionPayloadEnvelope` If the builder's [`SignedExecutionPayloadBid`][signed-execution-payload-bid] has diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 87dfdc61..75267c5d 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -44,7 +44,6 @@ that other builders do not DDOS or run replay attacks on the builder. ```python class RequestAuth(Container): builder_pubkey: BLSPubkey - validator_pubkey: BLSPubkey slot: Slot ``` @@ -78,15 +77,24 @@ If the validator chooses to authenticate its request, it constructs a - `builder_pubkey`: The BLS public key of the builder the request is intended for. -- `validator_pubkey`: The BLS public key of the validator making the request. - `slot`: The slot for which the bid is being requested. +The builder resolves the validator's public key from the `proposer_index` path +parameter of the [`getExecutionPayloadBid`][get-execution-payload-bid-api] +request, so it does not need to be carried inside `RequestAuth`. + The validator then constructs the `SignedRequestAuth` by signing the `RequestAuth`, and sends it in the body of the [`getExecutionPayloadBid`][get-execution-payload-bid-api] request. The signature lets builders authenticate the requesting validator and discard requests from other parties (e.g. DDOS or replay attempts from competing builders). +*Note*: validators MAY also set `builder_pubkey` to the zero pubkey +(`0x00…00`) to produce a `SignedRequestAuth` that authenticates the +proposer without binding the signature to any specific builder. This trades +cross-builder replay-protection for the ability to broadcast the same +signed object to multiple builders. + ### `max_trusted_bid` `max_trusted_bid` is the maximum value (in Gwei) that the proposer is willing to diff --git a/types/gloas/request_auth.yaml b/types/gloas/request_auth.yaml index 4919c0e1..b8742949 100644 --- a/types/gloas/request_auth.yaml +++ b/types/gloas/request_auth.yaml @@ -1,14 +1,11 @@ Gloas: RequestAuth: type: object - required: [builder_pubkey, validator_pubkey, slot] + required: [builder_pubkey, slot] properties: builder_pubkey: $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" description: "The BLS public key of the builder the request is intended for." - validator_pubkey: - $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" - description: "The BLS public key of the validator making the request." slot: $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" description: "The slot for which the bid is being requested." From 9d4a18d4f00599519e5ea5294a79dc5a6c2cf0a1 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 11 May 2026 11:26:16 +0530 Subject: [PATCH 56/59] minor updates --- specs/gloas/validator.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 75267c5d..cca2715e 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -89,12 +89,6 @@ The validator then constructs the `SignedRequestAuth` by signing the lets builders authenticate the requesting validator and discard requests from other parties (e.g. DDOS or replay attempts from competing builders). -*Note*: validators MAY also set `builder_pubkey` to the zero pubkey -(`0x00…00`) to produce a `SignedRequestAuth` that authenticates the -proposer without binding the signature to any specific builder. This trades -cross-builder replay-protection for the ability to broadcast the same -signed object to multiple builders. - ### `max_trusted_bid` `max_trusted_bid` is the maximum value (in Gwei) that the proposer is willing to @@ -126,11 +120,6 @@ messages for their proposal slots in the next epoch. Builders SHOULD subscribe to this gossip topic to learn about proposer preferences for upcoming slots. -Per-builder preferences (currently just `max_trusted_bid`) are not gossiped; -they are sent directly to the builder as a header on each -[`getExecutionPayloadBid`][get-execution-payload-bid-api] request. See -[`max_trusted_bid`](#max_trusted_bid). - ## Validating a `SignedExecutionPayloadBid` When the proposer receives a From fbc474780e4044296076a3ffa3ab0ca1b87e2cec Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 May 2026 16:06:00 +0530 Subject: [PATCH 57/59] add an error type for missing auth request --- apis/builder/execution_payload_bid.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index bb89fd7d..2887e8b0 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -169,6 +169,17 @@ post: value: code: 400 message: "Missing X-Eth-Max-Trusted-Bid header" + "401": + description: Authentication required. + content: + application/json: + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" + examples: + MissingAuth: + value: + code: 401 + message: "Missing SignedRequestAuth: this builder requires authenticated requests" "406": $ref: "../../builder-oapi.yaml#/components/responses/NotAcceptable" "415": From efaf888d4b23e129d3f88a52b201216e43fb962f Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 18 May 2026 23:01:51 +0530 Subject: [PATCH 58/59] add bid scoring --- specs/gloas/bid_scoring.md | 117 +++++++++++++++++++++++++++++++++++++ specs/gloas/validator.md | 79 +++++++------------------ 2 files changed, 139 insertions(+), 57 deletions(-) create mode 100644 specs/gloas/bid_scoring.md diff --git a/specs/gloas/bid_scoring.md b/specs/gloas/bid_scoring.md new file mode 100644 index 00000000..4d055342 --- /dev/null +++ b/specs/gloas/bid_scoring.md @@ -0,0 +1,117 @@ +# Gloas - Bid Scoring + +## Overview + +This document specifies how a validator selects the best execution payload bid +from among bids received over the p2p network, bids received via the offchain +builder API, and a locally built block. + +Bids from the two sources are scored differently because offchain bids carry an +`execution_payment` component that is conditionally trusted up to the +`max_trusted_bid` the validator expressed for that request. + +`min_bid` from `GlobalPreferences` applies equally to both p2p and offchain +bids. There is no separate threshold per source. + +## Scoring + +### P2P Bids + +P2P bids are scored solely by `bid.value`, the on-chain collateral commitment. +`bid.execution_payment` is ignored for p2p bids because there is no per-request +`max_trusted_bid` negotiation over gossip. + +A p2p bid is eligible only if `bid.value > min_bid`. + +```python +def select_best_p2p_bid( + bids: List[SignedExecutionPayloadBid], + min_bid: uint64, +) -> Optional[SignedExecutionPayloadBid]: + eligible = [b for b in bids if b.message.value > min_bid] + if not eligible: + return None + return max(eligible, key=lambda b: b.message.value) +``` + +### Offchain (Builder API) Bids + +For bids received via the offchain builder API, the total bid score accounts for +both the on-chain collateral commitment and the trusted execution layer payment, +capped at the `max_trusted_bid` the validator advertised for that request: + +``` +bid_score = bid.value + min(bid.execution_payment, max_trusted_bid) +``` + +A bid is eligible only if `bid_score > min_bid`, the same threshold applied to +p2p bids. + +```python +def score_offchain_bid( + bid: ExecutionPayloadBid, + max_trusted_bid: uint64, +) -> uint64: + return bid.value + min(bid.execution_payment, max_trusted_bid) + +def select_best_offchain_bid( + bids: List[Tuple[SignedExecutionPayloadBid, uint64]], + min_bid: uint64, +) -> Optional[SignedExecutionPayloadBid]: + """ + `bids` is a list of (signed_bid, max_trusted_bid) pairs, where + max_trusted_bid is the value sent in the X-Eth-Max-Trusted-Bid header of + the corresponding getExecutionPayloadBid request. + """ + eligible = [ + (b, score_offchain_bid(b.message, max_trusted_bid)) + for b, max_trusted_bid in bids + if score_offchain_bid(b.message, max_trusted_bid) > min_bid + ] + if not eligible: + return None + return max(eligible, key=lambda pair: pair[1])[0] +``` + +### Selecting the Best Bid + +Once the best p2p bid and best offchain bid are identified, the validator +compares them against the locally built block using `local_block_boost`. + +`local_block_boost` is a multiplier (in basis points, where `10000 = 100%`) +applied to the local block value before comparison. A value of `10000` means no +boost; a value of `11000` means the local block must be beaten by at least 10% +for an external bid to be preferred. + +```python +def select_best_bid( + local_block_value: uint64, + best_p2p_bid: Optional[SignedExecutionPayloadBid], + best_offchain_bid: Optional[SignedExecutionPayloadBid], + max_trusted_bid_for_offchain: uint64, + global_preferences: GlobalPreferences, +) -> Optional[SignedExecutionPayloadBid]: + boosted_local = local_block_value * global_preferences.local_block_boost // 10000 + + best_external: Optional[SignedExecutionPayloadBid] = None + best_external_score: uint64 = 0 + + if best_p2p_bid is not None: + p2p_score = best_p2p_bid.message.value + if p2p_score > best_external_score: + best_external = best_p2p_bid + best_external_score = p2p_score + + if best_offchain_bid is not None: + offchain_score = score_offchain_bid(best_offchain_bid.message, max_trusted_bid_for_offchain) + if offchain_score > best_external_score: + best_external = best_offchain_bid + best_external_score = offchain_score + + if best_external is not None and best_external_score > boosted_local: + return best_external + + return None # fall back to local block +``` + +Returning `None` indicates the validator should propose its locally built block. diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 39926386..ba8d61f1 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -8,14 +8,7 @@ - [New Containers](#new-containers) - [`RequestAuth`](#requestauth) - [`SignedRequestAuth`](#signedrequestauth) -<<<<<<< HEAD - - [`BuilderConfig`](#builderconfig) - - [`GlobalPreferences`](#globalpreferences) - - [`BuilderWhitelist`](#builderwhitelist) - - [Bid Authentication](#bid-authentication) -======= - [Bid Request](#bid-request) ->>>>>>> fbc474780e4044296076a3ffa3ab0ca1b87e2cec - [Constructing the `RequestAuth`](#constructing-the-requestauth) - [`max_trusted_bid`](#max_trusted_bid) - [Proposer Preferences](#proposer-preferences) @@ -23,12 +16,6 @@ - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [Receiving ExecutionPayloadBid](#receiving-executionpayloadbid) -<<<<<<< HEAD - - [Liveness failsafe](#liveness-failsafe) - - [Connecting with upstream block building](#connecting-with-upstream-block-building) - - [Builder Config](#builder-config) -======= ->>>>>>> fbc474780e4044296076a3ffa3ab0ca1b87e2cec @@ -70,13 +57,29 @@ class SignedRequestAuth(Container): ### `BuilderConfig` +`BuilderConfig` specifies the connection details and per-builder preferences for +a single whitelisted builder. This includes: + +- `url`: The URL of the builder where execution payload bids can be fetched. +- `builder_pubkey`: The advertised BLS public key of the builder. Configured + alongside the URL, it forms the builder's off-chain identity and is used to + bind request authentication to a specific builder, preventing cross-builder + replay attacks. +- `max_trusted_bid`: The maximum value (in Gwei) the proposer is willing to + accept as a trusted execution layer payment from this builder. A value of `0` + means the proposer does not accept any trusted payments from this builder, + requiring all payments to use the on-chain trustless mechanism. A value of + `MAX_TRUSTED_BID` means the proposer accepts any trusted payment amount. + Proposers should adjust this based on their level of trust in the builder. +- `bid_boost`: A multiplier factor (in basis points, where 10000 = 100%) applied + to this builder's bid score when comparing against bids from other builders. + ```python class BuilderConfig(Container): url: ByteList[MAX_URL_BYTES] builder_pubkey: BLSPubkey max_trusted_bid: uint64 bid_boost: uint64 - excluded_validators: List[BLSPubkey, MAX_EXCLUDED_VALIDATORS] ``` ### `GlobalPreferences` @@ -110,9 +113,9 @@ class BuilderWhitelist(Container): When calling [`getExecutionPayloadBid`][get-execution-payload-bid-api], the validator MUST send the `X-Eth-Max-Trusted-Bid` header carrying a decimal -`uint64` (in Gwei) expressing the per-builder `max_trusted_bid` for this -request. See [`max_trusted_bid`](#max_trusted_bid). If the header is missing, -the builder will not serve a bid for the proposer. +`uint64` (in Gwei) expressing the per-builder `max_trusted_bid` configured in +the [`BuilderConfig`](#builderconfig) for this builder. If the header is +missing, the builder will not serve a bid for the proposer. The validator MAY additionally send a [`SignedRequestAuth`](#signedrequestauth) as the request body to authenticate the request. The body MAY be encoded as JSON @@ -140,22 +143,6 @@ The validator then constructs the `SignedRequestAuth` by signing the lets builders authenticate the requesting validator and discard requests from other parties (e.g. DDOS or replay attempts from competing builders). -### `max_trusted_bid` - -`max_trusted_bid` is the maximum value (in Gwei) that the proposer is willing to -accept as a trusted execution layer payment from this builder for this request. -A value of `0` means the proposer does not accept any trusted payments from this -builder, requiring all payments to go through the on-chain trustless payments -mechanism. A value of `MAX_TRUSTED_BID` means the proposer will accept any -trusted payment amount from the builder. Proposers may adjust this parameter -based on their level of trust in the builder's reliability and reputation. - -The validator sends `max_trusted_bid` as a decimal `uint64` in the -`X-Eth-Max-Trusted-Bid` header. Note that `max_trusted_bid` is **not** covered -by the `RequestAuth` signature. The validator MUST remember the -`max_trusted_bid` value it sent for each request so it can validate the -resulting bid against the same value. - ## Proposer Preferences *Note*: Validator registrations (`ValidatorRegistrationV1`) are **deprecated** @@ -265,30 +252,8 @@ parsed. The `BuilderWhitelist` includes the per-builder configs along with the global preferences. -The following are the fields in the `BuilderConfig`: - -- `url`: The URL of the whitelisted builder where we can fetch bids from. -- `builder_pubkey`: The advertised public key of the builder. This is configured - alongside the URL and forms the builder's off-chain identity. It is used to - bind registrations and request auth to a specific builder, preventing - cross-builder replay attacks. -- `max_trusted_bid`: The maximum amount (in Gwei) which the proposer will accept - as a trusted execution layer payment from the builder. This will be sent in - the builder preferences to the corresponding builder. -- `bid_boost`: A multiplier factor (in basis points, where 10000 = 100%) applied - to the builder's bid value when comparing against other builder bids. -- `excluded_validators`: A list of validator public keys that should NOT - interact with this builder when proposing. By default all validators use all - whitelisted builders; this field allows operators to exclude specific - validators from specific builders. - -The `GlobalPreferences` contains cross-builder parameters: - -- `min_bid`: The minimum bid value (in Gwei) from any builder for the proposer - to consider. Below this threshold, the proposer falls back to the local block. -- `local_block_boost`: A multiplier factor (in basis points, where 10000 = 100%) - applied to the locally built block value when comparing against bids from - builders. +See [`BuilderConfig`](#builderconfig) and [`GlobalPreferences`](#globalpreferences) +for field descriptions. Aspects such as deadline enforcement and bid selection strategy are left up to the client implementation. From 1e8076d993f0661ecd6327db96fb3a9ccf46ae51 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 1 Jun 2026 10:36:38 +0530 Subject: [PATCH 59/59] some updates --- apis/builder/execution_payload_bid.yaml | 4 ++-- specs/gloas/bid_scoring.md | 26 ++++++++++++------------- specs/gloas/builder.md | 12 ++++++------ specs/gloas/validator.md | 20 ++++++++----------- 4 files changed, 29 insertions(+), 33 deletions(-) diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 2887e8b0..40261319 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -11,7 +11,7 @@ post: - The root of the beacon block the proposer will build on. - The index of the proposer. - The `X-Eth-Max-Trusted-Bid` header carrying the proposer's - `max_trusted_bid` (decimal `uint64`, in Gwei) for this request. + `max_execution_payment` (decimal `uint64`, in Gwei) for this request. - Optionally, a `SignedRequestAuth` in the request body that authenticates the request. The body MAY be encoded as JSON (`Content-Type: application/json`) or SSZ @@ -98,7 +98,7 @@ post: in: header required: true description: | - Decimal `uint64` (in Gwei) carrying the proposer's `max_trusted_bid` + Decimal `uint64` (in Gwei) carrying the proposer's `max_execution_payment` for this request. `bid.execution_payment` MUST NOT exceed this value. A value of `0` indicates that the proposer does not accept any trusted payments from this builder. A value of `2**64 - 1` diff --git a/specs/gloas/bid_scoring.md b/specs/gloas/bid_scoring.md index 4d055342..ba488ae2 100644 --- a/specs/gloas/bid_scoring.md +++ b/specs/gloas/bid_scoring.md @@ -8,7 +8,7 @@ builder API, and a locally built block. Bids from the two sources are scored differently because offchain bids carry an `execution_payment` component that is conditionally trusted up to the -`max_trusted_bid` the validator expressed for that request. +`max_execution_payment` the validator expressed for that request. `min_bid` from `GlobalPreferences` applies equally to both p2p and offchain bids. There is no separate threshold per source. @@ -19,7 +19,7 @@ bids. There is no separate threshold per source. P2P bids are scored solely by `bid.value`, the on-chain collateral commitment. `bid.execution_payment` is ignored for p2p bids because there is no per-request -`max_trusted_bid` negotiation over gossip. +`max_execution_payment` negotiation over gossip. A p2p bid is eligible only if `bid.value > min_bid`. @@ -38,10 +38,10 @@ def select_best_p2p_bid( For bids received via the offchain builder API, the total bid score accounts for both the on-chain collateral commitment and the trusted execution layer payment, -capped at the `max_trusted_bid` the validator advertised for that request: +capped at the `max_execution_payment` the validator advertised for that request: ``` -bid_score = bid.value + min(bid.execution_payment, max_trusted_bid) +bid_score = bid.value + min(bid.execution_payment, max_execution_payment) ``` A bid is eligible only if `bid_score > min_bid`, the same threshold applied to @@ -50,23 +50,23 @@ p2p bids. ```python def score_offchain_bid( bid: ExecutionPayloadBid, - max_trusted_bid: uint64, + max_execution_payment: uint64, ) -> uint64: - return bid.value + min(bid.execution_payment, max_trusted_bid) + return bid.value + min(bid.execution_payment, max_execution_payment) def select_best_offchain_bid( bids: List[Tuple[SignedExecutionPayloadBid, uint64]], min_bid: uint64, ) -> Optional[SignedExecutionPayloadBid]: """ - `bids` is a list of (signed_bid, max_trusted_bid) pairs, where - max_trusted_bid is the value sent in the X-Eth-Max-Trusted-Bid header of + `bids` is a list of (signed_bid, max_execution_payment) pairs, where + max_execution_payment is the value sent in the X-Eth-Max-Trusted-Bid header of the corresponding getExecutionPayloadBid request. """ eligible = [ - (b, score_offchain_bid(b.message, max_trusted_bid)) - for b, max_trusted_bid in bids - if score_offchain_bid(b.message, max_trusted_bid) > min_bid + (b, score_offchain_bid(b.message, max_execution_payment)) + for b, max_execution_payment in bids + if score_offchain_bid(b.message, max_execution_payment) > min_bid ] if not eligible: return None @@ -88,7 +88,7 @@ def select_best_bid( local_block_value: uint64, best_p2p_bid: Optional[SignedExecutionPayloadBid], best_offchain_bid: Optional[SignedExecutionPayloadBid], - max_trusted_bid_for_offchain: uint64, + max_execution_payment_for_offchain: uint64, global_preferences: GlobalPreferences, ) -> Optional[SignedExecutionPayloadBid]: boosted_local = local_block_value * global_preferences.local_block_boost // 10000 @@ -103,7 +103,7 @@ def select_best_bid( best_external_score = p2p_score if best_offchain_bid is not None: - offchain_score = score_offchain_bid(best_offchain_bid.message, max_trusted_bid_for_offchain) + offchain_score = score_offchain_bid(best_offchain_bid.message, max_execution_payment_for_offchain) if offchain_score > best_external_score: best_external = best_offchain_bid best_external_score = offchain_score diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index 7aedf458..6ec95c8a 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -7,7 +7,7 @@ - [Constants](#constants) - [Bidding](#bidding) - [Per-request Validator Inputs](#per-request-validator-inputs) - - [`max_trusted_bid`](#max_trusted_bid) + - [`max_execution_payment`](#max_execution_payment) - [Proposer Preferences (Deprecation of Validator Registrations)](#proposer-preferences-deprecation-of-validator-registrations) - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) - [Constructing a `SignedExecutionPayloadEnvelope`](#constructing-a-signedexecutionpayloadenvelope) @@ -81,7 +81,7 @@ Validators communicate per-request inputs to a builder on each [`getExecutionPayloadBid`][get-execution-payload-bid-api] call: - The `X-Eth-Max-Trusted-Bid` header carrying a decimal `uint64` (in Gwei) with - the proposer's `max_trusted_bid` for this request. This header is + the proposer's `max_execution_payment` for this request. This header is **required**. - Optionally, a [`SignedRequestAuth`][signed-request-auth] in the request body used to authenticate the requesting validator. The body MAY be encoded as JSON @@ -100,9 +100,9 @@ a 400 response. If the request body is absent, the builder MAY still serve a bid. -### `max_trusted_bid` +### `max_execution_payment` -`max_trusted_bid` is the maximum value (in Gwei) that a proposer is willing to +`max_execution_payment` is the maximum value (in Gwei) that a proposer is willing to accept as a trusted execution layer payment from this builder for this request. A value of `0` indicates that the proposer does not accept any trusted payments from the builder, requiring all payments to use the on-chain trustless payments @@ -110,7 +110,7 @@ mechanism. A value of `MAX_TRUSTED_BID` indicates that the proposer will accept any trusted payment amount from the builder. Proposers may adjust this parameter based on their level of trust in the builder's reliability and reputation. -`max_trusted_bid` is sent in the clear in the `X-Eth-Max-Trusted-Bid` header and +`max_execution_payment` is sent in the clear in the `X-Eth-Max-Trusted-Bid` header and is **not** covered by the `RequestAuth` signature. ## Proposer Preferences (Deprecation of Validator Registrations) @@ -138,7 +138,7 @@ MUST set `bid.value` to the amount they are committing to pay. If the builder intends to pay the proposer via an execution layer payment, they MUST set `bid.execution_payment`. This value MUST NOT exceed the -`max_trusted_bid` received in the `X-Eth-Max-Trusted-Bid` header of the +`max_execution_payment` received in the `X-Eth-Max-Trusted-Bid` header of the corresponding [`getExecutionPayloadBid`][get-execution-payload-bid-api] request. *Note*: `bid.value` and `bid.execution_payment` are not mutually exclusive. diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index ba8d61f1..70ef6b19 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -10,7 +10,7 @@ - [`SignedRequestAuth`](#signedrequestauth) - [Bid Request](#bid-request) - [Constructing the `RequestAuth`](#constructing-the-requestauth) - - [`max_trusted_bid`](#max_trusted_bid) + - [`max_execution_payment`](#max_execution_payment) - [Proposer Preferences](#proposer-preferences) - [Validating a `SignedExecutionPayloadBid`](#validating-a-signedexecutionpayloadbid) - [Block proposal](#block-proposal) @@ -65,21 +65,17 @@ a single whitelisted builder. This includes: alongside the URL, it forms the builder's off-chain identity and is used to bind request authentication to a specific builder, preventing cross-builder replay attacks. -- `max_trusted_bid`: The maximum value (in Gwei) the proposer is willing to +- `max_execution_payment`: The maximum value (in Gwei) the proposer is willing to accept as a trusted execution layer payment from this builder. A value of `0` means the proposer does not accept any trusted payments from this builder, requiring all payments to use the on-chain trustless mechanism. A value of `MAX_TRUSTED_BID` means the proposer accepts any trusted payment amount. Proposers should adjust this based on their level of trust in the builder. -- `bid_boost`: A multiplier factor (in basis points, where 10000 = 100%) applied - to this builder's bid score when comparing against bids from other builders. - ```python class BuilderConfig(Container): url: ByteList[MAX_URL_BYTES] builder_pubkey: BLSPubkey - max_trusted_bid: uint64 - bid_boost: uint64 + max_execution_payment: uint64 ``` ### `GlobalPreferences` @@ -113,7 +109,7 @@ class BuilderWhitelist(Container): When calling [`getExecutionPayloadBid`][get-execution-payload-bid-api], the validator MUST send the `X-Eth-Max-Trusted-Bid` header carrying a decimal -`uint64` (in Gwei) expressing the per-builder `max_trusted_bid` configured in +`uint64` (in Gwei) expressing the per-builder `max_execution_payment` configured in the [`BuilderConfig`](#builderconfig) for this builder. If the header is missing, the builder will not serve a bid for the proposer. @@ -176,7 +172,7 @@ are also defined in the consensus specs. def validate_bid( state: BeaconState, proposer_preferences: ProposerPreferences, - max_trusted_bid: uint64, + max_execution_payment: uint64, signed_bid: SignedExecutionPayloadBid, fee_recipient: ExecutionAddress, ) -> bool: @@ -194,7 +190,7 @@ def validate_bid( assert bid.prev_randao == get_randao_mix(state, get_current_epoch(state)) assert bid.gas_limit <= proposer_preferences.gas_limit - assert bid.execution_payment <= max_trusted_bid + assert bid.execution_payment <= max_execution_payment if bid.value > 0: assert can_builder_cover_bid(state, bid.builder_index, bid.value) @@ -202,10 +198,10 @@ def validate_bid( return verify_execution_payload_bid_signature(state, signed_bid) ``` -`max_trusted_bid` is the value the validator sent in the `X-Eth-Max-Trusted-Bid` +`max_execution_payment` is the value the validator sent in the `X-Eth-Max-Trusted-Bid` header of the corresponding [`getExecutionPayloadBid`][get-execution-payload-bid-api] request. Validators -MUST validate each bid against the `max_trusted_bid` they sent for that request. +MUST validate each bid against the `max_execution_payment` they sent for that request. Note that, the fee recipient specified in `bid.fee_recipient` does not necessarily correspond to the fee recipient of the execution payload. Even if a