use `RestPlainResponse` to improve builder API rerror reporting (#5819)

This commit is contained in:
tersec 2024-01-24 23:27:22 +00:00 committed by GitHub
parent 228c79007a
commit 128834a8eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 66 additions and 28 deletions

View File

@ -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.4.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.4.0/apis/builder/blinded_blocks.yaml

View File

@ -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/v0.4.0/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/v0.4.0/apis/builder/blinded_blocks.yaml

View File

@ -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,

View File

@ -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.4.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,