Skip to content
Closed
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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ There are likely to be descriptions etc outside of the list below, but new query
| [#608](https://github.com/ethereum/beacon-APIs/pull/608) `POST /eth/v1/validator/prepare_beacon_proposer` deprecated | | | | | |
| [#608](https://github.com/ethereum/beacon-APIs/pull/608) `POST /eth/v1/validator/register_validator` deprecated | | | | | |
| [#608](https://github.com/ethereum/beacon-APIs/pull/608) `POST /eth/v1/validator/beacon_committee_subscriptions` updated | | | | | |
| [#XXX](https://github.com/ethereum/beacon-APIs/pull/XXX) `GET /eth/v4/validator/blocks/{slot}` changed to `POST /eth/v4/validator/blocks/{slot}` | | | | | |

The Following are no longer in the Standard API, removed since the latest version.

Expand Down
57 changes: 21 additions & 36 deletions apis/validator/block.v4.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
get:
post:
tags:
- Validator
- ValidatorRequiredApi
Expand All @@ -7,6 +7,11 @@ get:
description: |
Requests a beacon node to produce a valid block, which can then be signed by a validator.

The validator supplies in the request body a list of `BuilderPreferences`, one for each
builder that will be contacted, which the beacon node MUST apply when requesting bids from
external builders. The `builder_boost_factor` is expressed per builder in the request body
rather than as a query parameter.
Comment on lines +12 to +13

@nflaig nflaig Jul 26, 2026

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.

The builder_boost_factor is expressed per builder

today we use this parameter as a global flag (applied to all builders) to let the operator decide their block production strategy

eg. you can set builder_boost_factor=0 which basically says "always pick local unless the EL fails to produce the payload", or builder_boost_factor=90 which says "pick local payload if builder payload(s) are only marginally more valuable"

having builder_boost_factor and also min_bid expressed per builder works with directly connected builders via api, but how does that work with p2p builders?

I think it would be good to have a way to globally apply builder boost factor and min bid

in #620 there is the following statement

The default BuilderPreferences can be sent explicitly as a default to the endpoint or just be assigned to the builder_id being the max uint.

this seems to suggest a way to do this? I don't see it expressed in this PR though

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Definitely an oversight on my side since I based 630 on 625.

To match the current conventions in beacon 630 / builder 165 / keymanager 88, p2p bids would be configurable per-key, per-builder like: "validator X accepts p2p bids from builder Y only above this min_bid, weighted by this builder_boost_factor." I'm personally not sure that's worth it. p2p bids are entirely trustless payments so a Gwei from one p2p builder is the same as a Gwei from another, and there's no trust dimension left to express per builder. The thing you might genuinely want to differentiate on is censorship but you can't read that off a bid. If the node operator's goal is to "blacklist" specific p2p builders, the builders could always get around by rotating keys. Also most builders worth configuring individually would likely also be reachable over the builder API, where the full knob set already applies.

I'm more partial to per-key p2p preferences. The pragmatic version is to bring back something like the builder_boost_factor query param on produceBlockV4 and treat these as per-key p2p knobs. Then either VCs hold them in their own config as a global, or we extend setBuilders here to carry them per key.

Curious what others think.

@nflaig nflaig Jul 26, 2026

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.

"validator X accepts p2p bids from builder Y only above this min_bid, weighted by this builder_boost_factor." I'm personally not sure that's worth it

yes I don't think it's worth it but neither is that worth it on the builder-api, this is not used today and will not be useful after gloas, builder boost factor and also min-bid is very hard to configure even just for local vs. builder payload, having this per builder doesn't seem very useful, but I rather implement that at this point and trying to debate it. But I don't see why we don't wanna support the full spectrum, all that seems to be required is add a builder_pubkey field, and make url optional and if configured the beacon-node reaches out to the builder via api too + enforcing same policy to the bids of that builder on p2p

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Following this thread, I updated #630 and #88 to offer the same config knobs for p2p bids. tl;dr is including builder_pubkey and excluding url means your BuilderEntry supplies min_bid and builder_boost_factor constraints on the p2p bids coming from builder_pubkey so the BN can apply them when determining the winning bid. Keymanager then needed to change to allow defaults to be configured.

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.

was thinking about this more and getting more convinced about your point of not needing per-BuilderEntry for p2p builders, maybe that also keeps the configuration simpler for users if we only allow global builder_boost_factor and min_bid to affect p2p bids while for directly connected builders we keep the per-builder configs. So since you made changes to support global parameters now, we might be fine with those only and can avoid overloading the usage of builder_pubkey.

one of the reasons is also the following p2p rule

[IGNORE] this bid is the highest value bid seen for the tuple (bid.slot, bid.parent_block_hash, bid.parent_block_root)

since builder_boost_factor and min_bid are local only, it would be kinda random based on how bids are propagated, so even if you boost a certain builder's bid by 100x, it might not even reach your node due to the rule above, so it might be better from a user perspective to see p2p bids as essentially a single (the highest value) bid as coming from a single builder (not sure that framing makes sense).

The use cases outlined by potuz in #620 sound pretty complete to me already, so if we can cover those which I think we can now since we added the global parameters, then we are kinda getting close to the final design we want.

image

@JasonVranek maybe you can double check this

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If I remove the per-builder p2p configs in favor for just the global it would definitely simplify. Overloading builder_pubkey is confusing / error prone. Regarding this list, almost everything would be covered.

The one exception is by dropping per-builder p2p configs, you can't blacklist a specific p2p builder by pubkey via config. But like you said the p2p rule might make this moot anyways.

I'll make the simplification across the PRs today (I think the keymanager interface can stay as-is since I think it's important for the API to be able to configure the global p2p defaults).

@nflaig nflaig Jul 29, 2026

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.

you can't blacklist a specific p2p builder by pubkey via config.

@potuz is this something we need/want? I feel like this isn't that relevant

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.

I don't see why we should have a separate object for p2p builders and direct-connection builders. In fact I kinda don't like enshrining this separation in the beacon API spec (I guess it's fine in the builder API one). There are reasons to want to have a per-builder config on the beacon API even if they are being applied to p2p builders. The reason being that specialized types of builders become viable in Gloas. Imagine an application that settles on-chain, doesn't care about latency and has purely private order flow (a chess-like game). This application may want to eventually submit full blocks on the P2P stack with a decent bid. At times of low congestion they may actually win since their POF would be superior to the regular builders. I hope many different dapps that don't have strong pressure for short inclusion may want to move to such a system and can advertise a p2p builder.

In such a case some validators may want to boost some of these apps. This is just one example of that type of builders. Another one would be a client team having an altruistic builder, purely trusted not to front-run and sending cheap bids and returning tips to participants. Some people may want to boost these types of known altruistic builders setting the "floor" on the auction.

At any rate, I dot not see any benefit in not allowing this on the API itself. A client that wants to use a global default just needs to fill all values with the same and then add a canary "default entry" that has the same values.

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.

@potuz yeah I am going back and forth on this whether it's useful, do you have good design in mind how we can express that over the api? my idea was just to overload the use of builder_pubkey is pubkey the right one to use, or should it be by index for p2p builders? This PR currently doesn't support per-p2p-builder preferences, the one that @JasonVranek didn't support it either, then I brought it up there and current state should support it. I am just not sure right now what is the clean design for users to configure on the keymanager and also to support it over the wire

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

#630 does currently support per-builder p2p bidding configs. Might be easiest to understand the complexity/footguns via this implementer guide.

In other words, as-is, 630 checks off anything in the list at the cost of some extra complexity. I'm happy to edit it if the complexity isn't justified.

Personally I feel like today's profile of builders / node operators / apps likely won't use these knobs, but maybe something like what @potuz described could be desirable in the future. So I'd lean towards keeping our options open, especially now when it's arguably the least friction time to add this. That being said, I'm not opinionated on p2p configs and this seems more like a client dev workload question.


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
Expand Down Expand Up @@ -63,41 +68,21 @@ get:
schema:
type: boolean
default: true
- 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_payload_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 payload 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: true
description: |
A list of `BuilderPreferences`, one entry for each builder that will be contacted. When
contacting a builder, the beacon node MUST apply the entry whose `url` matches the URL used
to contact that builder. An entry with an empty `url` string denotes the default preferences,
which the beacon node MUST apply to any contacted builder for which no matching entry was
supplied. At most one entry with an empty `url` may be supplied, and no two entries may share
the same `url`.
content:
application/json:
schema:
type: array
items:
$ref: "../../beacon-node-oapi.yaml#/components/schemas/BuilderPreferences"
responses:
"200":
description: Success response
Expand Down
6 changes: 6 additions & 0 deletions beacon-node-oapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,12 @@ components:
$ref: "./types/fulu/data_column_sidecar.yaml#/Fulu/DataColumnSidecars"
Gloas.DataColumnSidecars:
$ref: "./types/gloas/data_column_sidecar.yaml#/Gloas/DataColumnSidecars"
Gloas.RequestAuthV1:
$ref: "./types/gloas/request_auth.yaml#/Gloas/RequestAuthV1"
Gloas.SignedRequestAuthV1:
$ref: "./types/gloas/request_auth.yaml#/Gloas/SignedRequestAuthV1"
BuilderPreferences:
$ref: "./types/builder_preferences.yaml#/BuilderPreferences"
Node:
$ref: './types/fork_choice.yaml#/Node'
ExtraData:
Expand Down
63 changes: 63 additions & 0 deletions types/builder_preferences.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
BuilderPreferences:

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.

I wonder should we put this file under the directory types/gloas/builder_preferences.yaml rather than types/builder_preferences.yaml? As BuilderPreferences is a Gloas thing

type: object
description: "Per-builder preferences supplied by a validator when requesting block production."
required: [url, builder_boost_factor, max_trusted_bid, min_bid]
properties:
signed_request_auth:
allOf:
- $ref: "./gloas/request_auth.yaml#/Gloas/SignedRequestAuthV1"
- description: |
Optional `SignedRequestAuthV1` object, as defined in the
[Builder API](https://ethereum.github.io/builder-specs/) specification, that the beacon
node MUST forward to the builder when requesting a bid so that the builder can
authenticate the request. If omitted, requests to this builder are sent without
authentication.
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 PR deviates quite a lot from what you mentioned in #620

so first of all the BuilderPreferences as defined here do not support p2p builders at all, the builder_id concept is missing from this PR. Can you explain why we no longer wanna support this?

type: string

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.

Assuming this BuilderPreferences here is the same as the BuilderPreferencesV1 that is tentatively defined in the current builder-spec.. if we make this change, how are we supposed to ssz serialize / deserialize a BuilderPreferences object which now contains a field of type string?

description: |
URL that the beacon node should use to contact the builder. This URL MAY be equal to the
one contained in the `data` field of `signed_request_auth.message`, but it MAY also differ
from it. An empty string denotes the default preferences entry: these preferences are
applied to any contacted builder for which no entry with a matching `url` was supplied.
example: "https://builder.example.com"
builder_boost_factor:
allOf:
- $ref: "./primitive.yaml#/Uint64"
- description: |
Percentage multiplier to apply to this builder's bid value when choosing between a
builder bid and payload from the paired execution node. This value is only relevant
if the beacon node has a viable `ExecutionPayloadBid` available from this builder 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_payload_value // 100)`
for the highest builder bid known to the beacon node (each bid boosted by the
factor applicable to the builder it originates from), then return a block
committing to the local execution node payload.
* 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 payload pays
more.
* `builder_boost_factor=2**64 - 1`: prefer this builder's 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.
max_trusted_bid:
allOf:
- $ref: "./primitive.yaml#/Gwei"
- description: |
The maximum trusted bid, in Gwei, that will be accepted from this builder. Bids with a
value exceeding this amount MUST NOT be accepted.
min_bid:
allOf:
- $ref: "./primitive.yaml#/Gwei"
- description: |
The minimum bid, in Gwei, that the validator will accept from this builder. Bids with a
value lower than this amount MUST NOT be accepted.
26 changes: 26 additions & 0 deletions types/gloas/request_auth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Gloas:
RequestAuthV1:
type: object
description: "The `RequestAuthV1` object from the [Builder API](https://ethereum.github.io/builder-specs/) Gloas spec."
required: [data, slot]
properties:
data:
type: string
format: hex
description: "Opaque authentication data, up to `MAX_DATA_SIZE` bytes. Typically set to the URL of the builder the request is intended for."
example: "0x68747470733a2f2f6275696c6465722e6578616d706c652e636f6d"
pattern: "^0x[a-fA-F0-9]{0,8192}$"
slot:
allOf:
- $ref: "../primitive.yaml#/Uint64"
- description: "The slot for which the bid is being requested."

SignedRequestAuthV1:
type: object
description: "The `SignedRequestAuthV1` object from the [Builder API](https://ethereum.github.io/builder-specs/) Gloas spec."
required: [message, signature]
properties:
message:
$ref: "#/Gloas/RequestAuthV1"
signature:
$ref: "../primitive.yaml#/Signature"
1 change: 1 addition & 0 deletions wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ produceBlockV
stateful
CGC
RLP
auth
Loading