import { ExecutionPayloadStatus, StateHashTreeRootSource, stateTransition, } from "@lodestar/state-transition";
import { ErrorAborted, byteArrayEquals } from "@lodestar/utils";
import { nextEventLoop } from "../../util/eventLoop.js";
import { BlockError, BlockErrorCode } from "../errors/index.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, blocks, dataAvailabilityStatuses, logger, metrics, validatorMonitor, signal, opts) {
    const postStates = [];
    const proposerBalanceDeltas = [];
    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 = stateTransition(preState, 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.balances.get(proposerIndex) - preState.balances.get(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 };
}
//# sourceMappingURL=verifyBlocksStateTransitionOnly.js.map