fill in remaining capellaImplementationMissing holes for builder API (#4606)

This commit is contained in:
tersec 2023-02-14 11:49:48 +01:00 committed by GitHub
parent 3011d49946
commit 3977f1529a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 135 additions and 10 deletions

View File

@ -852,7 +852,21 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
of ConsensusFork.EIP4844:
return RestApiResponse.jsonError(Http500, $eip4844ImplementationMissing)
of ConsensusFork.Capella:
return RestApiResponse.jsonError(Http500, $capellaImplementationMissing)
let res =
block:
let restBlock = decodeBodyJsonOrSsz(
capella_mev.SignedBlindedBeaconBlock, body).valueOr:
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
$error)
await node.unblindAndRouteBlockMEV(restBlock)
if res.isErr():
return RestApiResponse.jsonError(
Http503, BeaconNodeInSyncError, $res.error())
if res.get().isNone():
return RestApiResponse.jsonError(Http202, BlockValidationError)
return RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
of ConsensusFork.Bellatrix:
let res =
block:

View File

@ -1010,11 +1010,9 @@ func checkForkConsistency*(cfg: RuntimeConfig) =
assertForkEpochOrder(cfg.CAPELLA_FORK_EPOCH, cfg.EIP4844_FORK_EPOCH)
# This is a readily/uniquely searchable token of where a false assertion is
# due to Capella implementation missing. checkForkConsistency() checks that
# Nimbus does not actually run any non-FAR_FUTURE_EPOCH Capella network, so
# such cases won't be hit.
const capellaImplementationMissing* = false
# due to a Deneb implementation missing. checkForkConsistency() checks that
# Nimbus does not run any non-FAR_FUTURE_EPOCH Deneb network, so such cases
# won't be hit.
const eip4844ImplementationMissing* = false
#template debugRaiseAssert*(x: string) = raiseAssert x

View File

