-
Notifications
You must be signed in to change notification settings - Fork 220
Update Beacon API for parity with Builder API and KeyManager API #630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
5daf8a7
21750d8
f8d2943
0ea8a06
adc1ae1
1028934
657d390
727f6b3
23b5555
3f886eb
979d038
ea9bafb
bb1e790
bd99f03
91db6c8
97c1cd9
f71b1b8
eb752a1
1dda02d
71c7315
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,30 @@ post: | |
| required: true | ||
| name: Eth-Consensus-Version | ||
| description: "The active consensus version to which the block being submitted belongs." | ||
| - name: Eth-Builder-Url | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we really need this header? if I understand correctly the sole purpose of this is so that the beacon node knows where to submit it via the only case where the beacon node doesn't know this is if it's a different node (eg. in a multi-node setup) if the primary node that sourced the bid from the builder via api went offline during the proposal flow but this seems like an edge case and even then, publishing only via p2p is a fine fallback behavior that is sufficient anyways most of the time that said, we can keep the header if people wanna have it, just questionable to me if it's useful enough
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this was solely for the multi-node support, not opinionated on it but it seemed like DVT teams wanted. Will leave for now and address most of the fat trimming comments
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not feel strongly about removing it, also I think when I commented there was in addition a |
||
| in: header | ||
| required: false | ||
| description: | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's required false but maybe we should specify that we must echo this and eth-builder-proxy when available if returned by produceBlockv4 just to be explicit |
||
| The `url` of the winning builder, as returned in the `Eth-Builder-Url` response header of | ||
| `produceBlockV4`. The beacon node forwards the signed block to this builder via the | ||
| builder-API `submitSignedBeaconBlock` so the builder can release the execution payload. | ||
| Omitted for a self-built block. | ||
| schema: | ||
| type: string | ||
| format: uri | ||
| example: "https://builder.example.com" | ||
| - name: Eth-Builder-Proxy | ||
| in: header | ||
| required: false | ||
| description: | | ||
| An optional proxy URL for the winning builder, as returned in the `Eth-Builder-Proxy` | ||
| response header of `produceBlockV4`. When set, the beacon node sends the request to this URL | ||
| and sets the `Eth-Builder-Url` header to the builder's `url` so the proxy can forward the | ||
| request to the intended builder; `Eth-Builder-Url` MUST therefore also be present. | ||
| schema: | ||
| type: string | ||
| format: uri | ||
| example: "http://side-car:9001" | ||
| requestBody: | ||
| description: "The `SignedBeaconBlock` object composed of `BeaconBlock` object (produced by beacon node) and validator signature." | ||
| required: true | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,25 +1,36 @@ | ||
| get: | ||
| post: | ||
| tags: | ||
| - Validator | ||
| - ValidatorRequiredApi | ||
| operationId: "produceBlockV4" | ||
| summary: "Produce a new block, without signature." | ||
| description: | | ||
| Requests a beacon node to produce a valid block, which can then be signed by a validator. | ||
| Requests a beacon node to produce a valid block, which the validator then signs. | ||
|
|
||
| Post-Gloas, proposers submit execution payload bids rather than full execution payloads, | ||
| so there is no longer a concept of blinded or unblinded blocks. Builders release the | ||
| payload later. This endpoint is specific to the post-Gloas forks and is not backwards compatible | ||
| with previous forks. | ||
| The beacon node always builds a local payload and considers any p2p bid. The validator optionally | ||
| supplies a list of `BuilderEntry` objects in the request body, one per external builder to also | ||
| request a bid from; the beacon node adds those bids to the comparison and returns the | ||
| highest-value candidate. It biases each builder's bid by the entry's `builder_boost_factor` and | ||
| rejects a bid that falls outside the entry's `min_bid` (floor) or `max_execution_payment` (ceiling | ||
| on the trusted execution payment). `builder_boost_factor` leans the choice toward the local | ||
| payload or a given builder without disabling either source. | ||
|
|
||
| When self-building (local execution payload), the response includes the full block contents | ||
| (beacon block, execution payload envelope, blobs, and KZG proofs) if `include_payload` is | ||
| set to `true`, otherwise only the `BeaconBlock` is returned. | ||
| When using an external builder bid, only the `BeaconBlock` is returned as the beacon node | ||
| does not have access to the builder's execution payload. | ||
| Post-Gloas, proposers submit execution payload bids rather than full execution payloads, so there | ||
| is no longer a blinded or unblinded distinction; builders release the payload later. This endpoint | ||
| is specific to the post-Gloas forks and is not backwards compatible with previous forks. | ||
|
|
||
| The `Eth-Execution-Payload-Included` header and `execution_payload_included` response field | ||
| indicate which response type was returned. | ||
| The beacon node returns the full block contents (beacon block, execution payload envelope, blobs, | ||
| and KZG proofs) only when it self-builds and `include_payload` is `true`; otherwise it returns only | ||
| the `BeaconBlock`. The `Eth-Execution-Payload-Included` header and `execution_payload_included` | ||
| field report which form was returned. For a block built on an external builder's bid the beacon | ||
| node has no payload to return, and the `Eth-Builder-Url` response header (and the optional | ||
| `Eth-Builder-Proxy`) identifies the winning builder for the validator to echo when publishing. | ||
|
|
||
| The two outcomes publish differently and never overlap. For an external builder's bid, the | ||
| validator publishes the signed block via `publishBlockV2`, echoing the builder headers, and the | ||
| winning builder releases the execution payload envelope. For a self-built block, the validator | ||
| publishes the signed block via `publishBlockV2` and the execution payload envelope via | ||
| `publishExecutionPayloadEnvelope`. | ||
| parameters: | ||
| - name: slot | ||
| in: path | ||
|
|
@@ -45,59 +56,46 @@ get: | |
| in: query | ||
| required: true | ||
| description: | | ||
| Controls whether the execution payload envelope and blobs are included in the response | ||
| when self-building (using local execution payload). | ||
| Whether a self-built block's response carries the execution payload inline, or omits it for | ||
| the beacon node to cache and the validator to retrieve separately. | ||
|
|
||
| When `true`, the response includes the full block contents: beacon block, | ||
| execution payload envelope, blobs, and KZG proofs. This enables stateless operation | ||
| where the validator client can use multiple beacon nodes (multi-BN setups, distributed validators, failover). | ||
| When `true`, a self-built response includes the full block contents (beacon block, execution | ||
| payload envelope, blobs, and KZG proofs) and reports `Eth-Execution-Payload-Included: true`. | ||
| The validator holds the whole payload, so it can sign and publish the envelope through any | ||
| beacon node (stateless operation: multi-BN setups, distributed validators, failover). | ||
|
|
||
| When `false`, only the beacon block is returned and the beacon node caches the execution | ||
| payload envelope and blobs internally. The validator client must then fetch them separately | ||
| via `GET /eth/v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root}`. This saves | ||
| bandwidth but requires the validator client to publish via the same beacon node that | ||
| produced the block (stateful operation). | ||
| When `false`, a self-built response returns only the beacon block, reporting | ||
| `Eth-Execution-Payload-Included: false`; the beacon node caches the execution payload envelope | ||
| and blobs. The validator retrieves the envelope via `getExecutionPayloadEnvelope` | ||
| (`GET /eth/v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root}`), signs it, and | ||
| publishes it via `publishExecutionPayloadEnvelope`, and the producing beacon node attaches its | ||
| cached blobs and KZG proofs before broadcasting. This sends fewer bytes but ties publishing to | ||
| that one beacon node (stateful operation). | ||
|
|
||
| This parameter only affects self-building scenarios. When using an external builder's bid, | ||
| only the beacon block is returned regardless of this parameter (the beacon node does not | ||
| have access to the builder's execution payload). | ||
| This parameter is independent of the request body and only affects the self-built case: the | ||
| `BuilderEntry` list selects which external builders to consider, and when an external builder's | ||
| bid wins the beacon node returns only the beacon block regardless of this parameter (it does | ||
| not have that builder's execution payload). | ||
| schema: | ||
| type: boolean | ||
| - name: builder_boost_factor | ||
| in: query | ||
| required: false | ||
| description: | | ||
| Percentage multiplier to apply to the builder's bid value when choosing between a | ||
| builder bid and payload from the paired execution node. This parameter is only relevant | ||
| if the beacon node has at least one viable builder `ExecutionPayloadBid` available and | ||
| receives a valid response from the paired execution node. When these preconditions are | ||
| met, the server MUST act as follows: | ||
|
|
||
| * if `exec_node_payload_value >= builder_boost_factor * (builder_bid_value // 100)` | ||
| for the highest builder bid known to the beacon node, then return a block committing | ||
| to the local execution node payload (with the payload itself included if | ||
| `include_payload` is set to true). | ||
| * otherwise, return a block committing to the builder bid (without execution payload, | ||
| as it is not yet available). | ||
|
|
||
| Servers must support the following values of the boost factor which encode common | ||
| preferences: | ||
|
|
||
| * `builder_boost_factor=0`: prefer the local execution node payload unless an error makes | ||
| it unviable. | ||
| * `builder_boost_factor=100`: profit maximization mode; choose whichever of the local | ||
| execution node payload and the builder bid pays more. | ||
| * `builder_boost_factor=2**64 - 1`: prefer the builder bid unless an error or beacon node | ||
| health check makes it unviable. | ||
|
|
||
| Servers should use saturating arithmetic or another technique to ensure that large values | ||
| of the `builder_boost_factor` do not trigger overflows or errors. If this parameter is | ||
| provided and no builder bid is available, the beacon node MUST respond with a block | ||
| committing to the local execution node payload, which the caller can choose to reject if | ||
| it wishes. If the value is provided but out of range for a 64-bit unsigned integer, then | ||
| an error response with status code 400 MUST be returned. | ||
| schema: | ||
| $ref: "../../beacon-node-oapi.yaml#/components/schemas/Uint64" | ||
| requestBody: | ||
| required: false | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. while this request body is not required, we should make the arguably it's only needed if also worth considering making passing |
||
| description: | | ||
| An optional list of `BuilderEntry` objects, one per external builder to request a bid from, | ||
| added to the beacon node's local build and any p2p bid when selecting a payload. Each entry | ||
| names a concrete builder `url`, and no two entries may share the same `url`. For each entry the | ||
| beacon node contacts the builder at its `proxy` if set, otherwise at its `url`; forwards the | ||
| entry's `auth` unchanged; and applies the entry's `max_execution_payment`, `min_bid`, and | ||
| `builder_boost_factor` when comparing its bid. Malformed entries are ignored rather than failing | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this says ignore malformed entries but submit builder preferences has a hard 400 error, maybe those should be ignored too instead?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. submitBuilderPreferences is now explicitly best-effort/non-atomic to match produceBlockV4 |
||
| the request, so a single bad entry never blocks block production. When the body is omitted or | ||
| empty, or every entry is ignored, the beacon node contacts no external builders and chooses | ||
| between its local build and any p2p bid. | ||
| content: | ||
| application/json: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wait we should also allow ssz |
||
| schema: | ||
| type: array | ||
| items: | ||
| $ref: "../../beacon-node-oapi.yaml#/components/schemas/Gloas.BuilderEntry" | ||
| responses: | ||
| "200": | ||
| description: Success response | ||
|
|
@@ -108,6 +106,29 @@ get: | |
| $ref: '../../beacon-node-oapi.yaml#/components/headers/Eth-Consensus-Block-Value' | ||
| Eth-Execution-Payload-Included: | ||
| $ref: '../../beacon-node-oapi.yaml#/components/headers/Eth-Execution-Payload-Included' | ||
| Eth-Builder-Url: | ||
| description: | | ||
| For a block built on an external builder's bid, the `url` of the winning builder. The | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this be clarified that it's won via the builder api bid channel and not that an external builder won ? or not that it's present only when builder api is used
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated in f8d2943 |
||
| validator echoes this value in the `Eth-Builder-Url` request header when publishing the | ||
| signed block, so the beacon node can forward the block to the same builder via the | ||
| builder-API `submitSignedBeaconBlock`. Absent when the block was self-built (no external | ||
| builder bid was used). | ||
| required: false | ||
| schema: | ||
| type: string | ||
| format: uri | ||
| example: "https://builder.example.com" | ||
| Eth-Builder-Proxy: | ||
| description: | | ||
| The proxy URL the beacon node used to reach the winning builder, when it reached the | ||
| builder through a proxy. The validator echoes it in the `Eth-Builder-Proxy` request header | ||
| when publishing. Present only alongside `Eth-Builder-Url`. Absent for a builder contacted | ||
| directly at its `url`, and for a self-built block. | ||
| required: false | ||
| schema: | ||
| type: string | ||
| format: uri | ||
| example: "http://side-car:9001" | ||
| content: | ||
| application/json: | ||
| schema: | ||
|
|
@@ -127,9 +148,9 @@ get: | |
| type: boolean | ||
| description: | | ||
| Indicates whether the execution payload envelope is included in the response. | ||
| When `true`, the `data` field contains a `BlockContents` object with the | ||
| beacon block, execution payload envelope, KZG proofs, and blobs. When `false`, | ||
| the `data` field contains only a `BeaconBlock`. | ||
| When `true`, the `data` field contains a `BlockContents` object with the beacon | ||
| block, execution payload envelope, blobs, and KZG proofs. When `false`, the `data` | ||
| field contains only a `BeaconBlock`. | ||
|
nflaig marked this conversation as resolved.
Outdated
|
||
| example: false | ||
| data: | ||
| anyOf: | ||
|
|
@@ -139,7 +160,9 @@ get: | |
| schema: | ||
| description: "SSZ serialized `BeaconBlock` or `BlockContents` bytes. Use Accept header to choose this response type, version string is sent in header `Eth-Consensus-Version` and payload inclusion indicated by `Eth-Execution-Payload-Included` header." | ||
| "400": | ||
| description: "Invalid block production request" | ||
| description: | | ||
| The request itself is invalid, for example an unparseable body, an invalid slot, or two | ||
| entries sharing the same `url`. An individual malformed `BuilderEntry` is ignored, not a 400. | ||
| content: | ||
| application/json: | ||
| schema: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| post: | ||
| operationId: "submitBuilderPreferences" | ||
| summary: "Submit builder preferences via the beacon node." | ||
| description: | | ||
| Submits a proposer's per-builder preferences to the builders on the validator's behalf, including | ||
| the `max_execution_payment` the proposer is willing to accept from each. The validator does not | ||
| contact builders directly: for each `BuilderPreferenceEntry` the beacon node makes one | ||
| `submitBuilderPreferences` builder-API call, contacting the builder at its `proxy` if set and its | ||
| `url` otherwise, forwarding the entry's `auth` unchanged, and submitting its | ||
| `max_execution_payment`. The `pubkey` path parameter is passed as the builder-API | ||
| `validator_pubkey`, and each entry's `auth.message.slot` is the proposal slot the preferences | ||
| apply to. | ||
|
|
||
| Validators MAY call this endpoint in the epoch prior to the epoch in which they will be proposing, | ||
| as determined from `state.proposer_lookahead`, so that builders have the preferences before the | ||
| bid request arrives. | ||
|
|
||
| `BuilderPreferencesRequestV1` is not fork-versioned, so no `Eth-Consensus-Version` header is | ||
| required. | ||
|
|
||
| This API is applicable from the Gloas fork onwards. | ||
| parameters: | ||
| - name: pubkey | ||
| in: path | ||
| required: true | ||
| description: | | ||
| The BLS public key of the proposing validator whose preferences these are. The beacon node | ||
| passes it as the `validator_pubkey` of each `submitBuilderPreferences` builder-API call, and | ||
| each entry's `auth` is signed by this key. | ||
| schema: | ||
| $ref: "../../types/primitive.yaml#/Pubkey" | ||
| tags: | ||
| - Validator | ||
| requestBody: | ||
| required: true | ||
| description: | | ||
| An array of `BuilderPreferenceEntry` objects, one per builder the beacon node should submit | ||
| preferences to. Each entry names a concrete builder `url`, and no two entries may share the | ||
| same `url`. The beacon node submits each entry independently, so a malformed entry does not | ||
| prevent the others from being submitted; a 400 response identifies the failing entries by index. | ||
| content: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this api is missing fork versioning, wouldn't it be good to keep the door open to easily update how to deal with this on the builder-api side can be different, but I would just add a fork version to leave the door open to add a new container in heze, there isn't much harm to add this metadata
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@JasonVranek let's discussion the versioning here, so from my experience, using a in my opinion and this has worked quite nicely so far is to use the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
yes I believe I'm doing this exactly, just to verify:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes sounds good to me, having |
||
| application/json: | ||
| schema: | ||
| type: array | ||
| items: | ||
| $ref: "../../beacon-node-oapi.yaml#/components/schemas/Gloas.BuilderPreferenceEntry" | ||
| responses: | ||
| "200": | ||
| description: "The beacon node submitted the preferences to every builder." | ||
| "400": | ||
| description: "One or more preference submissions were rejected. The index identifies the failing entry." | ||
| content: | ||
| application/json: | ||
| schema: | ||
| $ref: "../../beacon-node-oapi.yaml#/components/schemas/IndexedErrorMessage" | ||
| "500": | ||
| $ref: "../../beacon-node-oapi.yaml#/components/responses/InternalError" | ||
| "503": | ||
| $ref: "../../beacon-node-oapi.yaml#/components/responses/CurrentlySyncing" | ||
Uh oh!
There was an error while loading. Please reload this page.