Add REST endpoint to retrieve historical_summaries

This commit is contained in:
kdeme 2024-10-23 22:58:35 +02:00
parent e06af18f4d
commit 816276dfb8
No known key found for this signature in database
GPG Key ID: 4E8DD21420AF43F5
5 changed files with 117 additions and 0 deletions

View File

@ -269,3 +269,5 @@ const
"Unable to load state for parent block, database corrupt?"
RewardOverflowError* =
"Reward value overflow"
HistoricalSummariesUnavailable* =
"Historical summaries unavailable"

View File

@ -138,3 +138,52 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
bestDescendant: item.bestDescendant))
RestApiResponse.jsonResponsePlain(response)
router.metricsApi2(
MethodGet,
"/eth/v1/debug/beacon/states/{state_id}/historical_summaries",
{RestServerMetricsType.Status, Response},
) do(state_id: StateIdent) -> RestApiResponse:
let
sid = state_id.valueOr:
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError, $error)
bslot = node.getBlockSlotId(sid).valueOr:
return RestApiResponse.jsonError(Http404, StateNotFoundError, $error)
contentType = preferredContentType(jsonMediaType, sszMediaType).valueOr:
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
node.withStateForBlockSlotId(bslot):
return withState(state):
when consensusFork >= ConsensusFork.Capella:
# Build the proof for historical_summaries field (28th field in BeaconState)
let gIndex = GeneralizedIndex(59) # 31 + 28 = 59
var proof: array[5, Digest]
if forkyState.data.build_proof(gIndex, proof).isErr:
return RestApiResponse.jsonError(Http500, InvalidMerkleProofIndexError)
if contentType == jsonMediaType:
let response = RestHistoricalSummaries(
historical_summaries: forkyState.data.historical_summaries.asSeq(),
proof: proof,
slot: bslot.slot,
)
RestApiResponse.jsonResponseFinalized(
response, node.getStateOptimistic(state), node.dag.isFinalized(bslot.bid)
)
elif contentType == sszMediaType:
let
headers = [("eth-consensus-version", consensusFork.toString())]
response = GetHistoricalSummariesV1Response(
historical_summaries: forkyState.data.historical_summaries,
proof: proof,
slot: bslot.slot,
)
RestApiResponse.sszResponse(response, headers)
else:
RestApiResponse.jsonError(Http500, InvalidAcceptError)
else:
RestApiResponse.jsonError(Http404, HistoricalSummariesUnavailable)
RestApiResponse.jsonError(Http404, StateNotFoundError)

View File

@ -83,6 +83,7 @@ RestJson.useDefaultSerializationFor(
GetGenesisResponse,
GetHeaderResponseDeneb,
GetHeaderResponseElectra,
GetHistoricalSummariesV1Response,
GetKeystoresResponse,
GetNextWithdrawalsResponse,
GetPoolAttesterSlashingsResponse,
@ -131,6 +132,7 @@ RestJson.useDefaultSerializationFor(
RestEpochSyncCommittee,
RestExtraData,
RestGenesis,
RestHistoricalSummaries,
RestIndexedErrorMessage,
RestIndexedErrorMessageItem,
RestMetadata,
@ -384,6 +386,7 @@ type
DataOptimisticAndFinalizedObject |
GetBlockV2Response |
GetDistributedKeystoresResponse |
GetHistoricalSummariesV1Response |
GetKeystoresResponse |
GetRemoteKeystoresResponse |
GetStateForkResponse |

View File

@ -73,3 +73,56 @@ proc getStateV2*(client: RestClientRef, state_id: StateIdent,
msg: msg, status: error.code, message: error.message)
else:
raiseRestResponseError(resp)
proc getHistoricalSummariesV1Plain*(
state_id: StateIdent
): RestPlainResponse {.
rest,
endpoint: "/eth/v1/debug/beacon/states/{state_id}/historical_summaries",
accept: preferSSZ,
meth: MethodGet
.}
proc getHistoricalSummariesV1*(
client: RestClientRef, state_id: StateIdent, cfg: RuntimeConfig, restAccept = ""
): Future[Option[GetHistoricalSummariesV1Response]] {.async.} =
let resp =
if len(restAccept) > 0:
await client.getHistoricalSummariesV1Plain(state_id, restAcceptType = restAccept)
else:
await client.getHistoricalSummariesV1Plain(state_id)
return
case resp.status
of 200:
if resp.contentType.isNone() or isWildCard(resp.contentType.get().mediaType):
raise newException(RestError, "Missing or incorrect Content-Type")
else:
let mediaType = resp.contentType.get().mediaType
if mediaType == ApplicationJsonMediaType:
let summaries = decodeBytes(
GetHistoricalSummariesV1Response, resp.data, resp.contentType
).valueOr:
raise newException(RestError, $error)
some(summaries)
elif mediaType == OctetStreamMediaType:
let summaries =
try:
SSZ.decode(resp.data, GetHistoricalSummariesV1Response)
except SerializationError as exc:
raise newException(RestError, exc.msg)
some(summaries)
else:
raise newException(RestError, "Unsupported Content-Type")
of 404:
none(GetHistoricalSummariesV1Response)
of 400, 500:
let error = decodeBytes(RestErrorMessage, resp.data, resp.contentType).valueOr:
let msg =
"Incorrect response error format (" & $resp.status & ") [" & $error & "]"
raise (ref RestResponseError)(msg: msg, status: resp.status)
let msg = "Error response (" & $resp.status & ") [" & error.message & "]"
raise
(ref RestResponseError)(msg: msg, status: error.code, message: error.message)
else:
raiseRestResponseError(resp)

View File

@ -337,6 +337,11 @@ type
RestEpochRandao* = object
randao*: Eth2Digest
RestHistoricalSummaries* = object
historical_summaries*: seq[HistoricalSummary]
proof*: array[5, Eth2Digest]
slot*: Slot
DataEnclosedObject*[T] = object
data*: T
@ -469,6 +474,11 @@ type
GetStateV2Response* = ref ForkedHashedBeaconState
GetAggregatedAttestationV2Response* = ForkedAttestation
GetHistoricalSummariesV1Response* = object
historical_summaries*: HashList[HistoricalSummary, Limit HISTORICAL_ROOTS_LIMIT]
proof*: array[5, Eth2Digest]
slot*: Slot
RestRoot* = object
root*: Eth2Digest