diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 9d34b6399a5a..ea2b9870eea1 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -855,6 +855,24 @@ export class BeaconChain implements IBeaconChain { return null; } + async getSerializedExecutionPayloadEnvelope(blockSlot: Slot, blockRootHex: string): Promise { + const payloadInput = this.seenPayloadEnvelopeInputCache.get(blockRootHex); + if (payloadInput?.hasPayloadEnvelope()) { + const envelope = payloadInput.getPayloadEnvelope(); + const serialized = this.serializedCache.get(envelope); + if (serialized) { + return serialized; + } + return ssz.gloas.SignedExecutionPayloadEnvelope.serialize(envelope); + } + + return ( + (await this.db.executionPayloadEnvelope.getBinary(fromHex(blockRootHex))) ?? + (await this.db.executionPayloadEnvelopeArchive.getBinary(blockSlot)) ?? + null + ); + } + async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise { const blockInput = this.seenBlockInputCache.get(blockRootHex); if (blockInput) { diff --git a/packages/beacon-node/src/chain/interface.ts b/packages/beacon-node/src/chain/interface.ts index 25b8cb077951..d6d31e6a2a4e 100644 --- a/packages/beacon-node/src/chain/interface.ts +++ b/packages/beacon-node/src/chain/interface.ts @@ -223,6 +223,7 @@ export interface IBeaconChain { blockRootHex: string, indices: number[] ): Promise<(Uint8Array | undefined)[]>; + getSerializedExecutionPayloadEnvelope(blockSlot: Slot, blockRootHex: string): Promise; produceCommonBlockBody(blockAttributes: BlockAttributes): Promise; produceBlock(blockAttributes: BlockAttributes & {commonBlockBodyPromise: Promise}): Promise<{ diff --git a/packages/beacon-node/src/network/interface.ts b/packages/beacon-node/src/network/interface.ts index 25abc19d6c51..0c748e3eef70 100644 --- a/packages/beacon-node/src/network/interface.ts +++ b/packages/beacon-node/src/network/interface.ts @@ -38,7 +38,12 @@ import { import {BlockInputSource} from "../chain/blocks/blockInput/types.js"; import {CustodyConfig} from "../util/dataColumns.js"; import {PeerIdStr} from "../util/peerId.js"; -import {BeaconBlocksByRootRequest, BlobSidecarsByRootRequest, DataColumnSidecarsByRootRequest} from "../util/types.js"; +import { + BeaconBlocksByRootRequest, + BlobSidecarsByRootRequest, + DataColumnSidecarsByRootRequest, + ExecutionPayloadEnvelopesByRootRequest, +} from "../util/types.js"; import {INetworkCorePublic} from "./core/types.js"; import {INetworkEventBus} from "./events.js"; import {GossipType} from "./gossip/interface.js"; @@ -82,6 +87,14 @@ export interface INetwork extends INetworkCorePublic { peerId: PeerIdStr, request: DataColumnSidecarsByRootRequest ): Promise; + sendExecutionPayloadEnvelopesByRange( + peerId: PeerIdStr, + request: gloas.ExecutionPayloadEnvelopesByRangeRequest + ): Promise; + sendExecutionPayloadEnvelopesByRoot( + peerId: PeerIdStr, + request: ExecutionPayloadEnvelopesByRootRequest + ): Promise; // Gossip publishBeaconBlock(signedBlock: SignedBeaconBlock): Promise; diff --git a/packages/beacon-node/src/network/network.ts b/packages/beacon-node/src/network/network.ts index 9c208b553ae9..913aa3e625ca 100644 --- a/packages/beacon-node/src/network/network.ts +++ b/packages/beacon-node/src/network/network.ts @@ -40,7 +40,12 @@ import {IClock} from "../util/clock.js"; import {CustodyConfig} from "../util/dataColumns.js"; import {PeerIdStr, peerIdToString} from "../util/peerId.js"; import {promiseAllMaybeAsync} from "../util/promises.js"; -import {BeaconBlocksByRootRequest, BlobSidecarsByRootRequest, DataColumnSidecarsByRootRequest} from "../util/types.js"; +import { + BeaconBlocksByRootRequest, + BlobSidecarsByRootRequest, + DataColumnSidecarsByRootRequest, + ExecutionPayloadEnvelopesByRootRequest, +} from "../util/types.js"; import {INetworkCore, NetworkCore, WorkerNetworkCore} from "./core/index.js"; import {INetworkEventBus, NetworkEvent, NetworkEventBus, NetworkEventData} from "./events.js"; import {getActiveForkBoundaries} from "./forks.js"; @@ -636,6 +641,29 @@ export class Network implements INetwork { ); } + async sendExecutionPayloadEnvelopesByRange( + peerId: PeerIdStr, + request: gloas.ExecutionPayloadEnvelopesByRangeRequest + ): Promise { + return collectMaxResponseTyped( + this.sendReqRespRequest(peerId, ReqRespMethod.ExecutionPayloadEnvelopesByRange, [Version.V1], request), + request.count, + responseSszTypeByMethod[ReqRespMethod.ExecutionPayloadEnvelopesByRange] + ); + } + + async sendExecutionPayloadEnvelopesByRoot( + peerId: PeerIdStr, + request: ExecutionPayloadEnvelopesByRootRequest + ): Promise { + return collectMaxResponseTyped( + this.sendReqRespRequest(peerId, ReqRespMethod.ExecutionPayloadEnvelopesByRoot, [Version.V1], request), + request.length, + responseSszTypeByMethod[ReqRespMethod.ExecutionPayloadEnvelopesByRoot], + this.chain.serializedCache + ); + } + private sendReqRespRequest( peerId: PeerIdStr, method: ReqRespMethod, diff --git a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts index 9a3d5dc83204..5aba7277cbac 100644 --- a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts +++ b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts @@ -297,6 +297,19 @@ export class ReqRespBeaconNode extends ReqResp { ); } + if (ForkSeq[fork] >= ForkSeq.gloas) { + protocolsAtFork.push( + [ + protocols.ExecutionPayloadEnvelopesByRoot(fork, this.config), + this.getHandler(ReqRespMethod.ExecutionPayloadEnvelopesByRoot), + ], + [ + protocols.ExecutionPayloadEnvelopesByRange(fork, this.config), + this.getHandler(ReqRespMethod.ExecutionPayloadEnvelopesByRange), + ] + ); + } + return protocolsAtFork; } diff --git a/packages/beacon-node/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts b/packages/beacon-node/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts new file mode 100644 index 000000000000..89137bb3c096 --- /dev/null +++ b/packages/beacon-node/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts @@ -0,0 +1,94 @@ +import {ChainConfig} from "@lodestar/config"; +import {PayloadStatus} from "@lodestar/fork-choice"; +import {GENESIS_SLOT} from "@lodestar/params"; +import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp"; +import {computeEpochAtSlot} from "@lodestar/state-transition"; +import {gloas} from "@lodestar/types"; +import {IBeaconChain} from "../../../chain/index.js"; +import {IBeaconDb} from "../../../db/index.js"; + +export async function* onExecutionPayloadEnvelopesByRange( + request: gloas.ExecutionPayloadEnvelopesByRangeRequest, + chain: IBeaconChain, + db: IBeaconDb +): AsyncIterable { + const {startSlot, count} = validateExecutionPayloadEnvelopesByRangeRequest(chain.config, request); + const endSlot = startSlot + count; + + if (startSlot < chain.earliestAvailableSlot) { + return; + } + + const finalized = db.executionPayloadEnvelopeArchive; + const finalizedSlot = chain.forkChoice.getFinalizedCheckpointSlot(); + + // Finalized range of envelopes + if (startSlot <= finalizedSlot) { + for await (const {key, value: envelopeBytes} of finalized.binaryEntriesStream({ + gte: startSlot, + lt: endSlot, + })) { + const slot = finalized.decodeKey(key); + yield { + data: envelopeBytes, + boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(slot)), + }; + } + } + + // Non-finalized range of envelopes + if (endSlot > finalizedSlot) { + const headBlock = chain.forkChoice.getHead(); + const headRoot = headBlock.blockRoot; + const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus); + + // Iterate head chain with ascending block numbers + for (let i = headChain.length - 1; i >= 0; i--) { + const block = headChain[i]; + + if (block.slot >= startSlot && block.slot < endSlot) { + // Skip EMPTY blocks + if (block.payloadStatus !== PayloadStatus.FULL) { + continue; + } + + const envelopeBytes = await chain.getSerializedExecutionPayloadEnvelope(block.slot, block.blockRoot); + if (!envelopeBytes) { + throw new ResponseError( + RespStatus.SERVER_ERROR, + `No envelope for root ${block.blockRoot} slot ${block.slot}, startSlot=${startSlot} endSlot=${endSlot} finalizedSlot=${finalizedSlot}` + ); + } + + yield { + data: envelopeBytes, + boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(block.slot)), + }; + } else if (block.slot >= endSlot) { + break; + } + } + } +} + +export function validateExecutionPayloadEnvelopesByRangeRequest( + config: ChainConfig, + request: gloas.ExecutionPayloadEnvelopesByRangeRequest +): gloas.ExecutionPayloadEnvelopesByRangeRequest { + const {startSlot} = request; + let {count} = request; + + if (count < 1) { + throw new ResponseError(RespStatus.INVALID_REQUEST, "count < 1"); + } + // TODO: validate against MIN_EPOCHS_FOR_BLOCK_REQUESTS + if (startSlot < GENESIS_SLOT) { + throw new ResponseError(RespStatus.INVALID_REQUEST, "startSlot < genesis"); + } + + if (count > config.MAX_REQUEST_BLOCKS_DENEB) { + count = config.MAX_REQUEST_BLOCKS_DENEB; + } + + return {startSlot, count}; +} diff --git a/packages/beacon-node/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts b/packages/beacon-node/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts new file mode 100644 index 000000000000..87decc1a1946 --- /dev/null +++ b/packages/beacon-node/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts @@ -0,0 +1,43 @@ +import {ResponseOutgoing} from "@lodestar/reqresp"; +import {computeEpochAtSlot} from "@lodestar/state-transition"; +import {toRootHex} from "@lodestar/utils"; +import {IBeaconChain} from "../../../chain/index.js"; +import {IBeaconDb} from "../../../db/index.js"; +import {ExecutionPayloadEnvelopesByRootRequest} from "../../../util/types.js"; + +export async function* onExecutionPayloadEnvelopesByRoot( + requestBody: ExecutionPayloadEnvelopesByRootRequest, + chain: IBeaconChain, + db: IBeaconDb +): AsyncIterable { + // Spec: [max(GLOAS_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch] + const currentEpoch = chain.clock.currentEpoch; + const minimumRequestEpoch = Math.max( + currentEpoch - chain.config.MIN_EPOCHS_FOR_BLOCK_REQUESTS, + chain.config.GLOAS_FORK_EPOCH + ); + + for (const root of requestBody) { + const rootHex = toRootHex(root); + const block = chain.forkChoice.getBlockHexDefaultStatus(rootHex); + // If the block is not in fork choice, it may be finalized. Attempt to find its slot in block archive + const slot = block ? block.slot : await db.blockArchive.getSlotByRoot(root); + + if (slot === null) { + continue; + } + + const requestedEpoch = computeEpochAtSlot(slot); + if (requestedEpoch < minimumRequestEpoch) { + continue; + } + + const envelopeBytes = await chain.getSerializedExecutionPayloadEnvelope(slot, rootHex); + if (envelopeBytes) { + yield { + data: envelopeBytes, + boundary: chain.config.getForkBoundaryAtEpoch(requestedEpoch), + }; + } + } +} diff --git a/packages/beacon-node/src/network/reqresp/handlers/index.ts b/packages/beacon-node/src/network/reqresp/handlers/index.ts index 9777bbf23964..c5c6109741ea 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/index.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/index.ts @@ -6,6 +6,7 @@ import { BeaconBlocksByRootRequestType, BlobSidecarsByRootRequestType, DataColumnSidecarsByRootRequestType, + ExecutionPayloadEnvelopesByRootRequestType, } from "../../../util/types.js"; import {GetReqRespHandlerFn, ReqRespMethod} from "../types.js"; import {onBeaconBlocksByRange} from "./beaconBlocksByRange.js"; @@ -14,6 +15,8 @@ import {onBlobSidecarsByRange} from "./blobSidecarsByRange.js"; import {onBlobSidecarsByRoot} from "./blobSidecarsByRoot.js"; import {onDataColumnSidecarsByRange} from "./dataColumnSidecarsByRange.js"; import {onDataColumnSidecarsByRoot} from "./dataColumnSidecarsByRoot.js"; +import {onExecutionPayloadEnvelopesByRange} from "./executionPayloadEnvelopesByRange.js"; +import {onExecutionPayloadEnvelopesByRoot} from "./executionPayloadEnvelopesByRoot.js"; import {onLightClientBootstrap} from "./lightClientBootstrap.js"; import {onLightClientFinalityUpdate} from "./lightClientFinalityUpdate.js"; import {onLightClientOptimisticUpdate} from "./lightClientOptimisticUpdate.js"; @@ -62,6 +65,15 @@ export function getReqRespHandlers({db, chain}: {db: IBeaconDb; chain: IBeaconCh return onDataColumnSidecarsByRoot(body, chain, db, peerId, peerClient); }, + [ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: (req) => { + const body = ExecutionPayloadEnvelopesByRootRequestType(chain.config).deserialize(req.data); + return onExecutionPayloadEnvelopesByRoot(body, chain, db); + }, + [ReqRespMethod.ExecutionPayloadEnvelopesByRange]: (req) => { + const body = ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest.deserialize(req.data); + return onExecutionPayloadEnvelopesByRange(body, chain, db); + }, + [ReqRespMethod.LightClientBootstrap]: (req) => { const body = ssz.Root.deserialize(req.data); return onLightClientBootstrap(body, chain); diff --git a/packages/beacon-node/src/network/reqresp/protocols.ts b/packages/beacon-node/src/network/reqresp/protocols.ts index 1bc83984bedb..17dfc84b4910 100644 --- a/packages/beacon-node/src/network/reqresp/protocols.ts +++ b/packages/beacon-node/src/network/reqresp/protocols.ts @@ -94,6 +94,18 @@ export const DataColumnSidecarsByRoot = toProtocol({ contextBytesType: ContextBytesType.ForkDigest, }); +export const ExecutionPayloadEnvelopesByRoot = toProtocol({ + method: ReqRespMethod.ExecutionPayloadEnvelopesByRoot, + version: Version.V1, + contextBytesType: ContextBytesType.ForkDigest, +}); + +export const ExecutionPayloadEnvelopesByRange = toProtocol({ + method: ReqRespMethod.ExecutionPayloadEnvelopesByRange, + version: Version.V1, + contextBytesType: ContextBytesType.ForkDigest, +}); + export const LightClientBootstrap = toProtocol({ method: ReqRespMethod.LightClientBootstrap, version: Version.V1, diff --git a/packages/beacon-node/src/network/reqresp/rateLimit.ts b/packages/beacon-node/src/network/reqresp/rateLimit.ts index 0bacda6e931d..924ece0d6902 100644 --- a/packages/beacon-node/src/network/reqresp/rateLimit.ts +++ b/packages/beacon-node/src/network/reqresp/rateLimit.ts @@ -73,6 +73,24 @@ export const rateLimitQuotas: (fork: ForkName, config: BeaconConfig) => Record total + item.columns.length, 0) ), }, + [ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: { + byPeer: {quota: config.MAX_REQUEST_PAYLOADS, quotaTimeMs: 10_000}, + getRequestCount: getRequestCountFn( + fork, + config, + ReqRespMethod.ExecutionPayloadEnvelopesByRoot, + (req) => req.length + ), + }, + [ReqRespMethod.ExecutionPayloadEnvelopesByRange]: { + byPeer: {quota: config.MAX_REQUEST_BLOCKS_DENEB, quotaTimeMs: 10_000}, + getRequestCount: getRequestCountFn( + fork, + config, + ReqRespMethod.ExecutionPayloadEnvelopesByRange, + (req) => req.count + ), + }, [ReqRespMethod.LightClientBootstrap]: { // As similar in the nature of `Status` protocol so we use the same rate limits. byPeer: {quota: 5, quotaTimeMs: 15_000}, diff --git a/packages/beacon-node/src/network/reqresp/score.ts b/packages/beacon-node/src/network/reqresp/score.ts index e9f7cee3607d..7ed5e7dd1bec 100644 --- a/packages/beacon-node/src/network/reqresp/score.ts +++ b/packages/beacon-node/src/network/reqresp/score.ts @@ -46,6 +46,8 @@ export function onOutgoingReqRespError(e: RequestError, method: ReqRespMethod): return PeerAction.LowToleranceError; case ReqRespMethod.BeaconBlocksByRange: case ReqRespMethod.BeaconBlocksByRoot: + case ReqRespMethod.ExecutionPayloadEnvelopesByRoot: + case ReqRespMethod.ExecutionPayloadEnvelopesByRange: return PeerAction.MidToleranceError; default: return null; diff --git a/packages/beacon-node/src/network/reqresp/types.ts b/packages/beacon-node/src/network/reqresp/types.ts index b9bcec66f618..722324fbed50 100644 --- a/packages/beacon-node/src/network/reqresp/types.ts +++ b/packages/beacon-node/src/network/reqresp/types.ts @@ -14,6 +14,7 @@ import { altair, deneb, fulu, + gloas, phase0, ssz, sszTypesFor, @@ -25,6 +26,8 @@ import { BlobSidecarsByRootRequestType, DataColumnSidecarsByRootRequest, DataColumnSidecarsByRootRequestType, + ExecutionPayloadEnvelopesByRootRequest, + ExecutionPayloadEnvelopesByRootRequestType, } from "../../util/types.js"; export type ProtocolNoHandler = Omit; @@ -42,6 +45,8 @@ export enum ReqRespMethod { BlobSidecarsByRoot = "blob_sidecars_by_root", DataColumnSidecarsByRange = "data_column_sidecars_by_range", DataColumnSidecarsByRoot = "data_column_sidecars_by_root", + ExecutionPayloadEnvelopesByRoot = "execution_payload_envelopes_by_root", + ExecutionPayloadEnvelopesByRange = "execution_payload_envelopes_by_range", LightClientBootstrap = "light_client_bootstrap", LightClientUpdatesByRange = "light_client_updates_by_range", LightClientFinalityUpdate = "light_client_finality_update", @@ -60,6 +65,8 @@ export type RequestBodyByMethod = { [ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequest; [ReqRespMethod.DataColumnSidecarsByRange]: fulu.DataColumnSidecarsByRangeRequest; [ReqRespMethod.DataColumnSidecarsByRoot]: DataColumnSidecarsByRootRequest; + [ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: ExecutionPayloadEnvelopesByRootRequest; + [ReqRespMethod.ExecutionPayloadEnvelopesByRange]: gloas.ExecutionPayloadEnvelopesByRangeRequest; [ReqRespMethod.LightClientBootstrap]: Root; [ReqRespMethod.LightClientUpdatesByRange]: altair.LightClientUpdatesByRange; [ReqRespMethod.LightClientFinalityUpdate]: null; @@ -78,6 +85,8 @@ type ResponseBodyByMethod = { [ReqRespMethod.BlobSidecarsByRoot]: deneb.BlobSidecar; [ReqRespMethod.DataColumnSidecarsByRange]: fulu.DataColumnSidecar; [ReqRespMethod.DataColumnSidecarsByRoot]: fulu.DataColumnSidecar; + [ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: gloas.SignedExecutionPayloadEnvelope; + [ReqRespMethod.ExecutionPayloadEnvelopesByRange]: gloas.SignedExecutionPayloadEnvelope; [ReqRespMethod.LightClientBootstrap]: LightClientBootstrap; [ReqRespMethod.LightClientUpdatesByRange]: LightClientUpdate; @@ -105,6 +114,8 @@ export const requestSszTypeByMethod: ( [ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequestType(fork, config), [ReqRespMethod.DataColumnSidecarsByRange]: ssz.fulu.DataColumnSidecarsByRangeRequest, [ReqRespMethod.DataColumnSidecarsByRoot]: DataColumnSidecarsByRootRequestType(config), + [ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: ExecutionPayloadEnvelopesByRootRequestType(config), + [ReqRespMethod.ExecutionPayloadEnvelopesByRange]: ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest, [ReqRespMethod.LightClientBootstrap]: ssz.Root, [ReqRespMethod.LightClientUpdatesByRange]: ssz.altair.LightClientUpdatesByRange, @@ -137,6 +148,8 @@ export const responseSszTypeByMethod: {[K in ReqRespMethod]: ResponseTypeGetter< [ReqRespMethod.LightClientFinalityUpdate]: (fork) => sszTypesFor(onlyPostAltairFork(fork)).LightClientFinalityUpdate, [ReqRespMethod.DataColumnSidecarsByRange]: () => ssz.fulu.DataColumnSidecar, [ReqRespMethod.DataColumnSidecarsByRoot]: () => ssz.fulu.DataColumnSidecar, + [ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: () => ssz.gloas.SignedExecutionPayloadEnvelope, + [ReqRespMethod.ExecutionPayloadEnvelopesByRange]: () => ssz.gloas.SignedExecutionPayloadEnvelope, [ReqRespMethod.LightClientOptimisticUpdate]: (fork) => sszTypesFor(onlyPostAltairFork(fork)).LightClientOptimisticUpdate, }; diff --git a/packages/beacon-node/src/util/types.ts b/packages/beacon-node/src/util/types.ts index fc3880c616e9..10a26de4838f 100644 --- a/packages/beacon-node/src/util/types.ts +++ b/packages/beacon-node/src/util/types.ts @@ -29,3 +29,9 @@ export type BlobSidecarsByRootRequest = ValueOf new ListCompositeType(ssz.fulu.DataColumnsByRootIdentifier, config.MAX_REQUEST_BLOCKS_DENEB); export type DataColumnSidecarsByRootRequest = ValueOf>; + +export const ExecutionPayloadEnvelopesByRootRequestType = (config: BeaconConfig) => + new ListCompositeType(ssz.Root, config.MAX_REQUEST_PAYLOADS); +export type ExecutionPayloadEnvelopesByRootRequest = ValueOf< + ReturnType +>; diff --git a/packages/types/src/gloas/sszTypes.ts b/packages/types/src/gloas/sszTypes.ts index 38f1af75f369..b9bf89ad583f 100644 --- a/packages/types/src/gloas/sszTypes.ts +++ b/packages/types/src/gloas/sszTypes.ts @@ -282,3 +282,8 @@ export const DataColumnSidecar = new ContainerType( ); export const DataColumnSidecars = new ListCompositeType(DataColumnSidecar, NUMBER_OF_COLUMNS); + +export const ExecutionPayloadEnvelopesByRangeRequest = new ContainerType( + {startSlot: Slot, count: UintNum64}, + {typeName: "ExecutionPayloadEnvelopesByRangeRequest", jsonCase: "eth2"} +); diff --git a/packages/types/src/gloas/types.ts b/packages/types/src/gloas/types.ts index 6ef793c2e8cf..f90e74bbf416 100644 --- a/packages/types/src/gloas/types.ts +++ b/packages/types/src/gloas/types.ts @@ -21,3 +21,5 @@ export type BeaconState = ValueOf; export type DataColumnSidecar = ValueOf; export type DataColumnSidecars = ValueOf; + +export type ExecutionPayloadEnvelopesByRangeRequest = ValueOf;