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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import {
ProtoBlock,
} from "@lodestar/fork-choice";
import {ForkSeq} from "@lodestar/params";
import {CachedBeaconStateAllForks, isExecutionBlockBodyType, isExecutionStateType} from "@lodestar/state-transition";
import {
CachedBeaconStateAllForks,
isExecutionBlockBodyType,
isExecutionEnabled,
isExecutionStateType,
} from "@lodestar/state-transition";
import {bellatrix, electra} from "@lodestar/types";
import {ErrorAborted, Logger, toRootHex} from "@lodestar/utils";
import {ExecutionPayloadStatus, IExecutionEngine} from "../../execution/engine/interface.js";
Expand Down Expand Up @@ -145,7 +150,9 @@ export async function verifyBlockExecutionPayload(
const block = blockInput.getBlock();
/** Not null if execution is enabled */
const executionPayloadEnabled =
isExecutionStateType(preState0) && isExecutionBlockBodyType(block.message.body)
isExecutionStateType(preState0) &&
isExecutionBlockBodyType(block.message.body) &&
isExecutionEnabled(preState0, block.message)
? block.message.body.executionPayload
: null;

Expand Down
5 changes: 3 additions & 2 deletions packages/beacon-node/src/chain/forkChoice/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
getBlockRootAtSlot,
getEffectiveBalanceIncrementsZeroInactive,
isExecutionStateType,
isMergeTransitionComplete,
} from "@lodestar/state-transition";
import {Slot, ssz} from "@lodestar/types";
import {Logger, toRootHex} from "@lodestar/utils";
Expand Down Expand Up @@ -134,7 +135,7 @@ export function initializeForkChoiceFromFinalizedState(
unrealizedFinalizedEpoch: finalizedCheckpoint.epoch,
unrealizedFinalizedRoot: toRootHex(finalizedCheckpoint.root),

...(isExecutionStateType(state)
...(isExecutionStateType(state) && isMergeTransitionComplete(state)
? {
executionPayloadBlockHash: toRootHex(state.latestExecutionPayloadHeader.blockHash),
executionPayloadNumber: state.latestExecutionPayloadHeader.blockNumber,
Expand Down Expand Up @@ -215,7 +216,7 @@ export function initializeForkChoiceFromUnfinalizedState(
unrealizedFinalizedEpoch: finalizedCheckpoint.epoch,
unrealizedFinalizedRoot: toRootHex(finalizedCheckpoint.root),

...(isExecutionStateType(unfinalizedState)
...(isExecutionStateType(unfinalizedState) && isMergeTransitionComplete(unfinalizedState)
? {
executionPayloadBlockHash: toRootHex(unfinalizedState.latestExecutionPayloadHeader.blockHash),
executionPayloadNumber: unfinalizedState.latestExecutionPayloadHeader.blockNumber,
Expand Down
3 changes: 2 additions & 1 deletion packages/beacon-node/src/chain/validation/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
computeTimeAtSlot,
getBlockProposerSignatureSet,
isExecutionBlockBodyType,
isExecutionEnabled,
isExecutionStateType,
} from "@lodestar/state-transition";
import {SignedBeaconBlock, deneb} from "@lodestar/types";
Expand Down Expand Up @@ -139,7 +140,7 @@ export async function validateGossipBlock(
if (fork === ForkName.bellatrix) {
if (!isExecutionBlockBodyType(block.body)) throw Error("Not merge block type");
const executionPayload = block.body.executionPayload;
if (isExecutionStateType(blockState)) {
if (isExecutionStateType(blockState) && isExecutionEnabled(blockState, block)) {
const expectedTimestamp = computeTimeAtSlot(config, blockSlot, chain.genesisTime);
if (executionPayload.timestamp !== computeTimeAtSlot(config, blockSlot, chain.genesisTime)) {
throw new BlockGossipError(GossipAction.REJECT, {
Expand Down
20 changes: 13 additions & 7 deletions packages/beacon-node/src/node/notifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
computeEpochAtSlot,
computeStartSlotAtEpoch,
isExecutionCachedStateType,
isMergeTransitionComplete,
} from "@lodestar/state-transition";
import {Epoch} from "@lodestar/types";
import {ErrorAborted, Logger, prettyBytes, prettyBytesShort, sleep} from "@lodestar/utils";
Expand Down Expand Up @@ -171,13 +172,18 @@ function getHeadExecutionInfo(

// Add execution status to notifier only if head is on/post bellatrix
if (isExecutionCachedStateType(headState)) {
const executionPayloadHashInfo =
headInfo.executionStatus !== ExecutionStatus.PreMerge ? headInfo.executionPayloadBlockHash : "empty";
const executionPayloadNumberInfo =
headInfo.executionStatus !== ExecutionStatus.PreMerge ? headInfo.executionPayloadNumber : NaN;
return [
`exec-block: ${executionStatusStr}(${executionPayloadNumberInfo} ${prettyBytesShort(executionPayloadHashInfo)})`,
];
if (isMergeTransitionComplete(headState)) {
const executionPayloadHashInfo =
headInfo.executionStatus !== ExecutionStatus.PreMerge ? headInfo.executionPayloadBlockHash : "empty";
const executionPayloadNumberInfo =
headInfo.executionStatus !== ExecutionStatus.PreMerge ? headInfo.executionPayloadNumber : NaN;
return [
`exec-block: ${executionStatusStr}(${executionPayloadNumberInfo} ${prettyBytesShort(
executionPayloadHashInfo
)})`,
];
}
return [`exec-block: ${executionStatusStr}`];
}

return [];
Expand Down
7 changes: 1 addition & 6 deletions packages/beacon-node/test/spec/utils/specTestIterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,7 @@ export const defaultSkipOpts: SkipOpts = {
/^gloas\/(finality|fork_choice|networking|sanity|transition)\/.*$/,
/^gloas\/ssz_static\/ForkChoiceNode.*$/,
],
skippedTests: [
// These tests validate "first payload" scenarios where is_execution_enabled was false pre-merge.
// Since we removed merge transition support, these code paths no longer exist.
/^bellatrix\/operations\/execution_payload\/.+\/bad_parent_hash_first_payload$/,
/^bellatrix\/sanity\/blocks\/.+\/is_execution_enabled_false$/,
],
skippedTests: [],
skippedRunners: [],
};

Expand Down
3 changes: 2 additions & 1 deletion packages/fork-choice/src/forkChoice/forkChoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
computeStartSlotAtEpoch,
getAttesterSlashableIndices,
isExecutionBlockBodyType,
isExecutionEnabled,
isExecutionStateType,
} from "@lodestar/state-transition";
import {computeUnrealizedCheckpoints} from "@lodestar/state-transition/epoch";
Expand Down Expand Up @@ -741,7 +742,7 @@ export class ForkChoice implements IForkChoice {
unrealizedFinalizedEpoch: unrealizedFinalizedCheckpoint.epoch,
unrealizedFinalizedRoot: unrealizedFinalizedCheckpoint.rootHex,

...(isExecutionBlockBodyType(block.body) && isExecutionStateType(state)
...(isExecutionBlockBodyType(block.body) && isExecutionStateType(state) && isExecutionEnabled(state, block)
? {
executionPayloadBlockHash: toRootHex(block.body.executionPayload.blockHash),
executionPayloadNumber: block.body.executionPayload.blockNumber,
Expand Down
8 changes: 6 additions & 2 deletions packages/state-transition/src/block/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
CachedBeaconStateCapella,
CachedBeaconStateGloas,
} from "../types.js";
import {getFullOrBlindedPayload} from "../util/execution.js";
import {getFullOrBlindedPayload, isExecutionEnabled} from "../util/execution.js";
import {BlockExternalData, DataAvailabilityStatus} from "./externalData.js";
import {processBlobKzgCommitments} from "./processBlobKzgCommitments.js";
import {processBlockHeader} from "./processBlockHeader.js";
Expand Down Expand Up @@ -67,7 +67,11 @@ export function processBlock(
// The call to the process_execution_payload must happen before the call to the process_randao as the former depends
// on the randao_mix computed with the reveal of the previous block.
// TODO GLOAS: We call processExecutionPayload somewhere else post-gloas
if (fork >= ForkSeq.bellatrix && fork < ForkSeq.gloas) {
if (
fork < ForkSeq.gloas &&
fork >= ForkSeq.bellatrix &&
isExecutionEnabled(state as CachedBeaconStateBellatrix, block)
) {
processExecutionPayload(fork, state as CachedBeaconStateBellatrix, block.body, externalData);
}

Expand Down
22 changes: 14 additions & 8 deletions packages/state-transition/src/block/processExecutionPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import {ForkName, ForkSeq, isForkPostDeneb} from "@lodestar/params";
import {BeaconBlockBody, BlindedBeaconBlockBody, deneb, isExecutionPayload} from "@lodestar/types";
import {toHex, toRootHex} from "@lodestar/utils";
import {CachedBeaconStateBellatrix, CachedBeaconStateCapella} from "../types.js";
import {executionPayloadToPayloadHeader, getFullOrBlindedPayloadFromBody} from "../util/execution.js";
import {
executionPayloadToPayloadHeader,
getFullOrBlindedPayloadFromBody,
isMergeTransitionComplete,
} from "../util/execution.js";
import {computeEpochAtSlot, computeTimeAtSlot, getRandaoMix} from "../util/index.js";
import {BlockExternalData, ExecutionPayloadStatus} from "./externalData.js";

Expand All @@ -17,13 +21,15 @@ export function processExecutionPayload(
const forkName = ForkName[ForkSeq[fork] as ForkName];
// Verify consistency of the parent hash, block number, base fee per gas and gas limit
// with respect to the previous execution payload header
const {latestExecutionPayloadHeader} = state;
if (!byteArrayEquals(payload.parentHash, latestExecutionPayloadHeader.blockHash)) {
throw Error(
`Invalid execution payload parentHash ${toRootHex(payload.parentHash)} latest blockHash ${toRootHex(
latestExecutionPayloadHeader.blockHash
)}`
);
if (isMergeTransitionComplete(state)) {
const {latestExecutionPayloadHeader} = state;
if (!byteArrayEquals(payload.parentHash, latestExecutionPayloadHeader.blockHash)) {
throw Error(
`Invalid execution payload parentHash ${toRootHex(payload.parentHash)} latest blockHash ${toRootHex(
latestExecutionPayloadHeader.blockHash
)}`
);
}
}

// Verify random
Expand Down
39 changes: 39 additions & 0 deletions packages/state-transition/src/util/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,62 @@ import {ForkName, ForkPostBellatrix, ForkPreGloas, ForkSeq} from "@lodestar/para
import {
BeaconBlock,
BeaconBlockBody,
BlindedBeaconBlock,
BlindedBeaconBlockBody,
ExecutionPayload,
ExecutionPayloadHeader,
bellatrix,
capella,
deneb,
isBlindedBeaconBlockBody,
isExecutionPayload,
ssz,
} from "@lodestar/types";
import {
BeaconStateAllForks,
BeaconStateBellatrix,
BeaconStateCapella,
BeaconStateExecutions,
CachedBeaconStateAllForks,
CachedBeaconStateExecutions,
} from "../types.js";

/**
* Execution enabled = merge is done.
* When (A) state has execution data OR (B) block has execution data
*/
export function isExecutionEnabled(state: BeaconStateExecutions, block: BeaconBlock | BlindedBeaconBlock): boolean {
if (isMergeTransitionComplete(state)) {
return true;
}

// Throws if not post-bellatrix block. A fork-guard before isExecutionEnabled() prevents this from happening
const payload = getFullOrBlindedPayload(block);

return isExecutionPayload(payload)
? !ssz.bellatrix.ExecutionPayload.equals(payload, ssz.bellatrix.ExecutionPayload.defaultValue())
: !ssz.bellatrix.ExecutionPayloadHeader.equals(
state.latestExecutionPayloadHeader,
Comment thread
nflaig marked this conversation as resolved.
ssz.bellatrix.ExecutionPayloadHeader.defaultValue()
);
Comment thread
nflaig marked this conversation as resolved.
}

/**
* Merge is complete when the state includes execution layer data:
* state.latestExecutionPayloadHeader NOT EMPTY or state is post-capella
*/
export function isMergeTransitionComplete(state: BeaconStateExecutions): boolean {
if (isCapellaStateType(state)) {
// All networks have completed the merge transition before capella
return true;
}

return !ssz.bellatrix.ExecutionPayloadHeader.equals(
(state as BeaconStateBellatrix).latestExecutionPayloadHeader,
ssz.bellatrix.ExecutionPayloadHeader.defaultValue()
);
}

/** Type guard for bellatrix.BeaconState */
export function isExecutionStateType(state: BeaconStateAllForks): state is BeaconStateExecutions {
return (state as BeaconStateExecutions).latestExecutionPayloadHeader !== undefined;
Expand Down
Loading