diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts index e37442bfde8f..ab20af511def 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts @@ -304,6 +304,7 @@ export async function verifyBlockExecutionPayload( const parentBlockRoot = ForkSeq[fork] >= ForkSeq.deneb ? block.message.parentRoot : undefined; const executionRequests = ForkSeq[fork] >= ForkSeq.electra ? (block.message.body as electra.BeaconBlockBody).executionRequests : undefined; + const targetBlobsPerBlock = ForkSeq[fork] >= ForkSeq.electra ? 0 : undefined; const logCtx = {slot: block.message.slot, executionBlock: executionPayloadEnabled.blockNumber}; chain.logger.debug("Call engine api newPayload", logCtx); @@ -312,7 +313,8 @@ export async function verifyBlockExecutionPayload( executionPayloadEnabled, versionedHashes, parentBlockRoot, - executionRequests + executionRequests, + targetBlobsPerBlock ); chain.logger.debug("Receive engine api newPayload result", {...logCtx, status: execResult.status}); diff --git a/packages/beacon-node/src/chain/options.ts b/packages/beacon-node/src/chain/options.ts index f87fffd7b363..c7a8ba779494 100644 --- a/packages/beacon-node/src/chain/options.ts +++ b/packages/beacon-node/src/chain/options.ts @@ -41,6 +41,8 @@ export type IChainOptions = BlockProcessOpts & archiveBlobEpochs?: number; nHistoricalStates?: boolean; nHistoricalStatesFileDataStore?: boolean; + targetBlobsPerBlock?: number; + maxBlobsPerBlock?: number; }; export type BlockProcessOpts = { @@ -119,4 +121,6 @@ export const defaultChainOptions: IChainOptions = { nHistoricalStatesFileDataStore: false, maxBlockStates: DEFAULT_MAX_BLOCK_STATES, maxCPStateEpochsInMemory: DEFAULT_MAX_CP_STATE_EPOCHS_IN_MEMORY, + targetBlobsPerBlock: 4, // This is arbitrary as we have not agreed on an appropriate value yet + maxBlobsPerBlock: 8, // This is arbitrary as we have not agreed on an appropriate value yet }; diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index d26703050070..500f8438bb51 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -40,6 +40,7 @@ import {numToQuantity} from "../../eth1/provider/utils.js"; import {IExecutionBuilder, IExecutionEngine, PayloadAttributes, PayloadId} from "../../execution/index.js"; import type {BeaconChain} from "../chain.js"; import {CommonBlockBody} from "../interface.js"; +import {IChainOptions} from "../options.js"; import {validateBlobsAndKzgCommitments} from "./validateBlobsAndKzgCommitments.js"; // Time to provide the EL to generate a payload from new payload id @@ -375,6 +376,7 @@ export async function prepareExecutionPayload( eth1: IEth1ForBlockProduction; executionEngine: IExecutionEngine; config: ChainForkConfig; + opts: IChainOptions; }, logger: Logger, fork: ForkExecution, @@ -516,6 +518,7 @@ export async function getPayloadAttributesForSSE( chain: { eth1: IEth1ForBlockProduction; config: ChainForkConfig; + opts: IChainOptions; }, { prepareState, @@ -553,6 +556,7 @@ function preparePayloadAttributes( fork: ForkExecution, chain: { config: ChainForkConfig; + opts: IChainOptions; }, { prepareState, @@ -586,6 +590,13 @@ function preparePayloadAttributes( (payloadAttributes as deneb.SSEPayloadAttributes["payloadAttributes"]).parentBeaconBlockRoot = parentBlockRoot; } + if (ForkSeq[fork] >= ForkSeq.electra) { + (payloadAttributes as electra.SSEPayloadAttributes["payloadAttributes"]).targetBlobsPerBlock = + chain.opts.targetBlobsPerBlock ?? 4; + (payloadAttributes as electra.SSEPayloadAttributes["payloadAttributes"]).maxBlobsPerBlock = + chain.opts.maxBlobsPerBlock ?? 8; + } + return payloadAttributes; } diff --git a/packages/beacon-node/src/execution/engine/http.ts b/packages/beacon-node/src/execution/engine/http.ts index ea064d2fe816..50cfb045e3d2 100644 --- a/packages/beacon-node/src/execution/engine/http.ts +++ b/packages/beacon-node/src/execution/engine/http.ts @@ -201,7 +201,8 @@ export class ExecutionEngineHttp implements IExecutionEngine { executionPayload: ExecutionPayload, versionedHashes?: VersionedHashes, parentBlockRoot?: Root, - executionRequests?: ExecutionRequests + executionRequests?: ExecutionRequests, + maxBlobsPerBlock?: number ): Promise { const method = ForkSeq[fork] >= ForkSeq.electra @@ -230,7 +231,11 @@ export class ExecutionEngineHttp implements IExecutionEngine { if (executionRequests === undefined) { throw Error(`executionRequests required in notifyNewPayload for fork=${fork}`); } + if (maxBlobsPerBlock === undefined) { + throw Error(`maxBlobsPerBlock required in notifyNewPayload for fork=${fork}`); + } const serializedExecutionRequests = serializeExecutionRequests(executionRequests); + engineRequest = { method: "engine_newPayloadV4", params: [ @@ -238,6 +243,7 @@ export class ExecutionEngineHttp implements IExecutionEngine { serializedVersionedHashes, parentBeaconBlockRoot, serializedExecutionRequests, + numToQuantity(maxBlobsPerBlock), ], methodOpts: notifyNewPayloadOpts, }; @@ -341,11 +347,13 @@ export class ExecutionEngineHttp implements IExecutionEngine { // Once on capella, should this need to be permanently switched to v2 when payload attrs // not provided const method = - ForkSeq[fork] >= ForkSeq.deneb - ? "engine_forkchoiceUpdatedV3" - : ForkSeq[fork] >= ForkSeq.capella - ? "engine_forkchoiceUpdatedV2" - : "engine_forkchoiceUpdatedV1"; + ForkSeq[fork] >= ForkSeq.electra + ? "engine_forkchoiceUpdatedV4" + : ForkSeq[fork] >= ForkSeq.deneb + ? "engine_forkchoiceUpdatedV3" + : ForkSeq[fork] >= ForkSeq.capella + ? "engine_forkchoiceUpdatedV2" + : "engine_forkchoiceUpdatedV1"; const payloadAttributesRpc = payloadAttributes ? serializePayloadAttributes(payloadAttributes) : undefined; // If we are just fcUing and not asking execution for payload, retry is not required // and we can move on, as the next fcU will be issued soon on the new slot diff --git a/packages/beacon-node/src/execution/engine/interface.ts b/packages/beacon-node/src/execution/engine/interface.ts index c32cc1bc7215..b6ce0394fa78 100644 --- a/packages/beacon-node/src/execution/engine/interface.ts +++ b/packages/beacon-node/src/execution/engine/interface.ts @@ -88,6 +88,8 @@ export type PayloadAttributes = { suggestedFeeRecipient: string; withdrawals?: capella.Withdrawal[]; parentBeaconBlockRoot?: Uint8Array; + targetBlobsPerBlock?: number; + maxBlobsperBlock?: number; }; export type BlobsBundle = { @@ -135,7 +137,8 @@ export interface IExecutionEngine { executionPayload: ExecutionPayload, versionedHashes?: VersionedHashes, parentBeaconBlockRoot?: Root, - executionRequests?: ExecutionRequests + executionRequests?: ExecutionRequests, + targetBlobsPerBlock?: number ): Promise; /** diff --git a/packages/beacon-node/src/execution/engine/mock.ts b/packages/beacon-node/src/execution/engine/mock.ts index 8a84d2b0148e..dc2945cf1255 100644 --- a/packages/beacon-node/src/execution/engine/mock.ts +++ b/packages/beacon-node/src/execution/engine/mock.ts @@ -92,6 +92,7 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend { engine_forkchoiceUpdatedV1: this.notifyForkchoiceUpdate.bind(this), engine_forkchoiceUpdatedV2: this.notifyForkchoiceUpdate.bind(this), engine_forkchoiceUpdatedV3: this.notifyForkchoiceUpdate.bind(this), + engine_forkchoiceUpdatedV4: this.notifyForkchoiceUpdate.bind(this), engine_getPayloadV1: this.getPayload.bind(this), engine_getPayloadV2: this.getPayload.bind(this), engine_getPayloadV3: this.getPayload.bind(this), diff --git a/packages/beacon-node/src/execution/engine/types.ts b/packages/beacon-node/src/execution/engine/types.ts index f35a63aa3d96..497dbef58ce2 100644 --- a/packages/beacon-node/src/execution/engine/types.ts +++ b/packages/beacon-node/src/execution/engine/types.ts @@ -27,7 +27,7 @@ export type EngineApiRpcParamTypes = { engine_newPayloadV1: [ExecutionPayloadRpc]; engine_newPayloadV2: [ExecutionPayloadRpc]; engine_newPayloadV3: [ExecutionPayloadRpc, VersionedHashesRpc, DATA]; - engine_newPayloadV4: [ExecutionPayloadRpc, VersionedHashesRpc, DATA, ExecutionRequestsRpc]; + engine_newPayloadV4: [ExecutionPayloadRpc, VersionedHashesRpc, DATA, ExecutionRequestsRpc, QUANTITY]; /** * 1. Object - Payload validity status with respect to the consensus rules: * - blockHash: DATA, 32 Bytes - block hash value of the payload @@ -45,6 +45,10 @@ export type EngineApiRpcParamTypes = { forkChoiceData: {headBlockHash: DATA; safeBlockHash: DATA; finalizedBlockHash: DATA}, payloadAttributes?: PayloadAttributesRpc, ]; + engine_forkchoiceUpdatedV4: [ + forkChoiceData: {headBlockHash: DATA; safeBlockHash: DATA; finalizedBlockHash: DATA}, + payloadAttributes?: PayloadAttributesRpc, + ]; /** * 1. payloadId: QUANTITY, 64 Bits - Identifier of the payload building process */ @@ -99,6 +103,10 @@ export type EngineApiRpcReturnTypes = { payloadStatus: PayloadStatus; payloadId: QUANTITY | null; }; + engine_forkchoiceUpdatedV4: { + payloadStatus: PayloadStatus; + payloadId: QUANTITY | null; + }; /** * payloadId | Error: QUANTITY, 64 Bits - Identifier of the payload building process */ @@ -155,6 +163,7 @@ export type ExecutionPayloadRpc = { blobGasUsed?: QUANTITY; // DENEB excessBlobGas?: QUANTITY; // DENEB parentBeaconBlockRoot?: QUANTITY; // DENEB + targetBlobsPerBlock?: QUANTITY; // ELECTRA }; export type WithdrawalRpc = { @@ -193,6 +202,9 @@ export type PayloadAttributesRpc = { withdrawals?: WithdrawalRpc[]; /** DATA, 32 Bytes - value for the parentBeaconBlockRoot to be used for building block */ parentBeaconBlockRoot?: DATA; + // TODO + targetBlobsPerBlock?: QUANTITY; + maxBlobsPerBlock?: QUANTITY; }; export type ClientVersionRpc = { @@ -348,6 +360,8 @@ export function serializePayloadAttributes(data: PayloadAttributes): PayloadAttr suggestedFeeRecipient: data.suggestedFeeRecipient, withdrawals: data.withdrawals?.map(serializeWithdrawal), parentBeaconBlockRoot: data.parentBeaconBlockRoot ? bytesToData(data.parentBeaconBlockRoot) : undefined, + targetBlobsPerBlock: data.targetBlobsPerBlock ? numToQuantity(data.targetBlobsPerBlock) : undefined, + maxBlobsPerBlock: data.maxBlobsperBlock ? numToQuantity(data.maxBlobsperBlock) : undefined, }; } @@ -364,6 +378,8 @@ export function deserializePayloadAttributes(data: PayloadAttributesRpc): Payloa suggestedFeeRecipient: data.suggestedFeeRecipient, withdrawals: data.withdrawals?.map((withdrawal) => deserializeWithdrawal(withdrawal)), parentBeaconBlockRoot: data.parentBeaconBlockRoot ? dataToBytes(data.parentBeaconBlockRoot, 32) : undefined, + targetBlobsPerBlock: data.targetBlobsPerBlock ? quantityToNum(data.targetBlobsPerBlock) : undefined, + maxBlobsperBlock: data.maxBlobsPerBlock ? quantityToNum(data.maxBlobsPerBlock) : undefined, }; } diff --git a/packages/cli/src/options/beaconNodeOptions/chain.ts b/packages/cli/src/options/beaconNodeOptions/chain.ts index 21c05545c43d..057bcb477673 100644 --- a/packages/cli/src/options/beaconNodeOptions/chain.ts +++ b/packages/cli/src/options/beaconNodeOptions/chain.ts @@ -33,6 +33,8 @@ export type ChainArgs = { "chain.nHistoricalStatesFileDataStore"?: boolean; "chain.maxBlockStates"?: number; "chain.maxCPStateEpochsInMemory"?: number; + "chain.targetBlobsPerBlock"?: number; + "chain.maxBlobsPerBlock"?: number; }; export function parseArgs(args: ChainArgs): IBeaconNodeOptions["chain"] { @@ -68,6 +70,8 @@ export function parseArgs(args: ChainArgs): IBeaconNodeOptions["chain"] { args["chain.nHistoricalStatesFileDataStore"] ?? defaultOptions.chain.nHistoricalStatesFileDataStore, maxBlockStates: args["chain.maxBlockStates"] ?? defaultOptions.chain.maxBlockStates, maxCPStateEpochsInMemory: args["chain.maxCPStateEpochsInMemory"] ?? defaultOptions.chain.maxCPStateEpochsInMemory, + targetBlobsPerBlock: args["chain.targetBlobsPerBlock"] ?? defaultOptions.chain.targetBlobsPerBlock, + maxBlobsPerBlock: args["chain.maxBlobsPerBlock"] ?? defaultOptions.chain.maxBlobsPerBlock, }; } @@ -284,4 +288,20 @@ Will double processing times. Use only for debugging purposes.", default: defaultOptions.chain.maxCPStateEpochsInMemory, group: "chain", }, + + "chain.targetBlobsPerBlock": { + hidden: true, + description: "Target blobs per block per EIP-7742", + type: "number", + default: defaultOptions.chain.targetBlobsPerBlock, + group: "chain", + }, + + "chain.maxBlobsPerBlock": { + hidden: true, + description: "Max blobs per block per EIP-7742", + type: "number", + default: defaultOptions.chain.maxBlobsPerBlock, + group: "chain", + }, }; diff --git a/packages/types/src/electra/sszTypes.ts b/packages/types/src/electra/sszTypes.ts index f6b6c745803e..ece45a2e9322 100644 --- a/packages/types/src/electra/sszTypes.ts +++ b/packages/types/src/electra/sszTypes.ts @@ -392,8 +392,9 @@ export const LightClientStore = new ContainerType( // PayloadAttributes primarily for SSE event export const PayloadAttributes = new ContainerType( { - ...capellaSsz.PayloadAttributes.fields, - parentBeaconBlockRoot: Root, + ...denebSsz.PayloadAttributes.fields, + targetBlobsPerBlock: UintNum64, + maxBlobsPerBlock: UintNum64, }, {typeName: "PayloadAttributes", jsonCase: "eth2"} );