Skip to content

Add produceBlockV4WithBid - #627

Open
eth2353 wants to merge 9 commits into
ethereum:masterfrom
eth2353:produce-block-v4-with-bid
Open

Add produceBlockV4WithBid#627
eth2353 wants to merge 9 commits into
ethereum:masterfrom
eth2353:produce-block-v4-with-bid

Conversation

@eth2353

@eth2353 eth2353 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Multi-node validator clients may ask several beacon nodes to produce a block at the same time. Having every one of those beacon nodes request bids from the same set of builders at the exact same time is completely unnecessary and puts unnecessary load on beacon nodes and builders.

Instead, with this endpoint, the validator client can request bids from builders directly, pick one, and ask each beacon node to produce a beacon block with the supplied bid.

This should be a separate endpoint from produceBlockV4 which is already getting complex enough if we add something like #625 . Supporting both flows in the same endpoint would add more optional parameters and if/else branching.

The bid supplied by the validator client is included best-effort. The beacon node may replace it if its circuit breaker is active or if the bid is invalid, then continue with another viable bid or a local payload.

@eth2353
eth2353 force-pushed the produce-block-v4-with-bid branch from 98237f3 to 023df78 Compare July 13, 2026 13:19
@michaelsproul

Copy link
Copy Markdown
Contributor

I think optionally including this in the POST body (with #625) is actually simpler and cleaner isn't it? It save us copy-pasting the whole endpoint spec (which creates maintenance burden), and aligns with how this will probably be implemented in BNs (as an if inside a generic implementation of produceBlockV4).

@eth2353

eth2353 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

I think optionally including this in the POST body (with #625) is actually simpler and cleaner isn't it? It save us copy-pasting the whole endpoint spec (which creates maintenance burden), and aligns with how this will probably be implemented in BNs (as an if inside a generic implementation of produceBlockV4).

Well, it does lead to multiple optional parameters in an already quite complex endpoint, especially if #625 is merged in its current proposed state. I agree that BN side implementation will likely be shared for the most part among the two endpoints.

I don't have a strong opinion on separate vs one endpoint, open to changing it.

@eth2353
eth2353 marked this pull request as ready for review July 17, 2026 10:44
@AntiD2ta

Copy link
Copy Markdown

Supporting @eth2353 's initiative from the perspective of multi-beacon-node validator clients such as Vero/Vouch.

I think a dedicated endpoint is defensible here. These are materially different block-production pathways:

  • produceBlockV4 delegates bid acquisition and selection to the beacon node.
  • produceBlockV4WithBid makes the validator client responsible for acquiring and selecting the bid, while the beacon node validates it, applies circuit-breaker or fallback behaviour, and constructs the block.

That difference affects ownership, required inputs, failure semantics, observability, and implementation behaviour. Representing the flows as separate operations makes capability discovery, conformance testing, metrics, and future evolution clearer. It also avoids turning produceBlockV4 into an increasingly complex matrix of optional or mutually exclusive inputs.

The additional specification-maintenance burden is a valid concern though. A possible mitigation is refactoring much of the common request and response structure into shared OpenAPI components to reduce duplication and the risk of drift. A small amount of specification duplication seems preferable to leaving an important multi-BN capability implementation-specific.

That said, the exact URL is less important than standardizing the functionality. Adding a mutually exclusive SignedExecutionPayloadBid input to a POST version of produceBlockV4 could also satisfy the core requirement. I important that the final API explicitly supports submitting one preselected bid to multiple beacon nodes.

@residue-scaling

residue-scaling commented Jul 21, 2026

Copy link
Copy Markdown

From a security point of view: one goal of the current split between validator clients and beacons is for operators to isolate validator clients on machines/vms/containers with no internet access, so that in the event of a software bug on the validator client side or a security breach, validation keys potentially loaded in memory can't be easily accessed/exported to the outside.

Here we assume the validator clients have a way to source bids, is the intent here to source bids directly from builders and so, be open to the internet?

@eth2353

eth2353 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Here we assume the validator clients have a way to source bids, is the intent here to source bids directly from builders and so, be open to the internet?

Yes, pretty much. It feels important to say this is all opt-in, anyone who doesn't want this can let the beacon node do the bid selection.

But the idea is indeed that validator clients would be able to source bids by connecting to builders directly. To do that, they don't need to be open to the Internet entirely, their firewall could still deny any incoming connections. But they would need that link.

In terms of security, validator clients do not need to have validator keys in-memory, they can also use remote signers that can be located on more isolated machines.

Many Vouch users already do something similar today where they set up Vouch as a sort of mev-boost service that beacon nodes connect to to get execution payload headers. This however requires some extra unnecessary hops:

  1. Vouch requests block from BN
  2. BN requests payload header from Vouch
  3. Vouch asks relays for execution payload header
  4. BN receives header from Vouch, includes it in block
  5. Vouch receives block from BN

With this proposed change to the Beacon API the process (in Gloas) could look more like this:

  1. VC asks relays/builders for bids
  2. VC requests block from BN, providing its own bid for inclusion
  3. VC receives block from BN

-> No extra unnecessary hops which is especially useful if the VC and beacon node are not running out of the same datacenter.

Comment thread apis/validator/block.v4_with_bid.yaml Outdated
Eth-Execution-Payload-Value:
$ref: '../../beacon-node-oapi.yaml#/components/headers/Eth-Execution-Payload-Value'
Eth-Execution-Payload-Included:
$ref: '../../beacon-node-oapi.yaml#/components/headers/Eth-Execution-Payload-Included'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It would be nice if the beacon node could indicate to the VC if the supplied bid was used.

I'm not sure if it's worth a Eth-Execution-Payload-Bid-Used header or not. The VC can relatively easily check if the response bid matches the supplied bid.

Any opinions?

Co-authored-by: Nico Flaig <nflaig@protonmail.com>
@JasonVranek

Copy link
Copy Markdown
Contributor

Beacon nodes should use the bid supplied by the VC during block production, but if they have a good reason not to, they are free to override it.

From your discord comment ^ and a big motivation for this PR being multi-BN setups, having more predictability into why the BN would override it seems useful!

I'd think the main reasons a supplied bid is not taken are: 1) it's invalid, 2) the circuit breaker is active, or 3) it thinks it has a more valuable p2p bid or local payload. The first two are seem out of scope for this interface but for 3), it helps to sort the v4 knobs by what the VC can and can't see:

  • passing max_execution_payment and min_bid for the VC’s own bid seems pointless since it can apply before forwarding to the BN
  • passing min_bid and builder_boost_factor for p2p bids lets the VC filter and weight bids like PR630 did
  • passing builder_boost_factor for the VC's bid lets you express how it should weigh against the local payload

