UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

144 lines (126 loc) 4.51 kB
import {routes} from "@lodestar/api"; import {CheckpointWithHex, IForkChoice} from "@lodestar/fork-choice"; import {GENESIS_SLOT} from "@lodestar/params"; import {IBeaconStateView, PubkeyCache} from "@lodestar/state-transition"; import {BLSPubkey, Epoch, RootHex, Slot, ValidatorIndex, getValidatorStatus, phase0} from "@lodestar/types"; import {fromHex} from "@lodestar/utils"; import {IBeaconChain} from "../../../../chain/index.js"; import {ApiError, ValidationError} from "../../errors.js"; export function resolveStateId( forkChoice: IForkChoice, stateId: routes.beacon.StateId ): RootHex | Slot | CheckpointWithHex { if (stateId === "head") { return forkChoice.getHead().stateRoot; } if (stateId === "genesis") { return GENESIS_SLOT; } if (stateId === "finalized") { return forkChoice.getFinalizedCheckpoint(); } if (stateId === "justified") { return forkChoice.getJustifiedCheckpoint(); } if (typeof stateId === "string" && stateId.startsWith("0x")) { return stateId; } // id must be slot const blockSlot = parseInt(String(stateId), 10); if (Number.isNaN(blockSlot) && Number.isNaN(blockSlot - 0)) { throw new ValidationError(`Invalid block id '${stateId}'`, "blockId"); } return blockSlot; } export async function getStateResponseWithRegen( chain: IBeaconChain, inStateId: routes.beacon.StateId ): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean}> { const stateId = resolveStateId(chain.forkChoice, inStateId); const res = typeof stateId === "string" ? await chain.getStateByStateRoot(stateId, {allowRegen: true}) : typeof stateId === "number" ? stateId > chain.clock.currentSlot ? null // Don't try to serve future slots : stateId >= chain.forkChoice.getFinalizedBlock().slot ? await chain.getStateBySlot(stateId, {allowRegen: true}) : await chain.getHistoricalStateBySlot(stateId) : await chain.getStateOrBytesByCheckpoint(stateId); if (!res) { throw new ApiError(404, `State not found for id '${inStateId}'`); } return res; } export function toValidatorResponse( index: ValidatorIndex, validator: phase0.Validator, balance: number, currentEpoch: Epoch ): routes.beacon.ValidatorResponse { return { index, status: getValidatorStatus(validator, currentEpoch), balance, validator, }; } export function filterStateValidatorsByStatus( statuses: string[], state: IBeaconStateView, pubkeyCache: PubkeyCache, currentEpoch: Epoch ): routes.beacon.ValidatorResponse[] { const responses: routes.beacon.ValidatorResponse[] = []; const validators = state.getValidatorsByStatus(new Set(statuses), currentEpoch); for (const validator of validators) { const resp = getStateValidatorIndex(validator.pubkey, state, pubkeyCache); if (resp.valid) { responses.push( toValidatorResponse(resp.validatorIndex, validator, state.getBalance(resp.validatorIndex), currentEpoch) ); } } return responses; } type StateValidatorIndexResponse = | {valid: true; validatorIndex: ValidatorIndex} | {valid: false; code: number; reason: string}; export function getStateValidatorIndex( id: routes.beacon.ValidatorId | BLSPubkey, state: IBeaconStateView, pubkeyCache: PubkeyCache ): StateValidatorIndexResponse { if (typeof id === "string") { // mutate `id` and fallthrough to below if (id.startsWith("0x")) { try { id = fromHex(id); } catch (_e) { return {valid: false, code: 400, reason: "Invalid pubkey hex encoding"}; } } else { id = Number(id); } } if (typeof id === "number") { const validatorIndex = id; // validator is invalid or added later than given stateId if (!Number.isSafeInteger(validatorIndex)) { return {valid: false, code: 400, reason: "Invalid validator index"}; } if (validatorIndex >= state.validatorCount) { return {valid: false, code: 404, reason: "Validator index from future state"}; } return {valid: true, validatorIndex}; } // typeof id === Uint8Array const validatorIndex = pubkeyCache.getIndex(id); if (validatorIndex === null) { return {valid: false, code: 404, reason: "Validator pubkey not found in state"}; } if (validatorIndex >= state.validatorCount) { return {valid: false, code: 404, reason: "Validator pubkey from future state"}; } return {valid: true, validatorIndex}; }