Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e902e3b
add flag to include payload in produceBlockV4
shane-moore Dec 23, 2025
56be573
chore: add include_payload_flag
shane-moore Feb 5, 2026
e74fde6
chore: add get payload envelope
shane-moore Feb 11, 2026
5c28168
stateful and stateless envelope post
shane-moore Feb 11, 2026
6ee20eb
update validator flow
shane-moore Feb 12, 2026
25de51f
chore: update changelog
shane-moore Feb 12, 2026
0c45859
chore: remove builder index from get execution payload envelope
shane-moore Feb 19, 2026
8bf9080
chore: update blockv4 builder_boost_factor description
shane-moore Mar 6, 2026
b9b6fc6
chore: update post envelope with broadcast validation
shane-moore Mar 6, 2026
f78aaef
Merge branch 'master' into produce-block-v4-with-payload
rolfyone Apr 28, 2026
5413e57
chore: beacon_block_root as required param for getExecutionPayloadEnv…
shane-moore May 4, 2026
6a64ee1
Merge branch 'master' into produce-block-v4-with-payload
rolfyone May 10, 2026
45f142a
Merge branch 'master' into produce-block-v4-with-payload
rolfyone May 14, 2026
3fed1ee
chore: rename dvs to distributed validators
shane-moore May 15, 2026
b613dde
chore: gloas execution_payload
shane-moore May 27, 2026
7c1c6b1
move beacon_block_root from query to path
shane-moore May 28, 2026
72b9ba2
Merge remote-tracking branch 'upstream/master' into produce-block-v4-…
shane-moore May 28, 2026
4114350
align builder_boost_factor with nflaig: terminology + multi-source bids
shane-moore May 28, 2026
7c9fa88
chore: rename remaining DVs to distributed validators
shane-moore May 29, 2026
cb1244b
Merge remote-tracking branch 'upstream/master' into produce-block-v4-…
shane-moore Jun 1, 2026
947f91d
Pluralize new gloas execution payload envelope endpoint paths
shane-moore Jun 1, 2026
b92bc94
spike: blinded execution payload envelope for stateful publish
shane-moore May 27, 2026
e69b73e
broaden Eth-Execution-Payload-Blinded header to request/response
shane-moore May 28, 2026
7dcfcc4
align GET envelope with nflaig: lock to BlindedExecutionPayloadEnvelope
shane-moore May 28, 2026
76ea0ba
align blinded envelope with nflaig: payload_root, drop ExecutionPaylo…
shane-moore May 28, 2026
bed49d9
Merge branch 'master' into produce-block-v4-with-payload
rolfyone Jun 11, 2026
eaf5eec
Merge pull request #10 from shane-moore/blinded-execution-payload-env…
shane-moore Jun 22, 2026
19dc4db
Fix blinded envelope GET prose to match payload_root type
shane-moore Jun 22, 2026
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
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ There are likely to be descriptions etc outside of the list below, but new query

