Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions apis/validator_builders.yaml
Original file line number Diff line number Diff line change
@@ -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"
8 changes: 8 additions & 0 deletions keymanager-oapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,6 +54,8 @@ paths:
$ref: './apis/local_keystores.yaml'
/eth/v1/remotekeys:
$ref: './apis/remote_keystores.yaml'
/eth/v1/validator/{pubkey}/builders:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe if we do allow a default config per pubkey that applies to all builders we should rename this to builder_config since builders would be a field of the request body @eth2353 suggested that naming earlier here #87 (comment)

$ref: './apis/validator_builders.yaml'
/eth/v1/validator/{pubkey}/feerecipient:
$ref: './apis/fee_recipient.yaml'
/eth/v1/validator/{pubkey}/gas_limit:
Expand All @@ -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:
Expand Down
136 changes: 136 additions & 0 deletions types/builder_entry.yaml
Original file line number Diff line number Diff line change
@@ -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:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this configuration seems to be fully tailored towards builders connected via builder-api, based on the original intend from ethereum/beacon-APIs#620, this doesn't seem to match up, I left a related comment here ethereum/beacon-APIs#625 (comment)

we also lose functionality we currently have, eg. the builder_boost_factor query param as defined in the beacon-api (on master branch) allows to specify how local payload vs. bids (from api or p2p) should be selected, but with this change, we have 0 ways to configure any bid selection strategy from p2p

maybe @potuz, @terence, or @james-prysm can clarify this, I thought this follows the prysm implementation but it doesn't at all match what potuz documented in ethereum/beacon-APIs#620 which I do prefer since it at least supported the full spectrum of builders, and not just the builder-api

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.
Comment on lines +88 to +93

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be simpler/better to just treat this auth_data field as optional? I assume custom auth data will not be set in the vast majority of cases.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thinking was if the key manager API required fully resolved fields, it simplified everything downstream since the request auth could be required by beacon api -> builder api and remove the need to reason about the cases where it's absent. If everyone used the default auth data they could just supply auth_data = builder_url as part of the keymanager call.

some motivation was talking with @james-prysm here where it was hard to reason about how to resolve configs when VCs have different defaults + keymanager fields were all optional

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does auth_data need to be required in the config though? we could still make it required in the call to the beacon node and default it to url if it's not explicitly set

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it isn't in the config, how could a node operator set their auth_data if they aren't using the default? I assumed it would have to be by manually inserting into the config which is what this PR helps make programmatic

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

operators can still set the auth_data if they wanna override the default (based on the url), but why does it need to be a required field to be configured by the user?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It definitely doesn't need to be required. I'm happy to make it and all of the fields that can as optional. It was just simpler if the key manager API supplied the VC with fully resolved values derived from the caller's config, so we don't have to reason about how missing fields in the key manager request body have to be resolved differently across all the different VC implementations

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do see your point but most users are just fine with the default that validator clients provide and each client will have their own defaults, eg. we currently don't force users to provide a gas limit in their config, and most don't and just go with whatever the client sets as default. so the vc can easily fill missing fields with default and supply all fields to the beacon node via api. but when it comes to configuring the validator client from a user perspective, I would prefer if users only have to configure the values they are interested in instead of having to provide the full set of options

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From your comment here, if we use builder_pubkey + the absence of a url as the way to tell the BN "use these config values when evaluating p2p bids from this builder" then I think it makes sense to switch all of these keymanager fields to optional, since you need to show the absence of something.

I'm pretty in favor of this. Not too sure who will want to set per-key, per-builder p2p bid settings but it is elegant that we can support both p2p bids / builder api bids symmetrically. (will build towards this if it sounds good)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we use builder_pubkey + the absence of a url as the way to tell the BN "use these config values when evaluating p2p bids from this builder"

if we want the mutually exclusive it could even be a config field like builder_id, which could be a url, or a pubkey, or even a builder index. but it's probably better to have separate fields, also when transmitting this to the beacon node, it would be good if we can support ssz encoding, and union or optional isn't widely supported and not part of standard spec, but for missing pubkey we can just set zero bytes for example

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed separate fields over builder_id since url and pubkey are complementary rather than strictly alternatives. In Terence's PoC the url is where you send and the pubkey is what you check came back, and a union can only carry one of those.

What I'm thinking is optional fields in keymanager and VC has to fully resolve before sending to BN. The one thing is we'll still need per-key min_bid and builder_boost_factor that apply to p2p bids from builders with no entry. From Luca's comment:

All per-key configurability in Vero is handled through the Keymanager API which is standardized across all CL/VC clients. This is the only way Vero allows node operators to specify certain things, like different fee recipient per validator key. I hate the idea of adding support for some kind of YAML file (that hasn't even been standardized) instead of doing this in a more standard way using the Keymanager API. We should seriously stop with these CL-client-specific YAML files, we have the standard Keymanager API for a reason.

So thinking to also add a way for the keymanager api to express the per-key p2p defaults.


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"
5 changes: 5 additions & 0 deletions wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ VPN
BLS
oapi
Vero
Gwei
uri
UTF
RequestAuthV
produceBlockV