diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index 7fe28d7eb6ed..619cd75f597b 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -295,7 +295,7 @@ export async function importBlock( if (headBlockHash !== ZERO_HASH_HEX) { this.executionEngine .notifyForkchoiceUpdate( - this.config.getForkSeq(this.forkChoice.getHead().slot), + this.config.getForkName(this.forkChoice.getHead().slot), headBlockHash, safeBlockHash, finalizedBlockHash diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts index a9a7865f420c..36c4cdaa62c0 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts @@ -274,7 +274,7 @@ export async function verifyBlockExecutionPayload( // TODO: Handle better notifyNewPayload() returning error is syncing const execResult = await chain.executionEngine.notifyNewPayload( - chain.config.getForkSeq(block.message.slot), + chain.config.getForkName(block.message.slot), executionPayloadEnabled ); diff --git a/packages/beacon-node/src/chain/prepareNextSlot.ts b/packages/beacon-node/src/chain/prepareNextSlot.ts index 7e571858f094..4e640f178f37 100644 --- a/packages/beacon-node/src/chain/prepareNextSlot.ts +++ b/packages/beacon-node/src/chain/prepareNextSlot.ts @@ -5,7 +5,7 @@ import {Slot} from "@lodestar/types"; import {ILogger, sleep} from "@lodestar/utils"; import {GENESIS_SLOT, ZERO_HASH_HEX} from "../constants/constants.js"; import {IMetrics} from "../metrics/index.js"; -import {TransitionConfigurationV1} from "../execution/engine/interface.js"; +import {ForkExecution, TransitionConfigurationV1} from "../execution/engine/interface.js"; import {ChainEvent} from "./emitter.js"; import {prepareExecutionPayload} from "./produceBlock/produceBlockBody.js"; import {IBeaconChain} from "./interface.js"; @@ -55,11 +55,11 @@ export class PrepareNextSlotScheduler { const prepareEpoch = computeEpochAtSlot(prepareSlot); const nextEpoch = computeEpochAtSlot(clockSlot) + 1; const isEpochTransition = prepareEpoch === nextEpoch; - const forkSeq = this.config.getForkSeq(prepareSlot); + const fork = this.config.getForkName(prepareSlot); // Early return if we are pre-genesis // or we are pre-bellatrix and this is not an epoch transition - if (prepareSlot <= GENESIS_SLOT || (forkSeq < ForkSeq.bellatrix && !isEpochTransition)) { + if (prepareSlot <= GENESIS_SLOT || (ForkSeq[fork] < ForkSeq.bellatrix && !isEpochTransition)) { return; } @@ -137,8 +137,8 @@ export class PrepareNextSlotScheduler { // left for scheduler and this gives nice sematics to catch and log errors in the // try/catch wrapper here. await prepareExecutionPayload( - forkSeq, this.chain, + fork as ForkExecution, // State is of execution type safeBlockHash, finalizedBlockHash, prepareState, diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index b29a8ae35bf7..2754ea52a156 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -29,7 +29,13 @@ import {ForkName, ForkSeq} from "@lodestar/params"; import {toHex, sleep} from "@lodestar/utils"; import type {BeaconChain} from "../chain.js"; -import {PayloadId, IExecutionEngine, IExecutionBuilder} from "../../execution/index.js"; +import { + PayloadId, + IExecutionEngine, + IExecutionBuilder, + PayloadAttributes, + ForkExecution, +} from "../../execution/index.js"; import {ZERO_HASH, ZERO_HASH_HEX} from "../../constants/index.js"; import {IEth1ForBlockProduction} from "../../eth1/index.js"; import {numToQuantity} from "../../eth1/provider/utils.js"; @@ -114,8 +120,9 @@ export async function produceBlockBody( ); } - const forkInfo = currentState.config.getForkInfo(blockSlot); - if (forkInfo.name !== ForkName.phase0 && forkInfo.name !== ForkName.altair) { + const fork = currentState.config.getForkName(blockSlot); + + if (fork !== ForkName.phase0 && fork !== 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); @@ -128,8 +135,8 @@ export async function produceBlockBody( // header. if (this.executionBuilder.issueLocalFcUForBlockProduction) { await prepareExecutionPayload( - forkInfo.seq, this, + fork, safeBlockHash, finalizedBlockHash ?? ZERO_HASH_HEX, currentState as CachedBeaconStateBellatrix, @@ -141,8 +148,8 @@ export async function produceBlockBody( // fetched from the payload id and a blinded block will be produced instead of // fullblock for the validator to sign (blockBody as allForks.BlindedBeaconBlockBody).executionPayloadHeader = await prepareExecutionPayloadHeader( - forkInfo.seq, this, + fork, currentState as CachedBeaconStateBellatrix, proposerPubKey ); @@ -152,8 +159,8 @@ export async function produceBlockBody( // will takeover if the builder flow was activated and errors try { const prepareRes = await prepareExecutionPayload( - forkInfo.seq, this, + fork, safeBlockHash, finalizedBlockHash ?? ZERO_HASH_HEX, currentState as CachedBeaconStateBellatrix, @@ -161,7 +168,7 @@ export async function produceBlockBody( ); if (prepareRes.isPremerge) { (blockBody as allForks.ExecutionBlockBody).executionPayload = ssz.allForksExecution[ - forkInfo.name + fork ].ExecutionPayload.defaultValue(); } else { const {prepType, payloadId} = prepareRes; @@ -173,7 +180,7 @@ export async function produceBlockBody( // See: https://discord.com/channels/595666850260713488/892088344438255616/1009882079632314469 await sleep(PAYLOAD_GENERATION_TIME_MS); } - const payload = await this.executionEngine.getPayload(forkInfo.seq, payloadId); + const payload = await this.executionEngine.getPayload(fork, payloadId); (blockBody as allForks.ExecutionBlockBody).executionPayload = payload; const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime); @@ -194,7 +201,7 @@ export async function produceBlockBody( e as Error ); (blockBody as allForks.ExecutionBlockBody).executionPayload = ssz.allForksExecution[ - forkInfo.name + fork ].ExecutionPayload.defaultValue(); } else { // since merge transition is complete, we need a valid payload even if with an @@ -205,7 +212,7 @@ export async function produceBlockBody( } } - if (forkInfo.seq >= ForkSeq.capella) { + if (ForkSeq[fork] >= ForkSeq.capella) { // TODO: blsToExecutionChanges should be passed in the produceBlock call (blockBody as capella.BeaconBlockBody).blsToExecutionChanges = []; } @@ -221,12 +228,12 @@ export async function produceBlockBody( * @returns PayloadId = pow block found, null = pow NOT found */ export async function prepareExecutionPayload( - fork: ForkSeq, chain: { eth1: IEth1ForBlockProduction; executionEngine: IExecutionEngine; config: IChainForkConfig; }, + fork: ForkExecution, safeBlockHash: RootHex, finalizedBlockHash: RootHex, state: CachedBeaconStateExecutions, @@ -268,21 +275,23 @@ export async function prepareExecutionPayload( prepType = PayloadPreparationType.Fresh; } - const withdrawalAttrs = - fork >= ForkSeq.capella - ? {withdrawals: getExpectedWithdrawals(state as CachedBeaconStateCapella).withdrawals} - : {}; + const attributes: PayloadAttributes = { + fork, + timestamp, + prevRandao, + suggestedFeeRecipient, + }; + + if (ForkSeq[fork] >= ForkSeq.capella) { + attributes.withdrawals = getExpectedWithdrawals(state as CachedBeaconStateCapella).withdrawals; + } + payloadId = await chain.executionEngine.notifyForkchoiceUpdate( fork, toHex(parentHash), safeBlockHash, finalizedBlockHash, - { - timestamp, - prevRandao, - suggestedFeeRecipient, - ...withdrawalAttrs, - } + attributes ); } @@ -299,19 +308,19 @@ export async function prepareExecutionPayload( } async function prepareExecutionPayloadHeader( - fork: ForkSeq, chain: { eth1: IEth1ForBlockProduction; executionBuilder?: IExecutionBuilder; config: IChainForkConfig; }, + fork: ForkExecution, state: CachedBeaconStateBellatrix, proposerPubKey: BLSPubkey ): Promise { if (!chain.executionBuilder) { throw Error("executionBuilder required"); } - if (fork >= ForkSeq.capella) { + if (ForkSeq[fork] >= ForkSeq.capella) { throw Error("executionBuilder capella api not implemented"); } diff --git a/packages/beacon-node/src/eth1/provider/utils.ts b/packages/beacon-node/src/eth1/provider/utils.ts index 55a431b020d7..73a874a84e37 100644 --- a/packages/beacon-node/src/eth1/provider/utils.ts +++ b/packages/beacon-node/src/eth1/provider/utils.ts @@ -106,10 +106,10 @@ export function bytesToData(bytes: Uint8Array): DATA { /** * DATA as defined in ethereum execution layer JSON RPC https://eth.wiki/json-rpc/API */ -export function dataToBytes(hex: DATA, fixedLength?: number): Uint8Array { +export function dataToBytes(hex: DATA, fixedLength: number | null): Uint8Array { try { const bytes = fromHexString(hex); - if (fixedLength !== undefined && bytes.length !== fixedLength) { + if (fixedLength != null && bytes.length !== fixedLength) { throw Error(`Wrong data length ${bytes.length} expected ${fixedLength}`); } return bytes; diff --git a/packages/beacon-node/src/execution/engine/disabled.ts b/packages/beacon-node/src/execution/engine/disabled.ts index ca7bad91038b..f9f7f48b586c 100644 --- a/packages/beacon-node/src/execution/engine/disabled.ts +++ b/packages/beacon-node/src/execution/engine/disabled.ts @@ -14,6 +14,11 @@ export class ExecutionEngineDisabled implements IExecutionEngine { async getPayload(): Promise { throw Error("Execution engine disabled"); } + + async getBlobsBundle(): Promise { + throw Error("Execution engine disabled"); + } + async exchangeTransitionConfigurationV1(): Promise { throw Error("Execution engine disabled"); } diff --git a/packages/beacon-node/src/execution/engine/http.ts b/packages/beacon-node/src/execution/engine/http.ts index 1389a1a53d81..0e03858b5c9f 100644 --- a/packages/beacon-node/src/execution/engine/http.ts +++ b/packages/beacon-node/src/execution/engine/http.ts @@ -1,5 +1,12 @@ -import {RootHex, allForks, capella} from "@lodestar/types"; -import {BYTES_PER_LOGS_BLOOM, SLOTS_PER_EPOCH, ForkSeq} from "@lodestar/params"; +import {RootHex, allForks, capella, eip4844} from "@lodestar/types"; +import { + BYTES_PER_LOGS_BLOOM, + SLOTS_PER_EPOCH, + FIELD_ELEMENTS_PER_BLOB, + BYTES_PER_FIELD_ELEMENT, + ForkName, + ForkSeq, +} from "@lodestar/params"; import {fromHex} from "@lodestar/utils"; import {ErrorJsonRpcResponse, HttpRpcError, JsonRpcHttpClient} from "../../eth1/provider/jsonRpcHttpClient.js"; @@ -11,6 +18,7 @@ import { DATA, QUANTITY, quantityToBigint, + dataToRootHex, } from "../../eth1/provider/utils.js"; import {IJsonRpcHttpClient, ReqOpts} from "../../eth1/provider/jsonRpcHttpClient.js"; import {IMetrics} from "../../metrics/index.js"; @@ -26,6 +34,7 @@ import { ApiPayloadAttributes, WithdrawalV1, TransitionConfigurationV1, + BlobsBundle, } from "./interface.js"; import {PayloadIdCache} from "./payloadIdCache.js"; @@ -143,9 +152,9 @@ 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(seq: ForkSeq, executionPayload: allForks.ExecutionPayload): Promise { - const method = seq >= ForkSeq.capella ? "engine_newPayloadV2" : "engine_newPayloadV1"; - const serializedExecutionPayload = serializeExecutionPayload(seq, executionPayload); + async notifyNewPayload(fork: ForkName, executionPayload: allForks.ExecutionPayload): Promise { + const method = ForkSeq[fork] >= ForkSeq.capella ? "engine_newPayloadV2" : "engine_newPayloadV1"; + const serializedExecutionPayload = serializeExecutionPayload(fork, executionPayload); const {status, latestValidHash, validationError} = await (this.rpcFetchQueue.push({ method, params: [serializedExecutionPayload], @@ -225,7 +234,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 notifyForkchoiceUpdate( - seq: ForkSeq, + fork: ForkName, headBlockHash: RootHex, safeBlockHash: RootHex, finalizedBlockHash: RootHex, @@ -233,7 +242,7 @@ export class ExecutionEngineHttp implements IExecutionEngine { ): Promise { // Once on capella, should this need to be permanently switched to v2 when payload attrs // not provided - const method = seq >= ForkSeq.capella ? "engine_forkchoiceUpdatedV2" : "engine_forkchoiceUpdatedV1"; + const method = ForkSeq[fork] >= ForkSeq.capella ? "engine_forkchoiceUpdatedV2" : "engine_forkchoiceUpdatedV1"; const apiPayloadAttributes: ApiPayloadAttributes | undefined = payloadAttributes ? { timestamp: numToQuantity(payloadAttributes.timestamp), @@ -246,14 +255,18 @@ export class ExecutionEngineHttp implements IExecutionEngine { // and we can move on, as the next fcU will be issued soon on the new slot const fcUReqOpts = payloadAttributes !== undefined ? forkchoiceUpdatedV1Opts : {...forkchoiceUpdatedV1Opts, retryAttempts: 1}; - const { - payloadStatus: {status, latestValidHash: _latestValidHash, validationError}, - payloadId, - } = await (this.rpcFetchQueue.push({ + + const request = this.rpcFetchQueue.push({ method, params: [{headBlockHash, safeBlockHash, finalizedBlockHash}, apiPayloadAttributes], methodOpts: fcUReqOpts, - }) as Promise); + }) as Promise; + + const response = await request; + const { + payloadStatus: {status, latestValidHash: _latestValidHash, validationError}, + payloadId, + } = response; switch (status) { case ExecutePayloadStatus.VALID: @@ -295,8 +308,8 @@ 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(seq: ForkSeq, payloadId: PayloadId): Promise { - const method = seq >= ForkSeq.capella ? "engine_getPayloadV2" : "engine_getPayloadV1"; + async getPayload(fork: ForkName, payloadId: PayloadId): Promise { + const method = ForkSeq[fork] >= ForkSeq.capella ? "engine_getPayloadV2" : "engine_getPayloadV1"; const executionPayloadRpc = await this.rpc.fetchWithRetries< EngineApiRpcReturnTypes[typeof method], EngineApiRpcParamTypes[typeof method] @@ -307,7 +320,23 @@ export class ExecutionEngineHttp implements IExecutionEngine { }, getPayloadOpts ); - return parseExecutionPayload(seq, executionPayloadRpc); + return parseExecutionPayload(fork, executionPayloadRpc); + } + + async getBlobsBundle(payloadId: PayloadId): Promise { + const method = "engine_getBlobsBundleV1"; + const blobsBundle = await this.rpc.fetchWithRetries< + EngineApiRpcReturnTypes[typeof method], + EngineApiRpcParamTypes[typeof method] + >( + { + method, + params: [payloadId], + }, + getPayloadOpts + ); + + return parseBlobsBundle(blobsBundle); } /** @@ -367,6 +396,10 @@ type EngineApiRpcParamTypes = { * 1. Object - Instance of TransitionConfigurationV1 */ engine_exchangeTransitionConfigurationV1: [TransitionConfigurationV1]; + /** + * 1. payloadId: QUANTITY, 64 Bits - Identifier of the payload building process + */ + engine_getBlobsBundleV1: [QUANTITY]; }; type EngineApiRpcReturnTypes = { @@ -401,6 +434,8 @@ type EngineApiRpcReturnTypes = { * Object - Instance of TransitionConfigurationV1 */ engine_exchangeTransitionConfigurationV1: TransitionConfigurationV1; + + engine_getBlobsBundleV1: BlobsBundleRpc; }; type ExecutionPayloadRpc = { @@ -419,9 +454,16 @@ type ExecutionPayloadRpc = { blockHash: DATA; // 32 bytes transactions: DATA[]; withdrawals?: WithdrawalV1[]; // Capella hardfork + excessDataGas?: QUANTITY; // EIP-4844 }; -export function serializeExecutionPayload(seq: ForkSeq, data: allForks.ExecutionPayload): ExecutionPayloadRpc { +interface BlobsBundleRpc { + blockHash: DATA; // 32 Bytes + kzgs: DATA[]; // each 48 bytes + blobs: DATA[]; // each 4096 * 32 = 131072 bytes +} + +export function serializeExecutionPayload(fork: ForkName, data: allForks.ExecutionPayload): ExecutionPayloadRpc { const payload: ExecutionPayloadRpc = { parentHash: bytesToData(data.parentHash), feeRecipient: bytesToData(data.feeRecipient), @@ -439,14 +481,21 @@ export function serializeExecutionPayload(seq: ForkSeq, data: allForks.Execution transactions: data.transactions.map((tran) => bytesToData(tran)), }; - if (seq >= ForkSeq.capella) { + // Capella adds withdrawals to the ExecutionPayload + if (ForkSeq[fork] >= ForkSeq.capella) { const {withdrawals} = data as capella.ExecutionPayload; payload.withdrawals = withdrawals.map(serializeWithdrawal); } + + // EIP-4844 adds excessDataGas to the ExecutionPayload + if ((data as eip4844.ExecutionPayload).excessDataGas !== undefined) { + payload.excessDataGas = numToQuantity((data as eip4844.ExecutionPayload).excessDataGas); + } + return payload; } -export function parseExecutionPayload(seq: ForkSeq, data: ExecutionPayloadRpc): allForks.ExecutionPayload { +export function parseExecutionPayload(fork: ForkName, data: ExecutionPayloadRpc): allForks.ExecutionPayload { const payload = { parentHash: dataToBytes(data.parentHash, 32), feeRecipient: dataToBytes(data.feeRecipient, 20), @@ -458,24 +507,46 @@ export function parseExecutionPayload(seq: ForkSeq, data: ExecutionPayloadRpc): gasLimit: quantityToNum(data.gasLimit), gasUsed: quantityToNum(data.gasUsed), timestamp: quantityToNum(data.timestamp), - extraData: dataToBytes(data.extraData), + extraData: dataToBytes(data.extraData, null), baseFeePerGas: quantityToBigint(data.baseFeePerGas), blockHash: dataToBytes(data.blockHash, 32), - transactions: data.transactions.map((tran) => dataToBytes(tran)), + transactions: data.transactions.map((tran) => dataToBytes(tran, null)), }; - if (seq >= ForkSeq.capella) { + + if (ForkSeq[fork] >= ForkSeq.capella) { const {withdrawals} = data; // Geth can also reply with null - if (withdrawals === undefined || withdrawals === null) { + if (withdrawals == null) { throw Error( - `withdrawals missing for seq=${seq} >= capella seq=${ForkSeq.capella} executionPayload number=${payload.blockNumber} hash=${data.blockHash}` + `withdrawals missing for ${fork} >= capella executionPayload number=${payload.blockNumber} hash=${data.blockHash}` ); } - (payload as capella.ExecutionPayload).withdrawals = withdrawals.map(deserializeWithdrawal); + (payload as capella.ExecutionPayload).withdrawals = withdrawals.map((w) => deserializeWithdrawal(w)); } + + // EIP-4844 adds excessDataGas to the ExecutionPayload + if (ForkSeq[fork] >= ForkSeq.eip4844) { + if (data.excessDataGas == null) { + throw Error( + `excessDataGas missing for ${fork} >= eip4844 executionPayload number=${payload.blockNumber} hash=${data.blockHash}` + ); + } + (payload as eip4844.ExecutionPayload).excessDataGas = quantityToBigint(data.excessDataGas); + } + return payload; } +export function parseBlobsBundle(data: BlobsBundleRpc): BlobsBundle { + return { + // NOTE: Keep as hex, since it's only used for equality downstream + blockHash: dataToRootHex(data.blockHash), + // As of Nov 17th 2022 according to Dan's tests Geth returns null if no blobs in block + kzgs: (data.kzgs ?? []).map((kzg) => dataToBytes(kzg, 48)), + blobs: (data.blobs ?? []).map((blob) => dataToBytes(blob, BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB)), + }; +} + function serializeWithdrawal(withdrawal: capella.Withdrawal): WithdrawalV1 { return { index: numToQuantity(withdrawal.index), @@ -494,7 +565,7 @@ function deserializeWithdrawal(serialized: WithdrawalV1): capella.Withdrawal { return { index: quantityToNum(serialized.index), validatorIndex: quantityToNum(serialized.validatorIndex), - address: dataToBytes(serialized.address), + address: dataToBytes(serialized.address, 20), // see: https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md amount: quantityToBigint(serialized.amount) / GWEI_TO_WEI, } as capella.Withdrawal; diff --git a/packages/beacon-node/src/execution/engine/interface.ts b/packages/beacon-node/src/execution/engine/interface.ts index 2a4db9501e06..7158ebe71bf6 100644 --- a/packages/beacon-node/src/execution/engine/interface.ts +++ b/packages/beacon-node/src/execution/engine/interface.ts @@ -1,10 +1,13 @@ +import {ForkName} from "@lodestar/params"; +import {KZGCommitment, Blob} from "@lodestar/types/eip4844"; import {RootHex, allForks, capella} from "@lodestar/types"; -import {ForkSeq} from "@lodestar/params"; import {DATA, QUANTITY} from "../../eth1/provider/utils.js"; import {PayloadIdCache, PayloadId, ApiPayloadAttributes, WithdrawalV1} from "./payloadIdCache.js"; +export type ForkExecution = ForkName.bellatrix | ForkName.capella | ForkName.eip4844; export {PayloadIdCache, PayloadId, ApiPayloadAttributes, WithdrawalV1}; + export enum ExecutePayloadStatus { /** given payload is valid */ VALID = "VALID", @@ -48,6 +51,8 @@ export type PayloadAttributes = { // DATA is anyway a hex string, so we can just track it as a hex string to // avoid any conversions suggestedFeeRecipient: string; + // Not spec'ed, to know the type of the payload + fork: ForkExecution; withdrawals?: capella.Withdrawal[]; }; @@ -57,6 +62,16 @@ export type TransitionConfigurationV1 = { terminalBlockNumber: QUANTITY; }; +export type BlobsBundle = { + /** + * Execution payload `blockHash` for the caller to sanity-check the consistency with the `engine_getPayload` call + * https://github.com/protolambda/execution-apis/blob/bf44a8d08ab34b861ef97fa9ef5c5e7806194547/src/engine/blob-extension.md?plain=1#L49 + */ + blockHash: RootHex; + kzgs: KZGCommitment[]; + blobs: Blob[]; +}; + /** * Execution engine represents an abstract protocol to interact with execution clients. Potential transports include: * - JSON RPC over network @@ -74,7 +89,7 @@ export interface IExecutionEngine { * * Should be called in advance before, after or in parallel to block processing */ - notifyNewPayload(seq: ForkSeq, executionPayload: allForks.ExecutionPayload): Promise; + notifyNewPayload(fork: ForkName, executionPayload: allForks.ExecutionPayload): Promise; /** * Signal fork choice updates @@ -89,7 +104,7 @@ export interface IExecutionEngine { * Should be called in response to fork-choice head and finalized events */ notifyForkchoiceUpdate( - seq: ForkSeq, + fork: ForkName, headBlockHash: RootHex, safeBlockHash: RootHex, finalizedBlockHash: RootHex, @@ -103,7 +118,20 @@ export interface IExecutionEngine { * Required for block producing * https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/validator.md#get_payload */ - getPayload(seq: ForkSeq, payloadId: PayloadId): Promise; + getPayload(fork: ForkName, payloadId: PayloadId): Promise; + + /** + * "After retrieving the execution payload from the execution engine as specified in Bellatrix, + * use the payload_id to retrieve blobs and blob_kzg_commitments + * via get_blobs_and_kzg_commitments(payload_id)." + * https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/validator.md#blob-kzg-commitments + * + * This function calls the Engine API method engine_getBlobsBundleV1, what the consensus-spec + * describes as `get_blobs_and_kzg_commitments(payload_id)`. + * + * The Engine API spec is in PR: https://github.com/ethereum/execution-apis/pull/197 + */ + getBlobsBundle(payloadId: PayloadId): Promise; exchangeTransitionConfigurationV1( transitionConfiguration: TransitionConfigurationV1 diff --git a/packages/beacon-node/src/execution/engine/mock.ts b/packages/beacon-node/src/execution/engine/mock.ts index f74da0521a7b..31101523b19c 100644 --- a/packages/beacon-node/src/execution/engine/mock.ts +++ b/packages/beacon-node/src/execution/engine/mock.ts @@ -1,8 +1,8 @@ import crypto from "node:crypto"; -import {bellatrix, RootHex} from "@lodestar/types"; +import {allForks, bellatrix, eip4844, RootHex, ssz} from "@lodestar/types"; import {fromHex, toHex} from "@lodestar/utils"; -import {BYTES_PER_LOGS_BLOOM, ForkSeq} from "@lodestar/params"; -import {ZERO_HASH, ZERO_HASH_HEX} from "../../constants/index.js"; +import {ForkName} from "@lodestar/params"; +import {ZERO_HASH_HEX} from "../../constants/index.js"; import { ExecutePayloadStatus, ExecutePayloadResponse, @@ -11,8 +11,11 @@ import { PayloadAttributes, PayloadIdCache, TransitionConfigurationV1, + BlobsBundle, } from "./interface.js"; + const INTEROP_GAS_LIMIT = 30e6; +const PRUNE_PAYLOAD_ID_AFTER_MS = 5000; export type ExecutionEngineMockOpts = { genesisBlockHash: string; @@ -25,6 +28,13 @@ type ExecutionBlock = { blockNumber: number; }; +const TX_TYPE_EIP1559 = 2; + +type PreparedPayload = { + executionPayload: allForks.ExecutionPayload; + blobsBundle: BlobsBundle; +}; + /** * Mock ExecutionEngine for fast prototyping and unit testing */ @@ -38,7 +48,8 @@ export class ExecutionEngineMock implements IExecutionEngine { /** Known valid blocks, both pre-merge and post-merge */ private readonly validBlocks = new Map(); /** Preparing payloads to be retrieved via engine_getPayloadV1 */ - private readonly preparingPayloads = new Map(); + private readonly preparingPayloads = new Map(); + private readonly payloadsForDeletion = new Map(); private payloadId = 0; @@ -54,7 +65,10 @@ export class ExecutionEngineMock implements IExecutionEngine { /** * `engine_newPayloadV1` */ - async notifyNewPayload(_seq: ForkSeq, executionPayload: bellatrix.ExecutionPayload): Promise { + async notifyNewPayload( + _fork: ForkName, + executionPayload: bellatrix.ExecutionPayload + ): Promise { const blockHash = toHex(executionPayload.blockHash); const parentHash = toHex(executionPayload.parentHash); @@ -105,7 +119,7 @@ export class ExecutionEngineMock implements IExecutionEngine { * `engine_forkchoiceUpdatedV1` */ async notifyForkchoiceUpdate( - _seq: ForkSeq, + _fork: ForkName, headBlockHash: RootHex, safeBlockHash: RootHex, finalizedBlockHash: RootHex, @@ -175,21 +189,41 @@ export class ExecutionEngineMock implements IExecutionEngine { // identified via buildProcessId value if payloadAttributes is not null and the forkchoice state has been // updated successfully. The build process is specified in the Payload building section. const payloadId = this.payloadId++; + + // Generate empty payload first to be correct with respect to fork + const executionPayload = ssz[payloadAttributes.fork].ExecutionPayload.defaultValue(); + + // Make executionPayload valid + executionPayload.parentHash = fromHex(headBlockHash); + executionPayload.feeRecipient = fromHex(payloadAttributes.suggestedFeeRecipient); + executionPayload.prevRandao = payloadAttributes.prevRandao; + executionPayload.blockNumber = headBlock.blockNumber + 1; + executionPayload.gasLimit = INTEROP_GAS_LIMIT; + executionPayload.gasUsed = Math.floor(0.5 * INTEROP_GAS_LIMIT); + executionPayload.timestamp = payloadAttributes.timestamp; + executionPayload.blockHash = crypto.randomBytes(32); + executionPayload.transactions = []; + + // Between 0 and 4 transactions for all forks + const eip1559TxCount = Math.round(4 * Math.random()); + for (let i = 0; i < eip1559TxCount; i++) { + const tx = crypto.randomBytes(512); + tx[0] = TX_TYPE_EIP1559; + executionPayload.transactions.push(tx); + } + + const kzgs: eip4844.KZGCommitment[] = []; + const blobs: eip4844.Blob[] = []; + + // TODO EIP-4844: Populate blobs and KZG commitments + this.preparingPayloads.set(payloadId, { - parentHash: fromHex(headBlockHash), - feeRecipient: fromHex(payloadAttributes.suggestedFeeRecipient), - stateRoot: crypto.randomBytes(32), - receiptsRoot: crypto.randomBytes(32), - logsBloom: crypto.randomBytes(BYTES_PER_LOGS_BLOOM), - prevRandao: payloadAttributes.prevRandao, - blockNumber: headBlock.blockNumber + 1, - gasLimit: INTEROP_GAS_LIMIT, - gasUsed: Math.floor(0.5 * INTEROP_GAS_LIMIT), - timestamp: payloadAttributes.timestamp, - extraData: ZERO_HASH, - baseFeePerGas: BigInt(0), - blockHash: crypto.randomBytes(32), - transactions: [crypto.randomBytes(512)], + executionPayload, + blobsBundle: { + blockHash: toHex(executionPayload.blockHash), + kzgs, + blobs, + }, }); // IF the payload is deemed VALID and the build process has begun @@ -212,7 +246,7 @@ export class ExecutionEngineMock 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(_seq: ForkSeq, payloadId: PayloadId): Promise { + async getPayload(_fork: ForkName, payloadId: PayloadId): Promise { // 1. Given the payloadId client software MUST return the most recent version of the payload that is available in // the corresponding build process at the time of receiving the call. const payloadIdNbr = Number(payloadId); @@ -225,9 +259,28 @@ export class ExecutionEngineMock implements IExecutionEngine { } // 3. Client software MAY stop the corresponding build process after serving this call. - this.preparingPayloads.delete(payloadIdNbr); + // Do after a while to allow getBlobsBundle() + const now = Date.now(); + for (const [oldPayloadId, addedTimestampMs] of this.payloadsForDeletion.entries()) { + if (addedTimestampMs < now - PRUNE_PAYLOAD_ID_AFTER_MS) { + this.preparingPayloads.delete(oldPayloadId); + this.payloadsForDeletion.delete(oldPayloadId); + } + } + this.payloadsForDeletion.set(payloadIdNbr, now); + + return payload.executionPayload; + } + + async getBlobsBundle(payloadId: PayloadId): Promise { + const payloadIdNbr = Number(payloadId); + const payload = this.preparingPayloads.get(payloadIdNbr); + + if (!payload) { + throw Error(`Unknown payloadId ${payloadId}`); + } - return payload; + return payload.blobsBundle; } async exchangeTransitionConfigurationV1( diff --git a/packages/beacon-node/test/sim/merge-interop.test.ts b/packages/beacon-node/test/sim/merge-interop.test.ts index 5cc719ec38ce..acfde8b4f124 100644 --- a/packages/beacon-node/test/sim/merge-interop.test.ts +++ b/packages/beacon-node/test/sim/merge-interop.test.ts @@ -3,12 +3,12 @@ import {Context} from "mocha"; import {fromHexString} from "@chainsafe/ssz"; import {isExecutionStateType, isMergeTransitionComplete} from "@lodestar/state-transition"; import {LogLevel, sleep, TimestampFormatCode} from "@lodestar/utils"; -import {SLOTS_PER_EPOCH, ForkSeq} from "@lodestar/params"; +import {ForkName, SLOTS_PER_EPOCH} from "@lodestar/params"; import {IChainConfig} from "@lodestar/config"; import {Epoch} from "@lodestar/types"; import {ValidatorProposerConfig} from "@lodestar/validator"; -import {ExecutePayloadStatus} from "../../src/execution/engine/interface.js"; +import {ExecutePayloadStatus, PayloadAttributes} from "../../src/execution/engine/interface.js"; import {ExecutionEngineHttp} from "../../src/execution/engine/http.js"; import {ChainEvent} from "../../src/chain/index.js"; import {testLogger, TestLoggerOpts} from "../utils/logger.js"; @@ -118,17 +118,18 @@ describe("executionEngine / ExecutionEngineHttp", function () { * curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"engine_forkchoiceUpdatedV1","params":[{"headBlockHash":"0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", "safeBlockHash":"0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a", "finalizedBlockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}, {"timestamp":"0x5", "prevRandao":"0x0000000000000000000000000000000000000000000000000000000000000000", "feeRecipient":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"}],"id":67}' http://localhost:8550 **/ - const preparePayloadParams = { + const preparePayloadParams: PayloadAttributes = { // Note: this is created with a pre-defined genesis.json timestamp: quantityToNum("0x5"), - prevRandao: dataToBytes("0x0000000000000000000000000000000000000000000000000000000000000000"), + prevRandao: dataToBytes("0x0000000000000000000000000000000000000000000000000000000000000000", 32), suggestedFeeRecipient: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + fork: ForkName.bellatrix, }; const finalizedBlockHash = "0x0000000000000000000000000000000000000000000000000000000000000000"; const payloadId = await executionEngine.notifyForkchoiceUpdate( - ForkSeq.bellatrix, + ForkName.bellatrix, genesisBlockHash, //use finalizedBlockHash as safeBlockHash finalizedBlockHash, @@ -143,7 +144,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { * curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"engine_getPayloadV1","params":["0xa247243752eb10b4"],"id":67}' http://localhost:8550 **/ - const payload = await executionEngine.getPayload(ForkSeq.bellatrix, payloadId); + const payload = await executionEngine.getPayload(ForkName.bellatrix, payloadId); if (TX_SCENARIOS.includes("simple")) { if (payload.transactions.length !== 1) throw new Error("Expected a simple transaction to be in the fetched payload"); @@ -156,7 +157,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { * curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"engine_newPayloadV1","params":[{"parentHash":"0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a","coinbase":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b","stateRoot":"0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45","receiptRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prevRandao":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x5","extraData":"0x","baseFeePerGas":"0x7","blockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858","transactions":[]}],"id":67}' http://localhost:8550 **/ - const payloadResult = await executionEngine.notifyNewPayload(ForkSeq.bellatrix, payload); + const payloadResult = await executionEngine.notifyNewPayload(ForkName.bellatrix, payload); if (payloadResult.status !== ExecutePayloadStatus.VALID) { throw Error("getPayload returned payload that notifyNewPayload deems invalid"); } @@ -168,7 +169,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { **/ await executionEngine.notifyForkchoiceUpdate( - ForkSeq.bellatrix, + ForkName.bellatrix, bytesToData(payload.blockHash), genesisBlockHash, genesisBlockHash diff --git a/packages/beacon-node/test/sim/withdrawal-interop.test.ts b/packages/beacon-node/test/sim/withdrawal-interop.test.ts index 5e771d33900a..962dc239f0a8 100644 --- a/packages/beacon-node/test/sim/withdrawal-interop.test.ts +++ b/packages/beacon-node/test/sim/withdrawal-interop.test.ts @@ -2,12 +2,12 @@ import fs from "node:fs"; import {Context} from "mocha"; import {fromHexString, toHexString} from "@chainsafe/ssz"; import {LogLevel, sleep, TimestampFormatCode} from "@lodestar/utils"; -import {SLOTS_PER_EPOCH, ForkSeq} from "@lodestar/params"; +import {SLOTS_PER_EPOCH, ForkName} from "@lodestar/params"; import {IChainConfig} from "@lodestar/config"; import {Epoch} from "@lodestar/types"; import {ValidatorProposerConfig} from "@lodestar/validator"; -import {ExecutePayloadStatus} from "../../src/execution/engine/interface.js"; +import {ExecutePayloadStatus, PayloadAttributes} from "../../src/execution/engine/interface.js"; import {ExecutionEngineHttp} from "../../src/execution/engine/http.js"; import {ChainEvent} from "../../src/chain/index.js"; import {testLogger, TestLoggerOpts} from "../utils/logger.js"; @@ -133,22 +133,23 @@ describe("executionEngine / ExecutionEngineHttp", function () { const withdrawals = withdrawalsVector.map((testVec) => ({ index: testVec.Index, validatorIndex: testVec.Validator, - address: dataToBytes(testVec.Recipient), + address: dataToBytes(testVec.Recipient, 20), amount: BigInt(testVec.Amount) / GWEI_TO_WEI, })); - const preparePayloadParams = { + const preparePayloadParams: PayloadAttributes = { // Note: this is created with a pre-defined genesis.json timestamp: 47, - prevRandao: dataToBytes("0xff00000000000000000000000000000000000000000000000000000000000000"), + prevRandao: dataToBytes("0xff00000000000000000000000000000000000000000000000000000000000000", 32), suggestedFeeRecipient: "0xaa00000000000000000000000000000000000000", withdrawals, + fork: ForkName.capella, }; const finalizedBlockHash = "0xfe950635b1bd2a416ff6283b0bbd30176e1b1125ad06fa729da9f3f4c1c61710"; // 1. Prepare a payload const payloadId = await executionEngine.notifyForkchoiceUpdate( - ForkSeq.capella, + ForkName.capella, genesisBlockHash, //use finalizedBlockHash as safeBlockHash finalizedBlockHash, @@ -158,7 +159,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { if (!payloadId) throw Error("InvalidPayloadId"); // 2. Get the payload - const payload = await executionEngine.getPayload(ForkSeq.capella, payloadId); + const payload = await executionEngine.getPayload(ForkName.capella, payloadId); const blockHash = toHexString(payload.blockHash); const expectedBlockHash = "0x64707e5574d14103a7f583e702f09e68ca1eb334e8eb0632a4272efe54f2fc7c"; if (blockHash !== expectedBlockHash) { @@ -166,14 +167,14 @@ describe("executionEngine / ExecutionEngineHttp", function () { } // 3. Execute the payload - const payloadResult = await executionEngine.notifyNewPayload(ForkSeq.capella, payload); + const payloadResult = await executionEngine.notifyNewPayload(ForkName.capella, payload); if (payloadResult.status !== ExecutePayloadStatus.VALID) { throw Error("getPayload returned payload that notifyNewPayload deems invalid"); } // 4. Update the fork choice await executionEngine.notifyForkchoiceUpdate( - ForkSeq.capella, + ForkName.capella, bytesToData(payload.blockHash), genesisBlockHash, genesisBlockHash diff --git a/packages/beacon-node/test/unit/chain/prepareNextSlot.test.ts b/packages/beacon-node/test/unit/chain/prepareNextSlot.test.ts index d5aa0e632b6d..0336f2d19245 100644 --- a/packages/beacon-node/test/unit/chain/prepareNextSlot.test.ts +++ b/packages/beacon-node/test/unit/chain/prepareNextSlot.test.ts @@ -3,7 +3,7 @@ import sinon, {SinonStubbedInstance} from "sinon"; import {config} from "@lodestar/config/default"; import {ForkChoice, ProtoBlock} from "@lodestar/fork-choice"; import {WinstonLogger} from "@lodestar/utils"; -import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params"; +import {ForkName, SLOTS_PER_EPOCH} from "@lodestar/params"; import {IChainForkConfig} from "@lodestar/config"; import {BeaconChain, ChainEventEmitter} from "../../../src/chain/index.js"; import {IBeaconChain} from "../../../src/chain/interface.js"; @@ -30,7 +30,7 @@ describe("PrepareNextSlot scheduler", () => { let regenStub: SinonStubbedInstance & StateRegenerator; let loggerStub: SinonStubbedInstance & WinstonLogger; let beaconProposerCacheStub: SinonStubbedInstance & BeaconProposerCache; - let getForkSeqStub: SinonStubFn; + let getForkStub: SinonStubFn; let updateBuilderStatus: SinonStubFn; let executionEngineStub: SinonStubbedInstance & ExecutionEngineHttp; @@ -55,7 +55,7 @@ describe("PrepareNextSlot scheduler", () => { ((chainStub as unknown) as {beaconProposerCache: BeaconProposerCache})[ "beaconProposerCache" ] = beaconProposerCacheStub; - getForkSeqStub = sandbox.stub(config, "getForkSeq"); + getForkStub = sandbox.stub(config, "getForkName"); executionEngineStub = sandbox.createStubInstance(ExecutionEngineHttp) as SinonStubbedInstance & ExecutionEngineHttp; ((chainStub as unknown) as {executionEngine: IExecutionEngine}).executionEngine = executionEngineStub; @@ -68,13 +68,13 @@ describe("PrepareNextSlot scheduler", () => { }); it("pre bellatrix - should not run due to not last slot of epoch", async () => { - getForkSeqStub.returns(ForkSeq.phase0); + getForkStub.returns(ForkName.phase0); await scheduler.prepareForNextSlot(3); expect(chainStub.recomputeForkChoiceHead).not.to.be.called; }); it("pre bellatrix - should skip, headSlot is more than 1 epoch to prepare slot", async () => { - getForkSeqStub.returns(ForkSeq.phase0); + getForkStub.returns(ForkName.phase0); chainStub.recomputeForkChoiceHead.returns({slot: SLOTS_PER_EPOCH - 2} as ProtoBlock); await Promise.all([ scheduler.prepareForNextSlot(2 * SLOTS_PER_EPOCH - 1), @@ -85,7 +85,7 @@ describe("PrepareNextSlot scheduler", () => { }); it("pre bellatrix - should run regen.getBlockSlotState", async () => { - getForkSeqStub.returns(ForkSeq.phase0); + getForkStub.returns(ForkName.phase0); chainStub.recomputeForkChoiceHead.returns({slot: SLOTS_PER_EPOCH - 1} as ProtoBlock); regenStub.getBlockSlotState.resolves(); await Promise.all([ @@ -97,7 +97,7 @@ describe("PrepareNextSlot scheduler", () => { }); it("pre bellatrix - should handle regen.getBlockSlotState error", async () => { - getForkSeqStub.returns(ForkSeq.phase0); + getForkStub.returns(ForkName.phase0); chainStub.recomputeForkChoiceHead.returns({slot: SLOTS_PER_EPOCH - 1} as ProtoBlock); regenStub.getBlockSlotState.rejects("Unit test error"); expect(loggerStub.error).to.not.be.called; @@ -111,7 +111,7 @@ describe("PrepareNextSlot scheduler", () => { }); it("bellatrix - should skip, headSlot is more than 1 epoch to prepare slot", async () => { - getForkSeqStub.returns(ForkSeq.bellatrix); + getForkStub.returns(ForkName.bellatrix); chainStub.recomputeForkChoiceHead.returns({slot: SLOTS_PER_EPOCH - 2} as ProtoBlock); await Promise.all([ scheduler.prepareForNextSlot(2 * SLOTS_PER_EPOCH - 1), @@ -122,7 +122,7 @@ describe("PrepareNextSlot scheduler", () => { }); it("bellatrix - should skip, no block proposer", async () => { - getForkSeqStub.returns(ForkSeq.bellatrix); + getForkStub.returns(ForkName.bellatrix); chainStub.recomputeForkChoiceHead.returns({slot: SLOTS_PER_EPOCH - 3} as ProtoBlock); const state = generateCachedBellatrixState(); regenStub.getBlockSlotState.resolves(state); @@ -135,7 +135,7 @@ describe("PrepareNextSlot scheduler", () => { }); it("bellatrix - should prepare payload", async () => { - getForkSeqStub.returns(ForkSeq.bellatrix); + getForkStub.returns(ForkName.bellatrix); chainStub.recomputeForkChoiceHead.returns({slot: SLOTS_PER_EPOCH - 3} as ProtoBlock); forkChoiceStub.getJustifiedBlock.returns({} as ProtoBlock); forkChoiceStub.getFinalizedBlock.returns({} as ProtoBlock); diff --git a/packages/beacon-node/test/unit/executionEngine/http.test.ts b/packages/beacon-node/test/unit/executionEngine/http.test.ts index fcfde628e166..936c51a300ef 100644 --- a/packages/beacon-node/test/unit/executionEngine/http.test.ts +++ b/packages/beacon-node/test/unit/executionEngine/http.test.ts @@ -1,6 +1,6 @@ import {expect} from "chai"; import {fastify} from "fastify"; -import {ForkSeq} from "@lodestar/params"; +import {ForkName} from "@lodestar/params"; import { ExecutionEngineHttp, parseExecutionPayload, @@ -77,9 +77,9 @@ describe("ExecutionEngine / http", () => { }; returnValue = response; - const payload = await executionEngine.getPayload(ForkSeq.bellatrix, "0x0"); + const payload = await executionEngine.getPayload(ForkName.bellatrix, "0x0"); - expect(serializeExecutionPayload(ForkSeq.bellatrix, payload)).to.deep.equal( + expect(serializeExecutionPayload(ForkName.bellatrix, payload)).to.deep.equal( response.result, "Wrong returned payload" ); @@ -121,8 +121,8 @@ describe("ExecutionEngine / http", () => { }; const {status} = await executionEngine.notifyNewPayload( - ForkSeq.bellatrix, - parseExecutionPayload(ForkSeq.bellatrix, request.params[0]) + ForkName.bellatrix, + parseExecutionPayload(ForkName.bellatrix, request.params[0]) ); expect(status).to.equal("VALID", "Wrong returned execute payload result"); @@ -151,7 +151,7 @@ describe("ExecutionEngine / http", () => { }; await executionEngine.notifyForkchoiceUpdate( - ForkSeq.bellatrix, + ForkName.bellatrix, forkChoiceHeadData.headBlockHash, forkChoiceHeadData.safeBlockHash, forkChoiceHeadData.finalizedBlockHash @@ -169,7 +169,7 @@ describe("ExecutionEngine / http", () => { const response = {jsonrpc: "2.0", id: 67, error: {code: 5, message: "unknown payload"}}; returnValue = response; - await expect(executionEngine.getPayload(ForkSeq.bellatrix, request.params[0])).to.be.rejectedWith( + await expect(executionEngine.getPayload(ForkName.bellatrix, request.params[0])).to.be.rejectedWith( "JSON RPC error: unknown payload, engine_getPayload" ); }); diff --git a/packages/beacon-node/test/unit/executionEngine/httpRetry.test.ts b/packages/beacon-node/test/unit/executionEngine/httpRetry.test.ts index e31d644b2e59..b615908374b9 100644 --- a/packages/beacon-node/test/unit/executionEngine/httpRetry.test.ts +++ b/packages/beacon-node/test/unit/executionEngine/httpRetry.test.ts @@ -1,12 +1,11 @@ import {expect} from "chai"; import {fastify} from "fastify"; - +import {ForkName} from "@lodestar/params"; import {fromHexString} from "@chainsafe/ssz"; -import {ForkSeq} from "@lodestar/params"; import {ExecutionEngineHttp, defaultExecutionEngineHttpOpts} from "../../../src/execution/engine/http.js"; - import {bytesToData, numToQuantity} from "../../../src/eth1/provider/utils.js"; +import {PayloadAttributes} from "../../../src/execution/index.js"; describe("ExecutionEngine / http ", () => { const afterCallbacks: (() => Promise | void)[] = []; @@ -74,7 +73,7 @@ describe("ExecutionEngine / http ", () => { expect(errorResponsesBeforeSuccess).to.be.equal(2, "errorResponsesBeforeSuccess should be 2 before request"); try { await executionEngine.notifyForkchoiceUpdate( - ForkSeq.bellatrix, + ForkName.bellatrix, forkChoiceHeadData.headBlockHash, forkChoiceHeadData.safeBlockHash, forkChoiceHeadData.finalizedBlockHash @@ -97,10 +96,11 @@ describe("ExecutionEngine / http ", () => { safeBlockHash: "0xb084c10440f05f5a23a55d1d7ebcb1b3892935fb56f23cdc9a7f42c348eed174", finalizedBlockHash: "0xb084c10440f05f5a23a55d1d7ebcb1b3892935fb56f23cdc9a7f42c348eed174", }; - const payloadAttributes = { + const payloadAttributes: PayloadAttributes = { timestamp: 1647036763, prevRandao: fromHexString("0x0000000000000000000000000000000000000000000000000000000000000000"), suggestedFeeRecipient: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + fork: ForkName.bellatrix, }; const request = { @@ -129,7 +129,7 @@ describe("ExecutionEngine / http ", () => { "errorResponsesBeforeSuccess should not be zero before request" ); await executionEngine.notifyForkchoiceUpdate( - ForkSeq.bellatrix, + ForkName.bellatrix, forkChoiceHeadData.headBlockHash, forkChoiceHeadData.safeBlockHash, forkChoiceHeadData.finalizedBlockHash,