UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

113 lines (99 loc) 4.52 kB
import { DataAvailabilityStatus, ExecutionPayloadStatus, IBeaconStateView, StateHashTreeRootSource, } from "@lodestar/state-transition"; import {ErrorAborted, Logger, byteArrayEquals} from "@lodestar/utils"; import {Metrics} from "../../metrics/index.js"; import {nextEventLoop} from "../../util/eventLoop.js"; import {BlockError, BlockErrorCode} from "../errors/index.js"; import {BlockProcessOpts} from "../options.js"; import {ValidatorMonitor} from "../validatorMonitor.js"; import {IBlockInput} from "./blockInput/index.js"; import {ImportBlockOpts} from "./types.js"; /** * Verifies 1 or more blocks are fully valid running the full state transition; from a linear sequence of blocks. * * - Advance state to block's slot - per_slot_processing() * - For each block: * - STFN - per_block_processing() * - Check state root matches */ export async function verifyBlocksStateTransitionOnly( preState0: IBeaconStateView, blocks: IBlockInput[], dataAvailabilityStatuses: DataAvailabilityStatus[], logger: Logger, metrics: Metrics | null, validatorMonitor: ValidatorMonitor | null, signal: AbortSignal, opts: BlockProcessOpts & ImportBlockOpts ): Promise<{postStates: IBeaconStateView[]; proposerBalanceDeltas: number[]; verifyStateTime: number}> { const postStates: IBeaconStateView[] = []; const proposerBalanceDeltas: number[] = []; const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000); for (let i = 0; i < blocks.length; i++) { const {validProposerSignature, validSignatures} = opts; const block = blocks[i].getBlock(); const preState = i === 0 ? preState0 : postStates[i - 1]; const dataAvailabilityStatus = dataAvailabilityStatuses[i]; // STFN - per_slot_processing() + per_block_processing() // NOTE: `regen.getPreState()` should have dialed forward the state already caching checkpoint states const useBlsBatchVerify = !opts?.disableBlsBatchVerify; const postState = preState.stateTransition( block, { // NOTE: Assume valid for now while sending payload to execution engine in parallel // Latter verifyBlocksInEpoch() will make sure that payload is indeed valid executionPayloadStatus: ExecutionPayloadStatus.valid, dataAvailabilityStatus, // false because it's verified below with better error typing verifyStateRoot: false, // if block is trusted don't verify proposer or op signature verifyProposer: !useBlsBatchVerify && !validSignatures && !validProposerSignature, verifySignatures: !useBlsBatchVerify && !validSignatures, dontTransferCache: false, }, {metrics, validatorMonitor} ); const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({ source: StateHashTreeRootSource.blockTransition, }); const stateRoot = postState.hashTreeRoot(); hashTreeRootTimer?.(); // Check state root matches if (!byteArrayEquals(block.message.stateRoot, stateRoot)) { throw new BlockError(block, { code: BlockErrorCode.INVALID_STATE_ROOT, root: postState.hashTreeRoot(), expectedRoot: block.message.stateRoot, preState, postState, }); } postStates[i] = postState; // For metric block profitability const proposerIndex = block.message.proposerIndex; proposerBalanceDeltas[i] = postState.getBalance(proposerIndex) - preState.getBalance(proposerIndex); // If blocks are invalid in execution the main promise could resolve before this loop ends. // In that case stop processing blocks and return early. if (signal.aborted) { throw new ErrorAborted("verifyBlockStateTransitionOnly"); } // this avoids keeping our node busy processing blocks if (i < blocks.length - 1) { await nextEventLoop(); } } const verifyStateTime = Date.now(); if (blocks.length === 1 && opts.seenTimestampSec !== undefined) { const slot = blocks[0].getBlock().message.slot; const recvToValidation = verifyStateTime / 1000 - opts.seenTimestampSec; const validationTime = recvToValidation - recvToValLatency; metrics?.gossipBlock.stateTransition.recvToValidation.observe(recvToValidation); metrics?.gossipBlock.stateTransition.validationTime.observe(validationTime); logger.debug("Verified block state transition", {slot, recvToValLatency, recvToValidation, validationTime}); } return {postStates, proposerBalanceDeltas, verifyStateTime}; }