Skip to content
Merged
Changes from 2 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
23 changes: 12 additions & 11 deletions beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3285,17 +3285,7 @@ proc decodeBytesJsonOrSsz*(
version: string
): Result[T, RestErrorMessage] =
var res {.noinit.}: T

let
typeFork = kind(typeof(res.data))
consensusFork = ConsensusFork.decodeString(version).valueOr:
return err(RestErrorMessage.init(Http400, UnableDecodeVersionError,
[version, $error]))
if typeFork != consensusFork:
return err(
RestErrorMessage.init(Http400, UnexpectedForkVersionError,
["eth-consensus-version", consensusFork.toString(),
typeFork.toString()]))
let typeFork = kind(typeof(res.data))

if contentType == ApplicationJsonMediaType:
res =
Expand All @@ -3321,6 +3311,17 @@ proc decodeBytesJsonOrSsz*(
typeFork.toString()]))
ok(res)
elif contentType == OctetStreamMediaType:
let consensusFork =
ConsensusFork.decodeString(version).valueOr:
Copy link
Contributor

@tersec tersec Apr 2, 2025

Choose a reason for hiding this comment

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

version here comes from:

res = decodeBytesJsonOrSsz(
GetHeaderResponseDeneb, response.data, response.contentType,
response.headers.getString("eth-consensus-version"))
blindedHeader = res.valueOr:
return err(
"Unable to decode Deneb blinded header: " & $res.error &
" with HTTP status " & $response.status & ", Content-Type " &
$response.contentType & " and content " & $response.data)
elif EPH is electra_mev.BlindedExecutionPayloadAndBlobsBundle:
let
response = awaitWithTimeout(
payloadBuilderClient.getHeaderElectra(
slot, executionBlockHash, pubkey),
BUILDER_PROPOSAL_DELAY_TOLERANCE):
return err "Timeout obtaining Electra blinded header from builder"
res = decodeBytesJsonOrSsz(
GetHeaderResponseElectra, response.data, response.contentType,
response.headers.getString("eth-consensus-version"))
blindedHeader = res.valueOr:
return err(
"Unable to decode Electra blinded header: " & $res.error &
" with HTTP status " & $response.status & ", Content-Type " &
$response.contentType & " and content " & $response.data)
elif EPH is fulu_mev.BlindedExecutionPayloadAndBlobsBundle:
debugFuluComment "Because electra MEV isn't working yet, this is a placeholder copy"
let
response = awaitWithTimeout(
payloadBuilderClient.getHeaderFulu(
slot, executionBlockHash, pubkey),
BUILDER_PROPOSAL_DELAY_TOLERANCE):
return err "Timeout obtaining Fulu blinded header from builder"
res = decodeBytesJsonOrSsz(
GetHeaderResponseFulu, response.data, response.contentType,
response.headers.getString("eth-consensus-version"))

i.e. eth-consensus-version

Neither https://github.com/ethereum/builder-specs/blob/v0.5.0/apis/builder/header.yaml nor https://github.com/ethereum/builder-specs/blob/v0.5.0/apis/builder/blinded_blocks.yaml appears to require it even for SSZ mode, so it might compliantly be empty in/missing from the relay's REST response.

But, again, this isn't actually a real issue, because at any given callsite, there's only one valid fork, and it does not ever depend on eth-consensus-version, in no edge case.

when EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle:
let
response = awaitWithTimeout(
payloadBuilderClient.getHeaderDeneb(
slot, executionBlockHash, pubkey),
BUILDER_PROPOSAL_DELAY_TOLERANCE):
return err "Timeout obtaining Deneb blinded header from builder"
res = decodeBytesJsonOrSsz(
GetHeaderResponseDeneb, response.data, response.contentType,
response.headers.getString("eth-consensus-version"))

can only happen when it's Deneb, and therefore the consensus version it's expecting must, always, be Deneb.

elif EPH is electra_mev.BlindedExecutionPayloadAndBlobsBundle:
let
response = awaitWithTimeout(
payloadBuilderClient.getHeaderElectra(
slot, executionBlockHash, pubkey),
BUILDER_PROPOSAL_DELAY_TOLERANCE):
return err "Timeout obtaining Electra blinded header from builder"
res = decodeBytesJsonOrSsz(
GetHeaderResponseElectra, response.data, response.contentType,
response.headers.getString("eth-consensus-version"))

similarly with Electra

