UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

119 lines 5.32 kB
import { isForkPostDeneb } from "@lodestar/params"; import { fromHex, toRootHex } from "@lodestar/utils"; import { getBlobKzgCommitments } from "../../util/dataColumns.js"; import { isBlockInputBlobs, isBlockInputColumns } from "./blockInput/index.js"; import { BLOB_AVAILABILITY_TIMEOUT } from "./verifyBlocksDataAvailability.js"; /** * Persists block input data to DB. This operation must be eventually completed if a block is imported to the fork-choice. * Else the node will be in an inconsistent state that can lead to being stuck. * * This operation may be performed before, during or after importing to the fork-choice. As long as errors * are handled properly for eventual consistency. * * Block+blobs (pre-fulu) and data columns (fulu+) are written in parallel. */ export async function writeBlockInputToDb(blockInput) { const promises = [writeBlockAndBlobsToDb.call(this, blockInput)]; if (isBlockInputColumns(blockInput)) { promises.push(writeDataColumnsToDb.call(this, blockInput)); } await Promise.all(promises); this.logger.debug("Persisted blockInput to db", { slot: blockInput.slot, root: blockInput.blockRootHex }); } async function writeBlockAndBlobsToDb(blockInput) { const block = blockInput.getBlock(); const slot = block.message.slot; const blockRoot = this.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message); const blockRootHex = toRootHex(blockRoot); const numBlobs = isForkPostDeneb(blockInput.forkName) ? getBlobKzgCommitments(blockInput.forkName, block).length : undefined; const fnPromises = []; const blockBytes = this.serializedCache.get(block); if (blockBytes) { // skip serializing data if we already have it this.metrics?.importBlock.persistBlockWithSerializedDataCount.inc(); fnPromises.push(this.db.block.putBinary(this.db.block.getId(block), blockBytes)); } else { this.metrics?.importBlock.persistBlockNoSerializedDataCount.inc(); fnPromises.push(this.db.block.add(block)); } this.logger.debug("Persist block to hot DB", { slot, root: blockRootHex, inputType: blockInput.type, numBlobs }); if (isBlockInputBlobs(blockInput)) { fnPromises.push((async () => { if (!blockInput.hasAllData()) { await blockInput.waitForAllData(BLOB_AVAILABILITY_TIMEOUT); } const blobSidecars = blockInput.getBlobs(); await this.db.blobSidecars.add({ blockRoot, slot, blobSidecars }); this.logger.debug("Persisted blobSidecars to hot DB", { slot, root: blockRootHex, numBlobs: blobSidecars.length, }); })()); } await Promise.all(fnPromises); } /** * Persists data columns to DB for a given block. Accepts a narrow sub-interface of IBlockInput * so it can be reused across forks (e.g. Fulu, Gloas). * * NOTE: Old data is pruned on archive. */ export async function writeDataColumnsToDb(blockInput) { const { slot, blockRootHex } = blockInput; const blockRoot = fromHex(blockRootHex); if (!blockInput.hasComputedAllData()) { // Supernodes may only have a subset of the data columns by the time the block begins to be imported // because full data availability can be assumed after NUMBER_OF_COLUMNS / 2 columns are available. // Here, however, all data columns must be fully available/reconstructed before persisting to the DB. await blockInput.waitForComputedAllData(BLOB_AVAILABILITY_TIMEOUT).catch(() => { this.logger.debug("Failed to wait for computed all data", { slot, blockRoot: blockRootHex }); }); } const { custodyColumns } = this.custodyConfig; const dataColumnSidecars = blockInput.getCustodyColumns(); const binaryPuts = []; const nonbinaryPuts = []; for (const dataColumnSidecar of dataColumnSidecars) { // skip reserializing column if we already have it const serialized = this.serializedCache.get(dataColumnSidecar); if (serialized) { binaryPuts.push({ key: dataColumnSidecar.index, value: serialized }); } else { nonbinaryPuts.push(dataColumnSidecar); } } await Promise.all([ this.db.dataColumnSidecar.putManyBinary(blockRoot, binaryPuts), this.db.dataColumnSidecar.putMany(blockRoot, nonbinaryPuts), ]); this.logger.debug("Persisted dataColumnSidecars to hot DB", { slot, root: blockRootHex, dataColumnSidecars: dataColumnSidecars.length, custodyColumns: custodyColumns.length, numBlobs: dataColumnSidecars[0]?.column.length, }); } export async function persistBlockInput(blockInput) { await writeBlockInputToDb .call(this, blockInput) .catch((e) => { this.logger.debug("Error persisting block input in hot db", { slot: blockInput.slot, root: blockInput.blockRootHex, }, e); }) .finally(() => { this.seenBlockInputCache.prune(blockInput.blockRootHex); this.logger.debug("Pruned block input", { slot: blockInput.slot, root: blockInput.blockRootHex, }); }); } //# sourceMappingURL=writeBlockInputToDb.js.map