diff --git a/beacon_chain/rpc/rest_constants.nim b/beacon_chain/rpc/rest_constants.nim index 1cac1725d5..fcce13463e 100644 --- a/beacon_chain/rpc/rest_constants.nim +++ b/beacon_chain/rpc/rest_constants.nim @@ -229,6 +229,12 @@ const DeprecatedRemovalGetDebugChainHeadsV1* = "v1/debug/beacon/heads endpoint was deprecated and replaced by v2: " & "https://github.com/ethereum/beacon-APIs/pull/319" + DeprecatedRemovalProduceBlindedBlockV1* = + "v1/validator/blinded_blocks/{slot} was deprecated, removed, and replaced " & + "by produceBlockV3: https://github.com/ethereum/beacon-APIs/pull/466" + DeprecatedRemovalValidatorBlocksV2* = + "v2/validator/blocks/{slot} was deprecated, removed, and replaced " & + "by produceBlockV3: https://github.com/ethereum/beacon-APIs/pull/466" BlockIncorrectFork* = "Block has incorrect fork" ValidatorNotActive* = diff --git a/beacon_chain/rpc/rest_validator_api.nim b/beacon_chain/rpc/rest_validator_api.nim index 54beb5bda1..be1dc282aa 100644 --- a/beacon_chain/rpc/rest_validator_api.nim +++ b/beacon_chain/rpc/rest_validator_api.nim @@ -315,245 +315,25 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = RestApiResponse.jsonError(Http404, StateNotFoundError) - # https://ethereum.github.io/beacon-APIs/#/Validator/produceBlock router.api2(MethodGet, "/eth/v1/validator/blocks/{slot}") do ( slot: Slot, randao_reveal: Option[ValidatorSig], graffiti: Option[GraffitiBytes]) -> RestApiResponse: RestApiResponse.jsonError( Http410, DeprecatedRemovalValidatorBlocksV1) - # https://ethereum.github.io/beacon-APIs/#/Validator/produceBlockV2 router.api(MethodGet, "/eth/v2/validator/blocks/{slot}") do ( slot: Slot, randao_reveal: Option[ValidatorSig], graffiti: Option[GraffitiBytes], skip_randao_verification: Option[string]) -> RestApiResponse: - let - contentType = preferredContentType(jsonMediaType, sszMediaType).valueOr: - return RestApiResponse.jsonError(Http406, ContentNotAcceptableError) - let message = - block: - let qslot = block: - if slot.isErr(): - return RestApiResponse.jsonError(Http400, InvalidSlotValueError, - $slot.error()) - let res = slot.get() - - if res <= node.dag.finalizedHead.slot: - return RestApiResponse.jsonError(Http400, InvalidSlotValueError, - "Slot already finalized") - let - wallTime = node.beaconClock.now() + MAXIMUM_GOSSIP_CLOCK_DISPARITY - if res > wallTime.slotOrZero: - return RestApiResponse.jsonError(Http400, InvalidSlotValueError, - "Slot cannot be in the future") - res - let qskip_randao_verification = - if skip_randao_verification.isNone(): - false - else: - let res = skip_randao_verification.get() - if res.isErr() or res.get() != "": - return RestApiResponse.jsonError( - Http400, InvalidSkipRandaoVerificationValue) - true - let qrandao = - if randao_reveal.isNone(): - return RestApiResponse.jsonError(Http400, MissingRandaoRevealValue) - else: - let res = randao_reveal.get() - if res.isErr(): - return RestApiResponse.jsonError(Http400, - InvalidRandaoRevealValue, - $res.error()) - res.get() - let qgraffiti = - if graffiti.isNone(): - defaultGraffitiBytes() - else: - let res = graffiti.get() - if res.isErr(): - return RestApiResponse.jsonError(Http400, - InvalidGraffitiBytesValue, - $res.error()) - res.get() - let qhead = - block: - let res = node.getSyncedHead(qslot) - if res.isErr(): - return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError, - $res.error()) - let tres = res.get() - if not tres.executionValid: - return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) - tres - let - proposer = node.dag.getProposer(qhead, qslot).valueOr: - return RestApiResponse.jsonError(Http400, ProposerNotFoundError) - - if not node.verifyRandao( - qslot, proposer, qrandao, qskip_randao_verification): - return RestApiResponse.jsonError(Http400, InvalidRandaoRevealValue) - - let res = withConsensusFork( - node.dag.cfg.consensusForkAtEpoch(qslot.epoch)): - when consensusFork >= ConsensusFork.Bellatrix: - await makeBeaconBlockForHeadAndSlot( - consensusFork.ExecutionPayloadForSigning, - node, qrandao, proposer, qgraffiti, qhead, qslot) - else: - return RestApiResponse.jsonError(Http400, InvalidSlotValueError) - if res.isErr(): - return RestApiResponse.jsonError(Http400, res.error()) - res.get - return - withBlck(message.blck): - let data = - when consensusFork >= ConsensusFork.Fulu: - let blobsBundle = message.blobsBundleOpt.get() - fulu.BlockContents( - `block`: forkyBlck, - kzg_proofs: blobsBundle.proofs, - blobs: blobsBundle.blobs) - elif consensusFork >= ConsensusFork.Electra: - let blobsBundle = message.blobsBundleOpt.get() - electra.BlockContents( - `block`: forkyBlck, - kzg_proofs: blobsBundle.proofs, - blobs: blobsBundle.blobs) - elif consensusFork >= ConsensusFork.Deneb: - let blobsBundle = message.blobsBundleOpt.get() - deneb.BlockContents( - `block`: forkyBlck, - kzg_proofs: blobsBundle.proofs, - blobs: blobsBundle.blobs) - else: - forkyBlck - if contentType == sszMediaType: - let headers = [("eth-consensus-version", consensusFork.toString())] - RestApiResponse.sszResponse(data, headers) - elif contentType == jsonMediaType: - RestApiResponse.jsonResponseWVersion(data, consensusFork) - else: - raiseAssert "preferredContentType() returns invalid content type" + RestApiResponse.jsonError( + Http410, DeprecatedRemovalValidatorBlocksV2) - # https://ethereum.github.io/beacon-APIs/#/Validator/produceBlindedBlock - # https://github.com/ethereum/beacon-APIs/blob/c097f1a62c9a12c30e8175a39f205f92d3b931a9/apis/validator/blinded_block.yaml router.api(MethodGet, "/eth/v1/validator/blinded_blocks/{slot}") do ( slot: Slot, randao_reveal: Option[ValidatorSig], graffiti: Option[GraffitiBytes], skip_randao_verification: Option[string]) -> RestApiResponse: - ## 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. - ## - ## Metadata in the response indicates the type of block produced, and the - ## supported types of block will be added to as forks progress. - let contentType = - block: - let res = preferredContentType(jsonMediaType, - sszMediaType) - if res.isErr(): - return RestApiResponse.jsonError(Http406, ContentNotAcceptableError) - res.get() - let qslot = block: - if slot.isErr(): - return RestApiResponse.jsonError(Http400, InvalidSlotValueError, - $slot.error()) - let res = slot.get() - - if res <= node.dag.finalizedHead.slot: - return RestApiResponse.jsonError(Http400, InvalidSlotValueError, - "Slot already finalized") - let - wallTime = node.beaconClock.now() + MAXIMUM_GOSSIP_CLOCK_DISPARITY - if res > wallTime.slotOrZero: - return RestApiResponse.jsonError(Http400, InvalidSlotValueError, - "Slot cannot be in the future") - res - let qskip_randao_verification = - if skip_randao_verification.isNone(): - false - else: - let res = skip_randao_verification.get() - if res.isErr() or res.get() != "": - return RestApiResponse.jsonError(Http400, - InvalidSkipRandaoVerificationValue) - true - let qrandao = - if randao_reveal.isNone(): - return RestApiResponse.jsonError(Http400, MissingRandaoRevealValue) - else: - let res = randao_reveal.get() - if res.isErr(): - return RestApiResponse.jsonError(Http400, - InvalidRandaoRevealValue, - $res.error()) - res.get() - let qgraffiti = - if graffiti.isNone(): - defaultGraffitiBytes() - else: - let res = graffiti.get() - if res.isErr(): - return RestApiResponse.jsonError(Http400, - InvalidGraffitiBytesValue, - $res.error()) - res.get() - let qhead = - block: - let res = node.getSyncedHead(qslot) - if res.isErr(): - return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError, - $res.error()) - let tres = res.get() - if not tres.executionValid: - return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) - tres - let proposer = node.dag.getProposer(qhead, qslot).valueOr: - return RestApiResponse.jsonError(Http400, ProposerNotFoundError) - - if not node.verifyRandao( - qslot, proposer, qrandao, qskip_randao_verification): - return RestApiResponse.jsonError(Http400, InvalidRandaoRevealValue) - - template responseVersioned( - response: untyped, contextFork: ConsensusFork): untyped = - if contentType == sszMediaType: - let headers = [("eth-consensus-version", contextFork.toString())] - RestApiResponse.sszResponse(response, headers) - elif contentType == jsonMediaType: - RestApiResponse.jsonResponseWVersion(response, contextFork) - else: - RestApiResponse.jsonError(Http500, InvalidAcceptError) - - let - payloadBuilderClient = node.getPayloadBuilderClient( - proposer.distinctBase).valueOr: - return RestApiResponse.jsonError( - Http500, "Unable to initialize payload builder client: " & $error) - contextFork = node.dag.cfg.consensusForkAtEpoch(node.currentSlot.epoch) - - withConsensusFork(contextFork): - when consensusFork >= ConsensusFork.Deneb: - let res = await makeBlindedBeaconBlockForHeadAndSlot[ - consensusFork.BlindedBeaconBlock]( - node, payloadBuilderClient, qrandao, - proposer, qgraffiti, qhead, qslot) - if res.isErr(): - return RestApiResponse.jsonError(Http400, res.error()) - return responseVersioned(res.get().blindedBlckPart, contextFork) - elif consensusFork >= ConsensusFork.Bellatrix: - return RestApiResponse.jsonError( - Http400, "Pre-Deneb builder API unsupported") - else: - # Pre-Bellatrix, this endpoint will return a BeaconBlock - let res = await makeBeaconBlockForHeadAndSlot( - bellatrix.ExecutionPayloadForSigning, node, qrandao, - proposer, qgraffiti, qhead, qslot) - if res.isErr(): - return RestApiResponse.jsonError(Http400, res.error()) - withBlck(res.get().blck): - return responseVersioned(forkyBlck, contextFork) + RestApiResponse.jsonError( + Http410, DeprecatedRemovalProduceBlindedBlockV1) func getMaybeBlindedHeaders( consensusFork: ConsensusFork, diff --git a/beacon_chain/validators/beacon_validators.nim b/beacon_chain/validators/beacon_validators.nim index b6bd2dfd60..fc3d36a572 100644 --- a/beacon_chain/validators/beacon_validators.nim +++ b/beacon_chain/validators/beacon_validators.nim @@ -800,78 +800,6 @@ func constructSignableBlindedBlock[T: fulu_mev.SignedBlindedBeaconBlock]( blindedBlock -func constructPlainBlindedBlock[T: deneb_mev.BlindedBeaconBlock]( - blck: ForkyBeaconBlock, - blindedBundle: deneb_mev.BlindedExecutionPayloadAndBlobsBundle): T = - # https://github.com/nim-lang/Nim/issues/23020 workaround - static: doAssert T is deneb_mev.BlindedBeaconBlock - - const - blckFields = getFieldNames(typeof(blck)) - blckBodyFields = getFieldNames(typeof(blck.body)) - - var blindedBlock: T - - # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/validator.md#block-proposal - copyFields(blindedBlock, blck, blckFields) - copyFields(blindedBlock.body, blck.body, blckBodyFields) - assign( - blindedBlock.body.execution_payload_header, - blindedBundle.execution_payload_header) - assign( - blindedBlock.body.blob_kzg_commitments, - blindedBundle.blob_kzg_commitments) - - blindedBlock - -func constructPlainBlindedBlock[T: electra_mev.BlindedBeaconBlock]( - blck: ForkyBeaconBlock, - blindedBundle: electra_mev.BlindedExecutionPayloadAndBlobsBundle): T = - # https://github.com/nim-lang/Nim/issues/23020 workaround - static: doAssert T is electra_mev.BlindedBeaconBlock - - const - blckFields = getFieldNames(typeof(blck)) - blckBodyFields = getFieldNames(typeof(blck.body)) - - var blindedBlock: T - - # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/validator.md#block-proposal - copyFields(blindedBlock, blck, blckFields) - copyFields(blindedBlock.body, blck.body, blckBodyFields) - assign( - blindedBlock.body.execution_payload_header, - blindedBundle.execution_payload_header) - assign( - blindedBlock.body.blob_kzg_commitments, - blindedBundle.blob_kzg_commitments) - - blindedBlock - -func constructPlainBlindedBlock[T: fulu_mev.BlindedBeaconBlock]( - blck: ForkyBeaconBlock, - blindedBundle: fulu_mev.BlindedExecutionPayloadAndBlobsBundle): T = - # https://github.com/nim-lang/Nim/issues/23020 workaround - static: doAssert T is fulu_mev.BlindedBeaconBlock - - const - blckFields = getFieldNames(typeof(blck)) - blckBodyFields = getFieldNames(typeof(blck.body)) - - var blindedBlock: T - - # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/validator.md#block-proposal - copyFields(blindedBlock, blck, blckFields) - copyFields(blindedBlock.body, blck.body, blckBodyFields) - assign( - blindedBlock.body.execution_payload_header, - blindedBundle.execution_payload_header) - assign( - blindedBlock.body.blob_kzg_commitments, - blindedBundle.blob_kzg_commitments) - - blindedBlock - proc blindedBlockCheckSlashingAndSign[ T: deneb_mev.SignedBlindedBeaconBlock | electra_mev.SignedBlindedBeaconBlock | @@ -1145,81 +1073,6 @@ proc proposeBlockMEV( func isEFMainnet(cfg: RuntimeConfig): bool = cfg.DEPOSIT_CHAIN_ID == 1 and cfg.DEPOSIT_NETWORK_ID == 1 -proc makeBlindedBeaconBlockForHeadAndSlot*[BBB: ForkyBlindedBeaconBlock]( - node: BeaconNode, payloadBuilderClient: RestClientRef, - randao_reveal: ValidatorSig, validator_index: ValidatorIndex, - graffiti: GraffitiBytes, head: BlockRef, slot: Slot): - Future[BlindedBlockResult[BBB]] {.async: (raises: [CancelledError]).} = - ## 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. - ## - ## This function is used by the validator client, but not the beacon node for - ## its own validators. - when BBB is fulu_mev.BlindedBeaconBlock: - type EPH = fulu_mev.BlindedExecutionPayloadAndBlobsBundle - elif BBB is electra_mev.BlindedBeaconBlock: - type EPH = electra_mev.BlindedExecutionPayloadAndBlobsBundle - elif BBB is deneb_mev.BlindedBeaconBlock: - type EPH = deneb_mev.BlindedExecutionPayloadAndBlobsBundle - else: - static: doAssert false - - let - pubkey = - # Relevant state for knowledge of validators - withState(node.dag.headState): - if node.dag.cfg.isEFMainnet and livenessFailsafeInEffect( - forkyState.data.block_roots.data, forkyState.data.slot): - # It's head block's slot which matters here, not proposal slot - return err("Builder API liveness failsafe in effect") - - if distinctBase(validator_index) >= forkyState.data.validators.lenu64: - debug "makeBlindedBeaconBlockForHeadAndSlot: invalid validator index", - head = shortLog(head), - validator_index, - validators_len = forkyState.data.validators.len - return err("Invalid validator index") - - forkyState.data.validators.item(validator_index).pubkey - - blindedBlockParts = await getBlindedBlockParts[EPH]( - 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") - - let (executionPayloadHeader, bidValue, consensusValue, forkedBlck) = - blindedBlockParts.get - withBlck(forkedBlck): - when consensusFork >= ConsensusFork.Deneb: - when (consensusFork == ConsensusFork.Deneb and - EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle): - return ok( - BuilderBid[BBB]( - blindedBlckPart: - constructPlainBlindedBlock[BBB](forkyBlck, executionPayloadHeader), - executionRequests: default(ExecutionRequests), - executionPayloadValue: bidValue, - consensusBlockValue: consensusValue)) - - elif (consensusFork == ConsensusFork.Electra and - EPH is electra_mev.BlindedExecutionPayloadAndBlobsBundle) or - (consensusFork == ConsensusFork.Fulu and - EPH is fulu_mev.BlindedExecutionPayloadAndBlobsBundle): - return ok( - BuilderBid[BBB]( - blindedBlckPart: - constructPlainBlindedBlock[BBB](forkyBlck, executionPayloadHeader), - executionRequests: forkyBlck.body.execution_requests, - executionPayloadValue: bidValue, - consensusBlockValue: consensusValue)) - else: - return err("makeBlindedBeaconBlockForHeadAndSlot: mismatched block/payload types") - else: - return err("Attempt to create pre-Deneb blinded block") - proc collectBids( SBBB: typedesc, EPS: typedesc, node: BeaconNode, payloadBuilderClient: RestClientRef, validator_pubkey: ValidatorPubKey, diff --git a/ncli/resttest-rules.json b/ncli/resttest-rules.json index f925140ed0..9f7af745e9 100644 --- a/ncli/resttest-rules.json +++ b/ncli/resttest-rules.json @@ -4526,7 +4526,7 @@ "headers": {"Accept": "application/json"} }, "response": { - "status": {"operator": "equals", "value": "400"} + "status": {"operator": "equals", "value": "410"} } }, { @@ -4536,7 +4536,7 @@ "headers": {"Accept": "application/json"} }, "response": { - "status": {"operator": "equals", "value": "400"} + "status": {"operator": "equals", "value": "410"} } }, {