UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

95 lines 4.3 kB
import { computeStartSlotAtEpoch } from "@lodestar/state-transition"; import { toRootHex } from "@lodestar/utils"; import { BlockError, BlockErrorCode } from "../errors/index.js"; /** * Verifies some early cheap sanity checks on the block before running the full state transition. * * - Parent is known to the fork-choice * - Check skipped slots limit * - check_block_relevancy() * - Block not in the future * - Not genesis block * - Block's slot is < Infinity * - Not finalized slot * - Not already known */ export function verifyBlocksSanityChecks(chain, blocks, opts) { if (blocks.length === 0) { throw Error("Empty partiallyVerifiedBlocks"); } const relevantBlocks = []; const parentSlots = []; let parentBlock = null; for (const blockInput of blocks) { const { block } = blockInput; const blockSlot = block.message.slot; const blockHash = toRootHex(chain.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message)); if (chain.blacklistedBlocks.has(blockHash)) { // Blacklisting blocks via CLI flag only requires to set the block hash if (chain.blacklistedBlocks.get(blockHash) === null) { // Set actual slot observed when processing the block chain.blacklistedBlocks.set(blockHash, blockSlot); } throw new BlockError(block, { code: BlockErrorCode.BLACKLISTED_BLOCK }); } if (chain.blacklistedBlocks.has(toRootHex(block.message.parentRoot))) { chain.blacklistedBlocks.set(blockHash, blockSlot); throw new BlockError(block, { code: BlockErrorCode.BLACKLISTED_BLOCK }); } // Not genesis block // IGNORE if `partiallyVerifiedBlock.ignoreIfKnown` if (blockSlot === 0) { if (opts.ignoreIfKnown) { continue; } throw new BlockError(block, { code: BlockErrorCode.GENESIS_BLOCK }); } // Not finalized slot // IGNORE if `partiallyVerifiedBlock.ignoreIfFinalized` const finalizedSlot = computeStartSlotAtEpoch(chain.forkChoice.getFinalizedCheckpoint().epoch); if (blockSlot <= finalizedSlot) { if (opts.ignoreIfFinalized) { continue; } throw new BlockError(block, { code: BlockErrorCode.WOULD_REVERT_FINALIZED_SLOT, blockSlot, finalizedSlot }); } const relevantLastBlock = relevantBlocks.at(-1); let parentBlockSlot; if (relevantLastBlock) { parentBlockSlot = relevantLastBlock.block.message.slot; } else { // When importing a block segment, only the first NON-IGNORED block must be known to the fork-choice. const parentRoot = toRootHex(block.message.parentRoot); parentBlock = chain.forkChoice.getBlockHex(parentRoot); if (!parentBlock) { throw new BlockError(block, { code: BlockErrorCode.PARENT_UNKNOWN, parentRoot }); } // Parent is known to the fork-choice parentBlockSlot = parentBlock.slot; } // Block not in the future, also checks for infinity const currentSlot = chain.clock.currentSlot; if (blockSlot > currentSlot) { throw new BlockError(block, { code: BlockErrorCode.FUTURE_SLOT, blockSlot, currentSlot }); } // Not already known // IGNORE if `partiallyVerifiedBlock.ignoreIfKnown` if (chain.forkChoice.hasBlockHex(blockHash)) { if (opts.ignoreIfKnown) { continue; } throw new BlockError(block, { code: BlockErrorCode.ALREADY_KNOWN, root: blockHash }); } // Block is relevant relevantBlocks.push(blockInput); parentSlots.push(parentBlockSlot); } // Just assert to be over cautious and for purposes to be more explicit for someone // going through the code segment if (parentBlock === null && relevantBlocks.length > 0) { throw Error(`Internal error, parentBlock should not be null for relevantBlocks=${relevantBlocks.length}`); } return { relevantBlocks, parentSlots, parentBlock }; } //# sourceMappingURL=verifyBlocksSanityChecks.js.map