fill in remaining capellaImplementationMissing holes for builder API (#4606)
This commit is contained in:
parent
3011d49946
commit
3977f1529a
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue