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/block_and_envelope.yaml b/apis/builder/block_and_envelope.yaml new file mode 100644 index 00000000..628538d7 --- /dev/null +++ b/apis/builder/block_and_envelope.yaml @@ -0,0 +1,82 @@ +post: + operationId: "submitBlockAndEnvelope" + summary: Submit a signed beacon block and blinded execution payload envelope. + description: | + Submits a `SignedBeaconBlock` and a `SignedBlindedExecutionPayloadEnvelope` to an + unstaked builder, binding the proposer to the block. + + The validator sends the following: + - A `SignedBeaconBlock` containing the `ExecutionPayloadBid` from the builder + - A `SignedBlindedExecutionPayloadEnvelope` with: + - `beacon_block_root`: The root of the signed beacon block + - `payload_root`: The root of the execution payload + - `execution_requests_root`: The root of the execution requests + - `blob_kzg_commitments_root`: The root of the blob KZG commitments + - `builder_index`: Set to `BUILDER_INDEX_SELF_BUILD` + - `slot`: The slot of the block + - `state_root`: The post-state root + + A success response (202) indicates that the submission was valid. The builder will: + 1. Validate the beacon block and blinded envelope + 2. Construct the full `SignedExecutionPayloadEnvelope` by unblinding the payload + 3. Broadcast the `SignedExecutionPayloadEnvelope` to the PTC committee + + If the submission 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 for unstaked builders. + 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` and `SignedBlindedExecutionPayloadEnvelope`. + required: true + content: + application/json: + schema: + title: BlockAndBlindedEnvelopeRequest + type: object + required: [signed_beacon_block, signed_blinded_envelope] + properties: + signed_beacon_block: + $ref: "../../beacon-apis/types/gloas/block.yaml#/Gloas/SignedBeaconBlock" + description: "The signed beacon block containing the ExecutionPayloadBid." + signed_blinded_envelope: + $ref: "../../types/gloas/blinded_envelope.yaml#/Gloas/SignedBlindedExecutionPayloadEnvelope" + description: "The signed blinded execution payload envelope." + application/octet-stream: + schema: + description: "SSZ serialized `BlockAndBlindedEnvelope` bytes. Use content type header to indicate that SSZ data is contained in the request body." + responses: + "202": + description: Success response. The builder will construct and broadcast the SignedExecutionPayloadEnvelope to the PTC committee. + "400": + description: Error response. + content: + application/json: + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" + examples: + InvalidBlock: + value: + code: 400 + message: "Invalid block: missing signature" + InvalidEnvelope: + value: + code: 400 + message: "Invalid envelope: beacon_block_root mismatch" + BidMismatch: + value: + code: 400 + message: "Bid mismatch: ExecutionPayloadBid does not match provided bid" + "415": + $ref: "../../builder-oapi.yaml#/components/responses/UnsupportedMediaType" + "500": + $ref: "../../builder-oapi.yaml#/components/responses/InternalError" + diff --git a/apis/builder/builder_bid.yaml b/apis/builder/builder_bid.yaml new file mode 100644 index 00000000..5015e0c6 --- /dev/null +++ b/apis/builder/builder_bid.yaml @@ -0,0 +1,117 @@ +get: + operationId: "getBuilderBid" + summary: Get an execution payload bid from an unstaked builder. + description: | + Requests an unstaked builder node to produce a valid builder bid containing an + execution payload header and a `SignedExecutionPayloadBid`. This endpoint is used + for unstaked builders who provide full block contents to validators. + + The validator sends a GET request with the following path parameters: + - 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 validator's BLS public key. + + The builder responds with a 200 response containing a `SignedBuilderBid` if it can + provide one. The bid includes: + - An `ExecutionPayloadHeader` for the proposed block + - Blob KZG commitments for any associated blobs + - Execution requests (deposits, withdrawals, etc.) + - A `SignedExecutionPayloadBid` with `builder_index` set to `BUILDER_INDEX_SELF_BUILD` + + If the builder is unable to produce a valid 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 for unstaked builders. + 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 beacon block the proposer will build on. + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/Root" + - name: pubkey + in: path + required: true + description: The validator's BLS public key. + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/Pubkey" + - 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. + Builders should use this header to adjust the amount of time by which they delay + requests to maximise block rewards. Otherwise, requests will timeout and the proposer + will not receive the bid in time. + schema: + type: integer + format: int64 + example: 10000 + 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: GetBuilderBidResponse + type: object + required: [version, data] + properties: + version: + type: string + enum: [ gloas ] + example: "gloas" + data: + $ref: "../../types/gloas/builder_bid.yaml#/Gloas/SignedBuilderBid" + application/octet-stream: + schema: + description: "SSZ serialized `SignedBuilderBid` bytes. Use Accept header to choose this response type" + "204": + description: No bid 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/apis/builder/execution_payload_bid.yaml b/apis/builder/execution_payload_bid.yaml new file mode 100644 index 00000000..15dd90ea --- /dev/null +++ b/apis/builder/execution_payload_bid.yaml @@ -0,0 +1,126 @@ +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. + + 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 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. + + 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. + + This API is applicable from Glamsterdam fork onwards. + 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 beacon 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 + 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. + 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: 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. + 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/SignedExecutionPayloadBid" + application/octet-stream: + schema: + description: "SSZ serialized `SignedExecutionPayloadBid` bytes. Use Accept header to choose this response type" + "204": + description: No bid is available. + "400": + description: Error response. + content: + application/json: + schema: + $ref: "../../builder-oapi.yaml#/components/schemas/ErrorMessage" + examples: + 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": + $ref: "../../builder-oapi.yaml#/components/responses/InternalError" diff --git a/apis/builder/validators_v2.yaml b/apis/builder/validators_v2.yaml new file mode 100644 index 00000000..813ae98d --- /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/Gloas.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/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 a9c05f06..99e75455 100644 --- a/builder-oapi.yaml +++ b/builder-oapi.yaml @@ -51,8 +51,14 @@ tags: paths: /eth/v1/builder/validators: $ref: "./apis/builder/validators.yaml" + /eth/v2/builder/validators: + $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}: + $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 +78,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" @@ -102,7 +108,17 @@ 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.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/builder.md b/specs/gloas/builder.md new file mode 100644 index 00000000..7c613f1a --- /dev/null +++ b/specs/gloas/builder.md @@ -0,0 +1,211 @@ + + + + +- [Gloas - Builder Specification](#gloas---builder-specification) + - [Introduction](#introduction) + - [Constants](#constants) + - [Containers](#containers) + - [New Containers](#new-containers) + - [`BuilderPreferences`](#builderpreferences) + - [`ValidatorRegistrationV2`](#validatorregistrationv2) + - [`SignedValidatorRegistrationV2`](#signedvalidatorregistrationv2) + - [`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) + - [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 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. + +## Constants + +| Name | Value | | ----------------------------------------- | +------------------ | | `MAX_TRUSTED_BID` | `2**64 - 1` | + +## Containers + +### New Containers + +#### `BuilderPreferences` + +```python +class BuilderPreferences(Container): + max_trusted_bid: uint64 +``` + +#### `ValidatorRegistrationV2` + +```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 + signature: BLSSignature +``` + +### `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_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) + signing_root = compute_signing_root(signed_registration.message, domain) + return bls.Verify(pubkey, signing_root, signed_registration.signature) +``` + +## Bidding + +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. + +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, +): + # Verify slot + assert slot == state.slot + + assert validator_index in state.validators.keys() + + assert validator_index in registrations.keys() + + # Verify parent hash + # [Modified in Gloas:EIP7732] + assert parent_hash == state.latest_block_hash + + # Verify parent root + # [Modified in Gloas:EIP7732] + assert parent_root == hash_tree_root(state.latest_block_header) +``` + +## Builder Preferences + +Using validator registrations, a proposer can express the preferences it has for +a 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 + 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 + +The second version of ValidatorRegistrations adds the following new fields: + +- `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. + +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 they will be proposing. + +### `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, + signed_registration: SignedValidatorRegistrationV2, + registrations: Dict[ValidatorIndex, ValidatorRegistrationV2], +): + signature = signed_registration.signature + registration = signed_registration.message + validator_index = registration.validator_index + proposal_slot = registration.proposal_slot + + validator = state.validators[validator_index] + + # 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 validator_index in registrations.keys(): + prev_registration = registrations[validator_index] + assert registration.proposal_slot >= prev_registration.proposal_slot + + # Verify registration signature + assert verify_registration_v2_signature(state, signed_registration) +``` + +## Constructing a `SignedExecutionPayloadBid` + +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`][signed-execution-payload-bid] has +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. + +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 +[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 +[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/unstaked_builder.md b/specs/gloas/unstaked_builder.md new file mode 100644 index 00000000..36ebc8c0 --- /dev/null +++ b/specs/gloas/unstaked_builder.md @@ -0,0 +1,282 @@ + + + + +- [Gloas - Unstaked Builder Specification](#gloas---unstaked-builder-specification) + - [Introduction](#introduction) + - [Flow Overview](#flow-overview) + - [Constants](#constants) + - [Containers](#containers) + - [Modified Containers](#modified-containers) + - [`BuilderBid`](#builderbid) + - [`SignedBuilderBid`](#signedbuilderbid) + - [New Containers](#new-containers) + - [`BlindedExecutionPayloadEnvelope`](#blindedexecutionpayloadenvelope) + - [`SignedBlindedExecutionPayloadEnvelope`](#signedblindedexecutionpayloadenvelope) + - [`BlockAndBlindedEnvelope`](#blockandblindedenvelope) + - [Builder Behaviour](#builder-behaviour) + - [Constructing a `SignedBuilderBid`](#constructing-a-signedbuilderbid) + - [Processing `submitBlockAndEnvelope`](#processing-submitblockandenvelope) + - [`verify_blinded_envelope_signature`](#verify_blinded_envelope_signature) + - [`process_block_and_envelope`](#process_block_and_envelope) + - [Constructing a `SignedExecutionPayloadEnvelope`](#constructing-a-signedexecutionpayloadenvelope) + + + +# Gloas - Unstaked Builder Specification + +## Introduction + +This document specifies the behaviour for unstaked builders interacting with +validators through the Builder-API in Gloas. Unlike staked builders who have +collateral on the beacon chain, unstaked builders operate through a relay-like +mechanism where they provide full block contents to validators. + +The key difference from the staked builder flow is that unstaked builders use +`BUILDER_INDEX_SELF_BUILD` as their builder index in the +`SignedExecutionPayloadBid`, indicating that the proposer is effectively +self-building with the unstaked builder's block contents. + +## Flow Overview + +The unstaked builder interaction follows these steps: + +1. **Validator queries bid**: The validator calls the + [`getBuilderBid`][get-builder-bid-api] API to get a `SignedBuilderBid` + containing the execution payload header, blob KZG commitments, execution + requests, and a `SignedExecutionPayloadBid`. + +2. **Validator constructs block**: Upon receiving the bid, the validator: + + - Assembles a `SignedBeaconBlock` with the `ExecutionPayloadBid` from the + builder + - Constructs a `SignedBlindedExecutionPayloadEnvelope` using the + `beacon_block_root` of the `SignedBeaconBlock` + +3. **Validator submits to builder**: The validator returns both the + `SignedBlindedExecutionPayloadEnvelope` and `SignedBeaconBlock` to the + builder via the [`submitBlockAndEnvelope`][submit-block-and-envelope-api] + API. + +4. **Builder broadcasts envelope**: The builder constructs the full + `SignedExecutionPayloadEnvelope` (unblinding the blinded version) and + broadcasts it to the PTC committee via the `execution_payload_envelope` + gossip topic. + +## Constants + +| Name | Value | | ----------------------------------------- | +------------------ | | `BUILDER_INDEX_SELF_BUILD` | `2**64 - 1` | + +## Containers + +### Modified Containers + +#### `BuilderBid` + +`SignedBuilderBid` is indirectly updated through `BuilderBid`. The `value` and +`pubkey` fields have been removed since they are absorbed by the +`SignedExecutionPayloadBid`. + +*Note*: The `builder_index` in the `SignedExecutionPayloadBid` MUST be set to +`BUILDER_INDEX_SELF_BUILD` for unstaked builders. + +```python +class BuilderBid(Container): + header: ExecutionPayloadHeader + blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] + execution_requests: ExecutionRequests + bid: SignedExecutionPayloadBid # [New in Gloas] +``` + +#### `SignedBuilderBid` + +```python +class SignedBuilderBid(Container): + message: BuilderBid + signature: BLSSignature +``` + +### New Containers + +#### `BlindedExecutionPayloadEnvelope` + +The `BlindedExecutionPayloadEnvelope` contains the roots of the execution +payload components rather than the full data. This allows the validator to +commit to the envelope without having access to the full execution payload. + +```python +class BlindedExecutionPayloadEnvelope(Container): + payload_root: Root + execution_requests: ExecutionRequests + builder_index: BuilderIndex + beacon_block_root: Root + slot: Slot + blob_kzg_commitments_root: Root + state_root: Root +``` + +#### `SignedBlindedExecutionPayloadEnvelope` + +```python +class SignedBlindedExecutionPayloadEnvelope(Container): + message: BlindedExecutionPayloadEnvelope + signature: BLSSignature +``` + +#### `BlockAndBlindedEnvelope` + +Container for submitting both the signed beacon block and blinded envelope +together. + +```python +class BlockAndBlindedEnvelope(Container): + signed_beacon_block: SignedBeaconBlock + signed_blinded_envelope: SignedBlindedExecutionPayloadEnvelope +``` + +## Builder Behaviour + +### Constructing a `SignedBuilderBid` + +When a builder receives a request for a bid via the +[`getBuilderBid`][get-builder-bid-api] API, it MUST construct a +`SignedBuilderBid` with the following: + +1. **header**: The `ExecutionPayloadHeader` for the block being built +2. **blob_kzg_commitments**: The KZG commitments for any blobs attached to the + execution payload +3. **execution_requests**: The execution layer requests (deposits, withdrawals, + etc.) +4. **bid**: A `SignedExecutionPayloadBid` where: + - `builder_index` MUST be set to `BUILDER_INDEX_SELF_BUILD` + - Other fields follow the [Gloas consensus specs][gloas-builder-specs] + +The builder signs the `BuilderBid` message using its BLS private key. + +### Processing `submitBlockAndEnvelope` + +When a builder receives a `BlockAndBlindedEnvelope` via the +[`submitBlockAndEnvelope`][submit-block-and-envelope-api] API, it MUST: + +1. **Validate the signed beacon block**: Verify the block signature and ensure + the `ExecutionPayloadBid` in the block body matches the bid previously + provided + +2. **Validate the blinded envelope**: Verify that: + + - The `beacon_block_root` matches + `hash_tree_root(signed_beacon_block.message)` + - The `payload_root` matches the execution payload the builder constructed + - The `execution_requests_root` matches the execution requests + - The `blob_kzg_commitments_root` matches the commitments + - The signature is valid from the proposer + +3. **Construct the full envelope**: Create a `SignedExecutionPayloadEnvelope` + by: + + - Unblinding the payload root with the actual execution payload + - Including the full execution requests and blob commitments + - Copying the `beacon_block_root`, `slot`, `builder_index`, and `state_root` + - Signing with the builder's key + +4. **Broadcast**: Broadcast the `SignedExecutionPayloadEnvelope` to the PTC + committee via the `execution_payload_envelope` gossip topic + +### `verify_blinded_envelope_signature` + +*Note*: `compute_domain` and `compute_signing_root` are defined in the +[Gloas consensus specs][gloas-consensus-specs]. + +```python +def verify_blinded_envelope_signature( + state: BeaconState, + signed_envelope: SignedBlindedExecutionPayloadEnvelope, + proposer_index: ValidatorIndex, +) -> bool: + validator = state.validators[proposer_index] + pubkey = validator.pubkey + domain = compute_domain(DOMAIN_BEACON_PROPOSER) + signing_root = compute_signing_root(signed_envelope.message, domain) + return bls.Verify(pubkey, signing_root, signed_envelope.signature) +``` + +### `process_block_and_envelope` + +```python +def process_block_and_envelope( + state: BeaconState, + builder_bid: BuilderBid, + block_and_envelope: BlockAndBlindedEnvelope, + execution_payload: ExecutionPayload, + execution_requests: ExecutionRequests, + blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK], +) -> SignedExecutionPayloadEnvelope: + signed_block = block_and_envelope.signed_beacon_block + signed_blinded_envelope = block_and_envelope.signed_blinded_envelope + blinded_envelope = signed_blinded_envelope.message + + block = signed_block.message + proposer_index = block.proposer_index + + # Verify beacon block root matches + beacon_block_root = hash_tree_root(block) + assert blinded_envelope.beacon_block_root == beacon_block_root + + # Verify the bid in the block matches our bid + assert block.body.signed_execution_payload_bid == builder_bid.bid + + # Verify payload root matches + assert blinded_envelope.payload_root == hash_tree_root(execution_payload) + + # Verify execution requests root matches + assert blinded_envelope.execution_requests_root == hash_tree_root( + execution_requests + ) + + # Verify blob commitments root matches + assert blinded_envelope.blob_kzg_commitments_root == hash_tree_root( + blob_kzg_commitments + ) + + # Verify slot matches + assert blinded_envelope.slot == block.slot + + # Verify builder index is self-build + assert blinded_envelope.builder_index == BUILDER_INDEX_SELF_BUILD + + # Verify blinded envelope signature + assert verify_blinded_envelope_signature( + state, signed_blinded_envelope, proposer_index + ) + + # Construct the full execution payload envelope + envelope = ExecutionPayloadEnvelope( + payload=execution_payload, + execution_requests=execution_requests, + builder_index=BUILDER_INDEX_SELF_BUILD, + beacon_block_root=beacon_block_root, + blob_kzg_commitments=blob_kzg_commitments, + state_root=blinded_envelope.state_root, + ) + + # Sign and return + return sign_execution_payload_envelope(envelope) +``` + +## Constructing a `SignedExecutionPayloadEnvelope` + +After receiving and validating the `BlockAndBlindedEnvelope`, the builder +constructs a `SignedExecutionPayloadEnvelope` by unblinding the +`BlindedExecutionPayloadEnvelope` with the actual execution payload data. + +The specification for constructing a `SignedExecutionPayloadEnvelope` is +documented in the [Gloas consensus specs][gloas-builder-specs]. + +The builder MUST broadcast the `SignedExecutionPayloadEnvelope` to the PTC +committee via the `execution_payload_envelope` gossip topic. + +[get-builder-bid-api]: ./../../apis/builder/builder_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 +[submit-block-and-envelope-api]: ./../../apis/builder/block_and_envelope.yaml diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md new file mode 100644 index 00000000..109eed18 --- /dev/null +++ b/specs/gloas/validator.md @@ -0,0 +1,431 @@ + + + + +- [Gloas - Honest Validator](#gloas---honest-validator) + - [Introduction](#introduction) + - [Constants](#constants) + - [Containers](#containers) + - [New Containers](#new-containers) + - [`BidRequestAuth`](#bidrequestauth) + - [`SignedBidRequestAuth`](#signedbidrequestauth) + - [`BlindedExecutionPayloadEnvelope`](#blindedexecutionpayloadenvelope) + - [`SignedBlindedExecutionPayloadEnvelope`](#signedblindedexecutionpayloadenvelope) + - [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) + - [Validating a `SignedExecutionPayloadBid`](#validating-a-signedexecutionpayloadbid) + - [Block proposal](#block-proposal) + - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [Receiving ExecutionPayloadBid](#receiving-executionpayloadbid) + - [Receiving ExecutionPayloadBid from Unstaked Builder](#receiving-executionpayloadbid-from-unstaked-builder) + - [`process_blinded_execution_payload`](#process_blinded_execution_payload) + - [`construct_blinded_envelope`](#construct_blinded_envelope) + + + +# Gloas - Honest Validator + +## Introduction + +This document explains how a beacon-chain validator can participate in the +external block building market with the Builder-API post ePBS. + +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. + +## Constants + +| Name | Value | | ----------------------------------------- | +------------------ | | `MAX_SALT_BYTES` | `4096` | + +## Containers + +### New Containers + +### `BidRequestAuth` + +`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. + +```python +class BidRequestAuth(Container): + salt: ByteList[MAX_SALT_BYTES] +``` + +### `SignedBidRequestAuth` + +```python +class SignedBidRequestAuth(Container): + message: BidRequestAuth + signature: BLSSignature +``` + +### `BlindedExecutionPayloadEnvelope` + +```python +class BlindedExecutionPayloadEnvelope(Container): + payload_root: Root + execution_requests: ExecutionRequests + builder_index: BuilderIndex + beacon_block_root: Root + slot: Slot + blob_kzg_commitments_root: Root + state_root: Root +``` + +### `SignedBlindedExecutionPayloadEnvelope` + +```python +class SignedBlindedExecutionPayloadEnvelope(Container): + message: BlindedExecutionPayloadEnvelope + 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 `BidRequestAuth` + +To construct the `BidRequestAuth`, 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. + +## 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. +- `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 + 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 + +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. + +```python +def create_validator_registrations( + state: BeaconState, + 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] = [] + + 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 +``` + +## Validating a `SignedExecutionPayloadBid` + +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( + state: BeaconState, + reg: SignedValidatorRegistrationV2, + signed_bid: SignedExecutionPayloadBid, + fee_recipient: ExecutionAddress, +) -> bool: + bid = signed_bid.message + + 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 + + if bid.value > 0: + assert can_builder_cover_bid(state, bid.builder_index, bid.value) + + return verify_execution_payload_bid_signature(state, signed_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` + +#### Receiving ExecutionPayloadBid + +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. +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 + step. +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 + to the PTC committee. + +#### Receiving ExecutionPayloadBid from Unstaked Builder + +For unstaked builders, the flow is different. To obtain execution payloads from +an unstaked builder for a given `slot`, a block proposer building a block on top +of a beacon `state` must take the following actions: + +1. Call the unstaked builder to get a [`SignedBuilderBid`][signed-builder-bid] + using the [`getBuilderBid`][get-builder-bid-api] API call. The bid contains: + + - An `ExecutionPayloadHeader` + - Blob KZG commitments + - Execution requests + - A `SignedExecutionPayloadBid` with `builder_index` set to + `BUILDER_INDEX_SELF_BUILD` + +2. Assemble a `SignedBeaconBlock` according to the process outlined in the + [Gloas validator specs][gloas-validator-specs] using the + [`SignedExecutionPayloadBid`][signed-execution-payload-bid] from the builder + bid. + +3. Construct a `SignedBlindedExecutionPayloadEnvelope` by: + + - Setting `beacon_block_root` to + `hash_tree_root(signed_beacon_block.message)` + - Setting `payload_root` to the root of the execution payload (from header) + - Setting `execution_requests_root` to the root of execution requests + - Setting `blob_kzg_commitments_root` to the root of blob commitments + - Setting `builder_index` to `BUILDER_INDEX_SELF_BUILD` + - Setting `slot` to the block's slot + - Setting `state_root` to the post-state root + - Signing with the proposer's key + +4. Submit both the `SignedBeaconBlock` and + `SignedBlindedExecutionPayloadEnvelope` to the unstaked builder via the + [`submitBlockAndEnvelope`][submit-block-and-envelope-api] API call. + +5. The unstaked builder constructs the full + [`SignedExecutionPayloadEnvelope`][signed-execution-payload-envelope] by + unblinding the envelope and broadcasts it to the PTC committee. + +### `process_blinded_execution_payload` + +```python +def process_blinded_execution_payload( + state: BeaconState, + header: ExecutionPayloadHeader, + builder_bid: BuilderBid, + blinded_envelope: SignedBlindedExecutionPayloadEnvelope, +) -> None: + envelope = blinded_envelope.message + + # Cache latest block header state root + previous_state_root = hash_tree_root(state) + if state.latest_block_header.state_root == Root(): + state.latest_block_header.state_root = previous_state_root + + # Verify consistency with the beacon block + assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header) + assert envelope.slot == state.slot + + # Verify consistency with the committed bid + committed_bid = state.latest_execution_payload_bid + assert envelope.builder_index == committed_bid.builder_index + assert committed_bid.blob_kzg_commitments_root == hash_tree_root( + builder_bid.blob_kzg_commitments + ) + assert committed_bid.prev_randao == header.prev_randao + + # Verify consistency with expected withdrawals + assert header.withdrawals_root == hash_tree_root(state.payload_expected_withdrawals) + + # Verify the gas_limit + assert committed_bid.gas_limit == header.gas_limit + # Verify the block hash + assert committed_bid.block_hash == header.block_hash + # Verify consistency of the parent hash with respect to the previous execution payload + assert header.parent_hash == state.latest_block_hash + # Verify timestamp + assert header.timestamp == compute_time_at_slot(state, state.slot) + # Verify commitments are under limit + assert ( + len(builder_bid.blob_kzg_commitments) + <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block + ) + # Verify the execution payload is valid + versioned_hashes = [ + kzg_commitment_to_versioned_hash(commitment) + for commitment in builder_bid.blob_kzg_commitments + ] + + def for_ops( + operations: Sequence[Any], fn: Callable[[BeaconState, Any], None] + ) -> None: + for operation in operations: + fn(state, operation) + + for_ops(builder_bid.execution_requests.deposits, process_deposit_request) + for_ops(builder_bid.execution_requests.withdrawals, process_withdrawal_request) + for_ops(builder_bid.execution_requests.consolidations, process_consolidation_request) + + # Queue the builder payment + payment = state.builder_pending_payments[ + SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH + ] + amount = payment.withdrawal.amount + if amount > 0: + state.builder_pending_withdrawals.append(payment.withdrawal) + state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = ( + BuilderPendingPayment() + ) + + # Cache the execution payload hash + state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 + state.latest_block_hash = header.block_hash +``` + +### `construct_blinded_envelope` + +*Note*: `hash_tree_root` and `compute_domain` are defined in the +[Gloas consensus specs][gloas-consensus-specs]. + +```python +def construct_blinded_envelope( + state: BeaconState, + signed_block: SignedBeaconBlock, + builder_bid: BuilderBid, + privkey: int, +) -> SignedBlindedExecutionPayloadEnvelope: + block = signed_block.message + + blinded_envelope = BlindedExecutionPayloadEnvelope( + payload_root=hash_tree_root(builder_bid.header), + execution_requests_root=hash_tree_root(builder_bid.execution_requests), + builder_index=BUILDER_INDEX_SELF_BUILD, + beacon_block_root=hash_tree_root(block), + slot=block.slot, + blob_kzg_commitments_root=hash_tree_root(builder_bid.blob_kzg_commitments), + ) + + process_blinded_execution_payload_envelope( + state, builder_bid.header, builder_bid.requests, blinded_envelope + ) + + blinded_envelope.state_root = state.hash_tree_root() + + domain = compute_domain(DOMAIN_BEACON_PROPOSER) + signing_root = compute_signing_root(blinded_envelope, domain) + signature = bls.Sign(privkey, signing_root) + + return SignedBlindedExecutionPayloadEnvelope( + message=blinded_envelope, + signature=signature, + ) +``` + +The BeaconState passed to `construct_blinded_envelope` is the resulting state +after running `process_blinded_execution_payload`. + +[can-builder-cover-bid]: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/beacon-chain.md#can_builder_cover_bid +[get-builder-bid-api]: ./../../apis/builder/builder_bid.yaml +[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 +[signed-builder-bid]: ./unstaked_builder.md#signedbuilderBid +[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-block-and-envelope-api]: ./../../apis/builder/block_and_envelope.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/bid_request_auth.yaml b/types/gloas/bid_request_auth.yaml new file mode 100644 index 00000000..0393bcd9 --- /dev/null +++ b/types/gloas/bid_request_auth.yaml @@ -0,0 +1,17 @@ +Gloas: + BidRequestAuth: + 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: + type: object + required: [message, signature] + properties: + message: + $ref: "#/Gloas/BidRequestAuth" + signature: + $ref: "../../beacon-apis/types/primitive.yaml#/Signature" \ No newline at end of file diff --git a/types/gloas/blinded_envelope.yaml b/types/gloas/blinded_envelope.yaml new file mode 100644 index 00000000..887a2eec --- /dev/null +++ b/types/gloas/blinded_envelope.yaml @@ -0,0 +1,37 @@ +Gloas: + BlindedExecutionPayloadEnvelope: + type: object + description: "The `BlindedExecutionPayloadEnvelope` contains roots of execution payload components rather than full data. Used by validators to commit to an envelope without access to full payload." + required: [payload_root, execution_requests, builder_index, beacon_block_root, slot, blob_kzg_commitments_root, state_root] + properties: + payload_root: + $ref: "../../beacon-apis/types/primitive.yaml#/Root" + description: "Root of the execution payload." + execution_requests: + $ref: "../../beacon-apis/types/electra/requests.yaml#/Electra/ExecutionRequests" + description: "Execution layer requests (deposits, withdrawals, consolidations)." + builder_index: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + description: "Index of the builder. For unstaked builders, this MUST be BUILDER_INDEX_SELF_BUILD." + beacon_block_root: + $ref: "../../beacon-apis/types/primitive.yaml#/Root" + description: "Root of the beacon block this envelope corresponds to." + slot: + $ref: "../../beacon-apis/types/primitive.yaml#/Uint64" + description: "Slot of the block." + blob_kzg_commitments_root: + $ref: "../../beacon-apis/types/primitive.yaml#/Root" + description: "Root of the blob KZG commitments." + state_root: + $ref: "../../beacon-apis/types/primitive.yaml#/Root" + description: "Post-state root after applying the execution payload." + SignedBlindedExecutionPayloadEnvelope: + type: object + description: "The `SignedBlindedExecutionPayloadEnvelope` object containing a blinded envelope and proposer signature." + required: [message, signature] + properties: + message: + $ref: "#/Gloas/BlindedExecutionPayloadEnvelope" + signature: + $ref: "../../beacon-apis/types/primitive.yaml#/Signature" + diff --git a/types/gloas/builder_bid.yaml b/types/gloas/builder_bid.yaml new file mode 100644 index 00000000..8fa4e60e --- /dev/null +++ b/types/gloas/builder_bid.yaml @@ -0,0 +1,30 @@ +Gloas: + BuilderBid: + type: object + description: "The `BuilderBid` object for Gloas fork from unstaked builders. Contains execution payload header, blob commitments, execution requests, and a SignedExecutionPayloadBid." + required: [header, blob_kzg_commitments, execution_requests, bid] + properties: + header: + $ref: "../../beacon-apis/types/deneb/execution_payload.yaml#/Deneb/ExecutionPayloadHeader" + description: "The execution payload header for the proposed block." + blob_kzg_commitments: + type: array + items: + $ref: "../../beacon-apis/types/primitive.yaml#/KZGCommitment" + description: "KZG commitments for any blobs attached to the execution payload." + execution_requests: + $ref: "../../beacon-apis/types/electra/requests.yaml#/Electra/ExecutionRequests" + description: "Execution layer requests (deposits, withdrawals, consolidations)." + bid: + $ref: "../../beacon-apis/types/gloas/execution_payload_bid.yaml#/Gloas/SignedExecutionPayloadBid" + description: "The signed execution payload bid. builder_index MUST be set to BUILDER_INDEX_SELF_BUILD." + SignedBuilderBid: + type: object + description: "The `SignedBuilderBid` object for Gloas fork." + required: [message, signature] + properties: + message: + $ref: "#/Gloas/BuilderBid" + signature: + $ref: "../../beacon-apis/types/primitive.yaml#/Signature" + diff --git a/types/gloas/registration.yaml b/types/gloas/registration.yaml new file mode 100644 index 00000000..b0d46f4d --- /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: [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 diff --git a/wordlist.txt b/wordlist.txt index 970e86ca..353ab834 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -28,6 +28,12 @@ validator's vc wei EIP +ePBS Fulu fulu -submitBlindedBlockV \ No newline at end of file +Gloas +gloas +Gwei +PTC +submitBlindedBlockV +ValidatorRegistrationsV \ No newline at end of file