Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 63 additions & 45 deletions packages/beacon-node/src/chain/forkChoice/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,68 @@ export function initializeForkChoiceFromFinalizedState(
// Determine finalized checkpoint payload status
const finalizedPayloadStatus = getCheckpointPayloadStatus(state, finalizedCheckpoint.epoch);

const blockRootHex = toRootHex(checkpoint.root);
const isGloasPayloadFull = isForkPostGloas && isParentBlockFull(state as CachedBeaconStateGloas);

const protoArray = ProtoArray.initialize(
{
slot: blockHeader.slot,
parentRoot: toRootHex(blockHeader.parentRoot),
stateRoot: toRootHex(blockHeader.stateRoot),
blockRoot: blockRootHex,
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),

...(isForkPostGloas
? {
executionPayloadBlockHash: toRootHex((state as CachedBeaconStateGloas).latestBlockHash),
executionPayloadNumber: 0,
executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing,
}
: isExecutionStateType(state) && isMergeTransitionComplete(state)
? {
executionPayloadBlockHash: toRootHex(state.latestExecutionPayloadHeader.blockHash),
executionPayloadNumber: state.latestExecutionPayloadHeader.blockNumber,
executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing,
}
: {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),

dataAvailabilityStatus: DataAvailabilityStatus.PreData,
payloadStatus: isForkPostGloas
? isGloasPayloadFull
? PayloadStatus.FULL
: PayloadStatus.EMPTY
: PayloadStatus.FULL,
builderIndex: isForkPostGloas ? (state as CachedBeaconStateGloas).latestExecutionPayloadBid.builderIndex : null,
blockHashFromBid: isForkPostGloas
? toRootHex((state as CachedBeaconStateGloas).latestExecutionPayloadBid.blockHash)
: null,
parentBlockHash: isForkPostGloas ? toRootHex((state as CachedBeaconStateGloas).latestBlockHash) : null,
},
currentSlot
);

// For gloas with payload available, create the FULL variant in the proto array.
// onBlock() only creates PENDING + EMPTY; FULL must be created via onExecutionPayload.
if (isGloasPayloadFull) {
const gloasState = state as CachedBeaconStateGloas;
protoArray.onExecutionPayload(
blockRootHex,
currentSlot,
toRootHex(gloasState.latestBlockHash),
0,
toRootHex(blockHeader.stateRoot)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use payload state root for anchor FULL variant

When initializing from a finalized post-Gloas anchor that already has payload available, this call creates the FULL variant with blockHeader.stateRoot, but FULL variants are expected to use the payload/envelope state root (as done in importBlock and importExecutionPayloadEnvelope). regen.getPreState() later reads the FULL parent’s stateRoot for child transition, so using the header root can select a pre-envelope state and cause child block import/proposal to fail after restart (especially once execution requests mutate state).

Useful? React with 👍 / 👎.

);
}

return new forkchoiceConstructor(
config,

Expand All @@ -132,51 +194,7 @@ export function initializeForkChoiceFromFinalizedState(
}
),

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),

...(isForkPostGloas
? {
executionPayloadBlockHash: toRootHex((state as CachedBeaconStateGloas).latestBlockHash),
executionPayloadNumber: 0,
executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing,
}
: isExecutionStateType(state) && isMergeTransitionComplete(state)
? {
executionPayloadBlockHash: toRootHex(state.latestExecutionPayloadHeader.blockHash),
executionPayloadNumber: state.latestExecutionPayloadHeader.blockNumber,
executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing,
}
: {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),

dataAvailabilityStatus: DataAvailabilityStatus.PreData,
payloadStatus: isForkPostGloas
? isParentBlockFull(state as CachedBeaconStateGloas)
? PayloadStatus.FULL
: PayloadStatus.EMPTY
: PayloadStatus.FULL,
builderIndex: isForkPostGloas ? (state as CachedBeaconStateGloas).latestExecutionPayloadBid.builderIndex : null,
blockHashFromBid: isForkPostGloas
? toRootHex((state as CachedBeaconStateGloas).latestExecutionPayloadBid.blockHash)
: null,
parentBlockHash: isForkPostGloas ? toRootHex((state as CachedBeaconStateGloas).latestBlockHash) : null,
},
currentSlot
),
protoArray,
state.validators.length,
metrics,
opts,
Expand Down
5 changes: 5 additions & 0 deletions packages/fork-choice/src/protoArray/protoArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ export class ProtoArray {
return PayloadStatus.FULL;
}

