diff --git a/apis/builder/beacon_block.yaml b/apis/builder/beacon_block.yaml index e898595..35214e4 100644 --- a/apis/builder/beacon_block.yaml +++ b/apis/builder/beacon_block.yaml @@ -25,12 +25,7 @@ post: content: application/json: schema: - type: object - required: [data] - properties: - data: - $ref: "../../beacon-apis/types/gloas/block.yaml#/Gloas/SignedBeaconBlock" - description: "The signed beacon block." + $ref: "../../beacon-apis/types/gloas/block.yaml#/Gloas/SignedBeaconBlock" application/octet-stream: schema: description: "SSZ serialized `SignedBeaconBlock` bytes. Use content type header to indicate that SSZ data is contained in the request body." diff --git a/apis/builder/builder_preferences.yaml b/apis/builder/builder_preferences.yaml index 9630a26..6bafb6f 100644 --- a/apis/builder/builder_preferences.yaml +++ b/apis/builder/builder_preferences.yaml @@ -2,18 +2,19 @@ post: operationId: "submitBuilderPreferences" summary: Submit builder preferences for a proposer. description: | - Submits a proposer's `BuilderPreferencesRequest` to the builder, including - the `max_trusted_bid` that the proposer is willing to accept from this - builder, authenticated via a `SignedRequestAuth`. + Submits a proposer's `BuilderPreferencesRequestV1` to the builder, including + the `max_execution_payment` that the proposer is willing to accept from this + builder, authenticated via a `SignedRequestAuthV1`. Validators MAY call this endpoint in the epoch prior to the epoch in which they will be proposing, as determined from `state.lookahead`, so that builders have the preferences before the bid request arrives. The builder MUST verify the BLS signature in `auth` against - `validator_pubkey`, and check that `auth.message.builder_pubkey` - matches its own identity. If either check fails, the builder MUST return a - 400 response. + `validator_pubkey`, and check that `auth.message.builder_url` + matches its own URL. If the signature check fails, the builder MUST + return a 401 response. If the URL or slot check fails, the builder MUST + return a 400 response. A success response (202) indicates that the preferences were accepted. If the preferences are invalid, then the builder MUST return an error response @@ -29,18 +30,24 @@ post: description: "The BLS public key of the validator expressing these preferences." schema: $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" + - name: Eth-Consensus-Version + in: header + required: false + description: "The active consensus version to which the request body belongs. Required if the request body is SSZ encoded." + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ConsensusVersion" requestBody: - description: A `BuilderPreferencesRequest` containing the proposer's preferences and a `SignedRequestAuth` for authentication. + description: A `BuilderPreferencesRequestV1` containing the proposer's preferences and a `SignedRequestAuthV1` for authentication. required: true content: application/json: schema: - $ref: "../../types/gloas/builder_preferences.yaml#/Gloas/BuilderPreferencesRequest" + $ref: "../../types/gloas/builder_preferences.yaml#/Gloas/BuilderPreferencesRequestV1" application/octet-stream: schema: - description: "SSZ serialized `BuilderPreferencesRequest` bytes. Use content type header to indicate that SSZ data is contained in the request body." + description: "SSZ serialized `BuilderPreferencesRequestV1` bytes. Use content type header to indicate that SSZ data is contained in the request body." responses: - "200": + "202": description: Success response. "400": description: Error response. @@ -49,13 +56,28 @@ post: schema: $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" examples: + SlotMismatch: + value: + code: 400 + message: "Invalid SignedRequestAuthV1: auth.message.slot does not match the requested slot" WrongBuilder: value: code: 400 - message: "auth.message.builder_pubkey does not match this builder's identity" + message: "auth.message.builder_url does not match this builder's URL" InvalidPreferences: value: code: 400 - message: "Invalid builder preferences: max_trusted_bid malformed" + message: "Invalid builder preferences: max_execution_payment malformed" + "401": + description: Authentication required. + content: + application/json: + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" + examples: + SignatureVerificationFailed: + value: + code: 401 + message: "Invalid SignedRequestAuthV1: signature verification failed" "500": $ref: "../../builder-oapi.yaml#/components/responses/InternalError" diff --git a/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml index 3568a0c..b3b3106 100644 --- a/apis/builder/execution_payload_bid.yaml +++ b/apis/builder/execution_payload_bid.yaml @@ -10,23 +10,21 @@ 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-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 + - Optionally, a `SignedRequestAuthV1` 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`). - The `X-Eth-Max-Trusted-Bid` header is optional. A validator MAY send it - if they have not already submitted a `SignedBuilderPreferences` to this - builder. If both are present, the stored `BuilderPreferences` takes - precedence over the header. If neither is present, the builder MUST treat - `max_trusted_bid` as `0`. + The proposer's `max_execution_payment` is communicated exclusively via the + [`submitBuilderPreferences`][submit-builder-preferences-api] endpoint. If no + `BuilderPreferencesV1` have been submitted for the proposer, the builder MUST + treat `max_execution_payment` as `0` and MUST NOT include an execution layer + payment in the bid. - The `SignedRequestAuth` body is optional. If it is present but malformed - or fails signature verification, the builder MAY return a 400 response. + The `SignedRequestAuthV1` body is optional. If it is present but malformed + or fails signature verification, the builder MAY return a 401 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 + use the presence and validity of the `SignedRequestAuthV1` to apply per-validator policy (e.g. rate-limiting, prioritization, or refusing unauthenticated requests). @@ -59,12 +57,12 @@ post: description: Root of the beacon block the proposer will build on. schema: $ref: "../../builder-oapi.yaml#/components/schemas/Root" - - name: proposer_index + - name: proposer_pubkey in: path required: true - description: Index of the proposer. + description: BLS public key of the proposer. schema: - $ref: "../../builder-oapi.yaml#/components/schemas/Uint64" + $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" - name: Date-Milliseconds in: header required: false @@ -92,50 +90,33 @@ post: in: header required: false description: | - The active consensus version to which the `SignedRequestAuth` in the + The active consensus version to which the `SignedRequestAuthV1` in the request body belongs. Required if the request body is SSZ encoded. schema: $ref: "../../builder-oapi.yaml#/components/schemas/ConsensusVersion" - - name: X-Eth-Max-Trusted-Bid - in: header - required: false - description: | - Optional decimal `uint64` (in Gwei) carrying the proposer's - `max_trusted_bid` for this request. MAY be sent if the validator has - not already submitted a `SignedBuilderPreferences` to this builder. - If a stored `BuilderPreferences` exists for this proposer, it takes - precedence over this header. If no `BuilderPreferences` has been - submitted, `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. - schema: - type: integer - format: uint64 - example: 1000000000 requestBody: description: | - Optional `SignedRequestAuth` authenticating the request. If provided, + Optional `SignedRequestAuthV1` authenticating the request. If provided, 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 + resolved from the `proposer_pubkey` path parameter, and check that + `builder_url` matches its own URL 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" + $ref: "../../types/gloas/request_auth.yaml#/Gloas/SignedRequestAuthV1" 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." + description: "SSZ serialized `SignedRequestAuthV1` 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. headers: Eth-Consensus-Version: $ref: "../../builder-oapi.yaml#/components/headers/Eth-Consensus-Version" - required: false + required: true content: application/json: schema: @@ -165,10 +146,14 @@ post: value: code: 400 message: "Unknown hash: missing parent hash" - InvalidAuth: + SlotMismatch: + value: + code: 400 + message: "Invalid SignedRequestAuthV1: auth.message.slot does not match the requested slot" + WrongBuilder: value: code: 400 - message: "Invalid SignedRequestAuth: signature verification failed" + message: "Invalid SignedRequestAuthV1: auth.message.builder_url does not match this builder's URL" "401": description: Authentication required. content: @@ -179,7 +164,11 @@ post: MissingAuth: value: code: 401 - message: "Missing SignedRequestAuth: this builder requires authenticated requests" + message: "Missing SignedRequestAuthV1: this builder requires authenticated requests" + InvalidAuth: + value: + code: 401 + message: "Invalid SignedRequestAuthV1: signature verification failed" "406": $ref: "../../builder-oapi.yaml#/components/responses/NotAcceptable" "415": diff --git a/builder-oapi.yaml b/builder-oapi.yaml index 5e26db0..87d5a5c 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/{slot}/{parent_hash}/{parent_root}/{proposer_index}: + /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_pubkey}: $ref: "./apis/builder/execution_payload_bid.yaml" /eth/v1/builder/beacon_block: $ref: "./apis/builder/beacon_block.yaml" @@ -108,14 +108,14 @@ components: $ref: "./types/fulu/blobs_bundle.yaml#/Fulu/BlobsBundle" Fulu.ExecutionPayloadAndBlobsBundle: $ref: "./types/fulu/execution_payload_and_blobs_bundle.yaml#/Fulu/ExecutionPayloadAndBlobsBundle" - 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/builder_preferences.yaml#/Gloas/BuilderPreferences" - Gloas.BuilderPreferencesRequest: - $ref: "./types/gloas/builder_preferences.yaml#/Gloas/BuilderPreferencesRequest" + Gloas.RequestAuthV1: + $ref: "./types/gloas/request_auth.yaml#/Gloas/RequestAuthV1" + Gloas.SignedRequestAuthV1: + $ref: "./types/gloas/request_auth.yaml#/Gloas/SignedRequestAuthV1" + Gloas.BuilderPreferencesV1: + $ref: "./types/gloas/builder_preferences.yaml#/Gloas/BuilderPreferencesV1" + Gloas.BuilderPreferencesRequestV1: + $ref: "./types/gloas/builder_preferences.yaml#/Gloas/BuilderPreferencesRequestV1" responses: InternalError: @@ -165,5 +165,5 @@ components: $ref: "./examples/fulu/signed_blinded_beacon_block.json" Fulu.SignedBuilderBid: $ref: "./examples/fulu/signed_builder_bid.json" - Gloas.SignedRequestAuth: + Gloas.SignedRequestAuthV1: $ref: "./examples/gloas/signed_request_auth.json" \ No newline at end of file diff --git a/examples/gloas/signed_request_auth.json b/examples/gloas/signed_request_auth.json index eb5bfe9..1606db6 100644 --- a/examples/gloas/signed_request_auth.json +++ b/examples/gloas/signed_request_auth.json @@ -1,7 +1,7 @@ { "value": { "message": { - "builder_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", + "builder_url": "https://builder.example.com", "slot": "1" }, "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index c891501..a8dcbb6 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -7,7 +7,7 @@ - [Constants](#constants) - [Bidding](#bidding) - [Builder Preferences](#builder-preferences) - - [`max_trusted_bid`](#max_trusted_bid) + - [`max_execution_payment`](#max_execution_payment) - [Per-request Validator Inputs](#per-request-validator-inputs) - [Proposer Preferences (Deprecation of Validator Registrations)](#proposer-preferences-deprecation-of-validator-registrations) - [Constructing a `SignedExecutionPayloadBid`](#constructing-a-signedexecutionpayloadbid) @@ -26,9 +26,11 @@ describes how builders consume per-request inputs from validators and construct ## Constants -| Name | Value | -| ----------------- | ----------- | -| `MAX_TRUSTED_BID` | `2**64 - 1` | +| Name | Value | +| ----------------------- | -------------------------- | +| `MAX_EXECUTION_PAYMENT` | `2**64 - 1` | +| `MAX_URL_SIZE` | `2048` | +| `DOMAIN_REQUEST_AUTH` | `DomainType('0x0B000001')` | ## Bidding @@ -36,13 +38,13 @@ 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], 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 with ePBS, the beacon block and execution payload are decoupled. +The `parent_hash` could be associated with a different beacon block as that of +`parent_root`. -We update `is_eligible_for_bid` below. *Note*: `hash_tree_root` is defined in -the [Gloas consensus specs][gloas-consensus-specs]. +We update `is_eligible_for_bid` below. *Note*: `hash_tree_root` and +`get_beacon_proposer_index` are defined in the +[Gloas consensus specs][gloas-consensus-specs]. ```python def is_eligible_for_bid( @@ -53,12 +55,19 @@ def is_eligible_for_bid( # [New in Gloas] parent_root: Root, # [New in Gloas] - validator_index: ValidatorIndex, + proposer_pubkey: BLSPubkey, ): # Verify slot assert slot == state.slot - assert validator_index in state.validators.keys() + # Verify proposer pubkey matches the expected proposer for this slot + validator_index = get_beacon_proposer_index(state) + assert state.validators[validator_index].pubkey == proposer_pubkey + + # Verify the proposer is active + assert is_active_validator( + state.validators[validator_index], get_current_epoch(state) + ) # Verify that proposer preferences have been received via the gossip topic assert validator_index in proposer_preferences.keys() @@ -79,61 +88,68 @@ def is_eligible_for_bid( ## Builder Preferences Validators MAY communicate their per-builder preferences ahead of the bid -request by calling the [`submitBuilderPreferences`][submit-builder-preferences-api] -API in the epoch prior to the epoch in which they will be proposing, as -determined from `state.lookahead`. The builder receives a `BuilderPreferencesRequest` object containing: +request by calling the +[`submitBuilderPreferences`][submit-builder-preferences-api] API in the epoch +prior to the epoch in which they will be proposing, as determined from +`state.lookahead`. The builder receives a `BuilderPreferencesRequestV1` object +containing: - `validator_pubkey`: The BLS public key of the validator submitting these preferences, passed as a path parameter. -- `preferences`: A `BuilderPreferences` with: - - `max_trusted_bid`: The maximum trusted execution layer payment the proposer - will accept from this builder (in Gwei). -- `auth`: A `SignedRequestAuth` authenticating the request. The builder MUST - check that `auth.message.builder_pubkey` matches its own identity and MUST - verify the BLS signature against the `validator_pubkey` path parameter. If - either check fails, the builder MUST return a 400 response. +- `preferences`: A `BuilderPreferencesV1` with: + - `max_execution_payment`: The maximum execution layer payment the + proposer will accept from this builder (in Gwei). +- `auth`: A `SignedRequestAuthV1` authenticating the request. The builder MUST + check that `auth.message.builder_url` matches its own URL and MUST verify the + BLS signature against the `validator_pubkey` path parameter. If either check + fails, the builder MUST return a 400 response. The builder SHOULD store the preferences for each proposer and apply the -`max_trusted_bid` constraint when constructing bids. If no preferences have been -submitted for a proposer, the builder MUST treat the proposer's `max_trusted_bid` -as `0`. +`max_execution_payment` constraint when constructing bids. If no preferences +have been submitted for a proposer, the builder MUST treat the proposer's +`max_execution_payment` as `0`. The builder can also choose to not serve the bid. -### `max_trusted_bid` +### `max_execution_payment` -`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. 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_execution_payment` is the maximum value (in Gwei) that a proposer is +willing to accept as an execution layer payment from this builder. A +value of `0` indicates that the proposer does not accept any execution payments +from the builder, requiring all payments to use the on-chain trustless payments +mechanism. A value of `MAX_EXECUTION_PAYMENT` indicates that the proposer will +accept any execution layer payment amount from the builder. Proposers may adjust this +parameter based on their level of trust in the builder's reliability and +reputation. ## Per-request Validator Inputs Validators communicate per-request inputs to a builder on each [`getExecutionPayloadBid`][get-execution-payload-bid-api] call: -- Optionally, the `X-Eth-Max-Trusted-Bid` header carrying a decimal `uint64` - (in Gwei) with the proposer's `max_trusted_bid` for this request. MAY be - omitted if the proposer has already submitted a `BuilderPreferencesRequest` - to this builder. If a stored `BuilderPreferences` exists for the proposer, - it takes precedence over this header. -- Optionally, a [`SignedRequestAuth`][signed-request-auth] in the request body +- Optionally, a [`SignedRequestAuthV1`][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. -The builder resolves `max_trusted_bid` in the following order of precedence: -1. Stored `BuilderPreferences` for the proposer, if previously submitted. -2. The `X-Eth-Max-Trusted-Bid` header, if present on this request. -3. `0`, if neither is available. +The proposer's `max_execution_payment` is communicated exclusively via the +[`submitBuilderPreferences`][submit-builder-preferences-api] endpoint. If no +`BuilderPreferencesV1` have been submitted for the proposer, the builder MUST +treat `max_execution_payment` as `0`. -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 present, builders MAY verify the `SignedRequestAuthV1` +signature against the `proposer_pubkey` path parameter, and check that +`builder_url` matches their own URL and that `slot` matches the requested slot. +If verification fails, the builder MAY return a 401 response. + +```python +def verify_request_auth_signature( + signed_request_auth: SignedRequestAuthV1, + pubkey: BLSPubkey, +) -> bool: + domain = compute_domain(DOMAIN_REQUEST_AUTH) + signing_root = compute_signing_root(signed_request_auth.message, domain) + return bls.Verify(pubkey, signing_root, signed_request_auth.signature) +``` If the request body is absent, the builder MAY still serve a bid. @@ -162,14 +178,15 @@ 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. +`max_execution_payment` from the proposer's stored `BuilderPreferencesV1`. If no +`BuilderPreferencesV1` have been submitted, the builder MUST NOT include an +execution layer payment (i.e. MUST set `bid.execution_payment` to `0`). -*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. +*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` @@ -184,13 +201,12 @@ The specification for a block builder to construct a [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] is 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 -[submit-builder-preferences-api]: ./../../apis/builder/builder_preferences.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 -[signed-request-auth]: ./validator.md#signedrequestauth +[signed-request-auth]: ./validator.md#signedrequestauthv1 +[submit-builder-preferences-api]: ./../../apis/builder/builder_preferences.yaml diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index 7d6c876..5debe27 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -6,14 +6,14 @@ - [Introduction](#introduction) - [Containers](#containers) - [New Containers](#new-containers) - - [`RequestAuth`](#requestauth) - - [`SignedRequestAuth`](#signedrequestauth) - - [`BuilderPreferences`](#builderpreferences) - - [`BuilderPreferencesRequest`](#builderpreferencesrequest) + - [`RequestAuthV1`](#requestauthv1) + - [`SignedRequestAuthV1`](#signedrequestauthv1) + - [`BuilderPreferencesV1`](#builderpreferencesv1) + - [`BuilderPreferencesRequestV1`](#builderpreferencesrequestv1) - [Submitting Builder Preferences](#submitting-builder-preferences) - - [`max_trusted_bid`](#max_trusted_bid) + - [`max_execution_payment`](#max_execution_payment) - [Bid Request](#bid-request) - - [Constructing the `RequestAuth`](#constructing-the-requestauth) + - [Constructing the `RequestAuthV1`](#constructing-the-requestauthv1) - [Proposer Preferences](#proposer-preferences) - [Validating a `SignedExecutionPayloadBid`](#validating-a-signedexecutionpayloadbid) - [Block proposal](#block-proposal) @@ -39,126 +39,114 @@ corresponding to the included bid to the PTC committee. ### New Containers -#### `RequestAuth` +#### `RequestAuthV1` -`RequestAuth` is used to authenticate requests to a builder. This is useful so +`RequestAuthV1` 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 RequestAuth(Container): - builder_pubkey: BLSPubkey +class RequestAuthV1(Container): + builder_url: ByteList[MAX_URL_SIZE] slot: Slot ``` -#### `SignedRequestAuth` +#### `SignedRequestAuthV1` ```python -class SignedRequestAuth(Container): - message: RequestAuth +class SignedRequestAuthV1(Container): + message: RequestAuthV1 signature: BLSSignature ``` -#### `BuilderPreferences` +#### `BuilderPreferencesV1` -`BuilderPreferences` communicates a proposer's per-builder preferences to a +`BuilderPreferencesV1` communicates a proposer's per-builder preferences to a specific builder ahead of the bid request. ```python -class BuilderPreferences(Container): - max_trusted_bid: Gwei +class BuilderPreferencesV1(Container): + max_execution_payment: Gwei ``` -#### `BuilderPreferencesRequest` +#### `BuilderPreferencesRequestV1` ```python -class BuilderPreferencesRequest(Container): - preferences: BuilderPreferences - auth: SignedRequestAuth +class BuilderPreferencesRequestV1(Container): + preferences: BuilderPreferencesV1 + auth: SignedRequestAuthV1 ``` ## Submitting Builder Preferences The validator MAY submit its -[`BuilderPreferencesRequest`](#builderpreferencesrequest) to each builder via -the [`submitBuilderPreferences`][submit-builder-preferences-api] API call in the -epoch prior to the epoch in which they will be proposing, as determined from +[`BuilderPreferencesRequestV1`](#builderpreferencesrequestv1) to each builder +via the [`submitBuilderPreferences`][submit-builder-preferences-api] API call in +the epoch prior to the epoch in which they will be proposing, as determined from `state.lookahead`. This ensures builders have the preferences before the bid request arrives. -The validator constructs a `BuilderPreferences` with: +The validator constructs a `BuilderPreferencesV1` with: -- `max_trusted_bid`: The maximum trusted execution layer payment the proposer - will accept from this builder. See [`max_trusted_bid`](#max_trusted_bid). +- `max_execution_payment`: The maximum execution layer payment the + proposer will accept from this builder. See + [`max_execution_payment`](#max_execution_payment). -The validator's BLS public key is passed as the `validator_pubkey` path parameter -in the [`submitBuilderPreferences`][submit-builder-preferences-api] API call. +The validator's BLS public key is passed as the `validator_pubkey` path +parameter in the [`submitBuilderPreferences`][submit-builder-preferences-api] +API call. -The validator then constructs a `BuilderPreferencesRequest` with the -`BuilderPreferences` as `preferences` and a `SignedRequestAuth` as `auth`. The -`SignedRequestAuth` is constructed as described in -[Constructing the `RequestAuth`](#constructing-the-requestauth); its -`auth.message.builder_pubkey` identifies the intended builder. The builder MUST -verify the `auth` signature against the `validator_pubkey` path parameter and MUST -reject the request with a 400 response if `auth.message.builder_pubkey` does not -match its own identity. +The validator then constructs a `BuilderPreferencesRequestV1` with the +`BuilderPreferencesV1` as `preferences` and a `SignedRequestAuthV1` as `auth`. +The `SignedRequestAuthV1` is constructed as described in +[Constructing the `RequestAuthV1`](#constructing-the-requestauthv1); its +`auth.message.builder_url` identifies the intended builder. The builder MUST +verify the `auth` signature against the `validator_pubkey` path parameter and +MUST reject the request with a 400 response if `auth.message.builder_url` does +not match its own URL. If no preferences have been submitted, the builder MUST treat the proposer's -`max_trusted_bid` as `0`. - -### `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. 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 MAY also send `max_trusted_bid` as a decimal `uint64` in the -`X-Eth-Max-Trusted-Bid` header on a -[`getExecutionPayloadBid`][get-execution-payload-bid-api] request, for example -if `BuilderPreferences` have not been submitted to this builder. If a -`BuilderPreferences` has been submitted, it takes precedence over the header. -Note that `max_trusted_bid` is **not** covered by the `RequestAuth` signature. -The validator MUST remember the effective `max_trusted_bid` for each request so -it can validate the resulting bid against the same value. +`max_execution_payment` as `0`. + +### `max_execution_payment` + +`max_execution_payment` is the maximum value (in Gwei) that the proposer is +willing to accept as an execution layer payment from this builder. A +value of `0` means the proposer does not accept any execution payments from this +builder, requiring all payments to go through the on-chain trustless payments +mechanism. A value of `MAX_EXECUTION_PAYMENT` means the proposer will accept any +execution payment amount from the builder. Proposers may adjust this parameter +based on their level of trust in the builder's reliability and reputation. + +`max_execution_payment` is communicated exclusively via the +[`submitBuilderPreferences`][submit-builder-preferences-api] endpoint. If no +`BuilderPreferencesV1` have been submitted to a builder, that builder MUST NOT +include an execution layer payment in its bid. ## Bid Request When calling [`getExecutionPayloadBid`][get-execution-payload-bid-api], the -validator MAY send the `X-Eth-Max-Trusted-Bid` header carrying a decimal -`uint64` (in Gwei) expressing the per-request `max_trusted_bid`. This header -MAY be omitted if the validator has already submitted a -[`BuilderPreferencesRequest`](#builderpreferencesrequest) to this builder. If the -header is present, it takes precedence over the stored `BuilderPreferences` for -this request. - -The validator MAY additionally send a [`SignedRequestAuth`](#signedrequestauth) -as the request body to authenticate the request. The body MAY be encoded as JSON +validator MAY send a [`SignedRequestAuthV1`](#signedrequestauthv1) 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` +### Constructing the `RequestAuthV1` If the validator chooses to authenticate its request, it constructs a -`RequestAuth` with the following fields: +`RequestAuthV1` with the following fields: -- `builder_pubkey`: The BLS public key of the builder the request is intended - for. -- `slot`: The slot for which the bid is being requested. +- `builder_url`: The URL of the builder the request is intended for. +- `slot`: The slot for which the request is being sent. -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 proposer's public key is already carried as a path parameter in the relevant +API request, so it does not need to be carried inside `RequestAuthV1`. -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). +The validator then constructs the `SignedRequestAuthV1` by signing the +`RequestAuthV1`. The signature lets builders authenticate the requesting +validator and discard requests from other parties (e.g. DDOS or replay attempts +from competing builders). ## Proposer Preferences @@ -193,7 +181,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: @@ -211,7 +199,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) @@ -219,12 +207,13 @@ 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. +`max_execution_payment` is the value from the `BuilderPreferencesV1` the +validator submitted to this builder via +[`submitBuilderPreferences`][submit-builder-preferences-api]. Validators MUST +validate each bid against the `max_execution_payment` they submitted for that +builder. -Note that, the fee recipient specified in `bid.fee_recipient` does not +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 @@ -242,10 +231,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 MAY include the `X-Eth-Max-Trusted-Bid` header on the request if - `BuilderPreferences` have not been previously submitted to this builder. The - validator MAY additionally - send a `SignedRequestAuth` in the request body to authenticate the request. + validator MAY send a `SignedRequestAuthV1` 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 @@ -259,7 +246,6 @@ block on top of a beacon `state` must take the following actions: [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 -[submit-builder-preferences-api]: ./../../apis/builder/builder_preferences.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 @@ -267,5 +253,6 @@ block on top of a beacon `state` must take the following actions: [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/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/builder_preferences.yaml b/types/gloas/builder_preferences.yaml index 98a8e8f..01b045e 100644 --- a/types/gloas/builder_preferences.yaml +++ b/types/gloas/builder_preferences.yaml @@ -1,18 +1,18 @@ Gloas: - BuilderPreferences: + BuilderPreferencesV1: type: object description: "Per-builder preferences that a proposer can express." - required: [max_trusted_bid] + required: [max_execution_payment] properties: - max_trusted_bid: + max_execution_payment: $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." - BuilderPreferencesRequest: + description: "Indicates the maximum amount that a proposer is willing to accept as an execution payment from the builder." + BuilderPreferencesRequestV1: type: object description: "A builder preferences submission containing the proposer's preferences and a signed request authentication." required: [preferences, auth] properties: preferences: - $ref: "#/Gloas/BuilderPreferences" + $ref: "#/Gloas/BuilderPreferencesV1" auth: - $ref: "./request_auth.yaml#/Gloas/SignedRequestAuth" + $ref: "./request_auth.yaml#/Gloas/SignedRequestAuthV1" diff --git a/types/gloas/request_auth.yaml b/types/gloas/request_auth.yaml index b874294..2ca410e 100644 --- a/types/gloas/request_auth.yaml +++ b/types/gloas/request_auth.yaml @@ -1,19 +1,20 @@ Gloas: - RequestAuth: + RequestAuthV1: type: object - required: [builder_pubkey, slot] + required: [builder_url, slot] properties: - builder_pubkey: - $ref: "../../beacon-apis/types/primitive.yaml#/Pubkey" - description: "The BLS public key of the builder the request is intended for." + builder_url: + type: string + description: "The URL of the builder the request is intended for." + example: "https://builder.example.com" slot: $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" description: "The slot for which the bid is being requested." - SignedRequestAuth: + SignedRequestAuthV1: type: object required: [message, signature] properties: message: - $ref: "#/Gloas/RequestAuth" + $ref: "#/Gloas/RequestAuthV1" signature: $ref: "../../beacon-apis/types/primitive.yaml#/Signature" \ No newline at end of file diff --git a/wordlist.txt b/wordlist.txt index 353ab83..a7d4815 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -35,5 +35,8 @@ Gloas gloas Gwei PTC +BuilderPreferencesV +RequestAuthV +SignedRequestAuthV submitBlindedBlockV ValidatorRegistrationsV \ No newline at end of file