UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

170 lines 8.7 kB
import { callFnWhenAwait } from "@lodestar/utils"; import { isOptimisticBlock } from "../../util/forkChoice.js"; import { JobItemQueue, isQueueErrorAborted } from "../../util/queue/index.js"; import { ChainEvent } from "../emitter.js"; import { PROCESS_FINALIZED_CHECKPOINT_QUEUE_LENGTH } from "./constants.js"; import { HistoricalStateRegen } from "./historicalState/historicalStateRegen.js"; import { ArchiveMode } from "./interface.js"; import { FrequencyStateArchiveStrategy } from "./strategies/frequencyStateArchiveStrategy.js"; import { archiveBlocks } from "./utils/archiveBlocks.js"; import { pruneHistory } from "./utils/pruneHistory.js"; import { updateBackfillRange } from "./utils/updateBackfillRange.js"; export { ArchiveStoreTask }; var ArchiveStoreTask; (function (ArchiveStoreTask) { ArchiveStoreTask["ArchiveBlocks"] = "archive_blocks"; ArchiveStoreTask["PruneHistory"] = "prune_history"; ArchiveStoreTask["OnFinalizedCheckpoint"] = "on_finalized_checkpoint"; ArchiveStoreTask["MaybeArchiveState"] = "maybe_archive_state"; ArchiveStoreTask["RegenPruneOnFinalized"] = "regen_prune_on_finalized"; ArchiveStoreTask["ForkchoicePrune"] = "forkchoice_prune"; ArchiveStoreTask["UpdateBackfillRange"] = "update_backfill_range"; })(ArchiveStoreTask || (ArchiveStoreTask = {})); /** * Used for running tasks that depends on some events or are executed * periodically. */ export class ArchiveStore { archiveMode; jobQueue; archiveDataEpochs; statesArchiverStrategy; chain; db; logger; metrics; opts; signal; historicalStateRegen; constructor(modules, opts, signal) { this.chain = modules.chain; this.db = modules.db; this.logger = modules.logger; this.metrics = modules.metrics; this.opts = opts; this.signal = signal; this.archiveMode = opts.archiveMode; this.archiveDataEpochs = opts.archiveDataEpochs; this.jobQueue = new JobItemQueue(this.processFinalizedCheckpoint, { maxLength: PROCESS_FINALIZED_CHECKPOINT_QUEUE_LENGTH, signal, }); if (opts.archiveMode === ArchiveMode.Frequency) { this.statesArchiverStrategy = new FrequencyStateArchiveStrategy(this.chain.regen, this.db, this.logger, opts, this.chain.bufferPool); } else { throw new Error(`State archive strategy "${opts.archiveMode}" currently not supported.`); } if (!opts.disableArchiveOnCheckpoint) { this.chain.emitter.on(ChainEvent.forkChoiceFinalized, this.onFinalizedCheckpoint); this.chain.emitter.on(ChainEvent.checkpoint, this.onCheckpoint); this.signal.addEventListener("abort", () => { this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.onFinalizedCheckpoint); this.chain.emitter.off(ChainEvent.checkpoint, this.onCheckpoint); }, { once: true }); } } async init() { if (this.opts.pruneHistory) { // prune ALL stale data before starting this.logger.info("Pruning historical data"); await callFnWhenAwait(pruneHistory(this.chain.config, this.db, this.logger, this.metrics, this.opts.anchorState.finalizedCheckpoint.epoch, this.chain.clock.currentEpoch), () => this.logger.info("Still pruning historical data, please wait..."), 30_000, this.signal); } if (this.opts.serveHistoricalState) { this.historicalStateRegen = await HistoricalStateRegen.init({ opts: { genesisTime: this.chain.clock.genesisTime, dbLocation: this.opts.dbName, nativeStateView: this.opts.nativeStateView ?? false, }, config: this.chain.config, metrics: this.metrics, logger: this.logger, signal: this.signal, }); } } async close() { await this.historicalStateRegen?.close(); } async scrapeMetrics() { return this.historicalStateRegen?.scrapeMetrics() ?? ""; } async getHistoricalStateBySlot(slot) { const finalizedBlock = this.chain.forkChoice.getFinalizedBlock(); if (slot >= finalizedBlock.slot) { return null; } // request for finalized state using historical state regen const stateSerialized = await this.historicalStateRegen?.getHistoricalState(slot); if (!stateSerialized) { return null; } return { state: stateSerialized, executionOptimistic: isOptimisticBlock(finalizedBlock), finalized: true }; } /** * Archive latest finalized state * */ async persistToDisk() { return this.statesArchiverStrategy.archiveState(this.chain.forkChoice.getFinalizedCheckpoint()); } //------------------------------------------------------------------------- // Event handlers //------------------------------------------------------------------------- onFinalizedCheckpoint = (finalized) => { this.jobQueue.push(finalized).catch((e) => { if (!isQueueErrorAborted(e)) { this.logger.error("Error queuing finalized checkpoint", { epoch: finalized.epoch }, e); } }); }; onCheckpoint = () => { const headStateRoot = this.chain.forkChoice.getHead().stateRoot; this.chain.regen.pruneOnCheckpoint(this.chain.forkChoice.getFinalizedCheckpoint().epoch, this.chain.forkChoice.getJustifiedCheckpoint().epoch, headStateRoot); this.statesArchiverStrategy.onCheckpoint(headStateRoot, this.metrics).catch((err) => { this.logger.error("Error during state archive", { archiveMode: this.archiveMode }, err); }); }; processFinalizedCheckpoint = async (finalized) => { try { const finalizedEpoch = finalized.epoch; this.logger.verbose("Start processing finalized checkpoint", { epoch: finalizedEpoch, rootHex: finalized.rootHex }); let timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer(); await archiveBlocks(this.chain.config, this.db, this.chain.forkChoice, this.chain.lightClientServer, this.logger, finalized, this.chain.clock.currentEpoch, this.archiveDataEpochs, this.chain.opts.persistOrphanedBlocks, this.chain.opts.persistOrphanedBlocksDir); timer?.({ source: ArchiveStoreTask.ArchiveBlocks }); if (this.opts.pruneHistory) { timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer(); await pruneHistory(this.chain.config, this.db, this.logger, this.metrics, finalizedEpoch, this.chain.clock.currentEpoch); timer?.({ source: ArchiveStoreTask.PruneHistory }); } timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer(); await this.statesArchiverStrategy.onFinalizedCheckpoint(finalized, this.metrics); timer?.({ source: ArchiveStoreTask.OnFinalizedCheckpoint }); // should be after ArchiveBlocksTask to handle restart cleanly timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer(); await this.statesArchiverStrategy.maybeArchiveState(finalized, this.metrics); timer?.({ source: ArchiveStoreTask.MaybeArchiveState }); timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer(); this.chain.regen.pruneOnFinalized(finalizedEpoch); timer?.({ source: ArchiveStoreTask.RegenPruneOnFinalized }); // tasks rely on extended fork choice timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer(); const prunedBlocks = this.chain.forkChoice.prune(finalized.rootHex); timer?.({ source: ArchiveStoreTask.ForkchoicePrune }); timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer(); await updateBackfillRange({ chain: this.chain, db: this.db, logger: this.logger }, finalized); timer?.({ source: ArchiveStoreTask.UpdateBackfillRange }); this.logger.verbose("Finish processing finalized checkpoint", { epoch: finalizedEpoch, rootHex: finalized.rootHex, prunedBlocks: prunedBlocks.length, }); } catch (e) { if (!this.signal.aborted) { this.logger.error("Error processing finalized checkpoint", { epoch: finalized.epoch }, e); } } }; } //# sourceMappingURL=archiveStore.js.map