refactor payload builder REST client usage (#4973)
* refactor payload builder REST client usage * change HTTP response code
This commit is contained in:
parent
8833acbe23
commit
d1941b670a
|
@ -70,7 +70,6 @@ type
|
|||
lightClientPool*: ref LightClientPool
|
||||
validatorChangePool*: ref ValidatorChangePool
|
||||
elManager*: ELManager
|
||||
payloadBuilderRestClient*: RestClientRef
|
||||
restServer*: RestServerRef
|
||||
keymanagerHost*: ref KeymanagerHost
|
||||
keymanagerServer*: RestServerRef
|
||||
|
@ -115,3 +114,14 @@ template rng*(node: BeaconNode): ref HmacDrbgContext =
|
|||
|
||||
proc currentSlot*(node: BeaconNode): Slot =
|
||||
node.beaconClock.now.slotOrZero
|
||||
|
||||
proc getPayloadBuilderClient*(node: BeaconNode): RestResult[RestClientRef] =
|
||||
if node.config.payloadBuilderEnable:
|
||||
# Logging done in caller
|
||||
let res = RestClientRef.new(node.config.payloadBuilderUrl)
|
||||
if res.isOk and res.get.isNil:
|
||||
err "Got nil payload builder REST client reference"
|
||||
else:
|
||||
res
|
||||
else:
|
||||
err "Payload builder globally disabled"
|
||||
|
|
|
@ -696,16 +696,7 @@ proc init*(T: type BeaconNode,
|
|||
else:
|
||||
nil
|
||||
|
||||
let payloadBuilderRestClient =
|
||||
if config.payloadBuilderEnable:
|
||||
RestClientRef.new(config.payloadBuilderUrl).valueOr:
|
||||
warn "Payload builder REST client setup failed",
|
||||
payloadBuilderUrl = config.payloadBuilderUrl
|
||||
nil
|
||||
else:
|
||||
nil
|
||||
|
||||
if config.payloadBuilderEnable and payloadBuilderRestClient != nil:
|
||||
if config.payloadBuilderEnable:
|
||||
info "Using external payload builder",
|
||||
payloadBuilderUrl = config.payloadBuilderUrl
|
||||
|
||||
|
@ -719,7 +710,6 @@ proc init*(T: type BeaconNode,
|
|||
config: config,
|
||||
attachedValidators: validatorPool,
|
||||
elManager: elManager,
|
||||
payloadBuilderRestClient: payloadBuilderRestClient,
|
||||
restServer: restServer,
|
||||
keymanagerHost: keymanagerHost,
|
||||
keymanagerServer: keymanagerInitResult.server,
|
||||
|
|
|
@ -886,6 +886,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
currentEpochFork.toString != version:
|
||||
return RestApiResponse.jsonError(Http400, BlockIncorrectFork)
|
||||
|
||||
let payloadBuilderClient = node.getPayloadBuilderClient().valueOr:
|
||||
return RestApiResponse.jsonError(
|
||||
Http400, "Unable to initialize payload builder client: " & $error)
|
||||
|
||||
case currentEpochFork
|
||||
of ConsensusFork.Deneb:
|
||||
return RestApiResponse.jsonError(Http500, $denebImplementationMissing)
|
||||
|
@ -896,7 +900,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
capella_mev.SignedBlindedBeaconBlock, body).valueOr:
|
||||
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
|
||||
$error)
|
||||
await node.unblindAndRouteBlockMEV(restBlock)
|
||||
await node.unblindAndRouteBlockMEV(payloadBuilderClient, restBlock)
|
||||
|
||||
if res.isErr():
|
||||
return RestApiResponse.jsonError(
|
||||
|
@ -912,7 +916,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
bellatrix_mev.SignedBlindedBeaconBlock, body).valueOr:
|
||||
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
|
||||
$error)
|
||||
await node.unblindAndRouteBlockMEV(restBlock)
|
||||
await node.unblindAndRouteBlockMEV(payloadBuilderClient, restBlock)
|
||||
|
||||
if res.isErr():
|
||||
return RestApiResponse.jsonError(
|
||||
|
|
|
@ -508,7 +508,12 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
else:
|
||||
RestApiResponse.jsonError(Http500, InvalidAcceptError)
|
||||
|
||||
let contextFork = node.dag.cfg.consensusForkAtEpoch(node.currentSlot.epoch)
|
||||
let
|
||||
payloadBuilderClient = node.getPayloadBuilderClient().valueOr:
|
||||
return RestApiResponse.jsonError(
|
||||
Http500, "Unable to initialize payload builder client: " & $error)
|
||||
contextFork = node.dag.cfg.consensusForkAtEpoch(node.currentSlot.epoch)
|
||||
|
||||
case contextFork
|
||||
of ConsensusFork.Deneb:
|
||||
# TODO
|
||||
|
@ -518,14 +523,14 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
of ConsensusFork.Capella:
|
||||
let res = await makeBlindedBeaconBlockForHeadAndSlot[
|
||||
capella_mev.BlindedBeaconBlock](
|
||||
node, qrandao, proposer, qgraffiti, qhead, qslot)
|
||||
node, payloadBuilderClient, qrandao, proposer, qgraffiti, qhead, qslot)
|
||||
if res.isErr():
|
||||
return RestApiResponse.jsonError(Http400, res.error())
|
||||
return responseVersioned(res.get().blindedBlckPart, contextFork)
|
||||
of ConsensusFork.Bellatrix:
|
||||
let res = await makeBlindedBeaconBlockForHeadAndSlot[
|
||||
bellatrix_mev.BlindedBeaconBlock](
|
||||
node, qrandao, proposer, qgraffiti, qhead, qslot)
|
||||
node, payloadBuilderClient, qrandao, proposer, qgraffiti, qhead, qslot)
|
||||
if res.isErr():
|
||||
return RestApiResponse.jsonError(Http400, res.error())
|
||||
return responseVersioned(res.get().blindedBlckPart, contextFork)
|
||||
|
|
|
@ -45,17 +45,15 @@ macro copyFields*(
|
|||
# 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):
|
||||
node: BeaconNode, payloadBuilderRestClient: RestClientRef,
|
||||
blindedBlock: bellatrix_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),
|
||||
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
|
||||
|
@ -122,17 +120,15 @@ proc unblindAndRouteBlockMEV*(
|
|||
# Only difference is `var signedBlock = capella.SignedBeaconBlock` instead of
|
||||
# `var signedBlock = bellatrix.SignedBeaconBlock`
|
||||
proc unblindAndRouteBlockMEV*(
|
||||
node: BeaconNode, blindedBlock: capella_mev.SignedBlindedBeaconBlock):
|
||||
node: BeaconNode, payloadBuilderRestClient: RestClientRef,
|
||||
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),
|
||||
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
|
||||
|
|
|
@ -453,23 +453,19 @@ proc makeBeaconBlockForHeadAndSlot*(
|
|||
|
||||
proc getBlindedExecutionPayload[
|
||||
EPH: bellatrix.ExecutionPayloadHeader | capella.ExecutionPayloadHeader](
|
||||
node: BeaconNode, slot: Slot, executionBlockRoot: Eth2Digest,
|
||||
pubkey: ValidatorPubKey): Future[BlindedBlockResult[EPH]] {.async.} =
|
||||
if node.payloadBuilderRestClient.isNil:
|
||||
return err "getBlindedExecutionPayload: nil REST client"
|
||||
|
||||
node: BeaconNode, payloadBuilderClient: RestClientRef, slot: Slot,
|
||||
executionBlockRoot: Eth2Digest, pubkey: ValidatorPubKey):
|
||||
Future[BlindedBlockResult[EPH]] {.async.} =
|
||||
when EPH is capella.ExecutionPayloadHeader:
|
||||
let blindedHeader = awaitWithTimeout(
|
||||
node.payloadBuilderRestClient.getHeaderCapella(
|
||||
slot, executionBlockRoot, pubkey),
|
||||
payloadBuilderClient.getHeaderCapella(slot, executionBlockRoot, pubkey),
|
||||
BUILDER_PROPOSAL_DELAY_TOLERANCE):
|
||||
return err "Timeout when obtaining Capella blinded header from builder"
|
||||
return err "Timeout obtaining Capella blinded header from builder"
|
||||
elif EPH is bellatrix.ExecutionPayloadHeader:
|
||||
let blindedHeader = awaitWithTimeout(
|
||||
node.payloadBuilderRestClient.getHeaderBellatrix(
|
||||
slot, executionBlockRoot, pubkey),
|
||||
payloadBuilderClient.getHeaderBellatrix(slot, executionBlockRoot, pubkey),
|
||||
BUILDER_PROPOSAL_DELAY_TOLERANCE):
|
||||
return err "Timeout when obtaining Bellatrix blinded header from builder"
|
||||
return err "Timeout obtaining Bellatrix blinded header from builder"
|
||||
else:
|
||||
static: doAssert false
|
||||
|
||||
|
@ -584,17 +580,17 @@ proc getUnsignedBlindedBeaconBlock[
|
|||
return err("getUnsignedBlindedBeaconBlock: attempt to construct pre-Bellatrix blinded block")
|
||||
|
||||
proc getBlindedBlockParts[EPH: ForkyExecutionPayloadHeader](
|
||||
node: BeaconNode, head: BlockRef, pubkey: ValidatorPubKey,
|
||||
slot: Slot, randao: ValidatorSig, validator_index: ValidatorIndex,
|
||||
graffiti: GraffitiBytes): Future[Result[(EPH, UInt256, ForkedBeaconBlock), string]]
|
||||
{.async.} =
|
||||
node: BeaconNode, payloadBuilderClient: RestClientRef, head: BlockRef,
|
||||
pubkey: ValidatorPubKey, slot: Slot, randao: ValidatorSig,
|
||||
validator_index: ValidatorIndex, graffiti: GraffitiBytes):
|
||||
Future[Result[(EPH, UInt256, ForkedBeaconBlock), string]] {.async.} =
|
||||
let
|
||||
executionBlockRoot = node.dag.loadExecutionBlockHash(head)
|
||||
executionPayloadHeader =
|
||||
try:
|
||||
awaitWithTimeout(
|
||||
getBlindedExecutionPayload[EPH](
|
||||
node, slot, executionBlockRoot, pubkey),
|
||||
node, payloadBuilderClient, slot, executionBlockRoot, pubkey),
|
||||
BUILDER_PROPOSAL_DELAY_TOLERANCE):
|
||||
BlindedBlockResult[EPH].err("getBlindedExecutionPayload timed out")
|
||||
except RestDecodingError as exc:
|
||||
|
@ -661,8 +657,9 @@ proc getBlindedBlockParts[EPH: ForkyExecutionPayloadHeader](
|
|||
proc getBuilderBid[
|
||||
SBBB: bellatrix_mev.SignedBlindedBeaconBlock |
|
||||
capella_mev.SignedBlindedBeaconBlock](
|
||||
node: BeaconNode, head: BlockRef, validator: AttachedValidator, slot: Slot,
|
||||
randao: ValidatorSig, validator_index: ValidatorIndex):
|
||||
node: BeaconNode, payloadBuilderClient: RestClientRef, head: BlockRef,
|
||||
validator: AttachedValidator, slot: Slot, randao: ValidatorSig,
|
||||
validator_index: ValidatorIndex):
|
||||
Future[BlindedBlockResult[SBBB]] {.async.} =
|
||||
## Returns the unsigned blinded block obtained from the Builder API.
|
||||
## Used by the BN's own validators, but not the REST server
|
||||
|
@ -674,8 +671,8 @@ proc getBuilderBid[
|
|||
static: doAssert false
|
||||
|
||||
let blindedBlockParts = await getBlindedBlockParts[EPH](
|
||||
node, head, validator.pubkey, slot, randao, validator_index,
|
||||
node.graffitiBytes)
|
||||
node, payloadBuilderClient, head, validator.pubkey, slot, randao,
|
||||
validator_index, node.graffitiBytes)
|
||||
if blindedBlockParts.isErr:
|
||||
# Not signed yet, fine to try to fall back on EL
|
||||
beacon_block_builder_missed_with_fallback.inc()
|
||||
|
@ -694,9 +691,11 @@ proc getBuilderBid[
|
|||
|
||||
return ok (unsignedBlindedBlock.get, bidValue)
|
||||
|
||||
proc proposeBlockMEV(node: BeaconNode, blindedBlock: auto):
|
||||
proc proposeBlockMEV(
|
||||
node: BeaconNode, payloadBuilderClient: RestClientRef, blindedBlock: auto):
|
||||
Future[Result[BlockRef, string]] {.async.} =
|
||||
let unblindedBlockRef = await node.unblindAndRouteBlockMEV(blindedBlock)
|
||||
let unblindedBlockRef = await node.unblindAndRouteBlockMEV(
|
||||
payloadBuilderClient, blindedBlock)
|
||||
return if unblindedBlockRef.isOk and unblindedBlockRef.get.isSome:
|
||||
beacon_blocks_proposed.inc()
|
||||
ok(unblindedBlockRef.get.get)
|
||||
|
@ -727,9 +726,10 @@ func isEFMainnet(cfg: RuntimeConfig): bool =
|
|||
|
||||
proc makeBlindedBeaconBlockForHeadAndSlot*[
|
||||
BBB: bellatrix_mev.BlindedBeaconBlock | capella_mev.BlindedBeaconBlock](
|
||||
node: BeaconNode, randao_reveal: ValidatorSig,
|
||||
validator_index: ValidatorIndex, graffiti: GraffitiBytes, head: BlockRef,
|
||||
slot: Slot): Future[BlindedBlockResult[BBB]] {.async.} =
|
||||
node: BeaconNode, payloadBuilderClient: RestClientRef,
|
||||
randao_reveal: ValidatorSig, validator_index: ValidatorIndex,
|
||||
graffiti: GraffitiBytes, head: BlockRef, slot: Slot):
|
||||
Future[BlindedBlockResult[BBB]] {.async.} =
|
||||
## Requests a beacon node to produce a valid blinded block, which can then be
|
||||
## signed by a validator. A blinded block is a block with only a transactions
|
||||
## root, rather than a full transactions list.
|
||||
|
@ -762,7 +762,8 @@ proc makeBlindedBeaconBlockForHeadAndSlot*[
|
|||
forkyState.data.validators.item(validator_index).pubkey
|
||||
|
||||
blindedBlockParts = await getBlindedBlockParts[EPH](
|
||||
node, head, pubkey, slot, randao_reveal, validator_index, graffiti)
|
||||
node, payloadBuilderClient, head, pubkey, slot, randao_reveal,
|
||||
validator_index, graffiti)
|
||||
if blindedBlockParts.isErr:
|
||||
# Don't try EL fallback -- VC specifically requested a blinded block
|
||||
return err("Unable to create blinded block")
|
||||
|
@ -790,8 +791,17 @@ proc proposeBlockAux(
|
|||
genesis_validators_root: Eth2Digest,
|
||||
localBlockValueBoost: uint8): Future[BlockRef] {.async.} =
|
||||
# Collect bids
|
||||
var payloadBuilderClient: RestClientRef
|
||||
|
||||
let payloadBuilderClientMaybe = node.getPayloadBuilderClient()
|
||||
if payloadBuilderClientMaybe.isErr:
|
||||
warn "Unable to initialize payload builder client while proposing block",
|
||||
err = payloadBuilderClientMaybe.error
|
||||
else:
|
||||
payloadBuilderClient = payloadBuilderClientMaybe.get
|
||||
|
||||
let usePayloadBuilder =
|
||||
if node.config.payloadBuilderEnable:
|
||||
if node.config.payloadBuilderEnable and payloadBuilderClientMaybe.isOk:
|
||||
withState(node.dag.headState):
|
||||
# Head slot, not proposal slot, matters here
|
||||
# TODO it might make some sense to allow use of builder API if local
|
||||
|
@ -806,7 +816,9 @@ proc proposeBlockAux(
|
|||
let
|
||||
payloadBuilderBidFut =
|
||||
if usePayloadBuilder:
|
||||
getBuilderBid[SBBB](node, head, validator, slot, randao, validator_index)
|
||||
getBuilderBid[SBBB](
|
||||
node, payloadBuilderClient, head, validator, slot, randao,
|
||||
validator_index)
|
||||
else:
|
||||
let fut = newFuture[BlindedBlockResult[SBBB]]("builder-bid")
|
||||
fut.complete(BlindedBlockResult[SBBB].err(
|
||||
|
@ -885,7 +897,8 @@ proc proposeBlockAux(
|
|||
return head
|
||||
# Before proposeBlockMEV, can fall back to EL; after, cannot without
|
||||
# risking slashing.
|
||||
maybeUnblindedBlock = await proposeBlockMEV(node, blindedBlock)
|
||||
maybeUnblindedBlock = await proposeBlockMEV(
|
||||
node, payloadBuilderClient, blindedBlock)
|
||||
|
||||
return maybeUnblindedBlock.valueOr:
|
||||
warn "Blinded block proposal incomplete",
|
||||
|
@ -1383,14 +1396,15 @@ proc registerValidators*(node: BeaconNode, epoch: Epoch) {.async.} =
|
|||
if (not node.config.payloadBuilderEnable) or
|
||||
node.currentSlot.epoch < node.dag.cfg.BELLATRIX_FORK_EPOCH:
|
||||
return
|
||||
elif node.config.payloadBuilderEnable and
|
||||
node.payloadBuilderRestClient.isNil:
|
||||
warn "registerValidators: node.config.payloadBuilderEnable and node.payloadBuilderRestClient.isNil"
|
||||
return
|
||||
|
||||
const HttpOk = 200
|
||||
|
||||
let restBuilderStatus = awaitWithTimeout(node.payloadBuilderRestClient.checkBuilderStatus(),
|
||||
let payloadBuilderClient = node.getPayloadBuilderClient().valueOr:
|
||||
debug "Unable to initialize payload builder client while registering validators",
|
||||
err = error
|
||||
return
|
||||
|
||||
let restBuilderStatus = awaitWithTimeout(payloadBuilderClient.checkBuilderStatus(),
|
||||
BUILDER_STATUS_DELAY_TOLERANCE):
|
||||
debug "Timeout when obtaining builder status"
|
||||
return
|
||||
|
@ -1494,7 +1508,7 @@ proc registerValidators*(node: BeaconNode, epoch: Epoch) {.async.} =
|
|||
for chunkIdx in 0 ..< validatorRegistrations.len:
|
||||
let registerValidatorResult =
|
||||
awaitWithTimeout(
|
||||
node.payloadBuilderRestClient.registerValidator(
|
||||
payloadBuilderClient.registerValidator(
|
||||
validatorRegistrations[chunkIdx]),
|
||||
BUILDER_VALIDATOR_REGISTRATION_DELAY_TOLERANCE):
|
||||
error "Timeout when registering validator with builder"
|
||||
|
|
Loading…
Reference in New Issue