If the supplied builder_boost_factor defaults to 2**64 - 1, that satisfies "prefer the VC’s bid unless it's unviable" where unviable is clearly defined

@eth2353

eth2353 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for taking a look @JasonVranek !

Yesterday the topic of P2P bids in this context was discussed on the breakout call. The VC isn't completely blind to them - it can monitor and account for P2P bids using the execution_payload_bid SSE topic. I made a note of that here:

https://github.com/eth2353/beacon-APIs/blob/29514eea5da78e5e6fd28a37b2b08a1e8b01c081/validator-flow.md?plain=1#L36

Therefore the VC is able to determine the best external builder bid.

There is a chance that the local payload is more valuable than the bid picked by the VC. To account for that I just added the builder_boost_factor as a query param (like we do in produceBlockV3 and almost did for produceBlockV4) in 4008a0f.

@JasonVranek

Copy link
Copy Markdown
Contributor

There is a chance that the local payload is more valuable than the bid picked by the VC. To account for that I just added the builder_boost_factor as a query param (like we do in produceBlockV3 and almost did for produceBlockV4) in 4008a0f.

If the VC is sourcing a direct connection bid, it can carry a non-zero bid.execution_payment. In the produceBlockV4 case, the VC would supply max_execution_payment and builder_boost_factor, where the amount of the execution_payment that contributes to the bid's value is clamped at min(bid.execution_payment, max_execution_payment). With just a builder_boost_factor query param we run into this issue:

  • value = 100, execution_payment = 1000, max_execution_payment = 10
  • clamped_total_value = 110, unclamped_total_value = 1100

Even if the VC clamped on its side, the BN would use unclamped_total_value to compare against local payloads which is the wrong behavior. We could send an extra max_execution_payment query param but this isn't needed for the p2p bids and is kinda ugly.

Maybe as a different approach... we can replace the builder_boost_factor query param with something like an effective_bid_value = (builder_boost_factor * (value + min(bid.execution_payment, max_execution_payment))) that applies equally to direct connection and p2p bids (where p2p has bid.execution_payment == 0). Then the BN just uses this query param to directly compare against the local payload value * 100.

@eth2353

eth2353 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

the amount of the execution_payment that contributes to the bid's value is clamped at min(bid.execution_payment, max_execution_payment)

I don't understand. Why the need for clamping? Wouldn't a bid with an execution_payment higher than max_execution_payment simply not be accepted by the validator / VC? At least that's how I understood it to work.

This endpoint's primary usecase is for a validator client that already has a pretty strong preference for a bid it supplies (direct or p2p). Yes, the beacon node may override it in certain cases, but in 99+% of real-world cases it would not be expected to.

The only bid selection datapoint the validator client doesn't have access to is the value of the local payload, which is why having the builder_boost_factor does make sense to me (to compare supplied bid vs local payload). But I don't see the need to make it more complicated than that.

@JasonVranek

Copy link
Copy Markdown
Contributor

I don't understand. Why the need for clamping? Wouldn't a bid with an execution_payment higher than max_execution_payment simply not be accepted by the validator / VC? At least that's how I understood it to work.

That was my original interpretation, the clamping came from this conversation with @ethDreamer and makes sense to me since it lets more bids through within your trust assumptions

@eth2353

eth2353 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

I see. Will builders actually ever return a higher execution_payment bid than the proposer's max preference? Based on my understanding, this should only happen in very rare cases when the builder doesn't have stored builder preferences for the validator.

Because of the rareness of this actually happening, I don't believe we need to account for it in this API, and I think the current builder_boost_factor covers the vast majority of realistic cases sufficiently.

Basically, the combination of:

  1. the builder not having stored preferences
  2. its resulting bid winning thanks to a high execution_payment
  3. the local payload being of higher "effective" value

seems low enough for me not to account for it by complicating this API. Even if the edge case above does happen, the end result is a bid wrongly being picked over a local payload, not a failure to produce the block or anything catastrophic like that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Gloas api's needed in Gloas fork.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants