diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index af64d4c922ef..607479db091f 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -70,7 +70,7 @@ import {ChainEvent, CommonBlockBody} from "../../../chain/index.js"; import {PREPARE_NEXT_SLOT_BPS} from "../../../chain/prepareNextSlot.js"; import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/produceBlock/index.js"; import {RegenCaller} from "../../../chain/regen/index.js"; -import {CheckpointHexPayload} from "../../../chain/stateCache/types.js"; +import {CheckpointHex} from "../../../chain/stateCache/types.js"; import {validateApiAggregateAndProof} from "../../../chain/validation/index.js"; import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js"; import {ZERO_HASH} from "../../../constants/index.js"; @@ -301,7 +301,7 @@ export function getValidatorApi( * | * prepareNextSlot (4s before next slot) */ - async function waitForCheckpointState(cpHex: CheckpointHexPayload): Promise { + async function waitForCheckpointState(cpHex: CheckpointHex): Promise { const cpState = chain.regen.getCheckpointStateSync(cpHex); if (cpState) { return cpState; @@ -1113,7 +1113,6 @@ export function getValidatorApi( const cpState = await waitForCheckpointState({ rootHex: head.blockRoot, epoch, - payloadPresent: head.payloadStatus === PayloadStatus.FULL, }); if (cpState) { state = cpState; @@ -1642,7 +1641,7 @@ export function getValidatorApi( throw Error("Cached block production result is not full block"); } - const {executionPayload, executionRequests, payloadEnvelopeStateRoot} = produceResult as ProduceFullGloas; + const {executionPayload, executionRequests} = produceResult as ProduceFullGloas; const envelope: gloas.ExecutionPayloadEnvelope = { payload: executionPayload, @@ -1650,7 +1649,9 @@ export function getValidatorApi( builderIndex: BUILDER_INDEX_SELF_BUILD, beaconBlockRoot, slot, - stateRoot: payloadEnvelopeStateRoot, + // TODO GLOAS: stateRoot is no longer computed during block production. + // This field will be removed when we implement defer payload processing + stateRoot: ZERO_HASH, }; logger.info("Produced execution payload envelope", { diff --git a/packages/beacon-node/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts b/packages/beacon-node/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts index 09be1942c175..12ae1d162f59 100644 --- a/packages/beacon-node/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +++ b/packages/beacon-node/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts @@ -9,7 +9,6 @@ import {AllocSource, BufferPool} from "../../../util/bufferPool.js"; import {getStateSlotFromBytes} from "../../../util/multifork.js"; import {IStateRegenerator} from "../../regen/interface.js"; import {serializeState} from "../../serializeState.js"; -import {fcCheckpointToHexPayload} from "../../stateCache/persistentCheckpointsCache.js"; import {StateArchiveStrategy, StatesArchiveOpts} from "../interface.js"; /** @@ -108,9 +107,8 @@ export class FrequencyStateArchiveStrategy implements StateArchiveStrategy { async archiveState(finalized: CheckpointWithPayloadStatus, metrics?: Metrics | null): Promise { // starting from Mar 2024, the finalized state could be from disk or in memory let timer = metrics?.processFinalizedCheckpoint.frequencyStateArchive.startTimer(); - // Convert fork-choice checkpoint to beacon-node checkpoint with payloadPresent - const finalizedHexPayload = fcCheckpointToHexPayload(finalized); - const finalizedStateOrBytes = await this.regen.getCheckpointStateOrBytes(finalizedHexPayload); + const finalizedHex = {epoch: finalized.epoch, rootHex: finalized.rootHex}; + const finalizedStateOrBytes = await this.regen.getCheckpointStateOrBytes(finalizedHex); timer?.({step: FrequencyStateArchiveStep.GetFinalizedState}); const {rootHex} = finalized; diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index f352e0979417..46d38d984e41 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -8,7 +8,6 @@ import { ForkChoiceErrorCode, NotReorgedReason, getSafeExecutionBlockHash, - isGloasBlock, } from "@lodestar/fork-choice"; import { ForkPostAltair, @@ -87,7 +86,7 @@ export async function importBlock( fullyVerifiedBlock: FullyVerifiedBlock, opts: ImportBlockOpts ): Promise { - const {blockInput, postBlockState, parentBlockSlot, executionStatus, dataAvailabilityStatus, indexedAttestations} = + const {blockInput, postState, parentBlockSlot, executionStatus, dataAvailabilityStatus, indexedAttestations} = fullyVerifiedBlock; const block = blockInput.getBlock(); const source = blockInput.getBlockSource(); @@ -99,7 +98,7 @@ export async function importBlock( const blockEpoch = computeEpochAtSlot(blockSlot); const prevFinalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch; const blockDelaySec = - fullyVerifiedBlock.seenTimestampSec - computeTimeAtSlot(this.config, blockSlot, postBlockState.genesisTime); + fullyVerifiedBlock.seenTimestampSec - computeTimeAtSlot(this.config, blockSlot, postState.genesisTime); const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000); const fork = this.config.getForkSeq(blockSlot); @@ -122,10 +121,10 @@ export async function importBlock( // 2. Import block to fork choice // Should compute checkpoint balances before forkchoice.onBlock - this.checkpointBalancesCache.processState(blockRootHex, postBlockState); + this.checkpointBalancesCache.processState(blockRootHex, postState); const blockSummary = this.forkChoice.onBlock( block.message, - postBlockState, + postState, blockDelaySec, currentSlot, fork >= ForkSeq.gloas ? ExecutionStatus.PayloadSeparated : executionStatus, @@ -134,11 +133,7 @@ export async function importBlock( // This adds the state necessary to process the next block // Some block event handlers require state being in state cache so need to do this before emitting EventType.block - // Pre-Gloas: blockSummary.payloadStatus is always FULL, payloadPresent = true - // Post-Gloas: blockSummary.payloadStatus is always PENDING, so payloadPresent = false (block state only, no payload processing yet) - const payloadPresent = !isGloasBlock(blockSummary); - // processState manages both block state and payload state variants together for memory/disk management - this.regen.processBlockState(blockRootHex, postBlockState); + this.regen.processState(blockRootHex, postState); // For Gloas blocks, create PayloadEnvelopeInput so it's available for later payload import if (fork >= ForkSeq.gloas) { @@ -191,7 +186,7 @@ export async function importBlock( (opts.importAttestations !== AttestationImportOpt.Skip && blockEpoch >= currentEpoch - FORK_CHOICE_ATT_EPOCH_LIMIT) ) { const attestations = block.message.body.attestations; - const rootCache = new RootCache(postBlockState); + const rootCache = new RootCache(postState); const invalidAttestationErrorsByCode = new Map(); const addAttestation = fork >= ForkSeq.electra ? addAttestationPostElectra : addAttestationPreElectra; @@ -205,7 +200,7 @@ export async function importBlock( const attDataRoot = toRootHex(ssz.phase0.AttestationData.hashTreeRoot(indexedAttestation.data)); addAttestation.call( this, - postBlockState, + postState, target, attDataRoot, attestation as Attestation, @@ -320,7 +315,7 @@ export async function importBlock( if (newHead.blockRoot !== oldHead.blockRoot) { // Set head state as strong reference - this.regen.updateHeadState(newHead, postBlockState); + this.regen.updateHeadState(newHead, postState); try { this.emitter.emit(routes.events.EventType.head, { @@ -390,10 +385,10 @@ export async function importBlock( // we want to import block asap so do this in the next event loop callInNextEventLoop(() => { try { - if (isStatePostAltair(postBlockState)) { + if (isStatePostAltair(postState)) { this.lightClientServer?.onImportBlockHead( block.message as BeaconBlock, - postBlockState, + postState, parentBlockSlot ); } @@ -415,11 +410,11 @@ export async function importBlock( // and the block is weak and can potentially be reorged out. let shouldOverrideFcu = false; - if (blockSlot >= currentSlot && isStatePostBellatrix(postBlockState) && postBlockState.isExecutionStateType) { + if (blockSlot >= currentSlot && isStatePostBellatrix(postState) && postState.isExecutionStateType) { let notOverrideFcuReason = NotReorgedReason.Unknown; const proposalSlot = blockSlot + 1; try { - const proposerIndex = postBlockState.getBeaconProposer(proposalSlot); + const proposerIndex = postState.getBeaconProposer(proposalSlot); const feeRecipient = this.beaconProposerCache.get(proposerIndex); if (feeRecipient) { @@ -499,22 +494,22 @@ export async function importBlock( } } - if (!postBlockState.isStateValidatorsNodesPopulated()) { - this.logger.verbose("After importBlock caching postState without SSZ cache", {slot: postBlockState.slot}); + if (!postState.isStateValidatorsNodesPopulated()) { + this.logger.verbose("After importBlock caching postState without SSZ cache", {slot: postState.slot}); } // Cache shufflings when crossing an epoch boundary const parentEpoch = computeEpochAtSlot(parentBlockSlot); if (parentEpoch < blockEpoch) { - this.shufflingCache.processState(postBlockState); + this.shufflingCache.processState(postState); this.logger.verbose("Processed shuffling for next epoch", {parentEpoch, blockEpoch, slot: blockSlot}); } if (blockSlot % SLOTS_PER_EPOCH === 0) { // Cache state to preserve epoch transition work - const checkpointState = postBlockState; + const checkpointState = postState; const cp = getCheckpointFromState(checkpointState); - this.regen.addCheckpointState(cp, checkpointState, payloadPresent); + this.regen.addCheckpointState(cp, checkpointState); // consumers should not mutate state ever this.emitter.emit(ChainEvent.checkpoint, cp, checkpointState); @@ -602,11 +597,11 @@ export async function importBlock( this.metrics?.parentBlockDistance.observe(blockSlot - parentBlockSlot); this.metrics?.proposerBalanceDeltaAny.observe(fullyVerifiedBlock.proposerBalanceDelta); this.validatorMonitor?.registerImportedBlock(block.message, fullyVerifiedBlock); - if (isStatePostAltair(fullyVerifiedBlock.postBlockState)) { + if (isStatePostAltair(fullyVerifiedBlock.postState)) { this.validatorMonitor?.registerSyncAggregateInBlock( blockEpoch, (block as altair.SignedBeaconBlock).message.body.syncAggregate, - fullyVerifiedBlock.postBlockState.currentSyncCommitteeIndexed.validatorIndices + fullyVerifiedBlock.postState.currentSyncCommitteeIndexed.validatorIndices ); } diff --git a/packages/beacon-node/src/chain/blocks/importExecutionPayload.ts b/packages/beacon-node/src/chain/blocks/importExecutionPayload.ts index c50aea6face8..5ce1ef176f48 100644 --- a/packages/beacon-node/src/chain/blocks/importExecutionPayload.ts +++ b/packages/beacon-node/src/chain/blocks/importExecutionPayload.ts @@ -240,10 +240,10 @@ export async function importExecutionPayload( ); // 8. Cache payload state - this.regen.processPayloadState(postPayloadState); + this.regen.processState(blockRootHex, postPayloadState); if (postPayloadState.slot % SLOTS_PER_EPOCH === 0) { const {checkpoint} = postPayloadState.computeAnchorCheckpoint(); - this.regen.addCheckpointState(checkpoint, postPayloadState, true); + this.regen.addCheckpointState(checkpoint, postPayloadState); } // 9. Record metrics for payload envelope and column sources diff --git a/packages/beacon-node/src/chain/blocks/index.ts b/packages/beacon-node/src/chain/blocks/index.ts index a0b5fdfe28c1..787d283f758b 100644 --- a/packages/beacon-node/src/chain/blocks/index.ts +++ b/packages/beacon-node/src/chain/blocks/index.ts @@ -88,7 +88,7 @@ export async function processBlocks( const fullyVerifiedBlocks = relevantBlocks.map( (block, i): FullyVerifiedBlock => ({ blockInput: block, - postBlockState: postStates[i], + postState: postStates[i], postPayloadState: null, parentBlockSlot: parentSlots[i], executionStatus: executionStatuses[i], diff --git a/packages/beacon-node/src/chain/blocks/types.ts b/packages/beacon-node/src/chain/blocks/types.ts index e1858040a483..83e8a023dc10 100644 --- a/packages/beacon-node/src/chain/blocks/types.ts +++ b/packages/beacon-node/src/chain/blocks/types.ts @@ -90,7 +90,7 @@ export type ImportBlockOpts = { type FullyVerifiedBlockBase = { blockInput: IBlockInput; - postBlockState: IBeaconStateView; + postState: IBeaconStateView; parentBlockSlot: Slot; proposerBalanceDelta: number; dataAvailabilityStatus: DataAvailabilityStatus; diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index f27bc165085e..2ee877c9d7f3 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -5,7 +5,6 @@ import {BeaconConfig} from "@lodestar/config"; import {CheckpointWithPayloadStatus, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice"; import {LoggerNode} from "@lodestar/logger/node"; import { - BUILDER_INDEX_SELF_BUILD, EFFECTIVE_BALANCE_INCREMENT, type ForkPostFulu, type ForkPostGloas, @@ -24,7 +23,6 @@ import { getEffectiveBalancesFromStateBytes, isStatePostAltair, isStatePostElectra, - isStatePostGloas, } from "@lodestar/state-transition"; import { BeaconBlock, @@ -93,8 +91,8 @@ import { } from "./opPools/index.js"; import {IChainOptions} from "./options.js"; import {PrepareNextSlotScheduler} from "./prepareNextSlot.js"; -import {computeNewStateRoot, computePayloadEnvelopeStateRoot} from "./produceBlock/computeNewStateRoot.js"; -import {AssembledBlockType, BlockType, ProduceFullGloas, ProduceResult} from "./produceBlock/index.js"; +import {computeNewStateRoot} from "./produceBlock/computeNewStateRoot.js"; +import {AssembledBlockType, BlockType, ProduceResult} from "./produceBlock/index.js"; import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produceBlock/produceBlockBody.js"; import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js"; import {ReprocessController} from "./reprocess.js"; @@ -118,7 +116,7 @@ import {DbCPStateDatastore, checkpointToDatastoreKey} from "./stateCache/datasto import {FileCPStateDatastore} from "./stateCache/datastore/file.js"; import {CPStateDatastore} from "./stateCache/datastore/types.js"; import {FIFOBlockStateCache} from "./stateCache/fifoBlockStateCache.js"; -import {PersistentCheckpointStateCache, fcCheckpointToHexPayload} from "./stateCache/persistentCheckpointsCache.js"; +import {PersistentCheckpointStateCache} from "./stateCache/persistentCheckpointsCache.js"; import {CheckpointStateCache} from "./stateCache/types.js"; import {ValidatorMonitor} from "./validatorMonitor.js"; @@ -685,8 +683,8 @@ export class BeaconChain implements IBeaconChain { checkpoint: CheckpointWithPayloadStatus ): {state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean} | null { // finalized or justified checkpoint states maynot be available with PersistentCheckpointStateCache, use getCheckpointStateOrBytes() api to get Uint8Array - const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint); - const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHexPayload); + const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex}; + const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHex); if (cachedStateCtx) { const block = this.forkChoice.getBlockDefaultStatus( ssz.phase0.BeaconBlockHeader.hashTreeRoot(cachedStateCtx.latestBlockHeader) @@ -705,8 +703,8 @@ export class BeaconChain implements IBeaconChain { async getStateOrBytesByCheckpoint( checkpoint: CheckpointWithPayloadStatus ): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> { - const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint); - const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHexPayload); + const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex}; + const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHex); if (cachedStateCtx) { const block = this.forkChoice.getBlockDefaultStatus(checkpoint.root); const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch; @@ -1062,7 +1060,7 @@ export class BeaconChain implements IBeaconChain { body, } as AssembledBlockType; - const {newStateRoot, proposerReward, postBlockState} = computeNewStateRoot(this.metrics, state, block); + const {newStateRoot, proposerReward} = computeNewStateRoot(this.metrics, state, block); block.stateRoot = newStateRoot; const blockRoot = produceResult.type === BlockType.Full @@ -1071,26 +1069,9 @@ export class BeaconChain implements IBeaconChain { const blockRootHex = toRootHex(blockRoot); const fork = this.config.getForkName(slot); - if (isForkPostGloas(fork)) { - // TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built - if (produceResult.type !== BlockType.Full) { - throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`); - } - - const gloasResult = produceResult as ProduceFullGloas; - const envelope: gloas.ExecutionPayloadEnvelope = { - payload: gloasResult.executionPayload, - executionRequests: gloasResult.executionRequests, - builderIndex: BUILDER_INDEX_SELF_BUILD, - beaconBlockRoot: blockRoot, - slot, - stateRoot: ZERO_HASH, - }; - if (!isStatePostGloas(postBlockState)) { - throw Error(`Expected gloas+ post-state for execution payload envelope, got fork=${postBlockState.forkName}`); - } - const payloadEnvelopeStateRoot = computePayloadEnvelopeStateRoot(this.metrics, postBlockState, envelope); - gloasResult.payloadEnvelopeStateRoot = payloadEnvelopeStateRoot; + // TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built + if (isForkPostGloas(fork) && produceResult.type !== BlockType.Full) { + throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`); } // Track the produced block for consensus broadcast validations, later validation, etc. @@ -1338,8 +1319,8 @@ export class BeaconChain implements IBeaconChain { checkpoint: CheckpointWithPayloadStatus, blockState: IBeaconStateView ): {state: IBeaconStateView; stateId: string; shouldWarn: boolean} { - const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint); - const state = this.regen.getCheckpointStateSync(checkpointHexPayload); + const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex}; + const state = this.regen.getCheckpointStateSync(checkpointHex); if (state) { return {state, stateId: "checkpoint_state", shouldWarn: false}; } diff --git a/packages/beacon-node/src/chain/prepareNextSlot.ts b/packages/beacon-node/src/chain/prepareNextSlot.ts index fb049f35571d..f58d42be4931 100644 --- a/packages/beacon-node/src/chain/prepareNextSlot.ts +++ b/packages/beacon-node/src/chain/prepareNextSlot.ts @@ -1,6 +1,6 @@ import {routes} from "@lodestar/api"; import {ChainForkConfig} from "@lodestar/config"; -import {PayloadStatus, getSafeExecutionBlockHash} from "@lodestar/fork-choice"; +import {getSafeExecutionBlockHash} from "@lodestar/fork-choice"; import {ForkPostBellatrix, ForkSeq, SLOTS_PER_EPOCH, isForkPostBellatrix} from "@lodestar/params"; import { IBeaconStateView, @@ -217,11 +217,7 @@ export class PrepareNextSlotScheduler { // + if next slot is a skipped slot, it'd help getting target checkpoint state faster to validate attestations if (isEpochTransition) { this.metrics?.precomputeNextEpochTransition.count.inc({result: "success"}, 1); - // Determine payloadPresent from head block's payload status - // Pre-Gloas: payloadStatus is always FULL → payloadPresent = true - // Post-Gloas: FULL → true, EMPTY → false, PENDING → false (conservative, treat as block state) - const payloadPresent = headBlock.payloadStatus === PayloadStatus.FULL; - const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch, payloadPresent); + const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch); if (previousHits === 0) { this.metrics?.precomputeNextEpochTransition.waste.inc(); } diff --git a/packages/beacon-node/src/chain/produceBlock/computeNewStateRoot.ts b/packages/beacon-node/src/chain/produceBlock/computeNewStateRoot.ts index 2c5df920a6a6..37ede767a88c 100644 --- a/packages/beacon-node/src/chain/produceBlock/computeNewStateRoot.ts +++ b/packages/beacon-node/src/chain/produceBlock/computeNewStateRoot.ts @@ -1,12 +1,10 @@ import { DataAvailabilityStatus, ExecutionPayloadStatus, - G2_POINT_AT_INFINITY, IBeaconStateView, - IBeaconStateViewGloas, StateHashTreeRootSource, } from "@lodestar/state-transition"; -import {BeaconBlock, BlindedBeaconBlock, Gwei, Root, gloas} from "@lodestar/types"; +import {BeaconBlock, BlindedBeaconBlock, Gwei, Root} from "@lodestar/types"; import {ZERO_HASH} from "../../constants/index.js"; import {Metrics} from "../../metrics/index.js"; @@ -19,11 +17,11 @@ export function computeNewStateRoot( metrics: Metrics | null, state: IBeaconStateView, block: BeaconBlock | BlindedBeaconBlock -): {newStateRoot: Root; proposerReward: Gwei; postBlockState: IBeaconStateView} { +): {newStateRoot: Root; proposerReward: Gwei; postState: IBeaconStateView} { // Set signature to zero to re-use stateTransition() function which requires the SignedBeaconBlock type const blockEmptySig = {message: block, signature: ZERO_HASH}; - const postBlockState = state.stateTransition( + const postState = state.stateTransition( blockEmptySig, { // ExecutionPayloadStatus.valid: Assume payload valid, it has been produced by a trusted EL @@ -42,49 +40,14 @@ export function computeNewStateRoot( {metrics} ); - const {attestations, syncAggregate, slashing} = postBlockState.proposerRewards; + const {attestations, syncAggregate, slashing} = postState.proposerRewards; const proposerReward = BigInt(attestations + syncAggregate + slashing); const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({ source: StateHashTreeRootSource.computeNewStateRoot, }); - const newStateRoot = postBlockState.hashTreeRoot(); + const newStateRoot = postState.hashTreeRoot(); hashTreeRootTimer?.(); - return {newStateRoot, proposerReward, postBlockState}; -} - -/** - * Compute the state root after processing an execution payload envelope. - * Similar to `computeNewStateRoot` but for payload envelope processing. - * - */ -export function computePayloadEnvelopeStateRoot( - metrics: Metrics | null, - postBlockState: IBeaconStateViewGloas, - envelope: gloas.ExecutionPayloadEnvelope -): Root { - const signedEnvelope: gloas.SignedExecutionPayloadEnvelope = { - message: envelope, - signature: G2_POINT_AT_INFINITY, - }; - - const processEnvelopeTimer = metrics?.blockPayload.executionPayloadEnvelopeProcessingTime.startTimer(); - const postPayloadState = postBlockState.processExecutionPayloadEnvelope(signedEnvelope, { - // Signature is zero-ed (G2_POINT_AT_INFINITY), skip verification - verifySignature: false, - // State root is being computed here, the envelope doesn't have it yet - verifyStateRoot: false, - // Preserve cache in source state, since the resulting state is not added to the state cache - dontTransferCache: true, - }); - processEnvelopeTimer?.(); - - const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({ - source: StateHashTreeRootSource.computePayloadEnvelopeStateRoot, - }); - const stateRoot = postPayloadState.hashTreeRoot(); - hashTreeRootTimer?.(); - - return stateRoot; + return {newStateRoot, proposerReward, postState}; } diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index 9f888729ce22..34bdd7fc83b4 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -111,12 +111,6 @@ export type ProduceFullGloas = { executionRequests: electra.ExecutionRequests; blobsBundle: BlobsBundle; cells: fulu.Cell[][]; - /** - * Cached payload envelope state root computed during block production. - * This is the state root after running `processExecutionPayloadEnvelope` on the - * post-block state, and later used to construct the `ExecutionPayloadEnvelope`. - */ - payloadEnvelopeStateRoot: Root; }; export type ProduceFullFulu = { type: BlockType.Full; diff --git a/packages/beacon-node/src/chain/regen/errors.ts b/packages/beacon-node/src/chain/regen/errors.ts index 6352d765ea11..eb41e8321da3 100644 --- a/packages/beacon-node/src/chain/regen/errors.ts +++ b/packages/beacon-node/src/chain/regen/errors.ts @@ -1,4 +1,3 @@ -import {PayloadStatus} from "@lodestar/fork-choice"; import {Root, RootHex, Slot} from "@lodestar/types"; export enum RegenErrorCode { @@ -10,8 +9,6 @@ export enum RegenErrorCode { BLOCK_NOT_IN_DB = "REGEN_ERROR_BLOCK_NOT_IN_DB", STATE_TRANSITION_ERROR = "REGEN_ERROR_STATE_TRANSITION_ERROR", INVALID_STATE_ROOT = "REGEN_ERROR_INVALID_STATE_ROOT", - UNEXPECTED_PAYLOAD_STATUS = "REGEN_ERROR_UNEXPECTED_PAYLOAD_STATUS", - INTERNAL_ERROR = "REGEN_ERROR_INTERNAL_ERROR", } export type RegenErrorType = @@ -22,9 +19,7 @@ export type RegenErrorType = | {code: RegenErrorCode.TOO_MANY_BLOCK_PROCESSED; stateRoot: RootHex | Root} | {code: RegenErrorCode.BLOCK_NOT_IN_DB; blockRoot: RootHex | Root} | {code: RegenErrorCode.STATE_TRANSITION_ERROR; error: Error} - | {code: RegenErrorCode.INVALID_STATE_ROOT; slot: Slot; expected: RootHex; actual: RootHex} - | {code: RegenErrorCode.UNEXPECTED_PAYLOAD_STATUS; blockRoot: RootHex | Root; payloadStatus: PayloadStatus} - | {code: RegenErrorCode.INTERNAL_ERROR; message: string}; + | {code: RegenErrorCode.INVALID_STATE_ROOT; slot: Slot; expected: RootHex; actual: RootHex}; export class RegenError extends Error { type: RegenErrorType; diff --git a/packages/beacon-node/src/chain/regen/interface.ts b/packages/beacon-node/src/chain/regen/interface.ts index a4e981442771..e2f91819de62 100644 --- a/packages/beacon-node/src/chain/regen/interface.ts +++ b/packages/beacon-node/src/chain/regen/interface.ts @@ -2,7 +2,7 @@ import {routes} from "@lodestar/api"; import {ProtoBlock} from "@lodestar/fork-choice"; import {IBeaconStateView} from "@lodestar/state-transition"; import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types"; -import {CheckpointHexPayload} from "../stateCache/types.js"; +import {CheckpointHex} from "../stateCache/types.js"; export enum RegenCaller { getDuties = "getDuties", @@ -40,20 +40,15 @@ export interface IStateRegenerator extends IStateRegeneratorInternal { dumpCacheSummary(): routes.lodestar.StateCacheItem[]; getStateSync(stateRoot: RootHex): IBeaconStateView | null; getPreStateSync(block: BeaconBlock): IBeaconStateView | null; - getCheckpointStateOrBytes(cp: CheckpointHexPayload): Promise; - getCheckpointStateSync(cp: CheckpointHexPayload): IBeaconStateView | null; + getCheckpointStateOrBytes(cp: CheckpointHex): Promise; + getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null; getClosestHeadState(head: ProtoBlock): IBeaconStateView | null; pruneOnCheckpoint(finalizedEpoch: Epoch, justifiedEpoch: Epoch, headStateRoot: RootHex): void; pruneOnFinalized(finalizedEpoch: Epoch): void; - processBlockState(blockRootHex: RootHex, postState: IBeaconStateView): void; - processPayloadState(payloadState: IBeaconStateView): void; - /** - * payloadPresent is true if this is payload state, false if block state. - * payloadPresent is always true for pre-gloas. - */ - addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView, payloadPresent: boolean): void; + processState(blockRootHex: RootHex, postState: IBeaconStateView): void; + addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void; updateHeadState(newHead: ProtoBlock, maybeHeadState: IBeaconStateView): void; - updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch, payloadPresent: boolean): number | null; + updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null; } /** diff --git a/packages/beacon-node/src/chain/regen/queued.ts b/packages/beacon-node/src/chain/regen/queued.ts index 8652deb6a3a0..288a26633c7d 100644 --- a/packages/beacon-node/src/chain/regen/queued.ts +++ b/packages/beacon-node/src/chain/regen/queued.ts @@ -5,7 +5,7 @@ import {BeaconBlock, Epoch, RootHex, Slot, isGloasBeaconBlock, phase0} from "@lo import {Logger, toRootHex} from "@lodestar/utils"; import {Metrics} from "../../metrics/index.js"; import {JobItemQueue} from "../../util/queue/index.js"; -import {BlockStateCache, CheckpointHexPayload, CheckpointStateCache} from "../stateCache/types.js"; +import {BlockStateCache, CheckpointHex, CheckpointStateCache} from "../stateCache/types.js"; import {RegenError, RegenErrorCode} from "./errors.js"; import { IStateRegenerator, @@ -125,14 +125,14 @@ export class QueuedStateRegenerator implements IStateRegenerator { return null; } - async getCheckpointStateOrBytes(cp: CheckpointHexPayload): Promise { + async getCheckpointStateOrBytes(cp: CheckpointHex): Promise { return this.checkpointStateCache.getStateOrBytes(cp); } /** * Get checkpoint state from cache */ - getCheckpointStateSync(cp: CheckpointHexPayload): IBeaconStateView | null { + getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null { return this.checkpointStateCache.get(cp); } @@ -153,22 +153,14 @@ export class QueuedStateRegenerator implements IStateRegenerator { this.blockStateCache.deleteAllBeforeEpoch(finalizedEpoch); } - processBlockState(blockRootHex: RootHex, postState: IBeaconStateView): void { + processState(blockRootHex: RootHex, postState: IBeaconStateView): void { this.blockStateCache.add(postState); this.checkpointStateCache.processState(blockRootHex, postState).catch((e) => { this.logger.debug("Error processing block state", {blockRootHex, slot: postState.slot}, e); }); } - /** - * Process payload state for caching after importing execution payload. - */ - processPayloadState(payloadState: IBeaconStateView): void { - // Add payload state to block state cache (keyed by payload state root) - this.blockStateCache.add(payloadState); - } - - addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView, _payloadPresent: boolean): void { + addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void { this.checkpointStateCache.add(cp, item); } @@ -205,7 +197,7 @@ export class QueuedStateRegenerator implements IStateRegenerator { } } - updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch, _payloadPresent: boolean): number | null { + updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null { return this.checkpointStateCache.updatePreComputedCheckpoint(rootHex, epoch); } diff --git a/packages/beacon-node/src/chain/regen/regen.ts b/packages/beacon-node/src/chain/regen/regen.ts index e8a0fe213764..037ef023bfaa 100644 --- a/packages/beacon-node/src/chain/regen/regen.ts +++ b/packages/beacon-node/src/chain/regen/regen.ts @@ -332,11 +332,6 @@ async function processSlotsByCheckpoint( * emitting "checkpoint" events after every epoch processed. * * Stops processing after no more full epochs can be processed. - * - * Output state variant: - * - Post-Gloas: If slots are processed, returns block state (payloadPresent=false). - * If no slots processed, returns preState as-is (preserves variant). - * - Pre-Gloas: Always payloadPresent=true (no block/payload distinction). */ export async function processSlotsToNearestCheckpoint( modules: { @@ -380,9 +375,6 @@ export async function processSlotsToNearestCheckpoint( // This may becomes the "official" checkpoint state if the 1st block of epoch is skipped const checkpointState = postState; const cp = getCheckpointFromState(checkpointState); - // processSlots() only does epoch transitions, never processes payloads - // Pre-Gloas: payloadPresent is always true (execution payload embedded in block) - // Post-Gloas: result is a block state (payloadPresent=false) checkpointStateCache.add(cp, checkpointState); // consumers should not mutate state ever emitter?.emit(ChainEvent.checkpoint, cp, checkpointState); diff --git a/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts b/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts index 8e01dff5d648..4c8c7586d1a9 100644 --- a/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts +++ b/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts @@ -9,7 +9,7 @@ import {IClock} from "../../util/clock.js"; import {serializeState} from "../serializeState.js"; import {CPStateDatastore, DatastoreKey} from "./datastore/index.js"; import {MapTracker} from "./mapMetrics.js"; -import {BlockStateCache, CacheItemType, CheckpointHex, CheckpointHexPayload, CheckpointStateCache} from "./types.js"; +import {BlockStateCache, CacheItemType, CheckpointHex, CheckpointStateCache} from "./types.js"; export type PersistentCheckpointStateCacheOpts = { /** Keep max n state epochs in memory, persist the rest to disk */ @@ -843,19 +843,6 @@ export function toCheckpointHex(checkpoint: phase0.Checkpoint): CheckpointHex { }; } -/** TODO GLOAS: remove after rolling back regen dual-state changes */ -export function fcCheckpointToHexPayload(checkpoint: { - epoch: Epoch; - rootHex: RootHex; - payloadStatus?: number; -}): CheckpointHexPayload { - return { - epoch: checkpoint.epoch, - rootHex: checkpoint.rootHex, - payloadPresent: true, - }; -} - export function toCheckpointKey(cp: CheckpointHex): string { return `${cp.rootHex}:${cp.epoch}`; } diff --git a/packages/beacon-node/src/chain/stateCache/types.ts b/packages/beacon-node/src/chain/stateCache/types.ts index 062c3557ae66..1cd1956e2ef0 100644 --- a/packages/beacon-node/src/chain/stateCache/types.ts +++ b/packages/beacon-node/src/chain/stateCache/types.ts @@ -4,9 +4,6 @@ import {Epoch, RootHex, phase0} from "@lodestar/types"; export type CheckpointHex = {epoch: Epoch; rootHex: RootHex}; -/** TODO GLOAS: payloadPresent is ignored — remove after rolling back regen dual-state changes */ -export type CheckpointHexPayload = {epoch: Epoch; rootHex: RootHex; payloadPresent: boolean}; - /** * Lodestar currently keeps two state caches around. * diff --git a/packages/beacon-node/src/chain/validation/executionPayloadEnvelope.ts b/packages/beacon-node/src/chain/validation/executionPayloadEnvelope.ts index a4334ef71621..256b5f567b7a 100644 --- a/packages/beacon-node/src/chain/validation/executionPayloadEnvelope.ts +++ b/packages/beacon-node/src/chain/validation/executionPayloadEnvelope.ts @@ -107,7 +107,7 @@ async function validateExecutionPayloadEnvelope( }); } - // Get the post block state which is the pre-payload state to verify the builder's signature. + // Get the block state to verify the builder's signature. const blockState = await chain.regen .getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope) .catch(() => { diff --git a/packages/beacon-node/test/utils/node/simTest.ts b/packages/beacon-node/test/utils/node/simTest.ts index c2b11d9538c8..1c230c1e3fc8 100644 --- a/packages/beacon-node/test/utils/node/simTest.ts +++ b/packages/beacon-node/test/utils/node/simTest.ts @@ -68,11 +68,9 @@ export function simTestInfoTracker(bn: BeaconNode, logger: Logger): () => void { if (checkpoint.epoch <= lastSeenEpoch) return; lastSeenEpoch = checkpoint.epoch; - // Pre-Gloas: payloadPresent is always true (execution payload embedded in block) const checkpointState = bn.chain.regen.getCheckpointStateSync({ - ...checkpoint, + epoch: checkpoint.epoch, rootHex: toRootHex(checkpoint.root), - payloadPresent: true, }); if (checkpointState == null) { throw Error(`Checkpoint state not found for epoch ${checkpoint.epoch} root ${toRootHex(checkpoint.root)}`); diff --git a/packages/state-transition/src/stateTransition.ts b/packages/state-transition/src/stateTransition.ts index e9b0ffc21c2c..39ddd2e2c6c0 100644 --- a/packages/state-transition/src/stateTransition.ts +++ b/packages/state-transition/src/stateTransition.ts @@ -76,7 +76,6 @@ export enum StateHashTreeRootSource { prepareNextEpoch = "prepare_next_epoch", regenState = "regen_state", computeNewStateRoot = "compute_new_state_root", - computePayloadEnvelopeStateRoot = "compute_payload_envelope_state_root", } /**