UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

101 lines 3.8 kB
import { SLOTS_PER_EPOCH } from "@lodestar/params"; import { ssz } from "@lodestar/types"; import { MapDef } from "@lodestar/utils"; import { getLastProcessedSlotFromBeaconStateSerialized, getSlotFromBeaconStateSerialized, } from "../../../util/sszBytes.js"; /** * Implementation of CPStateDatastore using db. */ export class DbCPStateDatastore { db; constructor(db) { this.db = db; } async write(cpKey, stateBytes) { const serializedCheckpoint = checkpointToDatastoreKey(cpKey); await this.db.checkpointState.putBinary(serializedCheckpoint, stateBytes); return serializedCheckpoint; } async remove(serializedCheckpoint) { await this.db.checkpointState.delete(serializedCheckpoint); } async read(serializedCheckpoint) { return this.db.checkpointState.getBinary(serializedCheckpoint); } async readLatestSafe() { const allKeys = await this.readKeys(); if (allKeys.length === 0) return null; return getLatestSafeDatastoreKey(allKeys, this.read.bind(this)); } async readKeys() { return this.db.checkpointState.keys(); } } export function datastoreKeyToCheckpoint(key) { return ssz.phase0.Checkpoint.deserialize(key); } export function checkpointToDatastoreKey(cp) { return ssz.phase0.Checkpoint.serialize(cp); } /** * Get the latest safe checkpoint state the node can use to boot from * - it should be the checkpoint state that's unique in its epoch * - its last processed block slot should be at epoch boundary or last slot of previous epoch * - state slot should be at epoch boundary * - state slot should be equal to epoch * SLOTS_PER_EPOCH * * return the serialized data of Current Root Checkpoint State (CRCS) or Previous Root Checkpoint State (PRCS) * */ export async function getLatestSafeDatastoreKey(allKeys, readFn) { const checkpointsByEpoch = new MapDef(() => []); for (const key of allKeys) { const cp = datastoreKeyToCheckpoint(key); checkpointsByEpoch.getOrDefault(cp.epoch).push(key); } const dataStoreKeyByEpoch = new Map(); for (const [epoch, keys] of checkpointsByEpoch.entries()) { // only consider epochs with a single checkpoint to avoid ambiguity from forks if (keys.length === 1) { dataStoreKeyByEpoch.set(epoch, keys[0]); } } const epochsDesc = Array.from(dataStoreKeyByEpoch.keys()).sort((a, b) => b - a); for (const epoch of epochsDesc) { const datastoreKey = dataStoreKeyByEpoch.get(epoch); if (datastoreKey == null) { // should not happen continue; } const stateBytes = await readFn(datastoreKey); if (stateBytes == null) { // should not happen continue; } const lastProcessedSlot = getLastProcessedSlotFromBeaconStateSerialized(stateBytes); if (lastProcessedSlot == null) { // cannot extract last processed slot from serialized state, skip continue; } const stateSlot = getSlotFromBeaconStateSerialized(stateBytes); if (stateSlot == null) { // cannot extract slot from serialized state, skip continue; } if (lastProcessedSlot !== stateSlot && lastProcessedSlot !== stateSlot - 1) { // not CRCS or PRCS, skip continue; } if (stateSlot % SLOTS_PER_EPOCH !== 0) { // not at epoch boundary, skip continue; } if (stateSlot !== SLOTS_PER_EPOCH * epoch) { // should not happen after above checks, but just to be safe continue; } return stateBytes; } return null; } //# sourceMappingURL=db.js.map