diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts index e37442bfde8f..0dff8a69f09f 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts @@ -8,7 +8,7 @@ import { ProtoBlock, assertValidTerminalPowBlock, } from "@lodestar/fork-choice"; -import {ForkSeq, SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY} from "@lodestar/params"; +import {ForkSeq, MAX_BLOBS_PER_BLOCK, SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY} from "@lodestar/params"; import { CachedBeaconStateAllForks, isExecutionBlockBodyType, @@ -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 ? Math.floor(MAX_BLOBS_PER_BLOCK / 2) : 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/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index d26703050070..499de58ca462 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -1,5 +1,5 @@ import {ChainForkConfig} from "@lodestar/config"; -import {ForkExecution, ForkSeq, isForkExecution} from "@lodestar/params"; +import {ForkExecution, ForkSeq, MAX_BLOBS_PER_BLOCK, isForkExecution} from "@lodestar/params"; import { CachedBeaconStateAllForks, CachedBeaconStateBellatrix, @@ -586,6 +586,13 @@ function preparePayloadAttributes( (payloadAttributes as deneb.SSEPayloadAttributes["payloadAttributes"]).parentBeaconBlockRoot = parentBlockRoot; } + if (ForkSeq[fork] >= ForkSeq.electra) { + (payloadAttributes as electra.SSEPayloadAttributes["payloadAttributes"]).targetBlobsPerBlock = Math.floor( + MAX_BLOBS_PER_BLOCK / 2 + ); + (payloadAttributes as electra.SSEPayloadAttributes["payloadAttributes"]).maxBlobsPerBlock = MAX_BLOBS_PER_BLOCK; + } + 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..70c45bca3b2d 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..02d2184f9b55 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,10 @@ export type PayloadAttributesRpc = { withdrawals?: WithdrawalRpc[]; /** DATA, 32 Bytes - value for the parentBeaconBlockRoot to be used for building block */ parentBeaconBlockRoot?: DATA; + /** QUANTITY, 64 Bits - Average number of blobs to include per payload. */ + targetBlobsPerBlock?: QUANTITY; + /** QUANTITY, 64 Bits - Maximum number of blobs allowed per payload. */ + maxBlobsPerBlock?: QUANTITY; }; export type ClientVersionRpc = { @@ -348,6 +361,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 +379,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/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"} );