use RestPlainResponse to improve builder API rerror reporting (#5777)
This commit is contained in:
parent
0a1cb47d35
commit
545fb17649
|
@ -15,7 +15,7 @@ import
|
|||
web3, web3/[engine_api, primitives, conversions],
|
||||
eth/common/[eth_types, transaction],
|
||||
eth/async_utils, results,
|
||||
stew/[assign2, byteutils, objects, shims/hashes, endians2],
|
||||
stew/[assign2, byteutils, objects],
|
||||
eth/async_utils, stew/[assign2, byteutils, objects],
|
||||
# Local modules:
|
||||
../spec/[eth2_merkleization, forks, helpers],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2023 Status Research & Development GmbH
|
||||
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
@ -22,13 +22,13 @@ proc registerValidator*(body: seq[SignedValidatorRegistrationV1]
|
|||
proc getHeaderCapella*(slot: Slot,
|
||||
parent_hash: Eth2Digest,
|
||||
pubkey: ValidatorPubKey
|
||||
): RestResponse[GetHeaderResponseCapella] {.
|
||||
): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}",
|
||||
meth: MethodGet, connection: {Dedicated, Close}.}
|
||||
## https://github.com/ethereum/builder-specs/blob/v0.3.0/apis/builder/header.yaml
|
||||
|
||||
proc submitBlindedBlock*(body: capella_mev.SignedBlindedBeaconBlock
|
||||
): RestResponse[SubmitBlindedBlockResponseCapella] {.
|
||||
): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v1/builder/blinded_blocks",
|
||||
meth: MethodPost, connection: {Dedicated, Close}.}
|
||||
## https://github.com/ethereum/builder-specs/blob/v0.3.0/apis/builder/blinded_blocks.yaml
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2023 Status Research & Development GmbH
|
||||
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
@ -15,13 +15,13 @@ export chronos, client, rest_types, eth2_rest_serialization
|
|||
proc getHeaderDeneb*(slot: Slot,
|
||||
parent_hash: Eth2Digest,
|
||||
pubkey: ValidatorPubKey
|
||||
): RestResponse[GetHeaderResponseDeneb] {.
|
||||
): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}",
|
||||
meth: MethodGet, connection: {Dedicated, Close}.}
|
||||
## https://github.com/ethereum/builder-specs/blob/34509da74237942aa15a4c0ca828f67acdf77652/apis/builder/header.yaml
|
||||
|
||||
proc submitBlindedBlock*(body: deneb_mev.SignedBlindedBeaconBlock
|
||||
): RestResponse[SubmitBlindedBlockResponseDeneb] {.
|
||||
): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v1/builder/blinded_blocks",
|
||||
meth: MethodPost, connection: {Dedicated, Close}.}
|
||||
## https://github.com/ethereum/builder-specs/blob/34509da74237942aa15a4c0ca828f67acdf77652/apis/builder/blinded_blocks.yaml
|
||||
|
|
|
@ -568,34 +568,55 @@ proc getBlindedExecutionPayload[
|
|||
# Not ideal to use `when` where instead of splitting into separate functions,
|
||||
# but Nim doesn't overload on generic EPH type parameter.
|
||||
when EPH is capella.ExecutionPayloadHeader:
|
||||
let blindedHeader = awaitWithTimeout(
|
||||
payloadBuilderClient.getHeaderCapella(slot, executionBlockRoot, pubkey),
|
||||
BUILDER_PROPOSAL_DELAY_TOLERANCE):
|
||||
return err "Timeout obtaining Capella blinded header from builder"
|
||||
let
|
||||
response = awaitWithTimeout(
|
||||
payloadBuilderClient.getHeaderCapella(
|
||||
slot, executionBlockRoot, pubkey),
|
||||
BUILDER_PROPOSAL_DELAY_TOLERANCE):
|
||||
return err "Timeout obtaining Capella blinded header from builder"
|
||||
|
||||
res = decodeBytes(
|
||||
GetHeaderResponseCapella, response.data, response.contentType)
|
||||
|
||||
blindedHeader = res.valueOr:
|
||||
return err(
|
||||
"Unable to decode Capella blinded header: " & $res.error &
|
||||
" with HTTP status " & $response.status & ", Content-Type " &
|
||||
$response.contentType & " and content " & $response.data)
|
||||
elif EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle:
|
||||
let blindedHeader = awaitWithTimeout(
|
||||
payloadBuilderClient.getHeaderDeneb(slot, executionBlockRoot, pubkey),
|
||||
BUILDER_PROPOSAL_DELAY_TOLERANCE):
|
||||
return err "Timeout obtaining Deneb blinded header and blob bundle from builder"
|
||||
let
|
||||
response = awaitWithTimeout(
|
||||
payloadBuilderClient.getHeaderDeneb(
|
||||
slot, executionBlockRoot, pubkey),
|
||||
BUILDER_PROPOSAL_DELAY_TOLERANCE):
|
||||
return err "Timeout obtaining Deneb blinded header from builder"
|
||||
|
||||
res = decodeBytes(
|
||||
GetHeaderResponseDeneb, response.data, response.contentType)
|
||||
|
||||
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)
|
||||
else:
|
||||
static: doAssert false
|
||||
|
||||
const httpOk = 200
|
||||
if blindedHeader.status != httpOk:
|
||||
if response.status != httpOk:
|
||||
return err "getBlindedExecutionPayload: non-200 HTTP response"
|
||||
else:
|
||||
if not verify_builder_signature(
|
||||
node.dag.cfg.genesisFork, blindedHeader.data.data.message,
|
||||
blindedHeader.data.data.message.pubkey,
|
||||
blindedHeader.data.data.signature):
|
||||
node.dag.cfg.genesisFork, blindedHeader.data.message,
|
||||
blindedHeader.data.message.pubkey, blindedHeader.data.signature):
|
||||
return err "getBlindedExecutionPayload: signature verification failed"
|
||||
|
||||
when EPH is capella.ExecutionPayloadHeader:
|
||||
return ok((
|
||||
blindedBlckPart: blindedHeader.data.data.message.header,
|
||||
blockValue: blindedHeader.data.data.message.value))
|
||||
blindedBlckPart: blindedHeader.data.message.header,
|
||||
blockValue: blindedHeader.data.message.value))
|
||||
elif EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle:
|
||||
template builderBid: untyped = blindedHeader.data.data.message
|
||||
template builderBid: untyped = blindedHeader.data.message
|
||||
return ok((
|
||||
blindedBlckPart: EPH(
|
||||
execution_payload_header: builderBid.header,
|
||||
|
|
|
@ -55,7 +55,7 @@ proc unblindAndRouteBlockMEV*(
|
|||
|
||||
# By time submitBlindedBlock is called, must already have done slashing
|
||||
# protection check
|
||||
let bundle =
|
||||
let response =
|
||||
try:
|
||||
awaitWithTimeout(
|
||||
payloadBuilderRestClient.submitBlindedBlock(blindedBlock),
|
||||
|
@ -63,13 +63,11 @@ proc unblindAndRouteBlockMEV*(
|
|||
return err("Submitting blinded block timed out")
|
||||
# From here on, including error paths, disallow local EL production by
|
||||
# returning Opt.some, regardless of whether on head or newBlock.
|
||||
except RestDecodingError as exc:
|
||||
return err("REST decoding error submitting blinded block: " & exc.msg)
|
||||
except CatchableError as exc:
|
||||
return err("exception in submitBlindedBlock: " & exc.msg)
|
||||
|
||||
const httpOk = 200
|
||||
if bundle.status != httpOk:
|
||||
if response.status != httpOk:
|
||||
# https://github.com/ethereum/builder-specs/blob/v0.3.0/specs/bellatrix/validator.md#proposer-slashing
|
||||
# This means if a validator publishes a signature for a
|
||||
# `BlindedBeaconBlock` (via a dissemination of a
|
||||
|
@ -77,12 +75,31 @@ proc unblindAndRouteBlockMEV*(
|
|||
# local build process as a fallback, even in the event of some failure
|
||||
# with the external builder network.
|
||||
return err("submitBlindedBlock failed with HTTP error code " &
|
||||
$bundle.status & ": " & $shortLog(blindedBlock))
|
||||
$response.status & ": " & $shortLog(blindedBlock))
|
||||
|
||||
when consensusFork >= ConsensusFork.Deneb:
|
||||
template execution_payload: untyped = bundle.data.data.execution_payload
|
||||
let
|
||||
res = decodeBytes(
|
||||
SubmitBlindedBlockResponseDeneb, response.data, response.contentType)
|
||||
|
||||
bundle = res.valueOr:
|
||||
return err("Could not decode Deneb blinded block: " & $res.error &
|
||||
" with HTTP status " & $response.status & ", Content-Type " &
|
||||
$response.contentType & " and content " & $response.data)
|
||||
|
||||
template execution_payload: untyped = bundle.data.execution_payload
|
||||
else:
|
||||
template execution_payload: untyped = bundle.data.data
|
||||
let
|
||||
res = decodeBytes(
|
||||
SubmitBlindedBlockResponseCapella, response.data, response.contentType)
|
||||
|
||||
bundle = res.valueOr:
|
||||
return err("Could not decode Capella blinded block: " & $res.error &
|
||||
" with HTTP status " & $response.status & ", Content-Type " &
|
||||
$response.contentType & " and content " & $response.data)
|
||||
|
||||
template execution_payload: untyped = bundle.data
|
||||
|
||||
if hash_tree_root(blindedBlock.message.body.execution_payload_header) !=
|
||||
hash_tree_root(execution_payload):
|
||||
return err("unblinded payload doesn't match blinded payload header: " &
|
||||
|
@ -105,9 +122,9 @@ proc unblindAndRouteBlockMEV*(
|
|||
|
||||
let blobsOpt =
|
||||
when consensusFork >= ConsensusFork.Deneb:
|
||||
template blobs_bundle: untyped = bundle.data.data.blobs_bundle
|
||||
template blobs_bundle: untyped = bundle.data.blobs_bundle
|
||||
if blindedBlock.message.body.blob_kzg_commitments !=
|
||||
bundle.data.data.blobs_bundle.commitments:
|
||||
bundle.data.blobs_bundle.commitments:
|
||||
return err("unblinded blobs bundle has unexpected commitments")
|
||||
let ok = verifyProofs(
|
||||
asSeq blobs_bundle.blobs,
|
||||
|
|
Loading…
Reference in New Issue