Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capella hardfork and types #4568

Merged
merged 3 commits into from
Oct 2, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/api/src/builder/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type Api = {
): Promise<{version: ForkName; data: bellatrix.SignedBuilderBid}>;
submitBlindedBlock(
signedBlock: allForks.SignedBlindedBeaconBlock
): Promise<{version: ForkName; data: bellatrix.ExecutionPayload}>;
): Promise<{version: ForkName; data: allForks.ExecutionPayload}>;
};

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/beacon-node/src/api/impl/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
DOMAIN_SYNC_COMMITTEE,
DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF,
DOMAIN_CONTRIBUTION_AND_PROOF,
DOMAIN_BLS_TO_EXECUTION_CHANGE,
DOMAIN_APPLICATION_BUILDER,
TIMELY_SOURCE_FLAG_INDEX,
TIMELY_TARGET_FLAG_INDEX,
Expand Down Expand Up @@ -90,4 +91,7 @@ export const specConstants = {
// altair/validator.md
TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE,
SYNC_COMMITTEE_SUBNET_COUNT,

// ## Capella domain types
DOMAIN_BLS_TO_EXECUTION_CHANGE,
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
CachedBeaconStateAllForks,
isBellatrixStateType,
isBellatrixBlockBodyType,
isExecutionStateType,
isExecutionBlockBodyType,
isMergeTransitionBlock as isMergeTransitionBlockFn,
isExecutionEnabled,
} from "@lodestar/state-transition";
Expand Down Expand Up @@ -177,8 +177,8 @@ export async function verifyBlocksExecutionPayload(
// will still evalute to true for the following blocks leading to errors (while syncing)
// as the preState0 still belongs to the pre state of the first block on segment
mergeBlockFound === null &&
isBellatrixStateType(preState0) &&
isBellatrixBlockBodyType(block.message.body) &&
isExecutionStateType(preState0) &&
isExecutionBlockBodyType(block.message.body) &&
isMergeTransitionBlockFn(preState0, block.message.body);

// If this is a merge transition block, check to ensure if it references
Expand Down Expand Up @@ -256,8 +256,8 @@ export async function verifyBlockExecutionPayload(
): Promise<VerifyBlockExecutionResponse> {
/** Not null if execution is enabled */
const executionPayloadEnabled =
isBellatrixStateType(preState0) &&
isBellatrixBlockBodyType(block.message.body) &&
isExecutionStateType(preState0) &&
isExecutionBlockBodyType(block.message.body) &&
// Safe to use with a state previous to block's preState. isMergeComplete can only transition from false to true.
// - If preState0 is after merge block: condition is true, and will always be true
// - If preState0 is before merge block: the block could lie but then state transition function will throw above
Expand Down
4 changes: 2 additions & 2 deletions packages/beacon-node/src/chain/forkChoice/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import {
CachedBeaconStateAllForks,
getEffectiveBalanceIncrementsZeroInactive,
isBellatrixStateType,
isExecutionStateType,
isMergeTransitionComplete,
} from "@lodestar/state-transition";

Expand Down Expand Up @@ -78,7 +78,7 @@ export function initializeForkChoice(
unrealizedFinalizedEpoch: finalizedCheckpoint.epoch,
unrealizedFinalizedRoot: toHexString(finalizedCheckpoint.root),

...(isBellatrixStateType(state) && isMergeTransitionComplete(state)
...(isExecutionStateType(state) && isMergeTransitionComplete(state)
? {
executionPayloadBlockHash: toHexString(state.latestExecutionPayloadHeader.blockHash),
executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing,
Expand Down
4 changes: 2 additions & 2 deletions packages/beacon-node/src/chain/prepareNextSlot.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {computeEpochAtSlot, isBellatrixStateType, computeTimeAtSlot} from "@lodestar/state-transition";
import {computeEpochAtSlot, isExecutionStateType, computeTimeAtSlot} from "@lodestar/state-transition";
import {IChainForkConfig} from "@lodestar/config";
import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params";
import {Slot} from "@lodestar/types";
Expand Down Expand Up @@ -116,7 +116,7 @@ export class PrepareNextSlotScheduler {
});
}

if (isBellatrixStateType(prepareState)) {
if (isExecutionStateType(prepareState)) {
const proposerIndex = prepareState.epochCtx.getBeaconProposer(prepareSlot);
const feeRecipient = this.chain.beaconProposerCache.get(proposerIndex);
if (feeRecipient) {
Expand Down
23 changes: 15 additions & 8 deletions packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
bellatrix,
Bytes32,
phase0,
allForks,
Expand All @@ -15,14 +14,17 @@ import {
import {
CachedBeaconStateAllForks,
CachedBeaconStateBellatrix,
CachedBeaconStateExecutions,
computeEpochAtSlot,
computeTimeAtSlot,
getRandaoMix,
getCurrentEpoch,
isMergeTransitionComplete,
} from "@lodestar/state-transition";
import {IChainForkConfig} from "@lodestar/config";
import {ForkName} from "@lodestar/params";
import {toHex, sleep} from "@lodestar/utils";

import type {BeaconChain} from "../chain.js";
import {PayloadId, IExecutionEngine, IExecutionBuilder} from "../../execution/index.js";
import {ZERO_HASH, ZERO_HASH_HEX} from "../../constants/index.js";
Expand Down Expand Up @@ -109,7 +111,8 @@ export async function produceBlockBody<T extends BlockType>(
);
}

if (blockEpoch >= this.config.BELLATRIX_FORK_EPOCH) {
const forkName = currentState.config.getForkName(blockSlot);
if (forkName !== ForkName.phase0 && forkName !== ForkName.altair) {
const safeBlockHash = this.forkChoice.getJustifiedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
const feeRecipient = this.beaconProposerCache.getOrDefault(proposerIndex);
Expand Down Expand Up @@ -151,7 +154,9 @@ export async function produceBlockBody<T extends BlockType>(
feeRecipient
);
if (prepareRes.isPremerge) {
(blockBody as bellatrix.BeaconBlockBody).executionPayload = ssz.bellatrix.ExecutionPayload.defaultValue();
(blockBody as allForks.ExecutionBlockBody).executionPayload = ssz.allForksExecution[
forkName
].ExecutionPayload.defaultValue();
} else {
const {prepType, payloadId} = prepareRes;
if (prepType !== PayloadPreparationType.Cached) {
Expand All @@ -163,7 +168,7 @@ export async function produceBlockBody<T extends BlockType>(
await sleep(PAYLOAD_GENERATION_TIME_MS);
}
const payload = await this.executionEngine.getPayload(payloadId);
(blockBody as bellatrix.BeaconBlockBody).executionPayload = payload;
(blockBody as allForks.ExecutionBlockBody).executionPayload = payload;

const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime);
this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
Expand All @@ -182,7 +187,9 @@ export async function produceBlockBody<T extends BlockType>(
{},
e as Error
);
(blockBody as bellatrix.BeaconBlockBody).executionPayload = ssz.bellatrix.ExecutionPayload.defaultValue();
(blockBody as allForks.ExecutionBlockBody).executionPayload = ssz.allForksExecution[
forkName
].ExecutionPayload.defaultValue();
} else {
// since merge transition is complete, we need a valid payload even if with an
// empty (transactions) one. defaultValue isn't gonna cut it!
Expand Down Expand Up @@ -210,7 +217,7 @@ export async function prepareExecutionPayload(
},
safeBlockHash: RootHex,
finalizedBlockHash: RootHex,
state: CachedBeaconStateBellatrix,
state: CachedBeaconStateExecutions,
suggestedFeeRecipient: string
): Promise<{isPremerge: true} | {isPremerge: false; prepType: PayloadPreparationType; payloadId: PayloadId}> {
const parentHashRes = await getExecutionPayloadParentHash(chain, state);
Expand Down Expand Up @@ -281,7 +288,7 @@ async function prepareExecutionPayloadHeader(
},
state: CachedBeaconStateBellatrix,
proposerPubKey: BLSPubkey
): Promise<bellatrix.ExecutionPayloadHeader> {
): Promise<allForks.ExecutionPayloadHeader> {
if (!chain.executionBuilder) {
throw Error("executionBuilder required");
}
Expand All @@ -302,7 +309,7 @@ async function getExecutionPayloadParentHash(
eth1: IEth1ForBlockProduction;
config: IChainForkConfig;
},
state: CachedBeaconStateBellatrix
state: CachedBeaconStateExecutions
): Promise<{isPremerge: true} | {isPremerge: false; parentHash: Root}> {
// Use different POW block hash parent for block production based on merge status.
// Returned value of null == using an empty ExecutionPayload value
Expand Down
8 changes: 4 additions & 4 deletions packages/beacon-node/src/chain/validation/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {allForks} from "@lodestar/types";
import {
computeStartSlotAtEpoch,
computeTimeAtSlot,
isBellatrixBlockBodyType,
isBellatrixStateType,
isExecutionBlockBodyType,
isExecutionStateType,
isExecutionEnabled,
getProposerSignatureSet,
} from "@lodestar/state-transition";
Expand Down Expand Up @@ -126,9 +126,9 @@ export async function validateGossipBlock(
// [REJECT] The block's execution payload timestamp is correct with respect to the slot
// -- i.e. execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot).
if (fork === ForkName.bellatrix) {
if (!isBellatrixBlockBodyType(block.body)) throw Error("Not merge block type");
if (!isExecutionBlockBodyType(block.body)) throw Error("Not merge block type");
const executionPayload = block.body.executionPayload;
if (isBellatrixStateType(blockState) && isExecutionEnabled(blockState, block)) {
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
2 changes: 1 addition & 1 deletion packages/beacon-node/src/execution/builder/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class ExecutionBuilderHttp implements IExecutionBuilder {
return this.api.registerValidator(registrations);
}

async getHeader(slot: Slot, parentHash: Root, proposerPubKey: BLSPubkey): Promise<bellatrix.ExecutionPayloadHeader> {
async getHeader(slot: Slot, parentHash: Root, proposerPubKey: BLSPubkey): Promise<allForks.ExecutionPayloadHeader> {
const {data: signedBid} = await this.api.getHeader(slot, parentHash, proposerPubKey);
const executionPayloadHeader = signedBid.message.header;
return executionPayloadHeader;
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/execution/builder/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export interface IExecutionBuilder {
updateStatus(shouldEnable: boolean): void;
checkStatus(): Promise<void>;
registerValidator(registrations: bellatrix.SignedValidatorRegistrationV1[]): Promise<void>;
getHeader(slot: Slot, parentHash: Root, proposerPubKey: BLSPubkey): Promise<bellatrix.ExecutionPayloadHeader>;
getHeader(slot: Slot, parentHash: Root, proposerPubKey: BLSPubkey): Promise<allForks.ExecutionPayloadHeader>;
submitBlindedBlock(signedBlock: allForks.SignedBlindedBeaconBlock): Promise<allForks.SignedBeaconBlock>;
}
17 changes: 12 additions & 5 deletions packages/beacon-node/src/execution/engine/http.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {bellatrix, RootHex} from "@lodestar/types";
import {RootHex, allForks, capella} from "@lodestar/types";
import {BYTES_PER_LOGS_BLOOM} from "@lodestar/params";
import {fromHex} from "@lodestar/utils";

Expand Down Expand Up @@ -110,7 +110,7 @@ export class ExecutionEngineHttp implements IExecutionEngine {
*
* If any of the above fails due to errors unrelated to the normal processing flow of the method, client software MUST respond with an error object.
*/
async notifyNewPayload(executionPayload: bellatrix.ExecutionPayload): Promise<ExecutePayloadResponse> {
async notifyNewPayload(executionPayload: allForks.ExecutionPayload): Promise<ExecutePayloadResponse> {
const method = "engine_newPayloadV1";
const serializedExecutionPayload = serializeExecutionPayload(executionPayload);
const {status, latestValidHash, validationError} = await this.rpc
Expand Down Expand Up @@ -264,7 +264,7 @@ export class ExecutionEngineHttp implements IExecutionEngine {
* 2. The call MUST be responded with 5: Unavailable payload error if the building process identified by the payloadId doesn't exist.
* 3. Client software MAY stop the corresponding building process after serving this call.
*/
async getPayload(payloadId: PayloadId): Promise<bellatrix.ExecutionPayload> {
async getPayload(payloadId: PayloadId): Promise<allForks.ExecutionPayload> {
const method = "engine_getPayloadV1";
const executionPayloadRpc = await this.rpc.fetchWithRetries<
EngineApiRpcReturnTypes[typeof method],
Expand Down Expand Up @@ -371,9 +371,13 @@ type ExecutionPayloadRpc = {
baseFeePerGas: QUANTITY;
blockHash: DATA; // 32 bytes
transactions: DATA[];
withdrawals?: DATA[]; // Capella hardfork
};

export function serializeExecutionPayload(data: bellatrix.ExecutionPayload): ExecutionPayloadRpc {
export function serializeExecutionPayload(data: allForks.ExecutionPayload): ExecutionPayloadRpc {
if ((data as capella.ExecutionPayload).withdrawals !== undefined) {
throw Error("Capella Not implemented");
}
return {
parentHash: bytesToData(data.parentHash),
feeRecipient: bytesToData(data.feeRecipient),
Expand All @@ -392,7 +396,10 @@ export function serializeExecutionPayload(data: bellatrix.ExecutionPayload): Exe
};
}

export function parseExecutionPayload(data: ExecutionPayloadRpc): bellatrix.ExecutionPayload {
export function parseExecutionPayload(data: ExecutionPayloadRpc): allForks.ExecutionPayload {
if (data.withdrawals !== undefined) {
throw Error("Capella Not implemented");
}
return {
parentHash: dataToBytes(data.parentHash, 32),
feeRecipient: dataToBytes(data.feeRecipient, 20),
Expand Down
6 changes: 3 additions & 3 deletions packages/beacon-node/src/execution/engine/interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {bellatrix, RootHex} from "@lodestar/types";
import {RootHex, allForks} from "@lodestar/types";
import {DATA, QUANTITY} from "../../eth1/provider/utils.js";
import {PayloadIdCache, PayloadId, ApiPayloadAttributes} from "./payloadIdCache.js";

Expand Down Expand Up @@ -71,7 +71,7 @@ export interface IExecutionEngine {
*
* Should be called in advance before, after or in parallel to block processing
*/
notifyNewPayload(executionPayload: bellatrix.ExecutionPayload): Promise<ExecutePayloadResponse>;
notifyNewPayload(executionPayload: allForks.ExecutionPayload): Promise<ExecutePayloadResponse>;

/**
* Signal fork choice updates
Expand Down Expand Up @@ -99,7 +99,7 @@ export interface IExecutionEngine {
* Required for block producing
* https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/validator.md#get_payload
*/
getPayload(payloadId: PayloadId): Promise<bellatrix.ExecutionPayload>;
getPayload(payloadId: PayloadId): Promise<allForks.ExecutionPayload>;

exchangeTransitionConfigurationV1(
transitionConfiguration: TransitionConfigurationV1
Expand Down
5 changes: 4 additions & 1 deletion packages/beacon-node/src/network/gossip/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ export function msgIdFn(gossipTopicCache: GossipTopicCache, msg: GossipsubMessag
// )[:20]
// ```
// https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.7/specs/altair/p2p-interface.md#topics-and-messages
//
// TODO: check if the capella handling is same as the other forks
case ForkName.altair:
case ForkName.bellatrix: {
case ForkName.bellatrix:
case ForkName.capella: {
vec = [MESSAGE_DOMAIN_VALID_SNAPPY, intToBytes(msg.topic.length, 8), Buffer.from(msg.topic), msg.data];
break;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/beacon-node/src/node/notifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {CachedBeaconStateAllForks} from "@lodestar/state-transition";
import {ProtoBlock} from "@lodestar/fork-choice";
import {ErrorAborted, ILogger, sleep, prettyBytes} from "@lodestar/utils";
import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH} from "@lodestar/params";
import {computeEpochAtSlot, isBellatrixCachedStateType, isMergeTransitionComplete} from "@lodestar/state-transition";
import {computeEpochAtSlot, isExecutionCachedStateType, isMergeTransitionComplete} from "@lodestar/state-transition";
import {IBeaconChain} from "../chain/index.js";
import {INetwork} from "../network/index.js";
import {IBeaconSync, SyncState} from "../sync/index.js";
Expand Down Expand Up @@ -158,7 +158,7 @@ function getExecutionInfo(
const executionStatusStr = headInfo.executionStatus.toLowerCase();

// Add execution status to notifier only if head is on/post bellatrix
if (isBellatrixCachedStateType(headState)) {
if (isExecutionCachedStateType(headState)) {
if (isMergeTransitionComplete(headState)) {
return [`execution: ${executionStatusStr}(${prettyBytes(headInfo.executionPayloadBlockHash ?? "empty")})`];
} else {
Expand Down
4 changes: 2 additions & 2 deletions packages/beacon-node/test/sim/merge-interop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import net from "node:net";
import {spawn} from "node:child_process";
import {Context} from "mocha";
import {fromHexString} from "@chainsafe/ssz";
import {isBellatrixStateType, isMergeTransitionComplete} from "@lodestar/state-transition";
import {isExecutionStateType, isMergeTransitionComplete} from "@lodestar/state-transition";
import {LogLevel, sleep, TimestampFormatCode} from "@lodestar/utils";
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {IChainConfig} from "@lodestar/config";
Expand Down Expand Up @@ -418,7 +418,7 @@ describe("executionEngine / ExecutionEngineHttp", function () {
// By this slot, ttd should be reached and merge complete
case Number(ttd) + 3: {
const headState = bn.chain.getHeadState();
if (!(isBellatrixStateType(headState) && isMergeTransitionComplete(headState))) {
if (!(isExecutionStateType(headState) && isMergeTransitionComplete(headState))) {
reject("Merge not completed");
}

Expand Down
2 changes: 2 additions & 0 deletions packages/beacon-node/test/spec/presets/fork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const fork: TestRunnerFn<ForkStateCase, BeaconStateAllForks> = (forkNext)
return slotFns.upgradeStateToAltair(preState as CachedBeaconStatePhase0);
case ForkName.bellatrix:
return slotFns.upgradeStateToBellatrix(preState as CachedBeaconStateAltair);
case ForkName.capella:
throw Error("capella upgrade not implemented yet");
}
},
options: {
Expand Down
4 changes: 2 additions & 2 deletions packages/beacon-node/test/spec/presets/fork_choice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expect} from "chai";
import {BeaconStateAllForks, isBellatrixStateType} from "@lodestar/state-transition";
import {BeaconStateAllForks, isExecutionStateType} from "@lodestar/state-transition";
import {InputType} from "@lodestar/spec-test-util";
import {toHexString} from "@chainsafe/ssz";
import {CheckpointWithHex, ForkChoice} from "@lodestar/fork-choice";
Expand Down Expand Up @@ -46,7 +46,7 @@ export const forkChoiceTest: TestRunnerFn<ForkChoiceTestCase, void> = (fork) =>
const clock = new ClockStopped(currentSlot);
const eth1 = new Eth1ForBlockProductionMock();
const executionEngine = new ExecutionEngineMock({
genesisBlockHash: isBellatrixStateType(anchorState)
genesisBlockHash: isExecutionStateType(anchorState)
? toHexString(anchorState.latestExecutionPayloadHeader.blockHash)
: ZERO_HASH_HEX,
});
Expand Down
2 changes: 2 additions & 0 deletions packages/beacon-node/test/spec/presets/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ function getTransitionConfig(fork: ForkName, forkEpoch: number): Partial<IChainC
return {ALTAIR_FORK_EPOCH: forkEpoch};
case ForkName.bellatrix:
return {ALTAIR_FORK_EPOCH: 0, BELLATRIX_FORK_EPOCH: forkEpoch};
case ForkName.capella:
return {ALTAIR_FORK_EPOCH: 0, BELLATRIX_FORK_EPOCH: 0, CAPELLA_FORK_EPOCH: forkEpoch};
}
}

Expand Down
6 changes: 6 additions & 0 deletions packages/beacon-node/test/spec/utils/getConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,11 @@ export function getConfig(fork: ForkName, forkEpoch = 0): IChainForkConfig {
ALTAIR_FORK_EPOCH: 0,
BELLATRIX_FORK_EPOCH: forkEpoch,
});
case ForkName.capella:
return createIChainForkConfig({
ALTAIR_FORK_EPOCH: 0,
BELLATRIX_FORK_EPOCH: 0,
CAPELLA_FORK_EPOCH: forkEpoch,
});
}
}