diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index 28924f959244..98f96b3d19bf 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -296,7 +296,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/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index 0ea0a152beff..80bccfe34b92 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -30,7 +30,13 @@ import {ForkName, ForkSeq} from "@lodestar/params"; import {toHex, sleep} from "@lodestar/utils"; import type {BeaconChain} from "../chain.js"; -import {PayloadId, IExecutionEngine, IExecutionBuilder, ForkExecution, PayloadAttributes} from "../../execution/index.js"; +import { + PayloadId, + IExecutionEngine, + IExecutionBuilder, + ForkExecution, + PayloadAttributes, +} 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"; @@ -168,20 +174,11 @@ export async function produceBlockBody( currentState as CachedBeaconStateBellatrix, proposerPubKey ); - (blockBody as allForks.BlindedBeaconBlockBody).executionPayloadHeader = executionPayloadHeader; // Capella and later forks have withdrawalRoot on their ExecutionPayloadHeader // TODO Capella: Remove this. It will come from the execution client. if (fork === ForkName.capella || fork === ForkName.eip4844) { - (blockBody as capella.BlindedBeaconBlockBody).executionPayloadHeader.withdrawalsRoot = Uint8Array.from( - Buffer.alloc(32, 0) - ); - } - - if (fork === ForkName.eip4844) { - // Empty blobs for now - (blockBody as eip4844.BeaconBlockBody).blobKzgCommitments = []; - blobs = {blobs: [], blockHash: toHex(executionPayloadHeader.blockHash)}; + throw Error("Builder blinded blocks not supported for capella and eip4844"); } } @@ -194,7 +191,6 @@ export async function produceBlockBody( // https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/validator.md#constructing-the-beaconblockbody const prepareRes = await prepareExecutionPayload( - forkInfo.seq, this, fork, safeBlockHash, @@ -205,7 +201,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; @@ -266,7 +262,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 @@ -358,11 +354,10 @@ export async function prepareExecutionPayload( timestamp, prevRandao, suggestedFeeRecipient, - } - - if (fork >= ForkSeq.capella) { - attributes.withdrawals = getExpectedWithdrawals(state as CachedBeaconStateCapella).withdrawals} + }; + if (ForkSeq[fork] >= ForkSeq.capella) { + attributes.withdrawals = getExpectedWithdrawals(state as CachedBeaconStateCapella).withdrawals; } payloadId = await chain.executionEngine.notifyForkchoiceUpdate( @@ -370,7 +365,7 @@ export async function prepareExecutionPayload( toHex(parentHash), safeBlockHash, finalizedBlockHash, - attributes, + attributes ); } @@ -399,7 +394,7 @@ async function prepareExecutionPayloadHeader( if (!chain.executionBuilder) { throw Error("executionBuilder required"); } - if (ForkName[ 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/http.ts b/packages/beacon-node/src/execution/engine/http.ts index 02bde4a01dd1..0e03858b5c9f 100644 --- a/packages/beacon-node/src/execution/engine/http.ts +++ b/packages/beacon-node/src/execution/engine/http.ts @@ -234,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, @@ -242,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), diff --git a/packages/beacon-node/src/execution/engine/interface.ts b/packages/beacon-node/src/execution/engine/interface.ts index ef2c50ec32d6..7158ebe71bf6 100644 --- a/packages/beacon-node/src/execution/engine/interface.ts +++ b/packages/beacon-node/src/execution/engine/interface.ts @@ -1,7 +1,6 @@ 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"; @@ -90,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 @@ -105,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, @@ -119,7 +118,7 @@ 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, diff --git a/packages/beacon-node/src/execution/engine/mock.ts b/packages/beacon-node/src/execution/engine/mock.ts index 7d6e98640675..197e91a9e025 100644 --- a/packages/beacon-node/src/execution/engine/mock.ts +++ b/packages/beacon-node/src/execution/engine/mock.ts @@ -1,7 +1,7 @@ import crypto from "node:crypto"; import {allForks, bellatrix, RootHex, ssz} from "@lodestar/types"; import {fromHex, toHex} from "@lodestar/utils"; -import {ForkSeq} from "@lodestar/params"; +import {ForkName} from "@lodestar/params"; import {ZERO_HASH_HEX} from "../../constants/index.js"; import { ExecutePayloadStatus, @@ -58,7 +58,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); @@ -109,7 +112,7 @@ export class ExecutionEngineMock implements IExecutionEngine { * `engine_forkchoiceUpdatedV1` */ async notifyForkchoiceUpdate( - _seq: ForkSeq, + _fork: ForkName, headBlockHash: RootHex, safeBlockHash: RootHex, finalizedBlockHash: RootHex, @@ -216,7 +219,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); diff --git a/packages/beacon-node/test/sim/merge-interop.test.ts b/packages/beacon-node/test/sim/merge-interop.test.ts index e0c680916918..acfde8b4f124 100644 --- a/packages/beacon-node/test/sim/merge-interop.test.ts +++ b/packages/beacon-node/test/sim/merge-interop.test.ts @@ -121,7 +121,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { 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, }; @@ -129,7 +129,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { const finalizedBlockHash = "0x0000000000000000000000000000000000000000000000000000000000000000"; const payloadId = await executionEngine.notifyForkchoiceUpdate( - ForkSeq.bellatrix, + ForkName.bellatrix, genesisBlockHash, //use finalizedBlockHash as safeBlockHash finalizedBlockHash, @@ -144,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"); @@ -157,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"); } @@ -169,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/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 b84e11d5aeeb..b615908374b9 100644 --- a/packages/beacon-node/test/unit/executionEngine/httpRetry.test.ts +++ b/packages/beacon-node/test/unit/executionEngine/httpRetry.test.ts @@ -2,10 +2,8 @@ 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"; @@ -75,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 @@ -131,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, diff --git a/packages/types/src/eip4844/sszTypes.ts b/packages/types/src/eip4844/sszTypes.ts index 0ae443890553..569ac7556591 100644 --- a/packages/types/src/eip4844/sszTypes.ts +++ b/packages/types/src/eip4844/sszTypes.ts @@ -103,6 +103,15 @@ export const ExecutionPayloadHeader = new ExecutionPayloadHeaderEip4844Type( {typeName: "ExecutionPayloadHeader", jsonCase: "eth2"} ); +export class BlindedExecutionPayloadEip4844Type extends ContainerType {} +export const BlindedExecutionPayload = new BlindedExecutionPayloadEip4844Type( + { + ...capellaSsz.BlindedExecutionPayload.fields, + excessDataGas: ExcessDataGas, // New in EIP-4844 + }, + {typeName: "BlindedExecutionPayload", jsonCase: "eth2"} +); + // We have to preserve Fields ordering while changing the type of ExecutionPayload export class BeaconBlockBodyEip4844Type extends ContainerType {} export const BeaconBlockBody = new BeaconBlockBodyEip4844Type(