| Endpoint | [Lighthouse](https://github.com/sigp/lighthouse) | [Lodestar](https://github.com/ChainSafe/lodestar) | [Nimbus](https://github.com/status-im/nimbus-eth2) | [Prysm](https://github.com/prysmaticlabs/prysm) | [Teku](https://github.com/ConsenSys/teku) |
|---------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|---------------------------------------------------|----------------------------------------------------|-------------------------------------------------|-------------------------------------------|
| [#580](https://github.com/ethereum/beacon-APIs/pull/580) `GET /eth/v4/validator/blocks/{slot}` added | | | | | |
| [#552](https://github.com/ethereum/beacon-APIs/pull/552) `GET /eth/v1/validator/execution_payload_bids/{slot}/{builder_index}` added | | | | | |
| [#580](https://github.com/ethereum/beacon-APIs/pull/580) `GET /eth/v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root}` added | | | | | |
| [#552](https://github.com/ethereum/beacon-APIs/pull/552) `GET /eth/v1/validator/payload_attestation_data/{slot}` added | | | | | |
| [#552](https://github.com/ethereum/beacon-APIs/pull/552) `POST /eth/v1/validator/duties/ptc/{epoch}` added | | | | | |
| [#552](https://github.com/ethereum/beacon-APIs/pull/552) `POST /eth/v1/beacon/execution_payload_bids` added | | | | | |
| [#552](https://github.com/ethereum/beacon-APIs/pull/552) `GET /eth/v1/beacon/execution_payload_envelopes/{block_id}` added | | | | | |
| [#580](https://github.com/ethereum/beacon-APIs/pull/580) `POST /eth/v1/beacon/execution_payload_envelopes` added | | | | | |
| [#552](https://github.com/ethereum/beacon-APIs/pull/552) `GET /eth/v1/beacon/pool/payload_attestations` added | | | | | |
| [#552](https://github.com/ethereum/beacon-APIs/pull/552) `POST /eth/v1/beacon/pool/payload_attestations` added | | | | | |
| [#552](https://github.com/ethereum/beacon-APIs/pull/552) `GET /eth/v2/beacon/blocks/{block_id}` updated | | | | | |
Expand Down
102 changes: 102 additions & 0 deletions apis/beacon/execution_payload/envelope_post.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
post:
operationId: publishExecutionPayloadEnvelope
summary: Publish signed execution payload envelope
description: |
Instructs the beacon node to broadcast a signed execution payload envelope to the network,
to be gossiped for payload validation. A success response (20x) indicates
that the envelope passed gossip validation and was successfully broadcast onto the network.

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.

In publishBlockV2 we return 202 if block passed gossip validation but failed integration.

Are we doing something similar for publishExecutionPayloadEnvelope?

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 guess from another angle to think about this is we broadcast the payload regardless the result of the payload integration, or we integrate first, if it goes well, we then gossip?

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.

We probably want to propagate the envelope as quickly as possible, so ideally:

  1. Apply gossip validation
  2. Broadcast
  3. Run process_execution_payload and import to DB

If (1) fails, we 400. If (3) fails we should probably 202 just like for blocks.


I was hoping we could avoid the nastiness of broadcast_validation for envelopes, but I think there might actually be an unbundling attack (background) for envelopes now:

I think there is an unbundling attack something like:

  1. Malicious proposer publishes block A at slot N committing to bidA and payloadA, where bidA comes from an external builder (could be P2P or otherwise).
  2. Malicious proposer publishes block B (slashable) at slot N committing to bidB and payload payloadB. They will be slashed at the next slot.
  3. Builder for bidA starts to publish payloadA. Even on beacon nodes that have seen the equivocating block, the payload will PASS gossip validation as long as block A was imported.
  4. Proposer publishes payloadB for block B. Envelope gossip validation passes for payloadB because it is signed by a different builder from payloadA.
  5. Result: either A or B could become head, depending on timing. If B wins, the builder of A has revealed potentially valuable information, which could be worth the proposer slashing themselves for (they could use it for a self-build in the next slot).

Proposed fixes:

  1. We could put the burden on the builder to check for block equivocations prior to revealing their payload (no API/spec changes).
  2. We could add a gossip condition to reject envelopes for slashable blocks.
  3. We could add a broadcast_validation=equivocation flag (like we have for pre-Gloas blocks) to check that the envelope's block is not an equivocation. This flag would likely be used by all builders when interacting with their BNs.
  4. We could make the block equivocation check a default part of the API (same as broadcast_validation=equivocation from fix 3, but no flag required and no ability to opt-out).

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.

based off the discord convo, seems like a broadcast_validation flag is warranted. I built it out in b9b6fc6 to use same BroadcastValidation schema as produceBlocksV2 since all the possible values seem to make sense for envelopes as well, but lmk if have some other thoughts on it

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'm just catching up on the discussions here, but I think the broadcast_validation flag and its values make sense to me.

If im understanding correctly, the broadcast_validation =consensus_and_equivocation value on the publish_block endpoint will no longer be useful for unbundling protection? Probably no harm to keep it though.

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 im understanding correctly, the broadcast_validation =consensus_and_equivocation value on the publish_block endpoint will no longer be useful for unbundling protection?

no, it's not needed for this anymore, we could discuss if we wanna deprecate broadcast_validation on publishBlockV2, but might still be useful to have basic checks before publishing the block

The beacon node is also expected to integrate the envelope into the state, but may broadcast it
before doing so, so as to aid timely delivery of the envelope. Should the envelope fail full
validation, a separate success response code (202) is used to indicate that the envelope was
successfully broadcast but failed integration. The broadcast behaviour may be adjusted via
the `broadcast_validation` query parameter.

The request body can be either form, distinguished by the `Eth-Execution-Payload-Blinded`
header:
- A `SignedExecutionPayloadEnvelopeContents` object (header `false`) containing the signed
envelope along with blobs and KZG proofs. This is used in stateless operation (multi-BN
setups, distributed validators, failover) where the receiving beacon node does not have
the blobs cached.
- A `SignedBlindedExecutionPayloadEnvelope` object (header `true`) containing the signed
blinded envelope (transactions omitted). This is used in stateful operation where the
beacon node already has the full envelope and blobs cached from block production. The
beacon node reconstructs the full envelope from its cache before broadcasting; the
signature is valid over both forms by construction.
tags:
- Beacon
- ValidatorRequiredApi
parameters:
- name: broadcast_validation
in: query
required: false
description: |
Level of validation that must be applied to an envelope before it is broadcast.

Possible values:
- **`gossip`** (default): lightweight gossip checks only
- **`consensus`**: full consensus checks, including validation of all signatures and
envelope fields.
- **`consensus_and_equivocation`**: the same as `consensus`, with an extra check that
the envelope's beacon block is not an equivocation. If the block is found to be an
equivocation, validation fails. This is recommended for builders to protect against
unbundling attacks where a malicious proposer publishes equivocating blocks to extract
payload contents from honest builders.

If the envelope fails the requested level of validation, a 400 status MUST be returned
immediately and the envelope MUST NOT be broadcast to the network.

If validation succeeds, the envelope must still be fully verified before it is
incorporated into the state and a 20x status is returned to the caller.
schema:
$ref: '../../../beacon-node-oapi.yaml#/components/schemas/BroadcastValidation'
- in: header
schema:
$ref: "../../../beacon-node-oapi.yaml#/components/schemas/ConsensusVersion"
required: true
name: Eth-Consensus-Version
description: "The active consensus version to which the execution payload envelope being submitted belongs."
- in: header
name: Eth-Execution-Payload-Blinded
required: true
schema:
type: boolean
description: |
Indicates which body schema is submitted (see the
[`Eth-Execution-Payload-Blinded`](#/components/headers/Eth-Execution-Payload-Blinded)
header). When `false`, the body is a `SignedExecutionPayloadEnvelopeContents` (full
envelope plus blobs and KZG proofs, stateless flow). When `true`, the body is a
`SignedBlindedExecutionPayloadEnvelope` (blinded envelope only, stateful flow; the
beacon node must have cached the full envelope and blobs from block production).
requestBody:
description: "The `SignedExecutionPayloadEnvelopeContents` or `SignedBlindedExecutionPayloadEnvelope` object to be broadcast, selected via the `Eth-Execution-Payload-Blinded` header."
required: true
content:
application/json:
schema:
anyOf:
- $ref: "../../../beacon-node-oapi.yaml#/components/schemas/Gloas.SignedExecutionPayloadEnvelopeContents"
- $ref: "../../../beacon-node-oapi.yaml#/components/schemas/Gloas.SignedBlindedExecutionPayloadEnvelope"
application/octet-stream:
schema:
description: "SSZ serialized `SignedExecutionPayloadEnvelopeContents` or `SignedBlindedExecutionPayloadEnvelope` bytes. Use the `Eth-Execution-Payload-Blinded` header to select the schema."
responses:
"200":
description: "The envelope was validated successfully and has been broadcast. It has also been integrated into the beacon node's database."
"202":
description: "The envelope could not be integrated into the beacon node's database as it failed validation, but was successfully broadcast."
"400":
description: |
The signed envelope object is invalid, broadcast validation failed, or a blinded
envelope was submitted but the beacon node has no cached full envelope to reconstruct
from (e.g. because block production happened on a different beacon node).
content:
application/json:
schema:
$ref: "../../../beacon-node-oapi.yaml#/components/schemas/ErrorMessage"
example:
code: 400
message: "Invalid signed execution payload envelope"
"415":
$ref: "../../../beacon-node-oapi.yaml#/components/responses/UnsupportedMediaType"
"500":
$ref: "../../../beacon-node-oapi.yaml#/components/responses/InternalError"
157 changes: 157 additions & 0 deletions apis/validator/block.v4.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
get:
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.

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.

When self-building (local execution payload), the response will include the full block contents
including the beacon block, execution payload envelope, blobs, and KZG proofs.
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.

The `Eth-Execution-Payload-Included` header and `execution_payload_included` response field
indicate which response type was returned.
parameters:
- name: slot
in: path
required: true
description: "The slot for which the block should be proposed."
schema:
$ref: "../../beacon-node-oapi.yaml#/components/schemas/Uint64"
- name: randao_reveal
in: query
required: true
description: "The validator's randao reveal value."
schema:
$ref: '../../beacon-node-oapi.yaml#/components/schemas/Signature'
- name: graffiti
in: query
required: false
description: "Arbitrary data validator wants to include in block."
schema:
$ref: '../../beacon-node-oapi.yaml#/components/schemas/Graffiti'
- name: skip_randao_verification
$ref: '../../beacon-node-oapi.yaml#/components/parameters/SkipRandaoVerification'
- name: include_payload
in: query
required: false
description: |
Controls whether the execution payload envelope and blobs are included in the response
when self-building (using local execution payload).

When `true` (default), 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 `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).

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).
schema:
type: boolean
default: true
- name: builder_boost_factor

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.

@bharath-123 if a client doesn't have builders configured via api, we don't wanna submit builder preferences at all, so moving boost factor and other parameters there seem wrong to me, we need a way for the validator to signal this to the beacon node. And I don't think the beacon api should be concerned about off protocol builders or per builder configs

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"
responses:
"200":
description: Success response
headers:
Eth-Consensus-Version:
$ref: '../../beacon-node-oapi.yaml#/components/headers/Eth-Consensus-Version'
Eth-Consensus-Block-Value:
$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'
content:
application/json:
schema:
title: "ProduceBlockV4Response"
type: object
required: [version, consensus_block_value, execution_payload_included, data]
properties:
version:
type: string
enum: [gloas]
example: "gloas"
consensus_block_value:
type: string
example: "12345"
description: "Consensus rewards for this block in Wei"
execution_payload_included:
type: boolean
description: |
Indicates whether the execution payload envelope is included in the response.
When `true`, the `data` field contains the full
execution payload envelope, blobs, and KZG proofs. When `false`, the `data`
field contains only a `BeaconBlock`.
example: false
data:
anyOf:
- $ref: "../../beacon-node-oapi.yaml#/components/schemas/Gloas.BeaconBlock"
- $ref: "../../beacon-node-oapi.yaml#/components/schemas/Gloas.BlockContents"
application/octet-stream:
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"
content:
application/json:
schema:
$ref: "../../beacon-node-oapi.yaml#/components/schemas/ErrorMessage"
examples:
InvalidRequest:
value:
code: 400
message: "Invalid request to produce a block"
"406":
$ref: "../../beacon-node-oapi.yaml#/components/responses/NotAcceptable"
"500":
$ref: '../../beacon-node-oapi.yaml#/components/responses/InternalError'
"503":
$ref: '../../beacon-node-oapi.yaml#/components/responses/CurrentlySyncing'
Loading
Loading