UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

178 lines • 10.6 kB
import { ExecutionStatus, ForkChoice, ForkChoiceStore, PayloadStatus, ProtoArray, } from "@lodestar/fork-choice"; import { ZERO_HASH_HEX } from "@lodestar/params"; import { DataAvailabilityStatus, computeEpochAtSlot, computeStartSlotAtEpoch, isStatePostBellatrix, isStatePostGloas, } from "@lodestar/state-transition"; import { ssz } from "@lodestar/types"; import { toRootHex } from "@lodestar/utils"; import { GENESIS_SLOT } from "../../constants/index.js"; import { ChainEvent } from "../emitter.js"; export { ForkchoiceCaller }; var ForkchoiceCaller; (function (ForkchoiceCaller) { ForkchoiceCaller["prepareNextSlot"] = "prepare_next_slot"; ForkchoiceCaller["importBlock"] = "import_block"; })(ForkchoiceCaller || (ForkchoiceCaller = {})); /** * Fork Choice extended with a ChainEventEmitter */ export function initializeForkChoice(config, emitter, currentSlot, state, isFinalizedState, opts, justifiedBalancesGetter, metrics, logger) { return isFinalizedState ? initializeForkChoiceFromFinalizedState(config, emitter, currentSlot, state, opts, justifiedBalancesGetter, metrics, logger) : initializeForkChoiceFromUnfinalizedState(config, emitter, currentSlot, state, opts, justifiedBalancesGetter, metrics, logger); } /** * Initialize forkchoice from a finalized state. */ export function initializeForkChoiceFromFinalizedState(config, emitter, currentSlot, state, opts, justifiedBalancesGetter, metrics, logger) { const { blockHeader, checkpoint } = state.computeAnchorCheckpoint(); const finalizedCheckpoint = { ...checkpoint }; const justifiedCheckpoint = { ...checkpoint, // If not genesis epoch, justified checkpoint epoch must be set to finalized checkpoint epoch + 1 // So that we don't allow the chain to initially justify with a block that isn't also finalizing the anchor state. // If that happens, we will create an invalid head state, // with the head not matching the fork choice justified and finalized epochs. epoch: checkpoint.epoch === 0 ? checkpoint.epoch : checkpoint.epoch + 1, }; const justifiedBalances = state.getEffectiveBalanceIncrementsZeroInactive(); // forkchoiceConstructor is only used for some test cases // production code use ForkChoice constructor directly const forkchoiceConstructor = opts.forkchoiceConstructor ?? ForkChoice; const isForkPostGloas = computeEpochAtSlot(state.slot) >= config.GLOAS_FORK_EPOCH; return new forkchoiceConstructor(config, new ForkChoiceStore(currentSlot, justifiedCheckpoint, finalizedCheckpoint, justifiedBalances, justifiedBalancesGetter, { onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp), onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp), }), ProtoArray.initialize({ slot: blockHeader.slot, parentRoot: toRootHex(blockHeader.parentRoot), stateRoot: toRootHex(blockHeader.stateRoot), blockRoot: toRootHex(checkpoint.root), timeliness: true, // Optimistically assume is timely justifiedEpoch: justifiedCheckpoint.epoch, justifiedRoot: toRootHex(justifiedCheckpoint.root), finalizedEpoch: finalizedCheckpoint.epoch, finalizedRoot: toRootHex(finalizedCheckpoint.root), unrealizedJustifiedEpoch: justifiedCheckpoint.epoch, unrealizedJustifiedRoot: toRootHex(justifiedCheckpoint.root), unrealizedFinalizedEpoch: finalizedCheckpoint.epoch, unrealizedFinalizedRoot: toRootHex(finalizedCheckpoint.root), ...(isStatePostBellatrix(state) && state.isExecutionStateType && state.isMergeTransitionComplete ? { executionPayloadBlockHash: isStatePostGloas(state) ? toRootHex(state.latestBlockHash) : toRootHex(state.latestExecutionPayloadHeader.blockHash), // TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed // latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found. executionPayloadNumber: isStatePostGloas(state) ? 0 : state.payloadBlockNumber, executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing, } : { executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge }), dataAvailabilityStatus: DataAvailabilityStatus.PreData, payloadStatus: isForkPostGloas ? PayloadStatus.PENDING : PayloadStatus.FULL, parentBlockHash: isStatePostGloas(state) ? toRootHex(state.latestBlockHash) : null, }, currentSlot), state.validatorCount, metrics, opts, logger); } /** * Initialize forkchoice from an unfinalized state. */ export function initializeForkChoiceFromUnfinalizedState(config, emitter, currentSlot, unfinalizedState, opts, justifiedBalancesGetter, metrics, logger) { const { blockHeader } = unfinalizedState.computeAnchorCheckpoint(); const finalizedCheckpoint = unfinalizedState.finalizedCheckpoint; const justifiedCheckpoint = unfinalizedState.currentJustifiedCheckpoint; const headRoot = toRootHex(ssz.phase0.BeaconBlockHeader.hashTreeRoot(blockHeader)); const logCtx = { currentSlot: currentSlot, stateSlot: unfinalizedState.slot, headSlot: blockHeader.slot, headRoot: headRoot, finalizedEpoch: finalizedCheckpoint.epoch, finalizedRoot: toRootHex(finalizedCheckpoint.root), justifiedEpoch: justifiedCheckpoint.epoch, justifiedRoot: toRootHex(justifiedCheckpoint.root), }; logger?.warn("Initializing fork choice from unfinalized state", logCtx); // this is not the justified state, but there is no other ways to get justified balances const justifiedBalances = unfinalizedState.getEffectiveBalanceIncrementsZeroInactive(); const isForkPostGloas = computeEpochAtSlot(unfinalizedState.slot) >= config.GLOAS_FORK_EPOCH; const store = new ForkChoiceStore(currentSlot, justifiedCheckpoint, finalizedCheckpoint, justifiedBalances, justifiedBalancesGetter, { onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp), onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp), }); // this is the same to the finalized state const headBlock = { slot: blockHeader.slot, parentRoot: toRootHex(blockHeader.parentRoot), stateRoot: toRootHex(blockHeader.stateRoot), blockRoot: headRoot, targetRoot: headRoot, timeliness: true, // Optimistically assume is timely justifiedEpoch: justifiedCheckpoint.epoch, justifiedRoot: toRootHex(justifiedCheckpoint.root), finalizedEpoch: finalizedCheckpoint.epoch, finalizedRoot: toRootHex(finalizedCheckpoint.root), unrealizedJustifiedEpoch: justifiedCheckpoint.epoch, unrealizedJustifiedRoot: toRootHex(justifiedCheckpoint.root), unrealizedFinalizedEpoch: finalizedCheckpoint.epoch, unrealizedFinalizedRoot: toRootHex(finalizedCheckpoint.root), ...(isStatePostBellatrix(unfinalizedState) && unfinalizedState.isExecutionStateType && unfinalizedState.isMergeTransitionComplete ? { executionPayloadBlockHash: isStatePostGloas(unfinalizedState) ? toRootHex(unfinalizedState.latestBlockHash) : toRootHex(unfinalizedState.latestExecutionPayloadHeader.blockHash), // TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed // latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found. executionPayloadNumber: isStatePostGloas(unfinalizedState) ? 0 : unfinalizedState.payloadBlockNumber, executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing, } : { executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge }), dataAvailabilityStatus: DataAvailabilityStatus.PreData, payloadStatus: isForkPostGloas ? PayloadStatus.PENDING : PayloadStatus.FULL, parentBlockHash: isStatePostGloas(unfinalizedState) ? toRootHex(unfinalizedState.latestBlockHash) : null, }; const parentSlot = blockHeader.slot - 1; const parentEpoch = computeEpochAtSlot(parentSlot); // parent of head block const parentBlock = { ...headBlock, slot: parentSlot, // link this to the dummy justified block parentRoot: toRootHex(justifiedCheckpoint.root), // dummy data, we're not able to regen state before headBlock stateRoot: ZERO_HASH_HEX, blockRoot: headBlock.parentRoot, targetRoot: toRootHex(unfinalizedState.getBlockRootAtSlot(computeStartSlotAtEpoch(parentEpoch))), }; const justifiedBlock = { ...headBlock, slot: computeStartSlotAtEpoch(justifiedCheckpoint.epoch), // link this to the finalized root so that getAncestors can find the finalized block parentRoot: toRootHex(finalizedCheckpoint.root), // dummy data, we're not able to regen state before headBlock stateRoot: ZERO_HASH_HEX, blockRoot: toRootHex(justifiedCheckpoint.root), // same to blockRoot targetRoot: toRootHex(justifiedCheckpoint.root), }; const finalizedBlock = { ...headBlock, slot: computeStartSlotAtEpoch(finalizedCheckpoint.epoch), // we don't care parent of finalized block parentRoot: ZERO_HASH_HEX, // dummy data, we're not able to regen state before headBlock stateRoot: ZERO_HASH_HEX, blockRoot: toRootHex(finalizedCheckpoint.root), // same to blockRoot targetRoot: toRootHex(finalizedCheckpoint.root), }; const protoArray = ProtoArray.initialize(finalizedBlock, currentSlot); protoArray.onBlock(justifiedBlock, currentSlot, null); protoArray.onBlock(parentBlock, currentSlot, null); protoArray.onBlock(headBlock, currentSlot, null); logger?.verbose("Initialized protoArray successfully", { ...logCtx, length: protoArray.length() }); // forkchoiceConstructor is only used for some test cases // production code use ForkChoice constructor directly const forkchoiceConstructor = opts.forkchoiceConstructor ?? ForkChoice; return new forkchoiceConstructor(config, store, protoArray, unfinalizedState.validatorCount, metrics, opts, logger); } //# sourceMappingURL=index.js.map