diff --git a/apis/validator_builders.yaml b/apis/validator_builders.yaml new file mode 100644 index 0000000..552b82d --- /dev/null +++ b/apis/validator_builders.yaml @@ -0,0 +1,160 @@ +get: + operationId: getBuilders + summary: Get Builders + description: | + Get the builder configuration in effect for an individual validator key, with omitted values + resolved: an entry that omits a field is returned with the value that will be used for it, and a + key with no configuration of its own is returned with the validator client's. A returned + configuration is therefore fully resolved, so re-submitting it with `POST` fixes those values + rather than leaving them to follow the defaults. + security: + - bearerAuth: [] + tags: + - Builders + parameters: + - in: path + name: pubkey + schema: + $ref: "../keymanager-oapi.yaml#/components/schemas/Pubkey" + required: true + responses: + "200": + description: success response + content: + application/json: + schema: + title: GetBuildersResponse + type: object + required: [data] + properties: + data: + $ref: "../keymanager-oapi.yaml#/components/schemas/BuilderConfig" + examples: + BuilderConfig: + value: + data: + enabled: true + min_bid: "10000000" + builder_boost_factor: "100" + builders: + - url: "https://builder-a.example.com" + auth_data: "0x68747470733a2f2f6275696c6465722d612e6578616d706c652e636f6d" + max_execution_payment: "0" + min_bid: "0" + builder_boost_factor: "100" + - url: "https://builder-b.example.com" + auth_data: "0x1234567890abcdef" + builder_pubkey: "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a" + max_execution_payment: "250000000" + min_bid: "10000000" + builder_boost_factor: "100" + - builder_pubkey: "0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a" + max_execution_payment: "0" + min_bid: "20000000" + builder_boost_factor: "120" + "400": + $ref: "../keymanager-oapi.yaml#/components/responses/BadRequest" + "401": + $ref: "../keymanager-oapi.yaml#/components/responses/Unauthorized" + "403": + $ref: "../keymanager-oapi.yaml#/components/responses/Forbidden" + "404": + $ref: "../keymanager-oapi.yaml#/components/responses/NotFound" + "500": + $ref: "../keymanager-oapi.yaml#/components/responses/InternalError" + +post: + operationId: setBuilders + summary: Set Builders + description: | + Set the builder configuration for an individual validator key. The submission replaces the + key's stored configuration in full; the server MUST NOT merge it with what was stored before. + + Each entry MUST contain at least one of `url` and `builder_pubkey`. No two entries that contain + a `url` may share both that `url` and their `auth_data`, and no two entries without one may + share a `builder_pubkey`. The server MUST reject any of these with a 400 rather than storing a + partial list or silently dropping an entry. + + `POST` always leaves the key configured; use `DELETE` to remove the configuration so the key + follows the validator client again. An empty `builders` array is a configuration that names no + builders, not a removal. + security: + - bearerAuth: [] + tags: + - Builders + parameters: + - in: path + name: pubkey + schema: + $ref: "../keymanager-oapi.yaml#/components/schemas/Pubkey" + required: true + requestBody: + required: true + content: + application/json: + schema: + $ref: "../keymanager-oapi.yaml#/components/schemas/BuilderConfig" + examples: + BuilderConfig: + value: + enabled: true + min_bid: "10000000" + builder_boost_factor: "100" + builders: + - url: "https://builder-a.example.com" + max_execution_payment: "0" + - builder_pubkey: "0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a" + max_execution_payment: "0" + builder_boost_factor: "120" + responses: + "202": + description: successfully updated + "400": + $ref: "../keymanager-oapi.yaml#/components/responses/BadRequest" + "401": + $ref: "../keymanager-oapi.yaml#/components/responses/Unauthorized" + "403": + $ref: "../keymanager-oapi.yaml#/components/responses/Forbidden" + "404": + $ref: "../keymanager-oapi.yaml#/components/responses/NotFound" + "500": + $ref: "../keymanager-oapi.yaml#/components/responses/InternalError" + +delete: + operationId: deleteBuilders + summary: Delete Configured Builders + description: | + Remove this key's builder configuration entirely. The key then follows the validator client's + own configuration, in accordance with the startup of the validator client, exactly as a key that + was never configured does. + + This is not the same as `enabled: false`, which keeps a configuration saying this key sources no + builder bids at all. To stop using one builder while keeping the others, `POST` the remaining + entries instead. + security: + - bearerAuth: [] + tags: + - Builders + parameters: + - in: path + name: pubkey + schema: + $ref: "../keymanager-oapi.yaml#/components/schemas/Pubkey" + required: true + responses: + "204": + description: Successfully removed the builder configuration, or there was none set for the requested public key. + "400": + $ref: "../keymanager-oapi.yaml#/components/responses/BadRequest" + "401": + $ref: "../keymanager-oapi.yaml#/components/responses/Unauthorized" + "403": + description: A builder configuration was found, but cannot be removed. This may be because it was in configuration files that cannot be updated. + content: + application/json: + schema: + $ref: "../keymanager-oapi.yaml#/components/schemas/ErrorResponse" + "404": + $ref: "../keymanager-oapi.yaml#/components/responses/NotFound" + "500": + $ref: "../keymanager-oapi.yaml#/components/responses/InternalError" diff --git a/keymanager-oapi.yaml b/keymanager-oapi.yaml index a5422ab..5a1e84f 100644 --- a/keymanager-oapi.yaml +++ b/keymanager-oapi.yaml @@ -34,6 +34,8 @@ servers: default: "http://localhost/" tags: + - name: Builders + description: Set of endpoints for management of builders. - name: Fee Recipient description: Set of endpoints for management of fee recipient. - name: Gas Limit @@ -52,6 +54,8 @@ paths: $ref: './apis/local_keystores.yaml' /eth/v1/remotekeys: $ref: './apis/remote_keystores.yaml' + /eth/v1/validator/{pubkey}/builders: + $ref: './apis/validator_builders.yaml' /eth/v1/validator/{pubkey}/feerecipient: $ref: './apis/fee_recipient.yaml' /eth/v1/validator/{pubkey}/gas_limit: @@ -75,6 +79,10 @@ components: $ref: './types/eth_address.yaml' Keystore: $ref: './types/keystore.yaml' + BuilderConfig: + $ref: './types/builder_entry.yaml#/BuilderConfig' + BuilderEntry: + $ref: './types/builder_entry.yaml#/BuilderEntry' FeeRecipient: $ref: './types/fee_recipient.yaml' GasLimit: diff --git a/types/builder_entry.yaml b/types/builder_entry.yaml new file mode 100644 index 0000000..aa8e957 --- /dev/null +++ b/types/builder_entry.yaml @@ -0,0 +1,136 @@ +BuilderConfig: + type: object + description: | + How a validator key sources blocks from builders. + + `enabled: false` stops this key sourcing builder bids at all, including from any builders the + validator client is configured with globally. This is how a single key opts out of an otherwise + global builder setup, and differs from having no configuration, which follows the validator + client in full. + + When `enabled` is `true`, `builders` selects which builders this key uses. + + `BuilderConfig` carries a `min_bid` and a `builder_boost_factor` that are this key's defaults. + An entry that omits its own takes the default, so one value covers every builder without being + repeated on each entry, and the defaults also apply to bids from builders that no entry names. + + The validator client resolves every entry before it reaches the beacon node. An omitted + `min_bid` or `builder_boost_factor` takes this key's default, and then the validator client's + own configuration; an omitted `max_execution_payment` or `auth_data` takes the validator + client's configuration. An omitted `url` or `builder_pubkey` is not filled in: its absence is + what tells the beacon node that the entry requests no bid, or supplies no p2p policy. + required: [enabled] + properties: + enabled: + type: boolean + description: | + Whether blocks may be sourced from builders for this key. + example: true + builders: + type: array + maxItems: 64 # MAX_BUILDER_ENTRIES + description: | + The builders this key sources bids from, replacing any the validator client is configured + with. Omit this field to use the validator client's builders instead. Send an empty array to + use none, so no bid is requested from any builder and p2p bids remain this key's only + source. + items: + $ref: "../keymanager-oapi.yaml#/components/schemas/BuilderEntry" + min_bid: + allOf: + - $ref: "../keymanager-oapi.yaml#/components/schemas/Uint64" + - description: | + This key's default `min_bid`, in Gwei. Applies to any entry that omits its own, and to + a bid from a builder no entry identifies. + example: "10000000" + builder_boost_factor: + allOf: + - $ref: "../keymanager-oapi.yaml#/components/schemas/Uint64" + - description: | + This key's default `builder_boost_factor`. Applies to any entry that omits its own, and + to a bid from a builder no entry identifies. + example: "100" + +BuilderEntry: + type: object + description: | + One builder a validator key may source blocks from. At least one of `url` and `builder_pubkey` + MUST be present. + + An entry that contains a `url` is a bid request, and no two of them may share both that `url` + and their `auth_data`. `url` is compared as the exact string and `auth_data` as the decoded + bytes, so hex case does not distinguish entries, and an omitted `auth_data` is compared as the + value the validator client would derive for it. One request is made per entry, so several + entries MAY share a `url` with different `auth_data`, since several builders may be reachable + at one `url`. An entry with no `url` supplies p2p policy instead, and no two of them may share + a `builder_pubkey`. + + See [produceBlockV4](https://ethereum.github.io/beacon-APIs/#/Validator/produceBlockV4) for the + selection rule. + properties: + url: + type: string + format: uri + maxLength: 2048 # MAX_BUILDER_URL_SIZE + description: | + The URL this entry's requests are sent to. Omit it to supply policy for this builder's p2p + bids without requesting one. + example: "https://builder.example.com" + auth_data: + type: string + pattern: "^0x(?:[a-fA-F0-9]{2}){0,4096}$" + description: | + Opaque data used to authenticate this validator's requests to the builder. Agreed with the + builder out of band and placed verbatim in the `RequestAuthV1.data` the validator signs. The + builder checks the signature and the exact bytes. Its meaning is left to the two parties + (for example, a shared secret). At most `MAX_DATA_SIZE` (4096) bytes. + + The keymanager treats this value as opaque bytes: it only ever compares `auth_data` values + for equality, and never parses or inspects them. When no value has been agreed with the + builder out of band, the caller SHOULD set `auth_data` to the UTF-8 bytes of the builder's + own advertised URL, exactly as advertised, hex encoded: it is deterministic, distinct per + builder, and requires no coordination between the signers of a distributed validator. That + is purely a convention about what tooling writes into the field. + + Omitting it on an entry that contains a `url` leaves the validator client to derive it the + same way, since the builder requires authentication on every bid request. An entry with no + `url` requests no bid, so it needs no `auth_data`. + example: "0x68747470733a2f2f6275696c6465722e6578616d706c652e636f6d" + builder_pubkey: + description: | + The builder's BLS public key, distinct from the validator `pubkey` in the request path. On + an entry with a `url` it filters the response: a builder-API bid not signed by it MUST NOT + be accepted. On an entry without one it identifies the builder whose p2p bids this entry + applies to. _48-bytes, hex encoded with 0x prefix, case insensitive._ + allOf: + - $ref: "../keymanager-oapi.yaml#/components/schemas/Pubkey" + max_execution_payment: + allOf: + - $ref: "../keymanager-oapi.yaml#/components/schemas/Uint64" + - description: | + A ceiling, in Gwei, on the trusted execution payment accepted from this builder. The + remainder of its payment is expected in the trustless (in-protocol) form. + + `0` accepts no execution payments from this builder, requiring the whole payment to be + trustless. `MAX_EXECUTION_PAYMENT` (`2**64 - 1`) accepts any amount. + example: "250000000" + min_bid: + allOf: + - $ref: "../keymanager-oapi.yaml#/components/schemas/Uint64" + - description: | + A floor, in Gwei, on the total payment accepted from this builder, counting a bid's + `value` plus its `execution_payment`. + example: "10000000" + builder_boost_factor: + allOf: + - $ref: "../keymanager-oapi.yaml#/components/schemas/Uint64" + - description: | + Percentage multiplier weighting this builder's bid against the local payload. Below + `100` favors the local payload, above `100` favors this builder. + + Values that encode common preferences: + + * `0`: prefer the local payload unless an error makes it unviable. + * `100`: profit maximization; choose whichever pays more. + * `2**64 - 1`: prefer this builder unless an error or health check makes it unviable. + example: "100" diff --git a/wordlist.txt b/wordlist.txt index 2156f88..0e186f4 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -19,3 +19,8 @@ VPN BLS oapi Vero +Gwei +uri +UTF +RequestAuthV +produceBlockV