// Genesis block has no parent (parentRoot is zero hash), treat as FULL
if (block.parentRoot === HEX_ZERO_HASH) {
return PayloadStatus.FULL;
}

const parentVariants = this.indices.get(block.parentRoot);
if (parentVariants === undefined) {
throw new ProtoArrayError({
Expand Down
40 changes: 25 additions & 15 deletions packages/state-transition/src/util/genesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,46 +292,56 @@ export function initializeBeaconStateFromEth1(
const stateBellatrix = state as CompositeViewDU<typeof ssz.bellatrix.BeaconState>;
stateBellatrix.fork.previousVersion = config.BELLATRIX_FORK_VERSION;
stateBellatrix.fork.currentVersion = config.BELLATRIX_FORK_VERSION;
stateBellatrix.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.bellatrix.ExecutionPayloadHeader>) ??
ssz.bellatrix.ExecutionPayloadHeader.defaultViewDU();
if (fork < ForkSeq.gloas) {
stateBellatrix.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.bellatrix.ExecutionPayloadHeader>) ??
ssz.bellatrix.ExecutionPayloadHeader.defaultViewDU();
}
}

if (fork >= ForkSeq.capella) {
const stateCapella = state as CompositeViewDU<typeof ssz.capella.BeaconState>;
stateCapella.fork.previousVersion = config.CAPELLA_FORK_VERSION;
stateCapella.fork.currentVersion = config.CAPELLA_FORK_VERSION;
stateCapella.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.capella.ExecutionPayloadHeader>) ??
ssz.capella.ExecutionPayloadHeader.defaultViewDU();
if (fork < ForkSeq.gloas) {
stateCapella.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.capella.ExecutionPayloadHeader>) ??
ssz.capella.ExecutionPayloadHeader.defaultViewDU();
}
}

if (fork >= ForkSeq.deneb) {
const stateDeneb = state as CompositeViewDU<typeof ssz.deneb.BeaconState>;
stateDeneb.fork.previousVersion = config.DENEB_FORK_VERSION;
stateDeneb.fork.currentVersion = config.DENEB_FORK_VERSION;
stateDeneb.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.deneb.ExecutionPayloadHeader>) ??
ssz.deneb.ExecutionPayloadHeader.defaultViewDU();
if (fork < ForkSeq.gloas) {
stateDeneb.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.deneb.ExecutionPayloadHeader>) ??
ssz.deneb.ExecutionPayloadHeader.defaultViewDU();
}
}

if (fork >= ForkSeq.electra) {
const stateElectra = state as CompositeViewDU<typeof ssz.electra.BeaconState>;
stateElectra.fork.previousVersion = config.ELECTRA_FORK_VERSION;
stateElectra.fork.currentVersion = config.ELECTRA_FORK_VERSION;
stateElectra.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.electra.ExecutionPayloadHeader>) ??
ssz.electra.ExecutionPayloadHeader.defaultViewDU();
if (fork < ForkSeq.gloas) {
stateElectra.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.electra.ExecutionPayloadHeader>) ??
ssz.electra.ExecutionPayloadHeader.defaultViewDU();
}
stateElectra.depositRequestsStartIndex = UNSET_DEPOSIT_REQUESTS_START_INDEX;
}

if (fork >= ForkSeq.fulu) {
const stateFulu = state as CompositeViewDU<typeof ssz.fulu.BeaconState>;
stateFulu.fork.previousVersion = config.FULU_FORK_VERSION;
stateFulu.fork.currentVersion = config.FULU_FORK_VERSION;
stateFulu.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.fulu.ExecutionPayloadHeader>) ??
ssz.fulu.ExecutionPayloadHeader.defaultViewDU();
if (fork < ForkSeq.gloas) {
stateFulu.latestExecutionPayloadHeader =
(executionPayloadHeader as CompositeViewDU<typeof ssz.fulu.ExecutionPayloadHeader>) ??
ssz.fulu.ExecutionPayloadHeader.defaultViewDU();
}
}

if (fork >= ForkSeq.gloas) {
Expand Down
Loading