elif EPH is fulu_mev.BlindedExecutionPayloadAndBlobsBundle:
debugFuluComment "Because electra MEV isn't working yet, this is a placeholder copy"
let
response = awaitWithTimeout(
payloadBuilderClient.getHeaderFulu(
slot, executionBlockHash, pubkey),
BUILDER_PROPOSAL_DELAY_TOLERANCE):
return err "Timeout obtaining Fulu blinded header from builder"
res = decodeBytesJsonOrSsz(
GetHeaderResponseFulu, response.data, response.contentType,
response.headers.getString("eth-consensus-version"))

and with Fulu

This is all known at compile-time, statically; there's no need to use eth-consensus-version. There isn't never was any runtime detection of fork here.

If it's supplied, and it's incorrect, then, sure, can flag on that error, but otherwise its absence shouldn't matter at all.

Copy link
Contributor Author

@cheatfate cheatfate Apr 2, 2025

Choose a reason for hiding this comment

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

I would consider your opinion when specification will say exactly optional or not required, because right now it is:

The active consensus version to which the data belongs. Required in response so client can deserialize returned json or ssz data more effectively.

As you can see here present Required in response.
https://ethereum.github.io/builder-specs/#/Builder/getHeader

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One more proof to my opinion is present in original PR message ethereum/builder-specs#104

Adds Eth-Consensus-Version to responses, only required if response body is SSZ encoded but preferably should be added to all responses eventually.

Copy link
Contributor

Choose a reason for hiding this comment

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

That phrasing appears to come in via https://ethereum.github.io/builder-specs/releases/v0.5.0/builder-oapi.json via https://github.com/ethereum/builder-specs/blob/1569fc45d43e3ed2bed94ec6788c3f8fa7d2a4b2/builder-oapi.yaml#L108-L112

  headers:
    Eth-Consensus-Version:
      $ref: "./beacon-apis/beacon-node-oapi.yaml#/components/headers/Eth-Consensus-Version"
      schema:
        $ref: "#/components/schemas/ConsensusVersion"

which in turn points to https://github.com/ethereum/beacon-APIs/blob/2b1d7b5ac4756881bd29e7adacc9b7032343d981/beacon-node-oapi.yaml#L482-L489

  headers:
    Eth-Consensus-Version:
      description: |
        The active consensus version to which the data belongs. Required in response so client can deserialize returned json or ssz data 
        more effectively.
      required: true
      schema:
        $ref: '#/components/schemas/ConsensusVersion'

That's descriptive/explanatory phrasing from another non-builder API spec in a human-readable description field only, which sort of leaks its way into the website description (not the spec proper).

Meanwhile the builder-API spec itself is clear on this:
https://github.com/ethereum/builder-specs/blob/c7b0abad0d0b995915cde8d2e5ebcd650de5b163/apis/builder/header.yaml#L35-L41

  responses:
    "200":
      description: Success response.
      headers:
        Eth-Consensus-Version:
          $ref: "../../builder-oapi.yaml#/components/headers/Eth-Consensus-Version"
          required: false

and https://github.com/ethereum/builder-specs/blob/c7b0abad0d0b995915cde8d2e5ebcd650de5b163/apis/builder/blinded_blocks.yaml#L49-L55

  responses:
    "200":
      description: Success response.
      headers:
        Eth-Consensus-Version:
          $ref: "../../beacon-apis/beacon-node-oapi.yaml#/components/headers/Eth-Consensus-Version"
          required: false

The "required" is flavor text, and doesn't even .... necessarily mean it's required, per se. It's vague and handwavy enough even without the contradictory evidence from the builder API itself it's be questionable.

I can say, X is required for Y to happen, and it's a conditional requirement: if one wants Y to be true, X must be true, a kind of logical implication operator.

Copy link
Contributor

Choose a reason for hiding this comment

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

One more proof to my opinion is present in original PR message ethereum/builder-specs#104

Adds Eth-Consensus-Version to responses, only required if response body is SSZ encoded but preferably should be added to all responses eventually.

That speaks to intent of the PR creator, yes. Not what the spec actually says.

Copy link

Choose a reason for hiding this comment

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

sorry for that, I have opened a PR to clarify / formalize this on the spec ethereum/builder-specs#118

return err(RestErrorMessage.init(Http400, UnableDecodeVersionError,
[version, $error]))
if typeFork != consensusFork:
return err(
RestErrorMessage.init(
Http400, UnexpectedForkVersionError,
["eth-consensus-version", consensusFork.toString(),
typeFork.toString()]))

ok(T(
version: newJString(typeFork.toString()),
data:
Expand Down
Loading