From b0d50015710e6570d765a86bb2bff6d61b0a211d Mon Sep 17 00:00:00 2001 From: tersec Date: Sun, 29 Oct 2023 00:06:13 +0000 Subject: [PATCH] implement getBlobSidecars Beacon API endpoint (#5530) --- beacon_chain/rpc/rest_beacon_api.nim | 47 ++++++++++++++++++++++++++++ beacon_chain/rpc/rest_constants.nim | 3 +- ncli/resttest-rules.json | 24 ++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index 876b48d4e..352a46da6 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -1374,3 +1374,50 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = VoluntaryExitValidationError, $res.error()) return RestApiResponse.jsonMsgResponse(VoluntaryExitValidationSuccess) + + # https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.4.2#/Beacon/getBlobSidecars + # https://github.com/ethereum/beacon-APIs/blob/v2.4.2/apis/beacon/blob_sidecars/blob_sidecars.yaml + router.api(MethodGet, "/eth/v1/beacon/blob_sidecars/{block_id}") do ( + block_id: BlockIdent, indices: seq[uint64]) -> RestApiResponse: + let + bid = block_id.valueOr: + return RestApiResponse.jsonError(Http400, InvalidBlockIdValueError, + $error) + + bdata = node.getForkedBlock(bid).valueOr: + return RestApiResponse.jsonError(Http404, BlockNotFoundError) + + contentType = block: + let res = preferredContentType(jsonMediaType, + sszMediaType) + if res.isErr(): + return RestApiResponse.jsonError(Http406, ContentNotAcceptableError) + res.get() + + # https://github.com/ethereum/beacon-APIs/blob/v2.4.2/types/deneb/blob_sidecar.yaml#L2-L28 + let data = newClone(default(List[BlobSidecar, Limit MAX_BLOBS_PER_BLOCK])) + + if indices.isErr: + return RestApiResponse.jsonError(Http400, + InvalidSidecarIndexValueError) + + let indexFilter = indices.get.toHashSet + + for blobIndex in 0'u64 ..< MAX_BLOBS_PER_BLOCK: + if indexFilter.len > 0 and blobIndex notin indexFilter: + continue + + var blobSidecar = new BlobSidecar + + if node.dag.db.getBlobSidecar(bdata.root, blobIndex, blobSidecar[]): + discard data[].add blobSidecar[] + + return + if contentType == sszMediaType: + RestApiResponse.sszResponse( + data[], headers = [("eth-consensus-version", + node.dag.cfg.consensusForkAtEpoch(bid.slot.epoch).toString())]) + elif contentType == jsonMediaType: + RestApiResponse.jsonResponse(data) + else: + RestApiResponse.jsonError(Http500, InvalidAcceptError) diff --git a/beacon_chain/rpc/rest_constants.nim b/beacon_chain/rpc/rest_constants.nim index 09e4bdfdf..a57ddeb2a 100644 --- a/beacon_chain/rpc/rest_constants.nim +++ b/beacon_chain/rpc/rest_constants.nim @@ -241,4 +241,5 @@ const "Failed to obtain fork information" InvalidTimestampValue* = "Invalid or missing timestamp value" - + InvalidSidecarIndexValueError* = + "Invalid blob index" diff --git a/ncli/resttest-rules.json b/ncli/resttest-rules.json index a0e593c85..8b0999908 100644 --- a/ncli/resttest-rules.json +++ b/ncli/resttest-rules.json @@ -2669,6 +2669,30 @@ "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"message": {"validator_index": "", "from_bls_pubkey": "", "to_execution_address": ""}, "signature": ""}]}] } }, + { + "topics": ["beacon", "blob_sidecars_blockid"], + "request": { + "url": "/eth/v1/beacon/blob_sidecars/head", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "200"}} + }, + { + "topics": ["beacon", "blob_sidecars_blockid"], + "request": { + "url": "/eth/v1/beacon/blob_sidecars/finalized", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "200"}} + }, + { + "topics": ["beacon", "blob_sidecars_blockid"], + "request": { + "url": "/eth/v1/beacon/blob_sidecars/0x0000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "404"}} + }, { "topics": ["builder", "states_expected_withdrawals"], "request": {