@ -1050,8 +1050,17 @@ proc readValue*[BlockType: ForkedBlindedBeaconBlock](
value = ForkedBlindedBeaconBlock(kind: ConsensusFork.Bellatrix,
bellatrixData: res)
of ConsensusFork.Capella:
reader.raiseUnexpectedValue($capellaImplementationMissing)
let res =
try:
RestJson.decode(string(data.get()),
BlindedBeaconBlock,
requireAllFields = true,
allowUnknownFields = true)
except SerializationError as exc:
reader.raiseUnexpectedValue("Incorrect capella block format, [" &
exc.formatMsg("BlindedBlock") & "]")
value = ForkedBlindedBeaconBlock(kind: ConsensusFork.Capella,
capellaData: res)
of ConsensusFork.EIP4844:
reader.raiseUnexpectedValue($eip4844ImplementationMissing)

View File

@ -539,7 +539,10 @@ proc makeBeaconBlock*[T: bellatrix.ExecutionPayload | capella.ExecutionPayload |
# Override for MEV
if transactions_root.isSome and execution_payload_root.isSome:
withState(state):
when stateFork >= ConsensusFork.Bellatrix:
when stateFork < ConsensusFork.Bellatrix:
# Vacuously
discard
elif stateFork == ConsensusFork.Bellatrix:
forkyState.data.latest_execution_payload_header.transactions_root =
transactions_root.get
@ -547,7 +550,6 @@ proc makeBeaconBlock*[T: bellatrix.ExecutionPayload | capella.ExecutionPayload |
# Effectively hash_tree_root(ExecutionPayload) with the beacon block
# body, with the execution payload replaced by the execution payload
# header. htr(payload) == htr(payload header), so substitute.
discard $capellaImplementationMissing # need different htr to match capella changes
forkyState.data.latest_block_header.body_root = hash_tree_root(
[hash_tree_root(randao_reveal),
hash_tree_root(eth1_data),
@ -559,6 +561,28 @@ proc makeBeaconBlock*[T: bellatrix.ExecutionPayload | capella.ExecutionPayload |
hash_tree_root(validator_changes.voluntary_exits),
hash_tree_root(sync_aggregate),
execution_payload_root.get])
elif stateFork == ConsensusFork.Capella:
forkyState.data.latest_execution_payload_header.transactions_root =
transactions_root.get
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.2/specs/capella/beacon-chain.md#beaconblockbody
# Effectively hash_tree_root(ExecutionPayload) with the beacon block
# body, with the execution payload replaced by the execution payload
# header. htr(payload) == htr(payload header), so substitute.
forkyState.data.latest_block_header.body_root = hash_tree_root(
[hash_tree_root(randao_reveal),
hash_tree_root(eth1_data),
hash_tree_root(graffiti),
hash_tree_root(validator_changes.proposer_slashings),
hash_tree_root(validator_changes.attester_slashings),
hash_tree_root(List[Attestation, Limit MAX_ATTESTATIONS](attestations)),
hash_tree_root(List[Deposit, Limit MAX_DEPOSITS](deposits)),
hash_tree_root(validator_changes.voluntary_exits),
hash_tree_root(sync_aggregate),
execution_payload_root.get,
hash_tree_root(validator_changes.bls_to_execution_changes)])
elif stateFork > ConsensusFork.Capella:
discard eip4844ImplementationMissing
state.`kind Data`.root = hash_tree_root(state.`kind Data`.data)
blck.`kind Data`.state_root = state.`kind Data`.root

View File

@ -41,6 +41,9 @@ macro copyFields*(
result.add newAssignment(
newDotExpr(dst, ident(name)), newDotExpr(src, ident(name)))
# TODO when https://github.com/nim-lang/Nim/issues/21346 and/or
# https://github.com/nim-lang/Nim/issues/21347 fixed, combine and make generic
# these two very similar versions of unblindAndRouteBlockMEV
proc unblindAndRouteBlockMEV*(
node: BeaconNode, blindedBlock: bellatrix_mev.SignedBlindedBeaconBlock):
Future[Result[Opt[BlockRef], string]] {.async.} =
@ -114,3 +117,80 @@ proc unblindAndRouteBlockMEV*(
# local build process as a fallback, even in the event of some failure
# with the external buildernetwork.
return err("unblindAndRouteBlockMEV error")
# TODO currently cannot be combined into one generic function
# Only difference is `var signedBlock = capella.SignedBeaconBlock` instead of
# `var signedBlock = bellatrix.SignedBeaconBlock`
proc unblindAndRouteBlockMEV*(
node: BeaconNode, blindedBlock: capella_mev.SignedBlindedBeaconBlock):
Future[Result[Opt[BlockRef], string]] {.async.} =
# By time submitBlindedBlock is called, must already have done slashing
# protection check
if node.payloadBuilderRestClient.isNil:
return err "unblindAndRouteBlockMEV: nil REST client"
let unblindedPayload =
try:
awaitWithTimeout(
node.payloadBuilderRestClient.submitBlindedBlock(blindedBlock),
BUILDER_BLOCK_SUBMISSION_DELAY_TOLERANCE):
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 unblindedPayload.status == httpOk:
if hash_tree_root(
blindedBlock.message.body.execution_payload_header) !=
hash_tree_root(unblindedPayload.data.data):
debug "unblindAndRouteBlockMEV: unblinded payload doesn't match blinded payload",
blindedPayload =
blindedBlock.message.body.execution_payload_header
else:
# Signature provided is consistent with unblinded execution payload,
# so construct full beacon block
# https://github.com/ethereum/builder-specs/blob/v0.2.0/specs/validator.md#block-proposal
var signedBlock = capella.SignedBeaconBlock(
signature: blindedBlock.signature)
copyFields(
signedBlock.message, blindedBlock.message,
getFieldNames(typeof(signedBlock.message)))
copyFields(
signedBlock.message.body, blindedBlock.message.body,
getFieldNames(typeof(signedBlock.message.body)))
signedBlock.message.body.execution_payload = unblindedPayload.data.data
signedBlock.root = hash_tree_root(signedBlock.message)
doAssert signedBlock.root == hash_tree_root(blindedBlock.message)
debug "unblindAndRouteBlockMEV: proposing unblinded block",
blck = shortLog(signedBlock)
let newBlockRef =
(await node.router.routeSignedBeaconBlock(signedBlock)).valueOr:
# submitBlindedBlock has run, so don't allow fallback to run
return err("routeSignedBeaconBlock error") # Errors logged in router
if newBlockRef.isSome:
beacon_block_builder_proposed.inc()
notice "Block proposed (MEV)",
blockRoot = shortLog(signedBlock.root), blck = shortLog(signedBlock),
signature = shortLog(signedBlock.signature)
return ok newBlockRef
else:
debug "unblindAndRouteBlockMEV: submitBlindedBlock failed",
blindedBlock, payloadStatus = unblindedPayload.status
# https://github.com/ethereum/builder-specs/blob/v0.2.0/specs/validator.md#proposer-slashing
# This means if a validator publishes a signature for a
# `BlindedBeaconBlock` (via a dissemination of a
# `SignedBlindedBeaconBlock`) then the validator **MUST** not use the
# local build process as a fallback, even in the event of some failure
# with the external buildernetwork.
return err("unblindAndRouteBlockMEV error")