From 89ab742ab3597b588fa25876b47c37efbf30d0e9 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Tue, 18 Jun 2024 14:37:28 +0100 Subject: [PATCH 01/17] fix: avoid uncaught exception when polling validator indices (#6891) --- packages/validator/src/services/indices.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/validator/src/services/indices.ts b/packages/validator/src/services/indices.ts index dcc129cc591e..c6ef40b473e5 100644 --- a/packages/validator/src/services/indices.ts +++ b/packages/validator/src/services/indices.ts @@ -83,10 +83,8 @@ export class IndicesService { return this.pollValidatorIndicesPromise; } - this.pollValidatorIndicesPromise = this.pollValidatorIndicesInternal(pubkeysHex); - // Once the pollValidatorIndicesInternal() resolves or rejects null the cached promise so it can be called again. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.pollValidatorIndicesPromise.finally(() => { + this.pollValidatorIndicesPromise = this.pollValidatorIndicesInternal(pubkeysHex).finally(() => { + // Once the pollValidatorIndicesInternal() resolves or rejects null the cached promise so it can be called again. this.pollValidatorIndicesPromise = null; }); return this.pollValidatorIndicesPromise; From f37e50fb13a3b94e6a9a60fcacd7f27d3f6b375a Mon Sep 17 00:00:00 2001 From: Barnabas Busa Date: Tue, 18 Jun 2024 15:58:28 +0200 Subject: [PATCH 02/17] fix: update holesky / sepolia repository layout (#6827) * fix: update holesky repository layout * fix sepolia url too * update sepolia repository * fix lint --- packages/cli/src/networks/holesky.ts | 5 ++--- packages/cli/src/networks/sepolia.ts | 4 ++-- packages/config/src/chainConfig/networks/holesky.ts | 2 +- packages/config/src/chainConfig/networks/sepolia.ts | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/networks/holesky.ts b/packages/cli/src/networks/holesky.ts index 0f89cadaed33..b86f6b543582 100644 --- a/packages/cli/src/networks/holesky.ts +++ b/packages/cli/src/networks/holesky.ts @@ -1,10 +1,9 @@ export {holeskyChainConfig as chainConfig} from "@lodestar/config/networks"; export const depositContractDeployBlock = 0; -export const genesisFileUrl = - "https://media.githubusercontent.com/media/eth-clients/holesky/main/custom_config_data/genesis.ssz"; +export const genesisFileUrl = "https://media.githubusercontent.com/media/eth-clients/holesky/main/metadata/genesis.ssz"; export const bootnodesFileUrl = - "https://raw.githubusercontent.com/eth-clients/holesky/main/custom_config_data/bootstrap_nodes.txt"; + "https://raw.githubusercontent.com/eth-clients/holesky/main/metadata/bootstrap_nodes.txt"; export const bootEnrs = [ "enr:-Ku4QFo-9q73SspYI8cac_4kTX7yF800VXqJW4Lj3HkIkb5CMqFLxciNHePmMt4XdJzHvhrCC5ADI4D_GkAsxGJRLnQBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAhnTT-AQFwAP__________gmlkgnY0gmlwhLKAiOmJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyk", diff --git a/packages/cli/src/networks/sepolia.ts b/packages/cli/src/networks/sepolia.ts index dc967968cf34..b0ac965071a8 100644 --- a/packages/cli/src/networks/sepolia.ts +++ b/packages/cli/src/networks/sepolia.ts @@ -1,9 +1,9 @@ export {sepoliaChainConfig as chainConfig} from "@lodestar/config/networks"; export const depositContractDeployBlock = 1273020; -export const genesisFileUrl = "https://raw.githubusercontent.com/eth-clients/merge-testnets/main/sepolia/genesis.ssz"; +export const genesisFileUrl = "https://github.com/eth-clients/sepolia/raw/main/metadata/genesis.ssz"; export const bootnodesFileUrl = - "https://raw.githubusercontent.com/eth-clients/merge-testnets/main/sepolia/bootstrap_nodes.txt"; + "https://raw.githubusercontent.com/eth-clients/sepolia/main/metadata/bootstrap_nodes.txt"; export const bootEnrs = [ "enr:-Iq4QMCTfIMXnow27baRUb35Q8iiFHSIDBJh6hQM5Axohhf4b6Kr_cOCu0htQ5WvVqKvFgY28893DHAg8gnBAXsAVqmGAX53x8JggmlkgnY0gmlwhLKAlv6Jc2VjcDI1NmsxoQK6S-Cii_KmfFdUJL2TANL3ksaKUnNXvTCv1tLwXs0QgIN1ZHCCIyk", diff --git a/packages/config/src/chainConfig/networks/holesky.ts b/packages/config/src/chainConfig/networks/holesky.ts index 3115caf1ab76..187543b871f2 100644 --- a/packages/config/src/chainConfig/networks/holesky.ts +++ b/packages/config/src/chainConfig/networks/holesky.ts @@ -4,7 +4,7 @@ import {ChainConfig} from "../types.js"; import {chainConfig as mainnet} from "../configs/mainnet.js"; // Holesky beacon chain config: -// https://github.com/eth-clients/holesky/blob/main/custom_config_data/config.yaml +// https://github.com/eth-clients/holesky/blob/main/metadata/config.yaml export const holeskyChainConfig: ChainConfig = { ...mainnet, diff --git a/packages/config/src/chainConfig/networks/sepolia.ts b/packages/config/src/chainConfig/networks/sepolia.ts index a3598299e294..ab6facbec72e 100644 --- a/packages/config/src/chainConfig/networks/sepolia.ts +++ b/packages/config/src/chainConfig/networks/sepolia.ts @@ -4,7 +4,7 @@ import {ChainConfig} from "../types.js"; import {chainConfig as mainnet} from "../configs/mainnet.js"; // Sepolia beacon chain config: -// https://github.com/eth-clients/sepolia/blob/main/bepolia/config.yaml +// https://github.com/eth-clients/sepolia/blob/main/metadata/config.yaml export const sepoliaChainConfig: ChainConfig = { ...mainnet, From 85dc0ba06d1adaf05df0e4aac8f74dee267c9625 Mon Sep 17 00:00:00 2001 From: twoeths Date: Wed, 19 Jun 2024 15:01:30 +0700 Subject: [PATCH 03/17] fix: prune invalid ssz objects (#6875) * fix: prune invalid ssz objects * fix: handle non-existing invalidSszObjects folder * Review PR * fix: expect empty dirs to be removed in unit tests --------- Co-authored-by: Nico Flaig --- packages/cli/src/cmds/beacon/handler.ts | 30 ++++++++++--------- packages/cli/src/util/pruneOldFilesInDir.ts | 12 ++++++-- .../test/unit/util/pruneOldFilesInDir.test.ts | 4 +-- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/packages/cli/src/cmds/beacon/handler.ts b/packages/cli/src/cmds/beacon/handler.ts index ec96081d3c75..e24089194185 100644 --- a/packages/cli/src/cmds/beacon/handler.ts +++ b/packages/cli/src/cmds/beacon/handler.ts @@ -101,20 +101,22 @@ export async function beaconHandler(args: BeaconArgs & GlobalArgs): Promise { - try { - pruneOldFilesInDir( - persistInvalidSszObjectsDir, - (args.persistInvalidSszObjectsRetentionHours ?? DEFAULT_RETENTION_SSZ_OBJECTS_HOURS) * HOURS_TO_MS - ); - } catch (e) { - logger.warn("Error pruning invalid SSZ objects", {persistInvalidSszObjectsDir}, e as Error); - } - // Run every ~1 hour - }, HOURS_TO_MS) - : null; + const {persistInvalidSszObjectsDir, persistInvalidSszObjects} = options.chain; + const pruneInvalidSSZObjectsInterval = + persistInvalidSszObjectsDir && persistInvalidSszObjects + ? setInterval(() => { + try { + const deletedFileCount = pruneOldFilesInDir( + persistInvalidSszObjectsDir, + (args.persistInvalidSszObjectsRetentionHours ?? DEFAULT_RETENTION_SSZ_OBJECTS_HOURS) * HOURS_TO_MS + ); + logger.info("Pruned invalid SSZ objects", {deletedFileCount}); + } catch (e) { + logger.warn("Error pruning invalid SSZ objects", {persistInvalidSszObjectsDir}, e as Error); + } + // Run every ~1 hour + }, HOURS_TO_MS) + : null; // Intercept SIGINT signal, to perform final ops before exiting onGracefulShutdown(async () => { diff --git a/packages/cli/src/util/pruneOldFilesInDir.ts b/packages/cli/src/util/pruneOldFilesInDir.ts index 28d3aa51d16b..3d5cc2b60bcb 100644 --- a/packages/cli/src/util/pruneOldFilesInDir.ts +++ b/packages/cli/src/util/pruneOldFilesInDir.ts @@ -1,17 +1,25 @@ import fs from "node:fs"; import path from "node:path"; -export function pruneOldFilesInDir(dirpath: string, maxAgeMs: number): void { +export function pruneOldFilesInDir(dirpath: string, maxAgeMs: number): number { + let deletedFileCount = 0; for (const entryName of fs.readdirSync(dirpath)) { const entryPath = path.join(dirpath, entryName); const stat = fs.statSync(entryPath); if (stat.isDirectory()) { - pruneOldFilesInDir(entryPath, maxAgeMs); + deletedFileCount += pruneOldFilesInDir(entryPath, maxAgeMs); } else if (stat.isFile()) { if (Date.now() - stat.mtimeMs > maxAgeMs) { fs.unlinkSync(entryPath); + deletedFileCount += 1; } } } + + // if all files are deleted, delete the directory + if (fs.readdirSync(dirpath).length === 0) { + fs.rmdirSync(dirpath); + } + return deletedFileCount; } diff --git a/packages/cli/test/unit/util/pruneOldFilesInDir.test.ts b/packages/cli/test/unit/util/pruneOldFilesInDir.test.ts index d88f684902e0..76285afff081 100644 --- a/packages/cli/test/unit/util/pruneOldFilesInDir.test.ts +++ b/packages/cli/test/unit/util/pruneOldFilesInDir.test.ts @@ -43,7 +43,7 @@ describe("pruneOldFilesInDir", () => { pruneOldFilesInDir(dataDir, DAYS_TO_MS); - expect(fs.readdirSync(nestedDir)).toHaveLength(0); + expect(fs.existsSync(nestedDir)).toBe(false); }); it("should handle empty directories", () => { @@ -52,7 +52,7 @@ describe("pruneOldFilesInDir", () => { pruneOldFilesInDir(emptyDir, DAYS_TO_MS); - expect(fs.readdirSync(emptyDir)).toHaveLength(0); + expect(fs.existsSync(emptyDir)).toBe(false); }); function createFileWithAge(path: string, ageInDays: number): void { From a1228762d4567f0daa33163b4d62a59344e57993 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Wed, 19 Jun 2024 17:38:41 +0100 Subject: [PATCH 04/17] fix: default to http status text if error response is empty (#6898) --- packages/api/src/utils/client/response.ts | 27 ++++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/api/src/utils/client/response.ts b/packages/api/src/utils/client/response.ts index b7039f4865ae..ed008273588a 100644 --- a/packages/api/src/utils/client/response.ts +++ b/packages/api/src/utils/client/response.ts @@ -161,11 +161,11 @@ export class ApiResponse extends Response { return null; } - return new ApiError(getErrorMessage(this.resolvedErrorBody()), this.status, this.definition.operationId); + return new ApiError(this.getErrorMessage(), this.status, this.definition.operationId); } async errorBody(): Promise { - if (!this._errorBody) { + if (this._errorBody === undefined) { this._errorBody = await this.text(); } return this._errorBody; @@ -179,23 +179,24 @@ export class ApiResponse extends Response { } private resolvedErrorBody(): string { - if (!this._errorBody) { + if (this._errorBody === undefined) { throw Error("errorBody() must be called first"); } return this._errorBody; } -} -function getErrorMessage(errBody: string): string { - try { - const errJson = JSON.parse(errBody) as {message?: string}; - if (errJson.message) { - return errJson.message; - } else { - return errBody; + private getErrorMessage(): string { + const errBody = this.resolvedErrorBody(); + try { + const errJson = JSON.parse(errBody) as {message?: string}; + if (errJson.message) { + return errJson.message; + } else { + return errBody; + } + } catch (e) { + return errBody || this.statusText; } - } catch (e) { - return errBody; } } From 0c5adec4eb9360a7f3f71e8d9c56020cdd95ce5d Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Thu, 20 Jun 2024 18:53:50 +0100 Subject: [PATCH 05/17] fix: move server api method invocation out of try-catch for parsing (#6890) * fix: move server api method invocation out of try-catch for parsing * Further simplify / group handler logic --- packages/api/src/utils/server/handler.ts | 85 ++++++++++++------------ 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/packages/api/src/utils/server/handler.ts b/packages/api/src/utils/server/handler.ts index fc2d1fe6ae6e..479212493885 100644 --- a/packages/api/src/utils/server/handler.ts +++ b/packages/api/src/utils/server/handler.ts @@ -11,10 +11,11 @@ import { SszRequestData, SszRequestMethods, isRequestWithoutBody, + RequestWithoutBodyCodec, } from "../types.js"; import {WireFormat, fromWireFormat, getWireFormat} from "../wireFormat.js"; import {ApiError} from "./error.js"; -import {ApplicationMethod, ApplicationResponse} from "./method.js"; +import {ApplicationMethod} from "./method.js"; export type FastifyHandler = fastify.RouteHandlerMethod< fastify.RawServerDefault, @@ -57,53 +58,44 @@ export function createFastifyHandler( } const responseWireFormat = responseMediaType !== null ? getWireFormat(responseMediaType) : null; - let response: ApplicationResponse; - try { - if (isRequestWithoutBody(definition)) { - response = await method(definition.req.parseReq(req as RequestData), { - sszBytes: null, - returnBytes: responseWireFormat === WireFormat.ssz, - }); + let requestWireFormat: WireFormat | null; + if (isRequestWithoutBody(definition)) { + requestWireFormat = null; + } else { + const contentType = req.headers[HttpHeader.ContentType]; + if (contentType === undefined && req.body === undefined) { + // Default to json parser if body is omitted. This is not possible for most + // routes as request will fail schema validation before this handler is called + requestWireFormat = WireFormat.json; } else { - let requestWireFormat: WireFormat; - const contentType = req.headers[HttpHeader.ContentType]; - if (contentType === undefined && req.body === undefined) { - // Default to json parser if body is omitted. This is not possible for most - // routes as request will fail schema validation before this handler is called - requestWireFormat = WireFormat.json; - } else { - if (contentType === undefined) { - throw new ApiError(400, "Content-Type header is required"); - } - const requestMediaType = parseContentTypeHeader(contentType); - if (requestMediaType === null) { - throw new ApiError(415, `Unsupported media type: ${contentType.split(";", 1)[0]}`); - } - requestWireFormat = getWireFormat(requestMediaType); + if (contentType === undefined) { + throw new ApiError(400, "Content-Type header is required"); } - - const {onlySupport} = definition.req as RequestWithBodyCodec; - if (onlySupport !== undefined && onlySupport !== requestWireFormat) { - throw new ApiError(415, `Endpoint only supports ${onlySupport.toUpperCase()} requests`); + const requestMediaType = parseContentTypeHeader(contentType); + if (requestMediaType === null) { + throw new ApiError(415, `Unsupported media type: ${contentType.split(";", 1)[0]}`); } + requestWireFormat = getWireFormat(requestMediaType); + } - switch (requestWireFormat) { - case WireFormat.json: - response = await method((definition.req as JsonRequestMethods).parseReqJson(req as JsonRequestData), { - sszBytes: null, - returnBytes: responseWireFormat === WireFormat.ssz, - }); - break; - case WireFormat.ssz: - response = await method( - (definition.req as SszRequestMethods).parseReqSsz(req as SszRequestData), - { - sszBytes: req.body as Uint8Array, - returnBytes: responseWireFormat === WireFormat.ssz, - } - ); - break; - } + const {onlySupport} = definition.req as RequestWithBodyCodec; + if (onlySupport !== undefined && onlySupport !== requestWireFormat) { + throw new ApiError(415, `Endpoint only supports ${onlySupport.toUpperCase()} requests`); + } + } + + let args: E["args"]; + try { + switch (requestWireFormat) { + case WireFormat.json: + args = (definition.req as JsonRequestMethods).parseReqJson(req as JsonRequestData); + break; + case WireFormat.ssz: + args = (definition.req as SszRequestMethods).parseReqSsz(req as SszRequestData); + break; + case null: + args = (definition.req as RequestWithoutBodyCodec).parseReq(req as RequestData); + break; } } catch (e) { if (e instanceof ApiError) throw e; @@ -111,6 +103,11 @@ export function createFastifyHandler( throw new ApiError(400, (e as Error).message); } + const response = await method(args, { + sszBytes: requestWireFormat === WireFormat.ssz ? (req.body as Uint8Array) : null, + returnBytes: responseWireFormat === WireFormat.ssz, + }); + if (response?.status !== undefined) { resp.statusCode = response.status; } From a074310f32d5864abfcc300a6c681554d6a016bc Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Thu, 20 Jun 2024 22:09:11 +0100 Subject: [PATCH 06/17] chore: add default message if server closes eventstream (#6902) --- packages/api/src/beacon/client/events.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/api/src/beacon/client/events.ts b/packages/api/src/beacon/client/events.ts index 97e2fdc40d75..35383083ee44 100644 --- a/packages/api/src/beacon/client/events.ts +++ b/packages/api/src/beacon/client/events.ts @@ -45,7 +45,8 @@ export function getClient(config: ChainForkConfig, baseUrl: string): ApiClient { // Ignore noisy errors due to beacon node being offline if (!errEs.message?.includes("ECONNREFUSED")) { - onError?.(new Error(errEs.message)); + // If there is no message it likely indicates that the server closed the connection + onError?.(new Error(errEs.message ?? "Server closed connection")); } // Consider 400 and 500 status errors unrecoverable, close the eventsource From a2c389fc97b03c415e3bccaef5cf2d512065b7a7 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Fri, 21 Jun 2024 15:58:21 +0100 Subject: [PATCH 07/17] chore: remove apis that are no longer part of beacon spec (#6901) * chore: remove apis that are no longer part of beacon spec * Update state ssz tests to deneb --- .../api/src/beacon/routes/beacon/block.ts | 26 +----- packages/api/src/beacon/routes/debug.ts | 92 +++---------------- packages/api/src/beacon/routes/validator.ts | 45 --------- .../beacon/genericServerTest/debug.test.ts | 34 +++---- .../api/test/unit/beacon/testData/beacon.ts | 4 - .../api/test/unit/beacon/testData/debug.ts | 8 -- .../test/unit/beacon/testData/validator.ts | 4 - .../src/api/impl/beacon/blocks/index.ts | 5 - .../beacon-node/src/api/impl/debug/index.ts | 16 ---- .../src/api/impl/validator/index.ts | 11 --- 10 files changed, 30 insertions(+), 215 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/block.ts b/packages/api/src/beacon/routes/beacon/block.ts index 380efcc9825a..76c2b3664a3f 100644 --- a/packages/api/src/beacon/routes/beacon/block.ts +++ b/packages/api/src/beacon/routes/beacon/block.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {ContainerType, ListCompositeType, ValueOf} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; -import {allForks, Slot, ssz, RootHex, deneb, phase0, isSignedBlockContents} from "@lodestar/types"; +import {allForks, Slot, ssz, RootHex, deneb, isSignedBlockContents} from "@lodestar/types"; import {ForkName, ForkSeq} from "@lodestar/params"; import {Endpoint, RequestCodec, RouteDefinitions, Schema} from "../../../utils/index.js"; -import {EmptyMeta, EmptyMetaCodec, EmptyResponseCodec, EmptyResponseData, WithVersion} from "../../../utils/codecs.js"; +import {EmptyMeta, EmptyResponseCodec, EmptyResponseData, WithVersion} from "../../../utils/codecs.js"; import { ExecutionOptimisticAndFinalizedCodec, ExecutionOptimisticAndFinalizedMeta, @@ -66,19 +66,6 @@ export enum BroadcastValidation { } export type Endpoints = { - /** - * Get block - * Returns the complete `SignedBeaconBlock` for a given block ID. - */ - getBlock: Endpoint< - // ⏎ - "GET", - BlockArgs, - {params: {block_id: string}}, - phase0.SignedBeaconBlock, - EmptyMeta - >; - /** * Get block * Retrieves block details for given block id. @@ -220,15 +207,6 @@ const blockIdOnlyReq: RequestCodec { return { - getBlock: { - url: "/eth/v1/beacon/blocks/{block_id}", - method: "GET", - req: blockIdOnlyReq, - resp: { - data: ssz.phase0.SignedBeaconBlock, - meta: EmptyMetaCodec, - }, - }, getBlockV2: { url: "/eth/v2/beacon/blocks/{block_id}", method: "GET", diff --git a/packages/api/src/beacon/routes/debug.ts b/packages/api/src/beacon/routes/debug.ts index 2128f7204c6a..544cf795fdaf 100644 --- a/packages/api/src/beacon/routes/debug.ts +++ b/packages/api/src/beacon/routes/debug.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {ContainerType, Type, ValueOf} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; -import {allForks, ssz, StringType, phase0} from "@lodestar/types"; +import {allForks, ssz, StringType} from "@lodestar/types"; import { ArrayOf, EmptyArgs, @@ -14,8 +14,6 @@ import { import { ExecutionOptimisticFinalizedAndVersionCodec, ExecutionOptimisticFinalizedAndVersionMeta, - ExecutionOptimisticAndFinalizedCodec, - ExecutionOptimisticAndFinalizedMeta, } from "../../utils/metadata.js"; import {Endpoint, RouteDefinitions} from "../../utils/types.js"; import {WireFormat} from "../../utils/wireFormat.js"; @@ -25,7 +23,7 @@ import {StateArgs} from "./beacon/state.js"; // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes const stringType = new StringType(); -const ProtoNodeResponseType = new ContainerType( +const ProtoNodeType = new ContainerType( { executionPayloadBlockHash: stringType, executionPayloadNumber: ssz.UintNum64, @@ -51,14 +49,7 @@ const ProtoNodeResponseType = new ContainerType( }, {jsonCase: "eth2"} ); -const SlotRootType = new ContainerType( - { - slot: ssz.Slot, - root: stringType, - }, - {jsonCase: "eth2"} -); -const SlotRootExecutionOptimisticType = new ContainerType( +const DebugChainHeadType = new ContainerType( { slot: ssz.Slot, root: stringType, @@ -67,27 +58,13 @@ const SlotRootExecutionOptimisticType = new ContainerType( {jsonCase: "eth2"} ); -const ProtoNodeResponseListType = ArrayOf(ProtoNodeResponseType); -const SlotRootListType = ArrayOf(SlotRootType); -const SlotRootExecutionOptimisticListType = ArrayOf(SlotRootExecutionOptimisticType); +const ProtoNodeListType = ArrayOf(ProtoNodeType); +const DebugChainHeadListType = ArrayOf(DebugChainHeadType); -type ProtoNodeResponseList = ValueOf; -type SlotRootList = ValueOf; -type SlotRootExecutionOptimisticList = ValueOf; +type ProtoNodeList = ValueOf; +type DebugChainHeadList = ValueOf; export type Endpoints = { - /** - * Retrieves all possible chain heads (leaves of fork choice tree). - */ - getDebugChainHeads: Endpoint< - // ⏎ - "GET", - EmptyArgs, - EmptyRequest, - SlotRootList, - EmptyMeta - >; - /** * Retrieves all possible chain heads (leaves of fork choice tree). */ @@ -96,7 +73,7 @@ export type Endpoints = { "GET", EmptyArgs, EmptyRequest, - SlotRootExecutionOptimisticList, + DebugChainHeadList, EmptyMeta >; @@ -108,23 +85,10 @@ export type Endpoints = { "GET", EmptyArgs, EmptyRequest, - ProtoNodeResponseList, + ProtoNodeList, EmptyMeta >; - /** - * Get full BeaconState object - * Returns full BeaconState object for given stateId. - * Depending on `Accept` header it can be returned either as json or as bytes serialized by SSZ - */ - getState: Endpoint< - "GET", - StateArgs, - {params: {state_id: string}}, - phase0.BeaconState, - ExecutionOptimisticAndFinalizedMeta - >; - /** * Get full BeaconState object * Returns full BeaconState object for given stateId. @@ -139,27 +103,14 @@ export type Endpoints = { >; }; -// Default timeout is not sufficient to download state as JSON -const GET_STATE_TIMEOUT_MS = 5 * 60 * 1000; - export function getDefinitions(_config: ChainForkConfig): RouteDefinitions { return { - getDebugChainHeads: { - url: "/eth/v1/debug/beacon/heads", - method: "GET", - req: EmptyRequestCodec, - resp: { - data: SlotRootListType, - meta: EmptyMetaCodec, - onlySupport: WireFormat.json, - }, - }, getDebugChainHeadsV2: { url: "/eth/v2/debug/beacon/heads", method: "GET", req: EmptyRequestCodec, resp: { - data: SlotRootExecutionOptimisticListType, + data: DebugChainHeadListType, meta: EmptyMetaCodec, onlySupport: WireFormat.json, }, @@ -169,29 +120,11 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions ({params: {state_id: stateId.toString()}}), - parseReq: ({params}) => ({stateId: params.state_id}), - schema: { - params: {state_id: Schema.StringRequired}, - }, - }, - resp: { - data: ssz.phase0.BeaconState, - meta: ExecutionOptimisticAndFinalizedCodec, - }, - init: { - timeoutMs: GET_STATE_TIMEOUT_MS, - }, - }, getStateV2: { url: "/eth/v2/debug/beacon/states/{state_id}", method: "GET", @@ -207,7 +140,8 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions; - /** - * Produce a new block, without signature. - * Requests a beacon node to produce a valid block, which can then be signed by a validator. - */ - produceBlock: Endpoint< - "GET", - { - /** The slot for which the block should be proposed. */ - slot: Slot; - /** The validator's randao reveal value */ - randaoReveal: BLSSignature; - /** Arbitrary data validator wants to include in block */ - graffiti: string; - }, - {params: {slot: number}; query: {randao_reveal: string; graffiti: string}}, - allForks.BeaconBlock, - VersionMeta - >; - /** * Requests a beacon node to produce a valid block, which can then be signed by a validator. * Metadata in the response indicates the type of block produced, and the supported types of block @@ -607,32 +588,6 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions ({ - params: {slot}, - query: {randao_reveal: toHexString(randaoReveal), graffiti: toGraffitiHex(graffiti)}, - }), - parseReq: ({params, query}) => ({ - slot: params.slot, - randaoReveal: fromHexString(query.randao_reveal), - graffiti: fromGraffitiHex(query.graffiti), - }), - schema: { - params: {slot: Schema.UintRequired}, - query: { - randao_reveal: Schema.StringRequired, - graffiti: Schema.String, - }, - }, - }, - resp: { - data: WithVersion((fork) => ssz[fork].BeaconBlock), - meta: VersionCodec, - }, - }, produceBlockV2: { url: "/eth/v2/validator/blocks/{slot}", method: "GET", diff --git a/packages/api/test/unit/beacon/genericServerTest/debug.test.ts b/packages/api/test/unit/beacon/genericServerTest/debug.test.ts index 54158cda392c..f329382dbe46 100644 --- a/packages/api/test/unit/beacon/genericServerTest/debug.test.ts +++ b/packages/api/test/unit/beacon/genericServerTest/debug.test.ts @@ -23,7 +23,7 @@ describe("beacon / debug", () => { // Get state by SSZ - describe("getState() in SSZ format", () => { + describe("get state in SSZ format", () => { const mockApi = getMockApi(getDefinitions(config)); let baseUrl: string; let server: FastifyInstance; @@ -41,26 +41,22 @@ describe("beacon / debug", () => { if (server !== undefined) await server.close(); }); - for (const method of ["getState" as const, "getStateV2" as const]) { - it(method, async () => { - const state = ssz.phase0.BeaconState.defaultValue(); - const stateSerialized = ssz.phase0.BeaconState.serialize(state); - mockApi[method].mockResolvedValue({ - data: stateSerialized, - meta: {version: ForkName.phase0, executionOptimistic: false, finalized: false}, - }); - - const httpClient = new HttpClient({baseUrl}); - const client = getClient(config, httpClient); + it("getStateV2", async () => { + const state = ssz.deneb.BeaconState.defaultValue(); + const stateSerialized = ssz.deneb.BeaconState.serialize(state); + mockApi.getStateV2.mockResolvedValue({ + data: stateSerialized, + meta: {version: ForkName.deneb, executionOptimistic: false, finalized: false}, + }); - const res = await client[method]({stateId: "head"}, {responseWireFormat: WireFormat.ssz}); + const httpClient = new HttpClient({baseUrl}); + const client = getClient(config, httpClient); - expect(res.ok).toBe(true); + const res = await client.getStateV2({stateId: "head"}, {responseWireFormat: WireFormat.ssz}); - if (res.ok) { - expect(toHexString(res.ssz())).toBe(toHexString(stateSerialized)); - } - }); - } + expect(res.ok).toBe(true); + expect(res.wireFormat()).toBe(WireFormat.ssz); + expect(toHexString(res.ssz())).toBe(toHexString(stateSerialized)); + }); }); }); diff --git a/packages/api/test/unit/beacon/testData/beacon.ts b/packages/api/test/unit/beacon/testData/beacon.ts index 9dad5f0079a2..c7dde0077944 100644 --- a/packages/api/test/unit/beacon/testData/beacon.ts +++ b/packages/api/test/unit/beacon/testData/beacon.ts @@ -31,10 +31,6 @@ const validatorResponse: ValidatorResponse = { export const testData: GenericServerTestCases = { // block - getBlock: { - args: {blockId: "head"}, - res: {data: ssz.phase0.SignedBeaconBlock.defaultValue()}, - }, getBlockV2: { args: {blockId: "head"}, res: { diff --git a/packages/api/test/unit/beacon/testData/debug.ts b/packages/api/test/unit/beacon/testData/debug.ts index 386443c76f3d..aac3b379ff4d 100644 --- a/packages/api/test/unit/beacon/testData/debug.ts +++ b/packages/api/test/unit/beacon/testData/debug.ts @@ -7,10 +7,6 @@ import {GenericServerTestCases} from "../../../utils/genericServerTest.js"; const rootHex = toHexString(Buffer.alloc(32, 1)); export const testData: GenericServerTestCases = { - getDebugChainHeads: { - args: undefined, - res: {data: [{slot: 1, root: rootHex}]}, - }, getDebugChainHeadsV2: { args: undefined, res: {data: [{slot: 1, root: rootHex, executionOptimistic: true}]}, @@ -45,10 +41,6 @@ export const testData: GenericServerTestCases = { ], }, }, - getState: { - args: {stateId: "head"}, - res: {data: ssz.phase0.BeaconState.defaultValue(), meta: {executionOptimistic: true, finalized: false}}, - }, getStateV2: { args: {stateId: "head"}, res: { diff --git a/packages/api/test/unit/beacon/testData/validator.ts b/packages/api/test/unit/beacon/testData/validator.ts index 3a92beb7ad27..11fd7dd26425 100644 --- a/packages/api/test/unit/beacon/testData/validator.ts +++ b/packages/api/test/unit/beacon/testData/validator.ts @@ -42,10 +42,6 @@ export const testData: GenericServerTestCases = { meta: {executionOptimistic: true}, }, }, - produceBlock: { - args: {slot: 32000, randaoReveal, graffiti}, - res: {data: ssz.phase0.BeaconBlock.defaultValue(), meta: {version: ForkName.phase0}}, - }, produceBlockV2: { args: { slot: 32000, diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts index 613ffeaacfdd..dc85daff8575 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -366,11 +366,6 @@ export function getBeaconBlockApi({ }; }, - async getBlock({blockId}) { - const {block} = await resolveBlockId(chain, blockId); - return {data: block}; - }, - async getBlockV2({blockId}) { const {block, executionOptimistic, finalized} = await resolveBlockId(chain, blockId); return { diff --git a/packages/beacon-node/src/api/impl/debug/index.ts b/packages/beacon-node/src/api/impl/debug/index.ts index c5c0eeda03a3..f1254ae1b7fb 100644 --- a/packages/beacon-node/src/api/impl/debug/index.ts +++ b/packages/beacon-node/src/api/impl/debug/index.ts @@ -1,6 +1,5 @@ import {routes} from "@lodestar/api"; import {ApplicationMethods} from "@lodestar/api/server"; -import {phase0} from "@lodestar/types"; import {resolveStateId} from "../beacon/state/utils.js"; import {ApiModules} from "../types.js"; import {isOptimisticBlock} from "../../../util/forkChoice.js"; @@ -10,13 +9,6 @@ export function getDebugApi({ config, }: Pick): ApplicationMethods { return { - async getDebugChainHeads() { - const heads = chain.forkChoice.getHeads(); - return { - data: heads.map((blockSummary) => ({slot: blockSummary.slot, root: blockSummary.blockRoot})), - }; - }, - async getDebugChainHeadsV2() { const heads = chain.forkChoice.getHeads(); return { @@ -41,14 +33,6 @@ export function getDebugApi({ return {data: nodes}; }, - async getState({stateId}, context) { - const {state, executionOptimistic, finalized} = await resolveStateId(chain, stateId, {allowRegen: true}); - return { - data: context?.returnBytes ? state.serialize() : (state.toValue() as phase0.BeaconState), - meta: {executionOptimistic, finalized}, - }; - }, - async getStateV2({stateId}, context) { const {state, executionOptimistic, finalized} = await resolveStateId(chain, stateId, {allowRegen: true}); return { diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 2c0958de5d5a..a74c9b2ec8cb 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -739,17 +739,6 @@ export function getValidatorApi({ } return { - async produceBlock({slot, randaoReveal, graffiti}) { - const {data, ...meta} = await produceEngineFullBlockOrContents(slot, randaoReveal, graffiti); - if (isForkBlobs(meta.version)) { - throw Error(`Invalid call to produceBlock for deneb+ fork=${meta.version}`); - } else { - // TODO: need to figure out why typescript requires typecasting here - // by typing of produceFullBlockOrContents respose it should have figured this out itself - return {data: data as allForks.BeaconBlock, meta}; - } - }, - async produceBlockV2({slot, randaoReveal, graffiti, ...opts}) { const {data, ...meta} = await produceEngineFullBlockOrContents(slot, randaoReveal, graffiti, opts); return {data, meta}; From 802a875a73148c01cb2e077cd24b08d4e6a8c4b4 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Mon, 24 Jun 2024 09:44:32 +0200 Subject: [PATCH 08/17] refactor: improve types package to use forks as generics (#6825) * Update the types to use generics * Update the types with default generics * Fix the block type * Add missing type * Add missing type * Add ssz types * Remove allForks namespace * Make ssz types generic * Update config package * Fix blok fork types * Fix api package types * Update types * Update state-transition package * Update config package * Fix types * Fix types for all packages * Update api package * Fix types for tests files * Fix ssz types * Fix build error * Fix lint errors * Fix lint errors * Fix type issues after rebase * Update redundant generics * Fix the type * Update types to use single generic * Fix all types * Fix the code as per feedback * Update the code with feedback * Update packages/types/README.md Co-authored-by: Nico Flaig * chore: review #6825 (#6887) Review #6825 * Fix publishBlockWrapper input type * Updated the type guard * Remove ssz instance type * Rename BlindedExecutionPayload to ExecutionPayloadheader * Update code as per feedback --------- Co-authored-by: Nico Flaig --- .../api/src/beacon/routes/beacon/block.ts | 44 +-- packages/api/src/beacon/routes/debug.ts | 6 +- packages/api/src/beacon/routes/events.ts | 21 +- packages/api/src/beacon/routes/lightclient.ts | 23 +- packages/api/src/beacon/routes/validator.ts | 14 +- packages/api/src/builder/routes.ts | 30 +- packages/api/src/utils/fork.ts | 31 +- .../api/test/unit/beacon/testData/beacon.ts | 4 +- .../src/api/impl/beacon/blocks/index.ts | 15 +- .../src/api/impl/beacon/blocks/utils.ts | 8 +- .../src/api/impl/validator/index.ts | 20 +- .../src/chain/blocks/importBlock.ts | 6 +- .../beacon-node/src/chain/blocks/index.ts | 4 +- .../beacon-node/src/chain/blocks/types.ts | 12 +- .../blocks/verifyBlocksExecutionPayloads.ts | 8 +- .../chain/blocks/verifyBlocksSignatures.ts | 8 +- packages/beacon-node/src/chain/chain.ts | 26 +- .../src/chain/errors/blockError.ts | 4 +- packages/beacon-node/src/chain/initState.ts | 9 +- packages/beacon-node/src/chain/interface.ts | 22 +- .../src/chain/lightClient/index.ts | 66 ++-- .../src/chain/lightClient/proofs.ts | 6 +- .../beacon-node/src/chain/opPools/opPool.ts | 9 +- .../chain/produceBlock/computeNewStateRoot.ts | 6 +- .../chain/produceBlock/produceBlockBody.ts | 32 +- .../validateBlobsAndKzgCommitments.ts | 4 +- .../beacon-node/src/chain/regen/interface.ts | 10 +- .../beacon-node/src/chain/regen/queued.ts | 6 +- packages/beacon-node/src/chain/regen/regen.ts | 4 +- .../src/chain/rewards/blockRewards.ts | 14 +- .../src/chain/rewards/syncCommitteeRewards.ts | 4 +- .../chain/seenCache/seenGossipBlockInput.ts | 6 +- .../beacon-node/src/chain/validation/block.ts | 4 +- .../validation/lightClientFinalityUpdate.ts | 4 +- .../validation/lightClientOptimisticUpdate.ts | 6 +- packages/beacon-node/src/db/buckets.ts | 2 +- .../beacon-node/src/db/repositories/block.ts | 10 +- .../src/db/repositories/blockArchive.ts | 26 +- .../src/db/repositories/blockArchiveIndex.ts | 9 +- .../db/repositories/lightclientBestUpdate.ts | 8 +- .../lightclientCheckpointHeader.ts | 8 +- .../src/db/single/preGenesisState.ts | 6 +- .../beacon-node/src/execution/builder/http.ts | 18 +- .../src/execution/builder/interface.ts | 16 +- .../beacon-node/src/execution/engine/http.ts | 6 +- .../src/execution/engine/interface.ts | 6 +- .../beacon-node/src/execution/engine/types.ts | 6 +- .../src/metrics/metrics/lodestar.ts | 4 +- .../src/metrics/validatorMonitor.ts | 6 +- .../src/network/gossip/interface.ts | 23 +- packages/beacon-node/src/network/interface.ts | 22 +- packages/beacon-node/src/network/network.ts | 38 ++- .../src/network/peers/peerManager.ts | 6 +- .../src/network/processor/gossipHandlers.ts | 4 +- .../src/network/reqresp/ReqRespBeaconNode.ts | 4 +- .../reqresp/beaconBlocksMaybeBlobsByRange.ts | 4 +- .../beacon-node/src/network/reqresp/types.ts | 10 +- .../utils/collectSequentialBlocksInRange.ts | 6 +- .../beacon-node/src/sync/backfill/backfill.ts | 4 +- .../beacon-node/src/sync/backfill/verify.ts | 12 +- packages/beacon-node/src/util/blobs.ts | 10 +- packages/beacon-node/src/util/multifork.ts | 9 +- .../e2e/doppelganger/doppelganger.test.ts | 4 +- .../test/e2e/network/reqresp.test.ts | 4 +- .../test/mocks/fork-choice/timeliness.ts | 4 +- .../beacon-node/test/sim/mergemock.test.ts | 4 +- .../test/spec/presets/fork_choice.test.ts | 8 +- .../test/spec/presets/genesis.test.ts | 8 +- .../presets/light_client/update_ranking.ts | 4 +- .../test/spec/presets/sanity.test.ts | 4 +- .../test/spec/presets/transition.test.ts | 6 +- .../test/spec/utils/expectEqualBeaconState.ts | 6 +- .../blocks/verifyBlocksSanityChecks.test.ts | 26 +- .../upgradeLightClientHeader.test.ts | 4 +- .../test/unit/chain/validation/block.test.ts | 4 +- .../collectSequentialBlocksInRange.test.ts | 4 +- .../beacon-node/test/utils/node/simTest.ts | 6 +- packages/beacon-node/test/utils/state.ts | 4 +- .../cli/test/utils/crucible/interfaces.ts | 4 +- .../cli/test/utils/crucible/utils/network.ts | 4 +- .../cli/test/utils/crucible/utils/syncing.ts | 5 +- packages/config/src/forkConfig/index.ts | 26 +- packages/config/src/forkConfig/types.ts | 14 +- .../fork-choice/src/forkChoice/forkChoice.ts | 6 +- .../fork-choice/src/forkChoice/interface.ts | 4 +- packages/light-client/src/events.ts | 6 +- packages/light-client/src/index.ts | 24 +- packages/light-client/src/spec/index.ts | 16 +- .../light-client/src/spec/isBetterUpdate.ts | 4 +- .../src/spec/processLightClientUpdate.ts | 4 +- packages/light-client/src/spec/store.ts | 26 +- packages/light-client/src/spec/utils.ts | 67 ++-- .../src/spec/validateLightClientBootstrap.ts | 4 +- .../src/spec/validateLightClientUpdate.ts | 4 +- .../light-client/src/transport/interface.ts | 20 +- packages/light-client/src/transport/rest.ts | 24 +- packages/light-client/src/types.ts | 6 +- packages/light-client/src/validation.ts | 8 +- .../unit/isValidLightClientHeader.test.ts | 6 +- packages/light-client/test/utils/utils.ts | 4 +- packages/params/src/forkName.ts | 2 + .../src/proof_provider/payload_store.ts | 17 +- .../src/proof_provider/proof_provider.ts | 10 +- packages/prover/src/utils/consensus.ts | 10 +- packages/prover/src/utils/evm.ts | 10 +- packages/prover/src/utils/validation.ts | 4 +- .../unit/proof_provider/payload_store.test.ts | 10 +- packages/state-transition/src/block/index.ts | 4 +- .../src/block/processBlockHeader.ts | 4 +- .../src/block/processExecutionPayload.ts | 9 +- .../src/block/processOperations.ts | 4 +- .../src/block/processRandao.ts | 8 +- packages/state-transition/src/cache/types.ts | 28 +- .../src/signatureSets/attesterSlashings.ts | 4 +- .../src/signatureSets/index.ts | 4 +- .../src/signatureSets/indexedAttestation.ts | 4 +- .../src/signatureSets/proposer.ts | 6 +- .../src/signatureSets/proposerSlashings.ts | 4 +- .../src/signatureSets/randao.ts | 9 +- .../src/signatureSets/voluntaryExits.ts | 4 +- .../state-transition/src/stateTransition.ts | 4 +- .../state-transition/src/util/blindedBlock.ts | 62 ++-- .../state-transition/src/util/blockRoot.ts | 22 +- packages/state-transition/src/util/epoch.ts | 6 +- .../state-transition/src/util/execution.ts | 58 ++-- .../state-transition/src/util/sszBytes.ts | 9 +- .../src/util/weakSubjectivity.ts | 2 +- .../test/perf/analyzeEpochs.ts | 2 +- packages/state-transition/test/perf/types.ts | 4 +- packages/state-transition/test/perf/util.ts | 5 +- .../test/utils/testFileCache.ts | 4 +- packages/types/README.md | 6 +- packages/types/package.json | 3 - packages/types/src/allForks/index.ts | 7 - packages/types/src/allForks/sszTypes.ts | 160 ---------- packages/types/src/allForks/types.ts | 298 ------------------ packages/types/src/deneb/types.ts | 5 +- packages/types/src/index.ts | 4 +- packages/types/src/sszTypes.ts | 196 +++++++++++- packages/types/src/types.ts | 177 ++++++++++- packages/types/src/utils/typeguards.ts | 79 +++-- packages/validator/src/services/block.ts | 32 +- .../validator/src/services/validatorStore.ts | 11 +- .../src/util/externalSignerClient.ts | 6 +- 144 files changed, 1273 insertions(+), 1209 deletions(-) delete mode 100644 packages/types/src/allForks/index.ts delete mode 100644 packages/types/src/allForks/sszTypes.ts delete mode 100644 packages/types/src/allForks/types.ts diff --git a/packages/api/src/beacon/routes/beacon/block.ts b/packages/api/src/beacon/routes/beacon/block.ts index 76c2b3664a3f..dcf0d07c7a9b 100644 --- a/packages/api/src/beacon/routes/beacon/block.ts +++ b/packages/api/src/beacon/routes/beacon/block.ts @@ -1,7 +1,17 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {ContainerType, ListCompositeType, ValueOf} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; -import {allForks, Slot, ssz, RootHex, deneb, isSignedBlockContents} from "@lodestar/types"; +import { + Slot, + ssz, + RootHex, + deneb, + isSignedBlockContents, + SignedBeaconBlock, + BeaconBlockBody, + SignedBeaconBlockOrContents, + SignedBlindedBeaconBlock, +} from "@lodestar/types"; import {ForkName, ForkSeq} from "@lodestar/params"; import {Endpoint, RequestCodec, RouteDefinitions, Schema} from "../../../utils/index.js"; import {EmptyMeta, EmptyResponseCodec, EmptyResponseData, WithVersion} from "../../../utils/codecs.js"; @@ -74,7 +84,7 @@ export type Endpoints = { "GET", BlockArgs, {params: {block_id: string}}, - allForks.SignedBeaconBlock, + SignedBeaconBlock, ExecutionOptimisticFinalizedAndVersionMeta >; @@ -86,7 +96,7 @@ export type Endpoints = { "GET", BlockArgs, {params: {block_id: string}}, - allForks.BeaconBlockBody["attestations"], + BeaconBlockBody["attestations"], ExecutionOptimisticAndFinalizedMeta >; @@ -139,7 +149,7 @@ export type Endpoints = { */ publishBlock: Endpoint< "POST", - {signedBlockOrContents: allForks.SignedBeaconBlockOrContents}, + {signedBlockOrContents: SignedBeaconBlockOrContents}, {body: unknown; headers: {[MetaHeader.Version]: string}}, EmptyResponseData, EmptyMeta @@ -148,7 +158,7 @@ export type Endpoints = { publishBlockV2: Endpoint< "POST", { - signedBlockOrContents: allForks.SignedBeaconBlockOrContents; + signedBlockOrContents: SignedBeaconBlockOrContents; broadcastValidation?: BroadcastValidation; }, {body: unknown; headers: {[MetaHeader.Version]: string}; query: {broadcast_validation?: string}}, @@ -162,7 +172,7 @@ export type Endpoints = { */ publishBlindedBlock: Endpoint< "POST", - {signedBlindedBlock: allForks.SignedBlindedBeaconBlock}, + {signedBlindedBlock: SignedBlindedBeaconBlock}, {body: unknown; headers: {[MetaHeader.Version]: string}}, EmptyResponseData, EmptyMeta @@ -171,7 +181,7 @@ export type Endpoints = { publishBlindedBlockV2: Endpoint< "POST", { - signedBlindedBlock: allForks.SignedBlindedBeaconBlock; + signedBlindedBlock: SignedBlindedBeaconBlock; broadcastValidation?: BroadcastValidation; }, {body: unknown; headers: {[MetaHeader.Version]: string}; query: {broadcast_validation?: string}}, @@ -267,9 +277,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions; }; @@ -136,7 +136,7 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions ssz[fork].BeaconState as Type), + data: WithVersion((fork) => ssz[fork].BeaconState as Type), meta: ExecutionOptimisticFinalizedAndVersionCodec, }, init: { diff --git a/packages/api/src/beacon/routes/events.ts b/packages/api/src/beacon/routes/events.ts index 0b88175d7588..23be5e7c2288 100644 --- a/packages/api/src/beacon/routes/events.ts +++ b/packages/api/src/beacon/routes/events.ts @@ -1,6 +1,19 @@ import {ContainerType, ValueOf} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; -import {Epoch, phase0, capella, Slot, ssz, StringType, RootHex, altair, UintNum64, allForks} from "@lodestar/types"; +import { + Epoch, + phase0, + capella, + Slot, + ssz, + StringType, + RootHex, + altair, + UintNum64, + LightClientOptimisticUpdate, + LightClientFinalityUpdate, + SSEPayloadAttributes, +} from "@lodestar/types"; import {ForkName} from "@lodestar/params"; import {Endpoint, RouteDefinitions, Schema} from "../../utils/index.js"; @@ -113,9 +126,9 @@ export type EventData = { executionOptimistic: boolean; }; [EventType.contributionAndProof]: altair.SignedContributionAndProof; - [EventType.lightClientOptimisticUpdate]: {version: ForkName; data: allForks.LightClientOptimisticUpdate}; - [EventType.lightClientFinalityUpdate]: {version: ForkName; data: allForks.LightClientFinalityUpdate}; - [EventType.payloadAttributes]: {version: ForkName; data: allForks.SSEPayloadAttributes}; + [EventType.lightClientOptimisticUpdate]: {version: ForkName; data: LightClientOptimisticUpdate}; + [EventType.lightClientFinalityUpdate]: {version: ForkName; data: LightClientFinalityUpdate}; + [EventType.payloadAttributes]: {version: ForkName; data: SSEPayloadAttributes}; [EventType.blobSidecar]: BlobSidecarSSE; }; diff --git a/packages/api/src/beacon/routes/lightclient.ts b/packages/api/src/beacon/routes/lightclient.ts index ed5e290a747c..ab45323f8f64 100644 --- a/packages/api/src/beacon/routes/lightclient.ts +++ b/packages/api/src/beacon/routes/lightclient.ts @@ -1,6 +1,13 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {ListCompositeType, ValueOf} from "@chainsafe/ssz"; -import {ssz, SyncPeriod, allForks} from "@lodestar/types"; +import { + LightClientBootstrap, + LightClientFinalityUpdate, + LightClientOptimisticUpdate, + LightClientUpdate, + ssz, + SyncPeriod, +} from "@lodestar/types"; import {ForkName} from "@lodestar/params"; import {ChainForkConfig} from "@lodestar/config"; import {Endpoint, RouteDefinitions, Schema} from "../../utils/index.js"; @@ -33,7 +40,7 @@ export type Endpoints = { "GET", {startPeriod: SyncPeriod; count: number}, {query: {start_period: number; count: number}}, - allForks.LightClientUpdate[], + LightClientUpdate[], {versions: ForkName[]} >; @@ -46,7 +53,7 @@ export type Endpoints = { "GET", EmptyArgs, EmptyRequest, - allForks.LightClientOptimisticUpdate, + LightClientOptimisticUpdate, VersionMeta >; @@ -55,7 +62,7 @@ export type Endpoints = { "GET", EmptyArgs, EmptyRequest, - allForks.LightClientFinalityUpdate, + LightClientFinalityUpdate, VersionMeta >; @@ -68,7 +75,7 @@ export type Endpoints = { "GET", {blockRoot: string}, {params: {block_root: string}}, - allForks.LightClientBootstrap, + LightClientBootstrap, VersionMeta >; @@ -105,7 +112,7 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions { const updates = data as unknown[]; - const value: allForks.LightClientUpdate[] = []; + const value: LightClientUpdate[] = []; for (let i = 0; i < updates.length; i++) { const version = meta.versions[i]; value.push(getLightClientForkTypes(version).LightClientUpdate.fromJson(updates[i])); @@ -132,9 +139,9 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions; @@ -349,7 +350,7 @@ export type Endpoints = { blinded_local?: boolean; }; }, - allForks.FullOrBlindedBeaconBlockOrContents, + BeaconBlockOrContents | BlindedBeaconBlock, ProduceBlockV3Meta >; @@ -361,7 +362,7 @@ export type Endpoints = { graffiti: string; }, {params: {slot: number}; query: {randao_reveal: string; graffiti: string}}, - allForks.BlindedBeaconBlock, + BlindedBeaconBlock, VersionMeta >; @@ -623,8 +624,7 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions - (isForkBlobs(fork) ? BlockContentsType : ssz[fork].BeaconBlock) as Type + (fork) => (isForkBlobs(fork) ? BlockContentsType : ssz[fork].BeaconBlock) as Type ), meta: VersionCodec, }, @@ -688,7 +688,7 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions + : ssz[version].BeaconBlock) as Type ), meta: { toJson: (meta) => ({ diff --git a/packages/api/src/builder/routes.ts b/packages/api/src/builder/routes.ts index d703085583da..7e6a6e24b1b7 100644 --- a/packages/api/src/builder/routes.ts +++ b/packages/api/src/builder/routes.ts @@ -1,6 +1,16 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {fromHexString, toHexString} from "@chainsafe/ssz"; -import {ssz, allForks, bellatrix, Slot, Root, BLSPubkey} from "@lodestar/types"; +import { + ssz, + bellatrix, + Slot, + Root, + BLSPubkey, + ExecutionPayload, + ExecutionPayloadAndBlobsBundle, + SignedBlindedBeaconBlock, + SignedBuilderBid, +} from "@lodestar/types"; import {ForkName, isForkBlobs} from "@lodestar/params"; import {ChainForkConfig} from "@lodestar/config"; @@ -26,7 +36,7 @@ import {fromHeaders} from "../utils/headers.js"; // In this case, we receive a success response (204) which is not handled as an error. The generic response // handler already checks the status code and will not attempt to parse the body, but it will return no value. // It is important that this type indicates that there might be no value to ensure it is properly handled downstream. -export type MaybeSignedBuilderBid = allForks.SignedBuilderBid | undefined; +export type MaybeSignedBuilderBid = SignedBuilderBid | undefined; const RegistrationsType = ArrayOf(ssz.bellatrix.SignedValidatorRegistrationV1); @@ -62,9 +72,9 @@ export type Endpoints = { submitBlindedBlock: Endpoint< "POST", - {signedBlindedBlock: allForks.SignedBlindedBeaconBlock}, + {signedBlindedBlock: SignedBlindedBeaconBlock}, {body: unknown; headers: {[MetaHeader.Version]: string}}, - allForks.ExecutionPayload | allForks.ExecutionPayloadAndBlobsBundle, + ExecutionPayload | ExecutionPayloadAndBlobsBundle, VersionMeta >; }; @@ -138,13 +148,11 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions( - (fork: ForkName) => { - return isForkBlobs(fork) - ? ssz.allForksBlobs[fork].ExecutionPayloadAndBlobsBundle - : getExecutionForkTypes(fork).ExecutionPayload; - } - ), + data: WithVersion((fork: ForkName) => { + return isForkBlobs(fork) + ? ssz.allForksBlobs[fork].ExecutionPayloadAndBlobsBundle + : getExecutionForkTypes(fork).ExecutionPayload; + }), meta: VersionCodec, }, }, diff --git a/packages/api/src/utils/fork.ts b/packages/api/src/utils/fork.ts index 290147e7e47a..0925e750719a 100644 --- a/packages/api/src/utils/fork.ts +++ b/packages/api/src/utils/fork.ts @@ -1,5 +1,13 @@ -import {ForkName, isForkBlobs, isForkExecution, isForkLightClient} from "@lodestar/params"; -import {allForks, ssz} from "@lodestar/types"; +import { + ForkBlobs, + ForkExecution, + ForkLightClient, + ForkName, + isForkBlobs, + isForkExecution, + isForkLightClient, +} from "@lodestar/params"; +import {SSZBlindedTypesFor, SSZTypesFor, ssz, sszTypesFor} from "@lodestar/types"; export function toForkName(version: string): ForkName { // Teku returns fork as UPPERCASE @@ -11,30 +19,33 @@ export function toForkName(version: string): ForkName { return version as ForkName; } -export function getLightClientForkTypes(fork: ForkName): allForks.AllForksLightClientSSZTypes { +export function getLightClientForkTypes(fork: ForkName): SSZTypesFor { if (!isForkLightClient(fork)) { throw Error(`Invalid fork=${fork} for lightclient fork types`); } - return ssz.allForksLightClient[fork]; + + return sszTypesFor(fork); } -export function getExecutionForkTypes(fork: ForkName): allForks.AllForksExecutionSSZTypes { +export function getExecutionForkTypes(fork: ForkName): SSZTypesFor { if (!isForkExecution(fork)) { throw Error(`Invalid fork=${fork} for execution fork types`); } - return ssz.allForksExecution[fork]; + + return sszTypesFor(fork); } -export function getBlindedForkTypes(fork: ForkName): allForks.AllForksBlindedSSZTypes { +export function getBlindedForkTypes(fork: ForkName): SSZBlindedTypesFor { if (!isForkExecution(fork)) { throw Error(`Invalid fork=${fork} for blinded fork types`); } - return ssz.allForksBlinded[fork] as allForks.AllForksBlindedSSZTypes; + return ssz.allForksBlinded[fork]; } -export function getBlobsForkTypes(fork: ForkName): allForks.AllForksBlobsSSZTypes { +export function getBlobsForkTypes(fork: ForkName): SSZTypesFor { if (!isForkBlobs(fork)) { throw Error(`Invalid fork=${fork} for blobs fork types`); } - return ssz.allForksBlobs[fork]; + + return sszTypesFor(fork); } diff --git a/packages/api/test/unit/beacon/testData/beacon.ts b/packages/api/test/unit/beacon/testData/beacon.ts index c7dde0077944..a6b48320bf8c 100644 --- a/packages/api/test/unit/beacon/testData/beacon.ts +++ b/packages/api/test/unit/beacon/testData/beacon.ts @@ -1,6 +1,6 @@ import {toHexString} from "@chainsafe/ssz"; import {ForkName} from "@lodestar/params"; -import {Slot, allForks, ssz} from "@lodestar/types"; +import {SignedBlindedBeaconBlock, Slot, ssz} from "@lodestar/types"; import { BlockHeaderResponse, BroadcastValidation, @@ -242,7 +242,7 @@ export const testData: GenericServerTestCases = { }, }; -function getDefaultBlindedBlock(slot: Slot): allForks.SignedBlindedBeaconBlock { +function getDefaultBlindedBlock(slot: Slot): SignedBlindedBeaconBlock { const block = ssz.bellatrix.SignedBlindedBeaconBlock.defaultValue(); block.message.slot = slot; return block; diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts index dc85daff8575..d4675de21185 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -3,7 +3,14 @@ import {ApplicationMethods} from "@lodestar/api/server"; import {computeEpochAtSlot, computeTimeAtSlot, reconstructFullBlockOrContents} from "@lodestar/state-transition"; import {SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params"; import {sleep, fromHex, toHex} from "@lodestar/utils"; -import {allForks, deneb, isSignedBlockContents, ProducedBlockSource} from "@lodestar/types"; +import { + deneb, + isSignedBlockContents, + ProducedBlockSource, + SignedBeaconBlock, + SignedBeaconBlockOrContents, + SignedBlindedBeaconBlock, +} from "@lodestar/types"; import { BlockSource, getBlockInput, @@ -53,7 +60,7 @@ export function getBeaconBlockApi({ opts: PublishBlockOpts = {} ) => { const seenTimestampSec = Date.now() / 1000; - let blockForImport: BlockInput, signedBlock: allForks.SignedBeaconBlock, blobSidecars: deneb.BlobSidecars; + let blockForImport: BlockInput, signedBlock: SignedBeaconBlock, blobSidecars: deneb.BlobSidecars; if (isSignedBlockContents(signedBlockOrContents)) { ({signedBlock} = signedBlockOrContents); @@ -463,8 +470,8 @@ export function getBeaconBlockApi({ async function reconstructBuilderBlockOrContents( chain: ApiModules["chain"], - signedBlindedBlock: allForks.SignedBlindedBeaconBlock -): Promise { + signedBlindedBlock: SignedBlindedBeaconBlock +): Promise { const executionBuilder = chain.executionBuilder; if (!executionBuilder) { throw Error("executionBuilder required to publish SignedBlindedBeaconBlock"); diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/utils.ts b/packages/beacon-node/src/api/impl/beacon/blocks/utils.ts index e69e4fa74419..f0d243967c22 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/utils.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/utils.ts @@ -1,7 +1,7 @@ -import {allForks} from "@lodestar/types"; import {routes} from "@lodestar/api"; import {blockToHeader} from "@lodestar/state-transition"; import {ChainForkConfig} from "@lodestar/config"; +import {SignedBeaconBlock} from "@lodestar/types"; import {GENESIS_SLOT} from "../../../../constants/index.js"; import {ApiError, ValidationError} from "../../errors.js"; import {IBeaconChain} from "../../../../chain/interface.js"; @@ -9,7 +9,7 @@ import {rootHexRegex} from "../../../../eth1/provider/utils.js"; export function toBeaconHeaderResponse( config: ChainForkConfig, - block: allForks.SignedBeaconBlock, + block: SignedBeaconBlock, canonical = false ): routes.beacon.BlockHeaderResponse { return { @@ -25,7 +25,7 @@ export function toBeaconHeaderResponse( export async function resolveBlockId( chain: IBeaconChain, blockId: routes.beacon.BlockId -): Promise<{block: allForks.SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean}> { +): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean}> { const res = await resolveBlockIdOrNull(chain, blockId); if (!res) { throw new ApiError(404, `No block found for id '${blockId}'`); @@ -37,7 +37,7 @@ export async function resolveBlockId( async function resolveBlockIdOrNull( chain: IBeaconChain, blockId: routes.beacon.BlockId -): Promise<{block: allForks.SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> { +): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> { blockId = String(blockId).toLowerCase(); if (blockId === "head") { return chain.getBlockByRoot(chain.forkChoice.getHead().blockRoot); diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index a74c9b2ec8cb..8fb40e4ecb6b 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -31,12 +31,14 @@ import { Epoch, ProducedBlockSource, bellatrix, - allForks, BLSSignature, isBlindedBeaconBlock, isBlockContents, phase0, Wei, + BeaconBlock, + BlockContents, + BlindedBeaconBlock, } from "@lodestar/types"; import {ExecutionStatus, DataAvailabilityStatus} from "@lodestar/fork-choice"; import {fromHex, toHex, resolveOrRacePromises, prettyWeiToEth} from "@lodestar/utils"; @@ -91,11 +93,11 @@ const BLOCK_PRODUCTION_RACE_CUTOFF_MS = 2_000; const BLOCK_PRODUCTION_RACE_TIMEOUT_MS = 12_000; type ProduceBlockOrContentsRes = {executionPayloadValue: Wei; consensusBlockValue: Wei} & ( - | {data: allForks.BeaconBlock; version: ForkPreBlobs} - | {data: allForks.BlockContents; version: ForkBlobs} + | {data: BeaconBlock; version: ForkPreBlobs} + | {data: BlockContents; version: ForkBlobs} ); type ProduceBlindedBlockRes = {executionPayloadValue: Wei; consensusBlockValue: Wei} & { - data: allForks.BlindedBeaconBlock; + data: BlindedBeaconBlock; version: ForkExecution; }; @@ -503,7 +505,7 @@ export function getValidatorApi({ } return { - data: {block, ...contents} as allForks.BlockContents, + data: {block, ...contents} as BlockContents, version, executionPayloadValue, consensusBlockValue, @@ -760,13 +762,13 @@ export function getValidatorApi({ } else { if (isBlockContents(data)) { const {block} = data; - const blindedBlock = beaconBlockToBlinded(config, block as allForks.AllForksExecution["BeaconBlock"]); + const blindedBlock = beaconBlockToBlinded(config, block as BeaconBlock); return { data: blindedBlock, meta: {...meta, executionPayloadBlinded: true}, }; } else { - const blindedBlock = beaconBlockToBlinded(config, data as allForks.AllForksExecution["BeaconBlock"]); + const blindedBlock = beaconBlockToBlinded(config, data as BeaconBlock); return { data: blindedBlock, meta: {...meta, executionPayloadBlinded: true}, @@ -786,12 +788,12 @@ export function getValidatorApi({ if (isBlockContents(data)) { const {block} = data; - const blindedBlock = beaconBlockToBlinded(config, block as allForks.AllForksExecution["BeaconBlock"]); + const blindedBlock = beaconBlockToBlinded(config, block as BeaconBlock); return {data: blindedBlock, meta: {version}}; } else if (isBlindedBeaconBlock(data)) { return {data, meta: {version}}; } else { - const blindedBlock = beaconBlockToBlinded(config, data as allForks.AllForksExecution["BeaconBlock"]); + const blindedBlock = beaconBlockToBlinded(config, data as BeaconBlock); return {data: blindedBlock, meta: {version}}; } }, diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index 9c467c26ca50..e67b6d1e9dbc 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -1,6 +1,6 @@ import {toHexString} from "@chainsafe/ssz"; -import {capella, ssz, allForks, altair} from "@lodestar/types"; -import {ForkSeq, INTERVALS_PER_SLOT, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params"; +import {capella, ssz, altair, BeaconBlock} from "@lodestar/types"; +import {ForkLightClient, ForkSeq, INTERVALS_PER_SLOT, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params"; import { CachedBeaconStateAltair, computeEpochAtSlot, @@ -306,7 +306,7 @@ export async function importBlock( callInNextEventLoop(() => { try { this.lightClientServer.onImportBlockHead( - block.message as allForks.AllForksLightClient["BeaconBlock"], + block.message as BeaconBlock, postState as CachedBeaconStateAltair, parentBlockSlot ); diff --git a/packages/beacon-node/src/chain/blocks/index.ts b/packages/beacon-node/src/chain/blocks/index.ts index 083ab6bd4471..eb8c2663c9b6 100644 --- a/packages/beacon-node/src/chain/blocks/index.ts +++ b/packages/beacon-node/src/chain/blocks/index.ts @@ -1,5 +1,5 @@ -import {allForks} from "@lodestar/types"; import {toHex, isErrorAborted} from "@lodestar/utils"; +import {SignedBeaconBlock} from "@lodestar/types"; import {JobItemQueue, isQueueErrorAborted} from "../../util/queue/index.js"; import {Metrics} from "../../metrics/metrics.js"; import {BlockError, BlockErrorCode, isBlockErrorAborted} from "../errors/index.js"; @@ -158,7 +158,7 @@ export async function processBlocks( } } -function getBlockError(e: unknown, block: allForks.SignedBeaconBlock): BlockError { +function getBlockError(e: unknown, block: SignedBeaconBlock): BlockError { if (e instanceof BlockError) { return e; } else if (e instanceof Error) { diff --git a/packages/beacon-node/src/chain/blocks/types.ts b/packages/beacon-node/src/chain/blocks/types.ts index 2996bac7887f..da573bb76334 100644 --- a/packages/beacon-node/src/chain/blocks/types.ts +++ b/packages/beacon-node/src/chain/blocks/types.ts @@ -1,6 +1,6 @@ import {CachedBeaconStateAllForks, computeEpochAtSlot} from "@lodestar/state-transition"; import {MaybeValidExecutionStatus, DataAvailabilityStatus} from "@lodestar/fork-choice"; -import {allForks, deneb, Slot, RootHex} from "@lodestar/types"; +import {deneb, Slot, RootHex, SignedBeaconBlock} from "@lodestar/types"; import {ForkSeq, ForkName} from "@lodestar/params"; import {ChainForkConfig} from "@lodestar/config"; @@ -47,7 +47,7 @@ type Availability = {availabilityPromise: Promise; resolveAvailability: (d type CachedBlobs = {blobsCache: BlobsCacheMap} & Availability; export type CachedData = ForkBlobsInfo & CachedBlobs; -export type BlockInput = {block: allForks.SignedBeaconBlock; source: BlockSource; blockBytes: Uint8Array | null} & ( +export type BlockInput = {block: SignedBeaconBlock; source: BlockSource; blockBytes: Uint8Array | null} & ( | {type: BlockInputType.preData | BlockInputType.outOfRangeData} | ({type: BlockInputType.availableData} & {blockData: BlockInputData}) // the blobsSource here is added to BlockInputBlobs when availability is resolved @@ -68,7 +68,7 @@ export function blockRequiresBlobs(config: ChainForkConfig, blockSlot: Slot, clo export const getBlockInput = { preData( config: ChainForkConfig, - block: allForks.SignedBeaconBlock, + block: SignedBeaconBlock, source: BlockSource, blockBytes: Uint8Array | null ): BlockInput { @@ -91,7 +91,7 @@ export const getBlockInput = { // building states or where importing data isn't important if valid child exists like ILs outOfRangeData( config: ChainForkConfig, - block: allForks.SignedBeaconBlock, + block: SignedBeaconBlock, source: BlockSource, blockBytes: Uint8Array | null ): BlockInput { @@ -108,7 +108,7 @@ export const getBlockInput = { availableData( config: ChainForkConfig, - block: allForks.SignedBeaconBlock, + block: SignedBeaconBlock, source: BlockSource, blockBytes: Uint8Array | null, blockData: BlockInputData @@ -127,7 +127,7 @@ export const getBlockInput = { dataPromise( config: ChainForkConfig, - block: allForks.SignedBeaconBlock, + block: SignedBeaconBlock, source: BlockSource, blockBytes: Uint8Array | null, cachedData: CachedData diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts index ed3e27551b88..d08a747259b1 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts @@ -6,7 +6,7 @@ import { isMergeTransitionBlock as isMergeTransitionBlockFn, isExecutionEnabled, } from "@lodestar/state-transition"; -import {bellatrix, allForks, Slot, deneb} from "@lodestar/types"; +import {bellatrix, Slot, deneb, SignedBeaconBlock} from "@lodestar/types"; import { IForkChoice, assertValidTerminalPowBlock, @@ -68,7 +68,7 @@ type VerifyBlockExecutionResponse = export async function verifyBlocksExecutionPayload( chain: VerifyBlockExecutionPayloadModules, parentBlock: ProtoBlock, - blocks: allForks.SignedBeaconBlock[], + blocks: SignedBeaconBlock[], preState0: CachedBeaconStateAllForks, signal: AbortSignal, opts: BlockProcessOpts & ImportBlockOpts @@ -274,7 +274,7 @@ export async function verifyBlocksExecutionPayload( */ export async function verifyBlockExecutionPayload( chain: VerifyBlockExecutionPayloadModules, - block: allForks.SignedBeaconBlock, + block: SignedBeaconBlock, preState0: CachedBeaconStateAllForks, opts: BlockProcessOpts, isOptimisticallySafe: boolean, @@ -393,7 +393,7 @@ export async function verifyBlockExecutionPayload( function getSegmentErrorResponse( {verifyResponse, blockIndex}: {verifyResponse: VerifyExecutionErrorResponse; blockIndex: number}, parentBlock: ProtoBlock, - blocks: allForks.SignedBeaconBlock[] + blocks: SignedBeaconBlock[] ): SegmentExecStatus { const {executionStatus, lvhResponse, execError} = verifyResponse; let invalidSegmentLVH: LVHInvalidResponse | undefined = undefined; diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksSignatures.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksSignatures.ts index 6c66b5d74c4c..e86549cda7d3 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksSignatures.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksSignatures.ts @@ -1,6 +1,6 @@ import {CachedBeaconStateAllForks, getBlockSignatureSets} from "@lodestar/state-transition"; -import {allForks} from "@lodestar/types"; import {Logger} from "@lodestar/utils"; +import {SignedBeaconBlock} from "@lodestar/types"; import {Metrics} from "../../metrics/metrics.js"; import {IBlsVerifier} from "../bls/index.js"; import {BlockError, BlockErrorCode} from "../errors/blockError.js"; @@ -19,7 +19,7 @@ export async function verifyBlocksSignatures( logger: Logger, metrics: Metrics | null, preState0: CachedBeaconStateAllForks, - blocks: allForks.SignedBeaconBlock[], + blocks: SignedBeaconBlock[], opts: ImportBlockOpts ): Promise<{verifySignaturesTime: number}> { const isValidPromises: Promise[] = []; @@ -37,7 +37,9 @@ export async function verifyBlocksSignatures( : // // Verify signatures per block to track which block is invalid bls.verifySignatureSets( - getBlockSignatureSets(preState0, block, {skipProposerSignature: opts.validProposerSignature}) + getBlockSignatureSets(preState0, block, { + skipProposerSignature: opts.validProposerSignature, + }) ); // getBlockSignatureSets() takes 45ms in benchmarks for 2022Q2 mainnet blocks (100 sigs). When syncing a 32 blocks diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 2f58962f3cc5..ccd10f7b4d6f 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -16,7 +16,6 @@ import { } from "@lodestar/state-transition"; import {BeaconConfig} from "@lodestar/config"; import { - allForks, UintNum64, Root, phase0, @@ -28,6 +27,11 @@ import { Wei, bellatrix, isBlindedBeaconBlock, + BeaconBlock, + SignedBeaconBlock, + ExecutionPayload, + BlindedBeaconBlock, + BlindedBeaconBlockBody, } from "@lodestar/types"; import {CheckpointWithHex, ExecutionStatus, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice"; import {ProcessShutdownCallback} from "@lodestar/validator"; @@ -157,7 +161,7 @@ export class BeaconChain implements IBeaconChain { // Cache payload from the local execution so that produceBlindedBlock or produceBlockV3 and // send and get signed/published blinded versions which beacon can assemble into full before // actual publish - readonly producedBlockRoot = new Map(); + readonly producedBlockRoot = new Map(); readonly producedBlindedBlockRoot = new Set(); readonly opts: IChainOptions; @@ -498,7 +502,7 @@ export class BeaconChain implements IBeaconChain { async getCanonicalBlockAtSlot( slot: Slot - ): Promise<{block: allForks.SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> { + ): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> { const finalizedBlock = this.forkChoice.getFinalizedBlock(); if (slot > finalizedBlock.slot) { // Unfinalized slot, attempt to find in fork-choice @@ -520,7 +524,7 @@ export class BeaconChain implements IBeaconChain { async getBlockByRoot( root: string - ): Promise<{block: allForks.SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> { + ): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> { const block = this.forkChoice.getBlockHex(root); if (block) { const data = await this.db.block.get(fromHexString(root)); @@ -554,7 +558,7 @@ export class BeaconChain implements IBeaconChain { } produceBlock(blockAttributes: BlockAttributes & {commonBlockBody?: CommonBlockBody}): Promise<{ - block: allForks.BeaconBlock; + block: BeaconBlock; executionPayloadValue: Wei; consensusBlockValue: Wei; shouldOverrideBuilder?: boolean; @@ -563,7 +567,7 @@ export class BeaconChain implements IBeaconChain { } produceBlindedBlock(blockAttributes: BlockAttributes & {commonBlockBody?: CommonBlockBody}): Promise<{ - block: allForks.BlindedBeaconBlock; + block: BlindedBeaconBlock; executionPayloadValue: Wei; consensusBlockValue: Wei; }> { @@ -616,7 +620,7 @@ export class BeaconChain implements IBeaconChain { const bodyRoot = blockType === BlockType.Full ? this.config.getForkTypes(slot).BeaconBlockBody.hashTreeRoot(body) - : this.config.getBlindedForkTypes(slot).BeaconBlockBody.hashTreeRoot(body as allForks.BlindedBeaconBlockBody); + : this.config.getBlindedForkTypes(slot).BeaconBlockBody.hashTreeRoot(body as BlindedBeaconBlockBody); this.logger.debug("Computing block post state from the produced body", { slot, bodyRoot: toHexString(bodyRoot), @@ -636,7 +640,7 @@ export class BeaconChain implements IBeaconChain { const blockRoot = blockType === BlockType.Full ? this.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block) - : this.config.getBlindedForkTypes(slot).BeaconBlock.hashTreeRoot(block as allForks.BlindedBeaconBlock); + : this.config.getBlindedForkTypes(slot).BeaconBlock.hashTreeRoot(block as BlindedBeaconBlock); const blockRootHex = toHex(blockRoot); // track the produced block for consensus broadcast validations @@ -770,7 +774,7 @@ export class BeaconChain implements IBeaconChain { return this.reprocessController.waitForBlockOfAttestation(slot, root); } - persistBlock(data: allForks.BeaconBlock | allForks.BlindedBeaconBlock, suffix?: string): void { + persistBlock(data: BeaconBlock | BlindedBeaconBlock, suffix?: string): void { const slot = data.slot; if (isBlindedBeaconBlock(data)) { const sszType = this.config.getBlindedForkTypes(slot).BeaconBlock; @@ -1094,7 +1098,7 @@ export class BeaconChain implements IBeaconChain { } } - async getBlockRewards(block: allForks.FullOrBlindedBeaconBlock): Promise { + async getBlockRewards(block: BeaconBlock | BlindedBeaconBlock): Promise { const preState = this.regen.getPreStateSync(block); if (preState === null) { @@ -1133,7 +1137,7 @@ export class BeaconChain implements IBeaconChain { } async getSyncCommitteeRewards( - block: allForks.FullOrBlindedBeaconBlock, + block: BeaconBlock | BlindedBeaconBlock, validatorIds?: (ValidatorIndex | string)[] ): Promise { const preState = this.regen.getPreStateSync(block); diff --git a/packages/beacon-node/src/chain/errors/blockError.ts b/packages/beacon-node/src/chain/errors/blockError.ts index 6ab15275934e..5f12bd939342 100644 --- a/packages/beacon-node/src/chain/errors/blockError.ts +++ b/packages/beacon-node/src/chain/errors/blockError.ts @@ -1,5 +1,5 @@ import {toHexString} from "@chainsafe/ssz"; -import {allForks, RootHex, Slot, ValidatorIndex} from "@lodestar/types"; +import {RootHex, SignedBeaconBlock, Slot, ValidatorIndex} from "@lodestar/types"; import {LodestarError} from "@lodestar/utils"; import {CachedBeaconStateAllForks} from "@lodestar/state-transition"; import {ExecutionPayloadStatus} from "../../execution/engine/interface.js"; @@ -112,7 +112,7 @@ export class BlockGossipError extends GossipActionError {} export class BlockError extends LodestarError { constructor( - readonly signedBlock: allForks.SignedBeaconBlock, + readonly signedBlock: SignedBeaconBlock, type: BlockErrorType ) { super(type); diff --git a/packages/beacon-node/src/chain/initState.ts b/packages/beacon-node/src/chain/initState.ts index 20a2188136b5..aae03a07f50c 100644 --- a/packages/beacon-node/src/chain/initState.ts +++ b/packages/beacon-node/src/chain/initState.ts @@ -7,7 +7,7 @@ import { computeCheckpointEpochAtStateSlot, computeStartSlotAtEpoch, } from "@lodestar/state-transition"; -import {phase0, allForks, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {Logger, toHex} from "@lodestar/utils"; import {GENESIS_SLOT, ZERO_HASH} from "../constants/index.js"; @@ -21,7 +21,7 @@ import {GenesisResult} from "./genesis/interface.js"; export async function persistGenesisResult( db: IBeaconDb, genesisResult: GenesisResult, - genesisBlock: allForks.SignedBeaconBlock + genesisBlock: SignedBeaconBlock ): Promise { await Promise.all([ db.stateArchive.add(genesisResult.state), @@ -52,10 +52,7 @@ export async function persistAnchorState( } } -export function createGenesisBlock( - config: ChainForkConfig, - genesisState: BeaconStateAllForks -): allForks.SignedBeaconBlock { +export function createGenesisBlock(config: ChainForkConfig, genesisState: BeaconStateAllForks): SignedBeaconBlock { const types = config.getForkTypes(GENESIS_SLOT); const genesisBlock = types.SignedBeaconBlock.defaultValue(); const stateRoot = genesisState.hashTreeRoot(); diff --git a/packages/beacon-node/src/chain/interface.ts b/packages/beacon-node/src/chain/interface.ts index af7aeb47dd9c..e412d8e8aafa 100644 --- a/packages/beacon-node/src/chain/interface.ts +++ b/packages/beacon-node/src/chain/interface.ts @@ -1,6 +1,5 @@ import {CompositeTypeAny, TreeView, Type} from "@chainsafe/ssz"; import { - allForks, UintNum64, Root, phase0, @@ -12,6 +11,10 @@ import { Wei, capella, altair, + BeaconBlock, + ExecutionPayload, + SignedBeaconBlock, + BlindedBeaconBlock, } from "@lodestar/types"; import { BeaconStateAllForks, @@ -22,7 +25,6 @@ import { } from "@lodestar/state-transition"; import {BeaconConfig} from "@lodestar/config"; import {Logger} from "@lodestar/utils"; - import {CheckpointWithHex, IForkChoice, ProtoBlock} from "@lodestar/fork-choice"; import {IEth1ForBlockProduction} from "../eth1/index.js"; import {IExecutionEngine, IExecutionBuilder} from "../execution/index.js"; @@ -120,7 +122,7 @@ export interface IBeaconChain { readonly beaconProposerCache: BeaconProposerCache; readonly checkpointBalancesCache: CheckpointBalancesCache; readonly producedContentsCache: Map; - readonly producedBlockRoot: Map; + readonly producedBlockRoot: Map; readonly shufflingCache: ShufflingCache; readonly producedBlindedBlockRoot: Set; readonly opts: IChainOptions; @@ -162,25 +164,25 @@ export interface IBeaconChain { */ getCanonicalBlockAtSlot( slot: Slot - ): Promise<{block: allForks.SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null>; + ): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null>; /** * Get local block by root, does not fetch from the network */ getBlockByRoot( root: RootHex - ): Promise<{block: allForks.SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null>; + ): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null>; getContents(beaconBlock: deneb.BeaconBlock): deneb.Contents; produceCommonBlockBody(blockAttributes: BlockAttributes): Promise; produceBlock(blockAttributes: BlockAttributes & {commonBlockBody?: CommonBlockBody}): Promise<{ - block: allForks.BeaconBlock; + block: BeaconBlock; executionPayloadValue: Wei; consensusBlockValue: Wei; shouldOverrideBuilder?: boolean; }>; produceBlindedBlock(blockAttributes: BlockAttributes & {commonBlockBody?: CommonBlockBody}): Promise<{ - block: allForks.BlindedBeaconBlock; + block: BlindedBeaconBlock; executionPayloadValue: Wei; consensusBlockValue: Wei; }>; @@ -204,7 +206,7 @@ export interface IBeaconChain { updateBeaconProposerData(epoch: Epoch, proposers: ProposerPreparationData[]): Promise; - persistBlock(data: allForks.BeaconBlock | allForks.BlindedBeaconBlock, suffix?: string): void; + persistBlock(data: BeaconBlock | BlindedBeaconBlock, suffix?: string): void; persistInvalidSszValue(type: Type, sszObject: T | Uint8Array, suffix?: string): void; persistInvalidSszBytes(type: string, sszBytes: Uint8Array, suffix?: string): void; /** Persist bad items to persistInvalidSszObjectsDir dir, for example invalid state, attestations etc. */ @@ -220,13 +222,13 @@ export interface IBeaconChain { regenCanAcceptWork(): boolean; blsThreadPoolCanAcceptWork(): boolean; - getBlockRewards(blockRef: allForks.FullOrBlindedBeaconBlock): Promise; + getBlockRewards(blockRef: BeaconBlock | BlindedBeaconBlock): Promise; getAttestationsRewards( epoch: Epoch, validatorIds?: (ValidatorIndex | string)[] ): Promise<{rewards: AttestationsRewards; executionOptimistic: boolean; finalized: boolean}>; getSyncCommitteeRewards( - blockRef: allForks.FullOrBlindedBeaconBlock, + blockRef: BeaconBlock | BlindedBeaconBlock, validatorIds?: (ValidatorIndex | string)[] ): Promise; } diff --git a/packages/beacon-node/src/chain/lightClient/index.ts b/packages/beacon-node/src/chain/lightClient/index.ts index 75d094036f2d..5d25985df66e 100644 --- a/packages/beacon-node/src/chain/lightClient/index.ts +++ b/packages/beacon-node/src/chain/lightClient/index.ts @@ -1,5 +1,21 @@ import {BitArray, CompositeViewDU, toHexString} from "@chainsafe/ssz"; -import {altair, phase0, Root, RootHex, Slot, ssz, SyncPeriod, allForks} from "@lodestar/types"; +import { + altair, + BeaconBlock, + BeaconBlockBody, + LightClientBootstrap, + LightClientFinalityUpdate, + LightClientHeader, + LightClientOptimisticUpdate, + LightClientUpdate, + phase0, + Root, + RootHex, + Slot, + ssz, + SSZTypesFor, + SyncPeriod, +} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import { CachedBeaconStateAltair, @@ -16,7 +32,14 @@ import { } from "@lodestar/light-client/spec"; import {Logger, MapDef, pruneSetToMax} from "@lodestar/utils"; import {routes} from "@lodestar/api"; -import {MIN_SYNC_COMMITTEE_PARTICIPANTS, SYNC_COMMITTEE_SIZE, ForkName, ForkSeq, ForkExecution} from "@lodestar/params"; +import { + MIN_SYNC_COMMITTEE_PARTICIPANTS, + SYNC_COMMITTEE_SIZE, + ForkName, + ForkSeq, + ForkExecution, + ForkLightClient, +} from "@lodestar/params"; import {IBeaconDb} from "../../db/index.js"; import {Metrics} from "../../metrics/index.js"; @@ -40,7 +63,7 @@ type DependentRootHex = RootHex; type BlockRooHex = RootHex; export type SyncAttestedData = { - attestedHeader: allForks.LightClientHeader; + attestedHeader: LightClientHeader; /** Precomputed root to prevent re-hashing */ blockRoot: Uint8Array; } & ( @@ -178,11 +201,11 @@ export class LightClientServer { * Keep in memory since this data is very transient, not useful after a few slots */ private readonly prevHeadData = new Map(); - private checkpointHeaders = new Map(); - private latestHeadUpdate: allForks.LightClientOptimisticUpdate | null = null; + private checkpointHeaders = new Map(); + private latestHeadUpdate: LightClientOptimisticUpdate | null = null; private readonly zero: Pick; - private finalized: allForks.LightClientFinalityUpdate | null = null; + private finalized: LightClientFinalityUpdate | null = null; constructor( private readonly opts: LightClientServerOpts, @@ -225,7 +248,7 @@ export class LightClientServer { * - Use block's syncAggregate */ onImportBlockHead( - block: allForks.AllForksLightClient["BeaconBlock"], + block: BeaconBlock, postState: CachedBeaconStateAltair, parentBlockSlot: Slot ): void { @@ -260,7 +283,7 @@ export class LightClientServer { /** * API ROUTE to get `currentSyncCommittee` and `nextSyncCommittee` from a trusted state root */ - async getBootstrap(blockRoot: Uint8Array): Promise { + async getBootstrap(blockRoot: Uint8Array): Promise { const syncCommitteeWitness = await this.db.syncCommitteeWitness.get(blockRoot); if (!syncCommitteeWitness) { throw new LightClientServerError( @@ -305,7 +328,7 @@ export class LightClientServer { * - Has the most bits * - Signed header at the oldest slot */ - async getUpdate(period: number): Promise { + async getUpdate(period: number): Promise { // Signature data const update = await this.db.bestLightClientUpdate.get(period); if (!update) { @@ -336,11 +359,11 @@ export class LightClientServer { * API ROUTE to poll LightclientHeaderUpdate. * Clients should use the SSE type `light_client_optimistic_update` if available */ - getOptimisticUpdate(): allForks.LightClientOptimisticUpdate | null { + getOptimisticUpdate(): LightClientOptimisticUpdate | null { return this.latestHeadUpdate; } - getFinalityUpdate(): allForks.LightClientFinalityUpdate | null { + getFinalityUpdate(): LightClientFinalityUpdate | null { return this.finalized; } @@ -356,7 +379,7 @@ export class LightClientServer { } private async persistPostBlockImportData( - block: allForks.AllForksLightClient["BeaconBlock"], + block: BeaconBlock, postState: CachedBeaconStateAltair, parentBlockSlot: Slot ): Promise { @@ -476,7 +499,7 @@ export class LightClientServer { return; } - const headerUpdate: allForks.LightClientOptimisticUpdate = { + const headerUpdate: LightClientOptimisticUpdate = { attestedHeader, syncAggregate, signatureSlot, @@ -633,7 +656,7 @@ export class LightClientServer { finalityBranch, syncAggregate, signatureSlot, - } as allForks.LightClientUpdate; + } as LightClientUpdate; // attestedData and the block of syncAggregate may not be in same sync period // should not use attested data slot as sync period @@ -669,7 +692,7 @@ export class LightClientServer { /** * Get finalized header from db. Keeps a small in-memory cache to speed up most of the lookups */ - private async getFinalizedHeader(finalizedBlockRoot: Uint8Array): Promise { + private async getFinalizedHeader(finalizedBlockRoot: Uint8Array): Promise { const finalizedBlockRootHex = toHexString(finalizedBlockRoot); const cachedFinalizedHeader = this.checkpointHeaders.get(finalizedBlockRootHex); if (cachedFinalizedHeader) { @@ -697,28 +720,23 @@ export function sumBits(bits: BitArray): number { return bits.getTrueBitIndexes().length; } -export function blockToLightClientHeader( - fork: ForkName, - block: allForks.AllForksLightClient["BeaconBlock"] -): allForks.LightClientHeader { +export function blockToLightClientHeader(fork: ForkName, block: BeaconBlock): LightClientHeader { const blockSlot = block.slot; const beacon: phase0.BeaconBlockHeader = { slot: blockSlot, proposerIndex: block.proposerIndex, parentRoot: block.parentRoot, stateRoot: block.stateRoot, - bodyRoot: (ssz[fork].BeaconBlockBody as allForks.AllForksLightClientSSZTypes["BeaconBlockBody"]).hashTreeRoot( - block.body - ), + bodyRoot: (ssz[fork].BeaconBlockBody as SSZTypesFor).hashTreeRoot(block.body), }; if (ForkSeq[fork] >= ForkSeq.capella) { - const blockBody = block.body as allForks.AllForksExecution["BeaconBlockBody"]; + const blockBody = block.body as BeaconBlockBody; const execution = executionPayloadToPayloadHeader(ForkSeq[fork], blockBody.executionPayload); return { beacon, execution, executionBranch: getBlockBodyExecutionHeaderProof(fork as ForkExecution, blockBody), - } as allForks.LightClientHeader; + } as LightClientHeader; } else { return {beacon}; } diff --git a/packages/beacon-node/src/chain/lightClient/proofs.ts b/packages/beacon-node/src/chain/lightClient/proofs.ts index cf1735e706d6..87ad4544ec69 100644 --- a/packages/beacon-node/src/chain/lightClient/proofs.ts +++ b/packages/beacon-node/src/chain/lightClient/proofs.ts @@ -1,7 +1,7 @@ import {Tree} from "@chainsafe/persistent-merkle-tree"; import {BeaconStateAllForks} from "@lodestar/state-transition"; import {FINALIZED_ROOT_GINDEX, BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX, ForkExecution} from "@lodestar/params"; -import {allForks, ssz} from "@lodestar/types"; +import {BeaconBlockBody, SSZTypesFor, ssz} from "@lodestar/types"; import {SyncCommitteeWitness} from "./types.js"; @@ -47,8 +47,8 @@ export function getFinalizedRootProof(state: BeaconStateAllForks): Uint8Array[] export function getBlockBodyExecutionHeaderProof( fork: ForkExecution, - body: allForks.AllForksExecution["BeaconBlockBody"] + body: BeaconBlockBody ): Uint8Array[] { - const bodyView = (ssz[fork].BeaconBlockBody as allForks.AllForksExecutionSSZTypes["BeaconBlockBody"]).toView(body); + const bodyView = (ssz[fork].BeaconBlockBody as SSZTypesFor).toView(body); return new Tree(bodyView.node).getSingleProof(BigInt(BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX)); } diff --git a/packages/beacon-node/src/chain/opPools/opPool.ts b/packages/beacon-node/src/chain/opPools/opPool.ts index 1fdee886ff1d..69c331f6fd39 100644 --- a/packages/beacon-node/src/chain/opPools/opPool.ts +++ b/packages/beacon-node/src/chain/opPools/opPool.ts @@ -15,7 +15,7 @@ import { MAX_ATTESTER_SLASHINGS, ForkSeq, } from "@lodestar/params"; -import {Epoch, phase0, capella, ssz, ValidatorIndex, allForks} from "@lodestar/types"; +import {Epoch, phase0, capella, ssz, ValidatorIndex, SignedBeaconBlock} from "@lodestar/types"; import {IBeaconDb} from "../../db/index.js"; import {SignedBLSToExecutionChangeVersioned} from "../../util/types.js"; import {BlockType} from "../interface.js"; @@ -304,7 +304,7 @@ export class OpPool { /** * Prune all types of transactions given the latest head state */ - pruneAll(headBlock: allForks.SignedBeaconBlock, headState: CachedBeaconStateAllForks): void { + pruneAll(headBlock: SignedBeaconBlock, headState: CachedBeaconStateAllForks): void { this.pruneAttesterSlashings(headState); this.pruneProposerSlashings(headState); this.pruneVoluntaryExits(headState); @@ -377,10 +377,7 @@ export class OpPool { * In the worse case where head block is reorged, the same BlsToExecutionChange message can be re-added * to opPool once gossipsub seen cache TTL passes. */ - private pruneBlsToExecutionChanges( - headBlock: allForks.SignedBeaconBlock, - headState: CachedBeaconStateAllForks - ): void { + private pruneBlsToExecutionChanges(headBlock: SignedBeaconBlock, headState: CachedBeaconStateAllForks): void { const {config} = headState; const recentBlsToExecutionChanges = config.getForkSeq(headBlock.message.slot) >= ForkSeq.capella diff --git a/packages/beacon-node/src/chain/produceBlock/computeNewStateRoot.ts b/packages/beacon-node/src/chain/produceBlock/computeNewStateRoot.ts index ccc0595d0db6..bfa30e570e06 100644 --- a/packages/beacon-node/src/chain/produceBlock/computeNewStateRoot.ts +++ b/packages/beacon-node/src/chain/produceBlock/computeNewStateRoot.ts @@ -5,7 +5,7 @@ import { StateHashTreeRootSource, stateTransition, } from "@lodestar/state-transition"; -import {allForks, Gwei, Root} from "@lodestar/types"; +import {BeaconBlock, BlindedBeaconBlock, Gwei, Root} from "@lodestar/types"; import {ZERO_HASH} from "../../constants/index.js"; import {Metrics} from "../../metrics/index.js"; @@ -17,10 +17,10 @@ import {Metrics} from "../../metrics/index.js"; export function computeNewStateRoot( metrics: Metrics | null, state: CachedBeaconStateAllForks, - block: allForks.FullOrBlindedBeaconBlock + block: BeaconBlock | BlindedBeaconBlock ): {newStateRoot: Root; proposerReward: Gwei} { // Set signature to zero to re-use stateTransition() function which requires the SignedBeaconBlock type - const blockEmptySig = {message: block, signature: ZERO_HASH} as allForks.FullOrBlindedSignedBeaconBlock; + const blockEmptySig = {message: block, signature: ZERO_HASH}; const postState = stateTransition( state, diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index c1c1d5eaa447..5165b6b3f7ff 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -1,6 +1,5 @@ import { Bytes32, - allForks, Root, RootHex, Slot, @@ -11,6 +10,12 @@ import { capella, deneb, Wei, + SSEPayloadAttributes, + BeaconBlock, + BeaconBlockBody, + ExecutionPayloadHeader, + BlindedBeaconBlockBody, + BlindedBeaconBlock, } from "@lodestar/types"; import { CachedBeaconStateAllForks, @@ -27,7 +32,6 @@ import { import {ChainForkConfig} from "@lodestar/config"; import {ForkSeq, ForkExecution, isForkExecution} from "@lodestar/params"; import {toHex, sleep, Logger} from "@lodestar/utils"; - import type {BeaconChain} from "../chain.js"; import {PayloadId, IExecutionEngine, IExecutionBuilder, PayloadAttributes} from "../../execution/index.js"; import {ZERO_HASH, ZERO_HASH_HEX} from "../../constants/index.js"; @@ -73,11 +77,9 @@ export enum BlockType { Blinded = "Blinded", } export type AssembledBodyType = T extends BlockType.Full - ? allForks.BeaconBlockBody - : allForks.BlindedBeaconBlockBody; -export type AssembledBlockType = T extends BlockType.Full - ? allForks.BeaconBlock - : allForks.BlindedBeaconBlock; + ? BeaconBlockBody + : BlindedBeaconBlockBody; +export type AssembledBlockType = T extends BlockType.Full ? BeaconBlock : BlindedBeaconBlock; export enum BlobsResultType { preDeneb, @@ -191,7 +193,7 @@ export async function produceBlockBody( currentState as CachedBeaconStateBellatrix, proposerPubKey ); - (blockBody as allForks.BlindedBeaconBlockBody).executionPayloadHeader = builderRes.header; + (blockBody as BlindedBeaconBlockBody).executionPayloadHeader = builderRes.header; executionPayloadValue = builderRes.executionPayloadValue; const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime); @@ -237,7 +239,7 @@ export async function produceBlockBody( ); if (prepareRes.isPremerge) { - (blockBody as allForks.ExecutionBlockBody).executionPayload = + (blockBody as BeaconBlockBody).executionPayload = ssz.allForksExecution[fork].ExecutionPayload.defaultValue(); blobsResult = {type: BlobsResultType.preDeneb}; executionPayloadValue = BigInt(0); @@ -258,7 +260,7 @@ export async function produceBlockBody( const {executionPayload, blobsBundle} = engineRes; shouldOverrideBuilder = engineRes.shouldOverrideBuilder; - (blockBody as allForks.ExecutionBlockBody).executionPayload = executionPayload; + (blockBody as BeaconBlockBody).executionPayload = executionPayload; executionPayloadValue = engineRes.executionPayloadValue; Object.assign(logMeta, {transactions: executionPayload.transactions.length, shouldOverrideBuilder}); @@ -307,7 +309,7 @@ export async function produceBlockBody( {}, e as Error ); - (blockBody as allForks.ExecutionBlockBody).executionPayload = + (blockBody as BeaconBlockBody).executionPayload = ssz.allForksExecution[fork].ExecutionPayload.defaultValue(); blobsResult = {type: BlobsResultType.preDeneb}; executionPayloadValue = BigInt(0); @@ -441,7 +443,7 @@ async function prepareExecutionPayloadHeader( state: CachedBeaconStateBellatrix, proposerPubKey: BLSPubkey ): Promise<{ - header: allForks.ExecutionPayloadHeader; + header: ExecutionPayloadHeader; executionPayloadValue: Wei; blobKzgCommitments?: deneb.BlobKzgCommitments; }> { @@ -504,7 +506,7 @@ export async function getPayloadAttributesForSSE( parentBlockRoot, feeRecipient, }: {prepareState: CachedBeaconStateExecutions; prepareSlot: Slot; parentBlockRoot: Root; feeRecipient: string} -): Promise { +): Promise { const parentHashRes = await getExecutionPayloadParentHash(chain, prepareState); if (!parentHashRes.isPremerge) { @@ -516,7 +518,7 @@ export async function getPayloadAttributesForSSE( feeRecipient, }); - const ssePayloadAttributes: allForks.SSEPayloadAttributes = { + const ssePayloadAttributes: SSEPayloadAttributes = { proposerIndex: prepareState.epochCtx.getBeaconProposer(prepareSlot), proposalSlot: prepareSlot, parentBlockNumber: prepareState.latestExecutionPayloadHeader.blockNumber, @@ -546,7 +548,7 @@ function preparePayloadAttributes( parentBlockRoot: Root; feeRecipient: string; } -): allForks.SSEPayloadAttributes["payloadAttributes"] { +): SSEPayloadAttributes["payloadAttributes"] { const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime); const prevRandao = getRandaoMix(prepareState, prepareState.epochCtx.epoch); const payloadAttributes = { diff --git a/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts b/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts index 54e90672d189..ba086ecafc7e 100644 --- a/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts +++ b/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts @@ -1,11 +1,11 @@ -import {allForks} from "@lodestar/types"; +import {ExecutionPayload} from "@lodestar/types"; import {BlobsBundle} from "../../execution/index.js"; /** * Optionally sanity-check that the KZG commitments match the versioned hashes in the transactions * https://github.com/ethereum/consensus-specs/blob/11a037fd9227e29ee809c9397b09f8cc3383a8c0/specs/eip4844/validator.md#blob-kzg-commitments */ -export function validateBlobsAndKzgCommitments(payload: allForks.ExecutionPayload, blobsBundle: BlobsBundle): void { +export function validateBlobsAndKzgCommitments(payload: ExecutionPayload, blobsBundle: BlobsBundle): void { // sanity-check that the KZG commitments match the blobs (as produced by the execution engine) if (blobsBundle.blobs.length !== blobsBundle.commitments.length) { throw Error( diff --git a/packages/beacon-node/src/chain/regen/interface.ts b/packages/beacon-node/src/chain/regen/interface.ts index a1021de4aeab..341625c9ff1d 100644 --- a/packages/beacon-node/src/chain/regen/interface.ts +++ b/packages/beacon-node/src/chain/regen/interface.ts @@ -1,4 +1,4 @@ -import {allForks, phase0, Slot, RootHex, Epoch} from "@lodestar/types"; +import {phase0, Slot, RootHex, Epoch, BeaconBlock} from "@lodestar/types"; import {CachedBeaconStateAllForks} from "@lodestar/state-transition"; import {routes} from "@lodestar/api"; import {ProtoBlock} from "@lodestar/fork-choice"; @@ -36,7 +36,7 @@ export interface IStateRegenerator extends IStateRegeneratorInternal { dropCache(): void; dumpCacheSummary(): routes.lodestar.StateCacheItem[]; getStateSync(stateRoot: RootHex): CachedBeaconStateAllForks | null; - getPreStateSync(block: allForks.BeaconBlock): CachedBeaconStateAllForks | null; + getPreStateSync(block: BeaconBlock): CachedBeaconStateAllForks | null; getCheckpointStateOrBytes(cp: CheckpointHex): Promise; getCheckpointStateSync(cp: CheckpointHex): CachedBeaconStateAllForks | null; getClosestHeadState(head: ProtoBlock): CachedBeaconStateAllForks | null; @@ -56,11 +56,7 @@ export interface IStateRegeneratorInternal { * Return a valid pre-state for a beacon block * This will always return a state in the latest viable epoch */ - getPreState( - block: allForks.BeaconBlock, - opts: StateCloneOpts, - rCaller: RegenCaller - ): Promise; + getPreState(block: BeaconBlock, opts: StateCloneOpts, rCaller: RegenCaller): Promise; /** * Return a valid checkpoint state diff --git a/packages/beacon-node/src/chain/regen/queued.ts b/packages/beacon-node/src/chain/regen/queued.ts index 148ec756497d..358a37e6e638 100644 --- a/packages/beacon-node/src/chain/regen/queued.ts +++ b/packages/beacon-node/src/chain/regen/queued.ts @@ -1,5 +1,5 @@ import {toHexString} from "@chainsafe/ssz"; -import {phase0, Slot, allForks, RootHex, Epoch} from "@lodestar/types"; +import {phase0, Slot, RootHex, Epoch, BeaconBlock} from "@lodestar/types"; import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice"; import {CachedBeaconStateAllForks, computeEpochAtSlot} from "@lodestar/state-transition"; import {Logger} from "@lodestar/utils"; @@ -86,7 +86,7 @@ export class QueuedStateRegenerator implements IStateRegenerator { * which is usually the gossip block. */ getPreStateSync( - block: allForks.BeaconBlock, + block: BeaconBlock, opts: StateCloneOpts = {dontTransferCache: true} ): CachedBeaconStateAllForks | null { const parentRoot = toHexString(block.parentRoot); @@ -200,7 +200,7 @@ export class QueuedStateRegenerator implements IStateRegenerator { * - State after `block.parentRoot` dialed forward to block.slot */ async getPreState( - block: allForks.BeaconBlock, + block: BeaconBlock, opts: StateCloneOpts, rCaller: RegenCaller ): Promise { diff --git a/packages/beacon-node/src/chain/regen/regen.ts b/packages/beacon-node/src/chain/regen/regen.ts index 3fe6f08e1383..2b6fc835cf7c 100644 --- a/packages/beacon-node/src/chain/regen/regen.ts +++ b/packages/beacon-node/src/chain/regen/regen.ts @@ -1,5 +1,5 @@ import {fromHexString, toHexString} from "@chainsafe/ssz"; -import {allForks, phase0, Slot, RootHex} from "@lodestar/types"; +import {phase0, Slot, RootHex, BeaconBlock} from "@lodestar/types"; import { CachedBeaconStateAllForks, computeEpochAtSlot, @@ -50,7 +50,7 @@ export class StateRegenerator implements IStateRegeneratorInternal { * - reload state if needed in this flow */ async getPreState( - block: allForks.BeaconBlock, + block: BeaconBlock, opts: StateCloneOpts, regenCaller: RegenCaller ): Promise { diff --git a/packages/beacon-node/src/chain/rewards/blockRewards.ts b/packages/beacon-node/src/chain/rewards/blockRewards.ts index bd8bf3537582..65dc23496070 100644 --- a/packages/beacon-node/src/chain/rewards/blockRewards.ts +++ b/packages/beacon-node/src/chain/rewards/blockRewards.ts @@ -5,7 +5,7 @@ import { getAttesterSlashableIndices, processAttestationsAltair, } from "@lodestar/state-transition"; -import {allForks, altair, phase0} from "@lodestar/types"; +import {BeaconBlock, altair, phase0} from "@lodestar/types"; import {ForkName, WHISTLEBLOWER_REWARD_QUOTIENT} from "@lodestar/params"; import {routes} from "@lodestar/api"; @@ -21,7 +21,7 @@ type SubRewardValue = number; // All reward values should be integer * 3) Reporting slashable behaviours from proposer and attester */ export async function computeBlockRewards( - block: allForks.BeaconBlock, + block: BeaconBlock, preState: CachedBeaconStateAllForks, postState?: CachedBeaconStateAllForks ): Promise { @@ -99,10 +99,7 @@ function computeSyncAggregateReward(block: altair.BeaconBlock, preState: CachedB * Calculate rewards received by block proposer for including proposer slashings. * All proposer slashing rewards go to block proposer and none to whistleblower as of Deneb */ -function computeBlockProposerSlashingReward( - block: allForks.BeaconBlock, - state: CachedBeaconStateAllForks -): SubRewardValue { +function computeBlockProposerSlashingReward(block: BeaconBlock, state: CachedBeaconStateAllForks): SubRewardValue { let proposerSlashingReward = 0; for (const proposerSlashing of block.body.proposerSlashings) { @@ -119,10 +116,7 @@ function computeBlockProposerSlashingReward( * Calculate rewards received by block proposer for including attester slashings. * All attester slashing rewards go to block proposer and none to whistleblower as of Deneb */ -function computeBlockAttesterSlashingReward( - block: allForks.BeaconBlock, - preState: CachedBeaconStateAllForks -): SubRewardValue { +function computeBlockAttesterSlashingReward(block: BeaconBlock, preState: CachedBeaconStateAllForks): SubRewardValue { let attesterSlashingReward = 0; for (const attesterSlashing of block.body.attesterSlashings) { diff --git a/packages/beacon-node/src/chain/rewards/syncCommitteeRewards.ts b/packages/beacon-node/src/chain/rewards/syncCommitteeRewards.ts index 7922e20c2317..89ef84af43a2 100644 --- a/packages/beacon-node/src/chain/rewards/syncCommitteeRewards.ts +++ b/packages/beacon-node/src/chain/rewards/syncCommitteeRewards.ts @@ -1,5 +1,5 @@ import {CachedBeaconStateAllForks, CachedBeaconStateAltair} from "@lodestar/state-transition"; -import {ValidatorIndex, allForks, altair} from "@lodestar/types"; +import {BeaconBlock, ValidatorIndex, altair} from "@lodestar/types"; import {ForkName, SYNC_COMMITTEE_SIZE} from "@lodestar/params"; import {routes} from "@lodestar/api"; @@ -7,7 +7,7 @@ export type SyncCommitteeRewards = routes.beacon.SyncCommitteeRewards; type BalanceRecord = {val: number}; // Use val for convenient way to increment/decrement balance export async function computeSyncCommitteeRewards( - block: allForks.BeaconBlock, + block: BeaconBlock, preState: CachedBeaconStateAllForks, validatorIds: (ValidatorIndex | string)[] = [] ): Promise { diff --git a/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts b/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts index 7e8d8a7ebcbc..6b51332353f2 100644 --- a/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts +++ b/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts @@ -1,5 +1,5 @@ import {toHexString} from "@chainsafe/ssz"; -import {deneb, RootHex, ssz, allForks} from "@lodestar/types"; +import {deneb, RootHex, SignedBeaconBlock, ssz} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {pruneSetToMax} from "@lodestar/utils"; import {BLOBSIDECAR_FIXED_SIZE, isForkBlobs, ForkName} from "@lodestar/params"; @@ -23,12 +23,12 @@ export enum BlockInputAvailabilitySource { } type GossipedBlockInput = - | {type: GossipedInputType.block; signedBlock: allForks.SignedBeaconBlock; blockBytes: Uint8Array | null} + | {type: GossipedInputType.block; signedBlock: SignedBeaconBlock; blockBytes: Uint8Array | null} | {type: GossipedInputType.blob; blobSidecar: deneb.BlobSidecar; blobBytes: Uint8Array | null}; type BlockInputCacheType = { fork: ForkName; - block?: allForks.SignedBeaconBlock; + block?: SignedBeaconBlock; blockBytes?: Uint8Array | null; cachedData?: CachedData; // block promise and its callback cached for delayed resolution diff --git a/packages/beacon-node/src/chain/validation/block.ts b/packages/beacon-node/src/chain/validation/block.ts index 1d12110ad1c3..214eeaf0ab4e 100644 --- a/packages/beacon-node/src/chain/validation/block.ts +++ b/packages/beacon-node/src/chain/validation/block.ts @@ -1,6 +1,5 @@ import {toHexString} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; -import {allForks} from "@lodestar/types"; import { computeStartSlotAtEpoch, computeTimeAtSlot, @@ -11,6 +10,7 @@ import { } from "@lodestar/state-transition"; import {sleep} from "@lodestar/utils"; import {ForkName} from "@lodestar/params"; +import {SignedBeaconBlock} from "@lodestar/types"; import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../../constants/index.js"; import {IBeaconChain} from "../interface.js"; import {BlockGossipError, BlockErrorCode, GossipAction} from "../errors/index.js"; @@ -19,7 +19,7 @@ import {RegenCaller} from "../regen/index.js"; export async function validateGossipBlock( config: ChainForkConfig, chain: IBeaconChain, - signedBlock: allForks.SignedBeaconBlock, + signedBlock: SignedBeaconBlock, fork: ForkName ): Promise { const block = signedBlock.message; diff --git a/packages/beacon-node/src/chain/validation/lightClientFinalityUpdate.ts b/packages/beacon-node/src/chain/validation/lightClientFinalityUpdate.ts index 1837d8d8cd62..23b91dba5fd9 100644 --- a/packages/beacon-node/src/chain/validation/lightClientFinalityUpdate.ts +++ b/packages/beacon-node/src/chain/validation/lightClientFinalityUpdate.ts @@ -1,5 +1,5 @@ import {ChainForkConfig} from "@lodestar/config"; -import {allForks} from "@lodestar/types"; +import {LightClientFinalityUpdate} from "@lodestar/types"; import {IBeaconChain} from "../interface.js"; import {LightClientError, LightClientErrorCode} from "../errors/lightClientError.js"; import {GossipAction} from "../errors/index.js"; @@ -9,7 +9,7 @@ import {updateReceivedTooEarly} from "./lightClientOptimisticUpdate.js"; export function validateLightClientFinalityUpdate( config: ChainForkConfig, chain: IBeaconChain, - gossipedFinalityUpdate: allForks.LightClientFinalityUpdate + gossipedFinalityUpdate: LightClientFinalityUpdate ): void { // [IGNORE] No other finality_update with a lower or equal finalized_header.slot was already forwarded on the network const gossipedFinalitySlot = gossipedFinalityUpdate.finalizedHeader.beacon.slot; diff --git a/packages/beacon-node/src/chain/validation/lightClientOptimisticUpdate.ts b/packages/beacon-node/src/chain/validation/lightClientOptimisticUpdate.ts index 8077378f4758..54b69f56808c 100644 --- a/packages/beacon-node/src/chain/validation/lightClientOptimisticUpdate.ts +++ b/packages/beacon-node/src/chain/validation/lightClientOptimisticUpdate.ts @@ -1,6 +1,6 @@ -import {allForks} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {computeTimeAtSlot} from "@lodestar/state-transition"; +import {LightClientOptimisticUpdate} from "@lodestar/types"; import {IBeaconChain} from "../interface.js"; import {LightClientError, LightClientErrorCode} from "../errors/lightClientError.js"; import {GossipAction} from "../errors/index.js"; @@ -10,7 +10,7 @@ import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../../constants/index.js"; export function validateLightClientOptimisticUpdate( config: ChainForkConfig, chain: IBeaconChain, - gossipedOptimisticUpdate: allForks.LightClientOptimisticUpdate + gossipedOptimisticUpdate: LightClientOptimisticUpdate ): void { // [IGNORE] No other optimistic_update with a lower or equal attested_header.slot was already forwarded on the network const gossipedAttestedSlot = gossipedOptimisticUpdate.attestedHeader.beacon.slot; @@ -56,7 +56,7 @@ export function validateLightClientOptimisticUpdate( export function updateReceivedTooEarly( config: ChainForkConfig, genesisTime: number, - update: Pick + update: Pick ): boolean { const signatureSlot13TimestampMs = computeTimeAtSlot(config, update.signatureSlot + 1 / 3, genesisTime) * 1000; const earliestAllowedTimestampMs = signatureSlot13TimestampMs - MAXIMUM_GOSSIP_CLOCK_DISPARITY; diff --git a/packages/beacon-node/src/db/buckets.ts b/packages/beacon-node/src/db/buckets.ts index 9dffd0608d52..eff123879037 100644 --- a/packages/beacon-node/src/db/buckets.ts +++ b/packages/beacon-node/src/db/buckets.ts @@ -29,7 +29,7 @@ export enum Bucket { phase0_attesterSlashing = 15, // Root -> AttesterSlashing capella_blsToExecutionChange = 16, // ValidatorIndex -> SignedBLSToExecutionChange // checkpoint states - allForks_checkpointState = 17, // Root -> allForks.BeaconState + allForks_checkpointState = 17, // Root -> BeaconState // allForks_pendingBlock = 25, // Root -> SignedBeaconBlock // DEPRECATED on v0.30.0 phase0_depositEvent = 19, // depositIndex -> DepositEvent diff --git a/packages/beacon-node/src/db/repositories/block.ts b/packages/beacon-node/src/db/repositories/block.ts index 14f4490087da..b01acb8c2ea8 100644 --- a/packages/beacon-node/src/db/repositories/block.ts +++ b/packages/beacon-node/src/db/repositories/block.ts @@ -1,6 +1,6 @@ import {ChainForkConfig} from "@lodestar/config"; import {Db, Repository} from "@lodestar/db"; -import {allForks, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, ssz} from "@lodestar/types"; import {getSignedBlockTypeFromBytes} from "../../util/multifork.js"; import {Bucket, getBucketNameByValue} from "../buckets.js"; @@ -9,7 +9,7 @@ import {Bucket, getBucketNameByValue} from "../buckets.js"; * * Used to store unfinalized blocks */ -export class BlockRepository extends Repository { +export class BlockRepository extends Repository { constructor(config: ChainForkConfig, db: Db) { const bucket = Bucket.allForks_block; const type = ssz.phase0.SignedBeaconBlock; // Pick some type but won't be used @@ -19,15 +19,15 @@ export class BlockRepository extends Repository & { /** * Stores finalized blocks. Block slot is identifier. */ -export class BlockArchiveRepository extends Repository { +export class BlockArchiveRepository extends Repository { constructor(config: ChainForkConfig, db: Db) { const bucket = Bucket.allForks_blockArchive; const type = ssz.phase0.SignedBeaconBlock; // Pick some type but won't be used @@ -30,17 +30,17 @@ export class BlockArchiveRepository extends Repository { + async put(key: Slot, value: SignedBeaconBlock): Promise { const blockRoot = this.config.getForkTypes(value.message.slot).BeaconBlock.hashTreeRoot(value.message); const slot = value.message.slot; await Promise.all([ @@ -60,7 +60,7 @@ export class BlockArchiveRepository extends Repository[]): Promise { + async batchPut(items: KeyValue[]): Promise { await Promise.all([ super.batchPut(items), Array.from(items).map((item) => { @@ -84,7 +84,7 @@ export class BlockArchiveRepository extends Repository { + async remove(value: SignedBeaconBlock): Promise { await Promise.all([ super.remove(value), deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).SignedBeaconBlock, value), @@ -92,7 +92,7 @@ export class BlockArchiveRepository extends Repository { + async batchRemove(values: SignedBeaconBlock[]): Promise { await Promise.all([ super.batchRemove(values), Array.from(values).map((value) => @@ -102,7 +102,7 @@ export class BlockArchiveRepository extends Repository { + async *valuesStream(opts?: BlockFilterOptions): AsyncIterable { const firstSlot = this.getFirstSlot(opts); const valuesStream = super.valuesStream(opts); const step = (opts && opts.step) ?? 1; @@ -114,13 +114,13 @@ export class BlockArchiveRepository extends Repository { + async values(opts?: BlockFilterOptions): Promise { return all(this.valuesStream(opts)); } // INDEX - async getByRoot(root: Root): Promise { + async getByRoot(root: Root): Promise { const slot = await this.getSlotByRoot(root); return slot !== null ? this.get(slot) : null; } @@ -130,7 +130,7 @@ export class BlockArchiveRepository extends Repository) : null; } - async getByParentRoot(root: Root): Promise { + async getByParentRoot(root: Root): Promise { const slot = await this.getSlotByParentRoot(root); return slot !== null ? this.get(slot) : null; } diff --git a/packages/beacon-node/src/db/repositories/blockArchiveIndex.ts b/packages/beacon-node/src/db/repositories/blockArchiveIndex.ts index 53d08d3a2713..797142d09db7 100644 --- a/packages/beacon-node/src/db/repositories/blockArchiveIndex.ts +++ b/packages/beacon-node/src/db/repositories/blockArchiveIndex.ts @@ -1,6 +1,7 @@ import {Db, encodeKey} from "@lodestar/db"; -import {Slot, Root, allForks, ssz} from "@lodestar/types"; +import {Slot, Root, ssz, SignedBeaconBlock, SSZTypesFor} from "@lodestar/types"; import {intToBytes} from "@lodestar/utils"; +import {ForkAll} from "@lodestar/params"; import {Bucket} from "../buckets.js"; export async function storeRootIndex(db: Db, slot: Slot, blockRoot: Root): Promise { @@ -13,14 +14,14 @@ export async function storeParentRootIndex(db: Db, slot: Slot, parentRoot: Root) export async function deleteRootIndex( db: Db, - signedBeaconBlockType: allForks.AllForksSSZTypes["SignedBeaconBlock"], - block: allForks.SignedBeaconBlock + signedBeaconBlockType: SSZTypesFor, + block: SignedBeaconBlock ): Promise { const beaconBlockType = (signedBeaconBlockType as typeof ssz.phase0.SignedBeaconBlock).fields["message"]; return db.delete(getRootIndexKey(beaconBlockType.hashTreeRoot(block.message))); } -export async function deleteParentRootIndex(db: Db, block: allForks.SignedBeaconBlock): Promise { +export async function deleteParentRootIndex(db: Db, block: SignedBeaconBlock): Promise { return db.delete(getParentRootIndexKey(block.message.parentRoot)); } diff --git a/packages/beacon-node/src/db/repositories/lightclientBestUpdate.ts b/packages/beacon-node/src/db/repositories/lightclientBestUpdate.ts index 24e0883d1be6..26493e35e408 100644 --- a/packages/beacon-node/src/db/repositories/lightclientBestUpdate.ts +++ b/packages/beacon-node/src/db/repositories/lightclientBestUpdate.ts @@ -1,6 +1,6 @@ import {ChainForkConfig} from "@lodestar/config"; import {DatabaseController, Repository} from "@lodestar/db"; -import {ssz, SyncPeriod, allForks} from "@lodestar/types"; +import {LightClientUpdate, ssz, SyncPeriod} from "@lodestar/types"; import {Bucket, getBucketNameByValue} from "../buckets.js"; const SLOT_BYTE_COUNT = 8; @@ -10,7 +10,7 @@ const SLOT_BYTE_COUNT = 8; * * Used to prepare light client updates */ -export class BestLightClientUpdateRepository extends Repository { +export class BestLightClientUpdateRepository extends Repository { constructor(config: ChainForkConfig, db: DatabaseController) { // Pick some type but won't be used const bucket = Bucket.lightClient_bestLightClientUpdate; @@ -18,7 +18,7 @@ export class BestLightClientUpdateRepository extends Repository { +export class CheckpointHeaderRepository extends Repository { constructor(config: ChainForkConfig, db: DatabaseController) { // Pick some type but won't be used const bucket = Bucket.lightClient_checkpointHeader; @@ -18,11 +18,11 @@ export class CheckpointHeaderRepository extends Repository; constructor(config: ChainForkConfig, db: Db) { this.config = config; diff --git a/packages/beacon-node/src/execution/builder/http.ts b/packages/beacon-node/src/execution/builder/http.ts index 7637023a9c08..9791f93a8ee2 100644 --- a/packages/beacon-node/src/execution/builder/http.ts +++ b/packages/beacon-node/src/execution/builder/http.ts @@ -1,4 +1,14 @@ -import {allForks, bellatrix, Slot, Root, BLSPubkey, deneb, Wei} from "@lodestar/types"; +import { + bellatrix, + Slot, + Root, + BLSPubkey, + deneb, + Wei, + SignedBeaconBlockOrContents, + SignedBlindedBeaconBlock, + ExecutionPayloadHeader, +} from "@lodestar/types"; import {parseExecutionPayloadAndBlobsBundle, reconstructFullBlockOrContents} from "@lodestar/state-transition"; import {ChainForkConfig} from "@lodestar/config"; import {Logger} from "@lodestar/logger"; @@ -101,7 +111,7 @@ export class ExecutionBuilderHttp implements IExecutionBuilder { parentHash: Root, proposerPubkey: BLSPubkey ): Promise<{ - header: allForks.ExecutionPayloadHeader; + header: ExecutionPayloadHeader; executionPayloadValue: Wei; blobKzgCommitments?: deneb.BlobKzgCommitments; }> { @@ -116,9 +126,7 @@ export class ExecutionBuilderHttp implements IExecutionBuilder { return {header, executionPayloadValue, blobKzgCommitments}; } - async submitBlindedBlock( - signedBlindedBlock: allForks.SignedBlindedBeaconBlock - ): Promise { + async submitBlindedBlock(signedBlindedBlock: SignedBlindedBeaconBlock): Promise { const data = (await this.api.submitBlindedBlock({signedBlindedBlock}, {retries: 2})).value(); const {executionPayload, blobsBundle} = parseExecutionPayloadAndBlobsBundle(data); diff --git a/packages/beacon-node/src/execution/builder/interface.ts b/packages/beacon-node/src/execution/builder/interface.ts index 8754a3616610..9a655a68de02 100644 --- a/packages/beacon-node/src/execution/builder/interface.ts +++ b/packages/beacon-node/src/execution/builder/interface.ts @@ -1,4 +1,14 @@ -import {allForks, bellatrix, Root, Slot, BLSPubkey, deneb, Wei} from "@lodestar/types"; +import { + bellatrix, + Root, + Slot, + BLSPubkey, + deneb, + Wei, + SignedBeaconBlockOrContents, + ExecutionPayloadHeader, + SignedBlindedBeaconBlock, +} from "@lodestar/types"; import {ForkExecution} from "@lodestar/params"; export interface IExecutionBuilder { @@ -23,9 +33,9 @@ export interface IExecutionBuilder { parentHash: Root, proposerPubKey: BLSPubkey ): Promise<{ - header: allForks.ExecutionPayloadHeader; + header: ExecutionPayloadHeader; executionPayloadValue: Wei; blobKzgCommitments?: deneb.BlobKzgCommitments; }>; - submitBlindedBlock(signedBlock: allForks.SignedBlindedBeaconBlock): Promise; + submitBlindedBlock(signedBlock: SignedBlindedBeaconBlock): Promise; } diff --git a/packages/beacon-node/src/execution/engine/http.ts b/packages/beacon-node/src/execution/engine/http.ts index f5ec03f41626..0af956c87668 100644 --- a/packages/beacon-node/src/execution/engine/http.ts +++ b/packages/beacon-node/src/execution/engine/http.ts @@ -1,4 +1,4 @@ -import {Root, RootHex, allForks, Wei} from "@lodestar/types"; +import {ExecutionPayload, Root, RootHex, Wei} from "@lodestar/types"; import {SLOTS_PER_EPOCH, ForkName, ForkSeq} from "@lodestar/params"; import {Logger} from "@lodestar/logger"; import { @@ -171,7 +171,7 @@ export class ExecutionEngineHttp implements IExecutionEngine { */ async notifyNewPayload( fork: ForkName, - executionPayload: allForks.ExecutionPayload, + executionPayload: ExecutionPayload, versionedHashes?: VersionedHashes, parentBlockRoot?: Root ): Promise { @@ -364,7 +364,7 @@ export class ExecutionEngineHttp implements IExecutionEngine { fork: ForkName, payloadId: PayloadId ): Promise<{ - executionPayload: allForks.ExecutionPayload; + executionPayload: ExecutionPayload; executionPayloadValue: Wei; blobsBundle?: BlobsBundle; shouldOverrideBuilder?: boolean; diff --git a/packages/beacon-node/src/execution/engine/interface.ts b/packages/beacon-node/src/execution/engine/interface.ts index e5f612fc0965..b8f319700738 100644 --- a/packages/beacon-node/src/execution/engine/interface.ts +++ b/packages/beacon-node/src/execution/engine/interface.ts @@ -1,6 +1,6 @@ import {ForkName} from "@lodestar/params"; import {KZGCommitment, Blob, KZGProof} from "@lodestar/types/deneb"; -import {Root, RootHex, allForks, capella, Wei} from "@lodestar/types"; +import {Root, RootHex, capella, Wei, ExecutionPayload} from "@lodestar/types"; import {DATA} from "../../eth1/provider/utils.js"; import {PayloadIdCache, PayloadId, WithdrawalV1} from "./payloadIdCache.js"; @@ -101,7 +101,7 @@ export interface IExecutionEngine { */ notifyNewPayload( fork: ForkName, - executionPayload: allForks.ExecutionPayload, + executionPayload: ExecutionPayload, versionedHashes?: VersionedHashes, parentBeaconBlockRoot?: Root ): Promise; @@ -137,7 +137,7 @@ export interface IExecutionEngine { fork: ForkName, payloadId: PayloadId ): Promise<{ - executionPayload: allForks.ExecutionPayload; + executionPayload: ExecutionPayload; executionPayloadValue: Wei; blobsBundle?: BlobsBundle; shouldOverrideBuilder?: boolean; diff --git a/packages/beacon-node/src/execution/engine/types.ts b/packages/beacon-node/src/execution/engine/types.ts index 72a0100f7a51..9fe9a990f76d 100644 --- a/packages/beacon-node/src/execution/engine/types.ts +++ b/packages/beacon-node/src/execution/engine/types.ts @@ -1,4 +1,4 @@ -import {allForks, capella, deneb, Wei, bellatrix, Root} from "@lodestar/types"; +import {capella, deneb, Wei, bellatrix, Root, ExecutionPayload} from "@lodestar/types"; import { BYTES_PER_LOGS_BLOOM, FIELD_ELEMENTS_PER_BLOB, @@ -163,7 +163,7 @@ export interface BlobsBundleRpc { proofs: DATA[]; // some ELs could also provide proofs, each 48 bytes } -export function serializeExecutionPayload(fork: ForkName, data: allForks.ExecutionPayload): ExecutionPayloadRpc { +export function serializeExecutionPayload(fork: ForkName, data: ExecutionPayload): ExecutionPayloadRpc { const payload: ExecutionPayloadRpc = { parentHash: bytesToData(data.parentHash), feeRecipient: bytesToData(data.feeRecipient), @@ -209,7 +209,7 @@ export function parseExecutionPayload( fork: ForkName, response: ExecutionPayloadResponse ): { - executionPayload: allForks.ExecutionPayload; + executionPayload: ExecutionPayload; executionPayloadValue: Wei; blobsBundle?: BlobsBundle; shouldOverrideBuilder?: boolean; diff --git a/packages/beacon-node/src/metrics/metrics/lodestar.ts b/packages/beacon-node/src/metrics/metrics/lodestar.ts index 12b7f3538d6e..5ebdbac48959 100644 --- a/packages/beacon-node/src/metrics/metrics/lodestar.ts +++ b/packages/beacon-node/src/metrics/metrics/lodestar.ts @@ -1,5 +1,5 @@ import {EpochTransitionStep, StateCloneSource, StateHashTreeRootSource} from "@lodestar/state-transition"; -import {allForks} from "@lodestar/types"; +import {BeaconState} from "@lodestar/types"; import {BlockSource, BlobsSource} from "../../chain/blocks/types.js"; import {JobQueueItemType} from "../../chain/bls/index.js"; import {BlockErrorCode} from "../../chain/errors/index.js"; @@ -28,7 +28,7 @@ export type LodestarMetrics = ReturnType; export function createLodestarMetrics( register: RegistryMetricCreator, metadata?: LodestarMetadata, - anchorState?: Pick + anchorState?: Pick ) { if (metadata) { register.static({ diff --git a/packages/beacon-node/src/metrics/validatorMonitor.ts b/packages/beacon-node/src/metrics/validatorMonitor.ts index 1104b1198fae..a9d783786e88 100644 --- a/packages/beacon-node/src/metrics/validatorMonitor.ts +++ b/packages/beacon-node/src/metrics/validatorMonitor.ts @@ -10,7 +10,7 @@ import { ParticipationFlags, } from "@lodestar/state-transition"; import {LogData, LogHandler, LogLevel, Logger, MapDef, MapDefMax, toHex} from "@lodestar/utils"; -import {RootHex, allForks, altair, deneb} from "@lodestar/types"; +import {BeaconBlock, RootHex, altair, deneb} from "@lodestar/types"; import {ChainConfig, ChainForkConfig} from "@lodestar/config"; import {ForkSeq, INTERVALS_PER_SLOT, MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params"; import {Epoch, Slot, ValidatorIndex} from "@lodestar/types"; @@ -40,9 +40,9 @@ export type ValidatorMonitor = { registerLocalValidator(index: number): void; registerLocalValidatorInSyncCommittee(index: number, untilEpoch: Epoch): void; registerValidatorStatuses(currentEpoch: Epoch, statuses: AttesterStatus[], balances?: number[]): void; - registerBeaconBlock(src: OpSource, seenTimestampSec: Seconds, block: allForks.BeaconBlock): void; + registerBeaconBlock(src: OpSource, seenTimestampSec: Seconds, block: BeaconBlock): void; registerBlobSidecar(src: OpSource, seenTimestampSec: Seconds, blob: deneb.BlobSidecar): void; - registerImportedBlock(block: allForks.BeaconBlock, data: {proposerBalanceDelta: number}): void; + registerImportedBlock(block: BeaconBlock, data: {proposerBalanceDelta: number}): void; onPoolSubmitUnaggregatedAttestation( seenTimestampSec: number, indexedAttestation: IndexedAttestation, diff --git a/packages/beacon-node/src/network/gossip/interface.ts b/packages/beacon-node/src/network/gossip/interface.ts index df26c2328c70..25a871b4e2a0 100644 --- a/packages/beacon-node/src/network/gossip/interface.ts +++ b/packages/beacon-node/src/network/gossip/interface.ts @@ -2,7 +2,16 @@ import {Libp2p} from "libp2p"; import {Message, TopicValidatorResult} from "@libp2p/interface"; import {PeerIdStr} from "@chainsafe/libp2p-gossipsub/types"; import {ForkName} from "@lodestar/params"; -import {allForks, altair, capella, deneb, phase0, Slot} from "@lodestar/types"; +import { + altair, + capella, + deneb, + LightClientFinalityUpdate, + LightClientOptimisticUpdate, + phase0, + SignedBeaconBlock, + Slot, +} from "@lodestar/types"; import {BeaconConfig} from "@lodestar/config"; import {Logger} from "@lodestar/utils"; import {IBeaconChain} from "../../chain/index.js"; @@ -69,7 +78,7 @@ export type SSZTypeOfGossipTopic = T extends {type: infer : never; export type GossipTypeMap = { - [GossipType.beacon_block]: allForks.SignedBeaconBlock; + [GossipType.beacon_block]: SignedBeaconBlock; [GossipType.blob_sidecar]: deneb.BlobSidecar; [GossipType.beacon_aggregate_and_proof]: phase0.SignedAggregateAndProof; [GossipType.beacon_attestation]: phase0.Attestation; @@ -78,13 +87,13 @@ export type GossipTypeMap = { [GossipType.attester_slashing]: phase0.AttesterSlashing; [GossipType.sync_committee_contribution_and_proof]: altair.SignedContributionAndProof; [GossipType.sync_committee]: altair.SyncCommitteeMessage; - [GossipType.light_client_finality_update]: allForks.LightClientFinalityUpdate; - [GossipType.light_client_optimistic_update]: allForks.LightClientOptimisticUpdate; + [GossipType.light_client_finality_update]: LightClientFinalityUpdate; + [GossipType.light_client_optimistic_update]: LightClientOptimisticUpdate; [GossipType.bls_to_execution_change]: capella.SignedBLSToExecutionChange; }; export type GossipFnByType = { - [GossipType.beacon_block]: (signedBlock: allForks.SignedBeaconBlock) => Promise | void; + [GossipType.beacon_block]: (signedBlock: SignedBeaconBlock) => Promise | void; [GossipType.blob_sidecar]: (blobSidecar: deneb.BlobSidecar) => Promise | void; [GossipType.beacon_aggregate_and_proof]: (aggregateAndProof: phase0.SignedAggregateAndProof) => Promise | void; [GossipType.beacon_attestation]: (attestation: phase0.Attestation) => Promise | void; @@ -96,10 +105,10 @@ export type GossipFnByType = { ) => Promise | void; [GossipType.sync_committee]: (syncCommittee: altair.SyncCommitteeMessage) => Promise | void; [GossipType.light_client_finality_update]: ( - lightClientFinalityUpdate: allForks.LightClientFinalityUpdate + lightClientFinalityUpdate: LightClientFinalityUpdate ) => Promise | void; [GossipType.light_client_optimistic_update]: ( - lightClientOptimisticUpdate: allForks.LightClientOptimisticUpdate + lightClientOptimisticUpdate: LightClientOptimisticUpdate ) => Promise | void; [GossipType.bls_to_execution_change]: ( blsToExecutionChange: capella.SignedBLSToExecutionChange diff --git a/packages/beacon-node/src/network/interface.ts b/packages/beacon-node/src/network/interface.ts index aeeb61f1feb2..5012650e229a 100644 --- a/packages/beacon-node/src/network/interface.ts +++ b/packages/beacon-node/src/network/interface.ts @@ -16,7 +16,17 @@ import { import type {AddressManager, ConnectionManager, Registrar, TransportManager} from "@libp2p/interface-internal"; import type {Datastore} from "interface-datastore"; import {Identify} from "@chainsafe/libp2p-identify"; -import {Slot, SlotRootHex, allForks, altair, capella, deneb, phase0} from "@lodestar/types"; +import { + LightClientFinalityUpdate, + LightClientOptimisticUpdate, + SignedBeaconBlock, + Slot, + SlotRootHex, + altair, + capella, + deneb, + phase0, +} from "@lodestar/types"; import {PeerIdStr} from "../util/peerId.js"; import {INetworkEventBus} from "./events.js"; import {INetworkCorePublic} from "./core/types.js"; @@ -50,16 +60,16 @@ export interface INetwork extends INetworkCorePublic { sendBeaconBlocksByRange( peerId: PeerIdStr, request: phase0.BeaconBlocksByRangeRequest - ): Promise[]>; + ): Promise[]>; sendBeaconBlocksByRoot( peerId: PeerIdStr, request: phase0.BeaconBlocksByRootRequest - ): Promise[]>; + ): Promise[]>; sendBlobSidecarsByRange(peerId: PeerIdStr, request: deneb.BlobSidecarsByRangeRequest): Promise; sendBlobSidecarsByRoot(peerId: PeerIdStr, request: deneb.BlobSidecarsByRootRequest): Promise; // Gossip - publishBeaconBlock(signedBlock: allForks.SignedBeaconBlock): Promise; + publishBeaconBlock(signedBlock: SignedBeaconBlock): Promise; publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise; publishBeaconAggregateAndProof(aggregateAndProof: phase0.SignedAggregateAndProof): Promise; publishBeaconAttestation(attestation: phase0.Attestation, subnet: number): Promise; @@ -69,8 +79,8 @@ export interface INetwork extends INetworkCorePublic { publishAttesterSlashing(attesterSlashing: phase0.AttesterSlashing): Promise; publishSyncCommitteeSignature(signature: altair.SyncCommitteeMessage, subnet: number): Promise; publishContributionAndProof(contributionAndProof: altair.SignedContributionAndProof): Promise; - publishLightClientFinalityUpdate(update: allForks.LightClientFinalityUpdate): Promise; - publishLightClientOptimisticUpdate(update: allForks.LightClientOptimisticUpdate): Promise; + publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise; + publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise; // Debug dumpGossipQueue(gossipType: GossipType): Promise; diff --git a/packages/beacon-node/src/network/network.ts b/packages/beacon-node/src/network/network.ts index be8bb5114d40..52b9d85c0064 100644 --- a/packages/beacon-node/src/network/network.ts +++ b/packages/beacon-node/src/network/network.ts @@ -5,7 +5,19 @@ import {BeaconConfig} from "@lodestar/config"; import {sleep} from "@lodestar/utils"; import {LoggerNode} from "@lodestar/logger/node"; import {computeStartSlotAtEpoch, computeTimeAtSlot} from "@lodestar/state-transition"; -import {phase0, allForks, deneb, altair, Root, capella, SlotRootHex} from "@lodestar/types"; +import { + phase0, + deneb, + altair, + Root, + capella, + SlotRootHex, + SignedBeaconBlock, + LightClientBootstrap, + LightClientFinalityUpdate, + LightClientOptimisticUpdate, + LightClientUpdate, +} from "@lodestar/types"; import {routes} from "@lodestar/api"; import {ResponseIncoming} from "@lodestar/reqresp"; import {ForkSeq, MAX_BLOBS_PER_BLOCK} from "@lodestar/params"; @@ -287,7 +299,7 @@ export class Network implements INetwork { // Gossip - async publishBeaconBlock(signedBlock: allForks.SignedBeaconBlock): Promise { + async publishBeaconBlock(signedBlock: SignedBeaconBlock): Promise { const fork = this.config.getForkName(signedBlock.message.slot); return this.publishGossip({type: GossipType.beacon_block, fork}, signedBlock, { ignoreDuplicatePublishError: true, @@ -380,7 +392,7 @@ export class Network implements INetwork { ); } - async publishLightClientFinalityUpdate(update: allForks.LightClientFinalityUpdate): Promise { + async publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise { const fork = this.config.getForkName(update.signatureSlot); return this.publishGossip( {type: GossipType.light_client_finality_update, fork}, @@ -388,7 +400,7 @@ export class Network implements INetwork { ); } - async publishLightClientOptimisticUpdate(update: allForks.LightClientOptimisticUpdate): Promise { + async publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise { const fork = this.config.getForkName(update.signatureSlot); return this.publishGossip( {type: GossipType.light_client_optimistic_update, fork}, @@ -419,7 +431,7 @@ export class Network implements INetwork { async sendBeaconBlocksByRange( peerId: PeerIdStr, request: phase0.BeaconBlocksByRangeRequest - ): Promise[]> { + ): Promise[]> { return collectSequentialBlocksInRange( this.sendReqRespRequest( peerId, @@ -435,7 +447,7 @@ export class Network implements INetwork { async sendBeaconBlocksByRoot( peerId: PeerIdStr, request: phase0.BeaconBlocksByRootRequest - ): Promise[]> { + ): Promise[]> { return collectMaxResponseTypedWithBytes( this.sendReqRespRequest( peerId, @@ -449,21 +461,21 @@ export class Network implements INetwork { ); } - async sendLightClientBootstrap(peerId: PeerIdStr, request: Root): Promise { + async sendLightClientBootstrap(peerId: PeerIdStr, request: Root): Promise { return collectExactOneTyped( this.sendReqRespRequest(peerId, ReqRespMethod.LightClientBootstrap, [Version.V1], request), responseSszTypeByMethod[ReqRespMethod.LightClientBootstrap] ); } - async sendLightClientOptimisticUpdate(peerId: PeerIdStr): Promise { + async sendLightClientOptimisticUpdate(peerId: PeerIdStr): Promise { return collectExactOneTyped( this.sendReqRespRequest(peerId, ReqRespMethod.LightClientOptimisticUpdate, [Version.V1], null), responseSszTypeByMethod[ReqRespMethod.LightClientOptimisticUpdate] ); } - async sendLightClientFinalityUpdate(peerId: PeerIdStr): Promise { + async sendLightClientFinalityUpdate(peerId: PeerIdStr): Promise { return collectExactOneTyped( this.sendReqRespRequest(peerId, ReqRespMethod.LightClientFinalityUpdate, [Version.V1], null), responseSszTypeByMethod[ReqRespMethod.LightClientFinalityUpdate] @@ -473,7 +485,7 @@ export class Network implements INetwork { async sendLightClientUpdatesByRange( peerId: PeerIdStr, request: altair.LightClientUpdatesByRange - ): Promise { + ): Promise { return collectMaxResponseTyped( this.sendReqRespRequest(peerId, ReqRespMethod.LightClientUpdatesByRange, [Version.V1], request), request.count, @@ -571,7 +583,7 @@ export class Network implements INetwork { return this.core.writeDiscv5HeapSnapshot(prefix, dirpath); } - private onLightClientFinalityUpdate = async (finalityUpdate: allForks.LightClientFinalityUpdate): Promise => { + private onLightClientFinalityUpdate = async (finalityUpdate: LightClientFinalityUpdate): Promise => { // TODO: Review is OK to remove if (this.hasAttachedSyncCommitteeMember()) try { @@ -588,9 +600,7 @@ export class Network implements INetwork { } }; - private onLightClientOptimisticUpdate = async ( - optimisticUpdate: allForks.LightClientOptimisticUpdate - ): Promise => { + private onLightClientOptimisticUpdate = async (optimisticUpdate: LightClientOptimisticUpdate): Promise => { // TODO: Review is OK to remove if (this.hasAttachedSyncCommitteeMember()) try { diff --git a/packages/beacon-node/src/network/peers/peerManager.ts b/packages/beacon-node/src/network/peers/peerManager.ts index 5149b129c363..43fbee723f0e 100644 --- a/packages/beacon-node/src/network/peers/peerManager.ts +++ b/packages/beacon-node/src/network/peers/peerManager.ts @@ -2,7 +2,7 @@ import {Connection, PeerId} from "@libp2p/interface"; import {BitArray} from "@chainsafe/ssz"; import {SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params"; import {BeaconConfig} from "@lodestar/config"; -import {allForks, altair, phase0} from "@lodestar/types"; +import {Metadata, altair, phase0} from "@lodestar/types"; import {withTimeout} from "@lodestar/utils"; import {LoggerNode} from "@lodestar/logger/node"; import {GoodByeReasonCode, GOODBYE_KNOWN_CODES, Libp2pEvent} from "../../constants/index.js"; @@ -90,7 +90,7 @@ export interface IReqRespBeaconNodePeerManager { sendPing(peerId: PeerId): Promise; sendStatus(peerId: PeerId, request: phase0.Status): Promise; sendGoodbye(peerId: PeerId, request: phase0.Goodbye): Promise; - sendMetadata(peerId: PeerId): Promise; + sendMetadata(peerId: PeerId): Promise; } export type PeerManagerModules = { @@ -301,7 +301,7 @@ export class PeerManager { /** * Handle a METADATA request + response (rpc handler responds with METADATA automatically) */ - private onMetadata(peer: PeerId, metadata: allForks.Metadata): void { + private onMetadata(peer: PeerId, metadata: Metadata): void { // Store metadata always in case the peer updates attnets but not the sequence number // Trust that the peer always sends the latest metadata (From Lighthouse) const peerData = this.connectedPeers.get(peer.toString()); diff --git a/packages/beacon-node/src/network/processor/gossipHandlers.ts b/packages/beacon-node/src/network/processor/gossipHandlers.ts index 1a71cc7de334..82fe7d8db358 100644 --- a/packages/beacon-node/src/network/processor/gossipHandlers.ts +++ b/packages/beacon-node/src/network/processor/gossipHandlers.ts @@ -1,7 +1,7 @@ import {toHexString} from "@chainsafe/ssz"; import {BeaconConfig, ChainForkConfig} from "@lodestar/config"; import {LogLevel, Logger, prettyBytes} from "@lodestar/utils"; -import {Root, Slot, ssz, allForks, deneb, UintNum64} from "@lodestar/types"; +import {Root, Slot, ssz, deneb, UintNum64, SignedBeaconBlock} from "@lodestar/types"; import {ForkName, ForkSeq} from "@lodestar/params"; import {routes} from "@lodestar/api"; import {computeTimeAtSlot} from "@lodestar/state-transition"; @@ -113,7 +113,7 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler const {chain, config, metrics, events, logger, core, aggregatorTracker} = modules; async function validateBeaconBlock( - signedBlock: allForks.SignedBeaconBlock, + signedBlock: SignedBeaconBlock, blockBytes: Uint8Array, fork: ForkName, peerIdStr: string, diff --git a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts index cfe13b527183..8d131e9e9945 100644 --- a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts +++ b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts @@ -13,7 +13,7 @@ import { ResponseIncoming, ResponseOutgoing, } from "@lodestar/reqresp"; -import {allForks, phase0, ssz} from "@lodestar/types"; +import {Metadata, phase0, ssz} from "@lodestar/types"; import {Logger} from "@lodestar/utils"; import {INetworkEventBus, NetworkEvent} from "../events.js"; import {MetadataController} from "../metadata.js"; @@ -184,7 +184,7 @@ export class ReqRespBeaconNode extends ReqResp { ); } - async sendMetadata(peerId: PeerId): Promise { + async sendMetadata(peerId: PeerId): Promise { return collectExactOneTyped( this.sendReqRespRequest( peerId, diff --git a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts index ff5689a7b8c3..4dae4831d716 100644 --- a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts +++ b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts @@ -1,5 +1,5 @@ import {ChainForkConfig} from "@lodestar/config"; -import {deneb, Epoch, phase0, allForks, Slot} from "@lodestar/types"; +import {deneb, Epoch, phase0, SignedBeaconBlock, Slot} from "@lodestar/types"; import {ForkSeq} from "@lodestar/params"; import {computeEpochAtSlot} from "@lodestar/state-transition"; @@ -55,7 +55,7 @@ export async function beaconBlocksMaybeBlobsByRange( // Assumes that the blobs are in the same sequence as blocks, doesn't require block to be sorted export function matchBlockWithBlobs( config: ChainForkConfig, - allBlocks: WithBytes[], + allBlocks: WithBytes[], allBlobSidecars: deneb.BlobSidecar[], endSlot: Slot, blockSource: BlockSource, diff --git a/packages/beacon-node/src/network/reqresp/types.ts b/packages/beacon-node/src/network/reqresp/types.ts index f690d282307f..78625f6ea414 100644 --- a/packages/beacon-node/src/network/reqresp/types.ts +++ b/packages/beacon-node/src/network/reqresp/types.ts @@ -1,7 +1,7 @@ import {Type} from "@chainsafe/ssz"; import {ForkLightClient, ForkName, isForkLightClient} from "@lodestar/params"; import {Protocol, ProtocolHandler, ReqRespRequest} from "@lodestar/reqresp"; -import {Root, allForks, altair, deneb, phase0, ssz} from "@lodestar/types"; +import {Metadata, Root, SignedBeaconBlock, altair, deneb, phase0, ssz} from "@lodestar/types"; export type ProtocolNoHandler = Omit; @@ -42,10 +42,10 @@ type ResponseBodyByMethod = { [ReqRespMethod.Status]: phase0.Status; [ReqRespMethod.Goodbye]: phase0.Goodbye; [ReqRespMethod.Ping]: phase0.Ping; - [ReqRespMethod.Metadata]: allForks.Metadata; + [ReqRespMethod.Metadata]: Metadata; // Do not matter - [ReqRespMethod.BeaconBlocksByRange]: allForks.SignedBeaconBlock; - [ReqRespMethod.BeaconBlocksByRoot]: allForks.SignedBeaconBlock; + [ReqRespMethod.BeaconBlocksByRange]: SignedBeaconBlock; + [ReqRespMethod.BeaconBlocksByRoot]: SignedBeaconBlock; [ReqRespMethod.BlobSidecarsByRange]: deneb.BlobSidecar; [ReqRespMethod.BlobSidecarsByRoot]: deneb.BlobSidecar; [ReqRespMethod.LightClientBootstrap]: altair.LightClientBootstrap; @@ -74,7 +74,7 @@ export const requestSszTypeByMethod: { export type ResponseTypeGetter = (fork: ForkName, version: number) => Type; -const blocksResponseType: ResponseTypeGetter = (fork, version) => { +const blocksResponseType: ResponseTypeGetter = (fork, version) => { if (version === Version.V1) { return ssz.phase0.SignedBeaconBlock; } else { diff --git a/packages/beacon-node/src/network/reqresp/utils/collectSequentialBlocksInRange.ts b/packages/beacon-node/src/network/reqresp/utils/collectSequentialBlocksInRange.ts index e1a637c7df89..c2cf0ad16ea0 100644 --- a/packages/beacon-node/src/network/reqresp/utils/collectSequentialBlocksInRange.ts +++ b/packages/beacon-node/src/network/reqresp/utils/collectSequentialBlocksInRange.ts @@ -1,6 +1,6 @@ import {ResponseIncoming} from "@lodestar/reqresp"; -import {allForks, phase0} from "@lodestar/types"; import {LodestarError} from "@lodestar/utils"; +import {phase0, SignedBeaconBlock} from "@lodestar/types"; import {WithBytes} from "../../interface.js"; import {ReqRespMethod, responseSszTypeByMethod} from "../types.js"; import {sszDeserializeResponse} from "./collect.js"; @@ -12,8 +12,8 @@ import {sszDeserializeResponse} from "./collect.js"; export async function collectSequentialBlocksInRange( blockStream: AsyncIterable, {count, startSlot}: Pick -): Promise[]> { - const blocks: WithBytes[] = []; +): Promise[]> { + const blocks: WithBytes[] = []; for await (const chunk of blockStream) { const blockType = responseSszTypeByMethod[ReqRespMethod.BeaconBlocksByRange](chunk.fork, chunk.protocolVersion); diff --git a/packages/beacon-node/src/sync/backfill/backfill.ts b/packages/beacon-node/src/sync/backfill/backfill.ts index 7d06f1dc7b65..6d9716a37329 100644 --- a/packages/beacon-node/src/sync/backfill/backfill.ts +++ b/packages/beacon-node/src/sync/backfill/backfill.ts @@ -3,7 +3,7 @@ import {StrictEventEmitter} from "strict-event-emitter-types"; import {toHexString} from "@chainsafe/ssz"; import {BeaconStateAllForks, blockToHeader} from "@lodestar/state-transition"; import {BeaconConfig, ChainForkConfig} from "@lodestar/config"; -import {phase0, Root, Slot, allForks, ssz} from "@lodestar/types"; +import {phase0, Root, SignedBeaconBlock, Slot, ssz} from "@lodestar/types"; import {ErrorAborted, Logger, sleep, toHex} from "@lodestar/utils"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; @@ -94,7 +94,7 @@ type BackfillSyncEmitter = StrictEventEmitter; */ type BackFillSyncAnchor = | { - anchorBlock: allForks.SignedBeaconBlock; + anchorBlock: SignedBeaconBlock; anchorBlockRoot: Root; anchorSlot: Slot; lastBackSyncedBlock: BackfillBlock; diff --git a/packages/beacon-node/src/sync/backfill/verify.ts b/packages/beacon-node/src/sync/backfill/verify.ts index eba4feca48ef..462762a5576f 100644 --- a/packages/beacon-node/src/sync/backfill/verify.ts +++ b/packages/beacon-node/src/sync/backfill/verify.ts @@ -1,6 +1,6 @@ import {CachedBeaconStateAllForks, ISignatureSet, getBlockProposerSignatureSet} from "@lodestar/state-transition"; import {BeaconConfig} from "@lodestar/config"; -import {allForks, Root, allForks as allForkTypes, ssz, Slot} from "@lodestar/types"; +import {Root, ssz, Slot, SignedBeaconBlock} from "@lodestar/types"; import {GENESIS_SLOT} from "@lodestar/params"; import {IBlsVerifier} from "../../chain/bls/index.js"; import {WithBytes} from "../../network/interface.js"; @@ -11,21 +11,21 @@ export type BackfillBlockHeader = { root: Root; }; -export type BackfillBlock = BackfillBlockHeader & {block: allForks.SignedBeaconBlock}; +export type BackfillBlock = BackfillBlockHeader & {block: SignedBeaconBlock}; export function verifyBlockSequence( config: BeaconConfig, - blocks: WithBytes[], + blocks: WithBytes[], anchorRoot: Root ): { nextAnchor: BackfillBlock | null; - verifiedBlocks: WithBytes[]; + verifiedBlocks: WithBytes[]; error?: BackfillSyncErrorCode.NOT_LINEAR; } { let nextRoot: Root = anchorRoot; let nextAnchor: BackfillBlock | null = null; - const verifiedBlocks: WithBytes[] = []; + const verifiedBlocks: WithBytes[] = []; for (const block of blocks.reverse()) { const blockRoot = config.getForkTypes(block.data.message.slot).BeaconBlock.hashTreeRoot(block.data.message); if (!ssz.Root.equals(blockRoot, nextRoot)) { @@ -44,7 +44,7 @@ export function verifyBlockSequence( export async function verifyBlockProposerSignature( bls: IBlsVerifier, state: CachedBeaconStateAllForks, - blocks: WithBytes[] + blocks: WithBytes[] ): Promise { if (blocks.length === 1 && blocks[0].data.message.slot === GENESIS_SLOT) return; const signatures = blocks.reduce((sigs: ISignatureSet[], block) => { diff --git a/packages/beacon-node/src/util/blobs.ts b/packages/beacon-node/src/util/blobs.ts index 13d935ba29da..fcc31092464f 100644 --- a/packages/beacon-node/src/util/blobs.ts +++ b/packages/beacon-node/src/util/blobs.ts @@ -1,7 +1,7 @@ import {digest as sha256Digest} from "@chainsafe/as-sha256"; import {Tree} from "@chainsafe/persistent-merkle-tree"; -import {VERSIONED_HASH_VERSION_KZG, KZG_COMMITMENT_GINDEX0, ForkName} from "@lodestar/params"; -import {deneb, ssz, allForks} from "@lodestar/types"; +import {VERSIONED_HASH_VERSION_KZG, KZG_COMMITMENT_GINDEX0, ForkName, ForkAll} from "@lodestar/params"; +import {deneb, ssz, BeaconBlockBody, SignedBeaconBlock, SSZTypesFor} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {signedBlockToSignedHeader} from "@lodestar/state-transition"; @@ -16,17 +16,17 @@ export function kzgCommitmentToVersionedHash(kzgCommitment: deneb.KZGCommitment) export function computeInclusionProof( fork: ForkName, - body: allForks.BeaconBlockBody, + body: BeaconBlockBody, index: number ): deneb.KzgCommitmentInclusionProof { - const bodyView = (ssz[fork].BeaconBlockBody as allForks.AllForksSSZTypes["BeaconBlockBody"]).toView(body); + const bodyView = (ssz[fork].BeaconBlockBody as SSZTypesFor).toView(body); const commitmentGindex = KZG_COMMITMENT_GINDEX0 + index; return new Tree(bodyView.node).getSingleProof(BigInt(commitmentGindex)); } export function computeBlobSidecars( config: ChainForkConfig, - signedBlock: allForks.SignedBeaconBlock, + signedBlock: SignedBeaconBlock, contents: deneb.Contents & {kzgCommitmentInclusionProofs?: deneb.KzgCommitmentInclusionProof[]} ): deneb.BlobSidecars { const blobKzgCommitments = (signedBlock as deneb.SignedBeaconBlock).message.body.blobKzgCommitments; diff --git a/packages/beacon-node/src/util/multifork.ts b/packages/beacon-node/src/util/multifork.ts index 4abeacd2e566..0a00677afadf 100644 --- a/packages/beacon-node/src/util/multifork.ts +++ b/packages/beacon-node/src/util/multifork.ts @@ -1,6 +1,7 @@ import {ChainForkConfig} from "@lodestar/config"; -import {Slot, allForks} from "@lodestar/types"; +import {SSZTypesFor, Slot} from "@lodestar/types"; import {bytesToInt} from "@lodestar/utils"; +import {ForkAll, ForkLightClient} from "@lodestar/params"; import {getSlotFromSignedBeaconBlockSerialized} from "./sszBytes.js"; /** @@ -23,7 +24,7 @@ const SLOT_BYTES_POSITION_IN_STATE = 40; export function getSignedBlockTypeFromBytes( config: ChainForkConfig, bytes: Buffer | Uint8Array -): allForks.AllForksSSZTypes["SignedBeaconBlock"] { +): SSZTypesFor { const slot = getSlotFromSignedBeaconBlockSerialized(bytes); if (slot === null) { throw Error("getSignedBlockTypeFromBytes: invalid bytes"); @@ -35,7 +36,7 @@ export function getSignedBlockTypeFromBytes( export function getStateTypeFromBytes( config: ChainForkConfig, bytes: Buffer | Uint8Array -): allForks.AllForksSSZTypes["BeaconState"] { +): SSZTypesFor { const slot = getStateSlotFromBytes(bytes); return config.getForkTypes(slot).BeaconState; } @@ -60,7 +61,7 @@ const SLOT_BYTES_POSITION_IN_LIGHTCLIENTHEADER = 0; export function getLightClientHeaderTypeFromBytes( config: ChainForkConfig, bytes: Buffer | Uint8Array -): allForks.AllForksLightClientSSZTypes["LightClientHeader"] { +): SSZTypesFor { const slot = bytesToInt( bytes.subarray(SLOT_BYTES_POSITION_IN_LIGHTCLIENTHEADER, SLOT_BYTES_POSITION_IN_LIGHTCLIENTHEADER + SLOT_BYTE_COUNT) ); diff --git a/packages/beacon-node/test/e2e/doppelganger/doppelganger.test.ts b/packages/beacon-node/test/e2e/doppelganger/doppelganger.test.ts index 8a1d1585ec2d..ab9061f1eacd 100644 --- a/packages/beacon-node/test/e2e/doppelganger/doppelganger.test.ts +++ b/packages/beacon-node/test/e2e/doppelganger/doppelganger.test.ts @@ -88,7 +88,7 @@ describe.skip("doppelganger / doppelganger test", function () { const validatorUnderTest = validatorsWithDoppelganger[0]; const pubKey = validatorUnderTest.validatorStore.votingPubkeys()[0]; - const beaconBlock = ssz.allForks.phase0.BeaconBlock.defaultValue(); + const beaconBlock = ssz.phase0.BeaconBlock.defaultValue(); await expect( validatorUnderTest.validatorStore.signBlock(fromHexString(pubKey), beaconBlock, bn.chain.clock.currentSlot) @@ -241,7 +241,7 @@ describe.skip("doppelganger / doppelganger test", function () { const validatorUnderTest = validatorsWithDoppelganger[0]; const pubKey = validatorUnderTest.validatorStore.votingPubkeys()[0]; - const beaconBlock = ssz.allForks.phase0.BeaconBlock.defaultValue(); + const beaconBlock = ssz.phase0.BeaconBlock.defaultValue(); await expect( validatorUnderTest.validatorStore.signBlock(fromHexString(pubKey), beaconBlock, bn.chain.clock.currentSlot) diff --git a/packages/beacon-node/test/e2e/network/reqresp.test.ts b/packages/beacon-node/test/e2e/network/reqresp.test.ts index 38b5cda4db5f..7969282194dd 100644 --- a/packages/beacon-node/test/e2e/network/reqresp.test.ts +++ b/packages/beacon-node/test/e2e/network/reqresp.test.ts @@ -3,7 +3,7 @@ import {createChainForkConfig, ChainForkConfig} from "@lodestar/config"; import {chainConfig} from "@lodestar/config/default"; import {ForkName} from "@lodestar/params"; import {RequestError, RequestErrorCode, ResponseOutgoing} from "@lodestar/reqresp"; -import {allForks, altair, phase0, Root, ssz} from "@lodestar/types"; +import {altair, phase0, Root, SignedBeaconBlock, ssz} from "@lodestar/types"; import {sleep as _sleep} from "@lodestar/utils"; import {Network, ReqRespBeaconNodeOpts} from "../../../src/network/index.js"; import {expectRejectedWithLodestarError} from "../../utils/errors.js"; @@ -328,7 +328,7 @@ function getEmptyEncodedPayloadSignedBeaconBlock(config: ChainForkConfig): Respo return wrapBlockAsEncodedPayload(config, config.getForkTypes(0).SignedBeaconBlock.defaultValue()); } -function wrapBlockAsEncodedPayload(config: ChainForkConfig, block: allForks.SignedBeaconBlock): ResponseOutgoing { +function wrapBlockAsEncodedPayload(config: ChainForkConfig, block: SignedBeaconBlock): ResponseOutgoing { return { data: config.getForkTypes(block.message.slot).SignedBeaconBlock.serialize(block), fork: config.getForkName(block.message.slot), diff --git a/packages/beacon-node/test/mocks/fork-choice/timeliness.ts b/packages/beacon-node/test/mocks/fork-choice/timeliness.ts index 72b3ff66a084..f5fe66902dee 100644 --- a/packages/beacon-node/test/mocks/fork-choice/timeliness.ts +++ b/packages/beacon-node/test/mocks/fork-choice/timeliness.ts @@ -1,5 +1,5 @@ import {ForkChoice} from "@lodestar/fork-choice"; -import {Slot, allForks} from "@lodestar/types"; +import {BeaconBlock, Slot} from "@lodestar/types"; /** * A specific forkchoice implementation to mark some blocks as timely or not. @@ -14,7 +14,7 @@ export class TimelinessForkChoice extends ForkChoice { /** * This is to mark the `lateSlot` as not timely. */ - protected isBlockTimely(block: allForks.BeaconBlock, blockDelaySec: number): boolean { + protected isBlockTimely(block: BeaconBlock, blockDelaySec: number): boolean { if (block.slot === this.lateSlot) { return false; } diff --git a/packages/beacon-node/test/sim/mergemock.test.ts b/packages/beacon-node/test/sim/mergemock.test.ts index 3705b845d805..ee9839d58822 100644 --- a/packages/beacon-node/test/sim/mergemock.test.ts +++ b/packages/beacon-node/test/sim/mergemock.test.ts @@ -5,7 +5,7 @@ import {LogLevel, sleep} from "@lodestar/utils"; import {TimestampFormatCode} from "@lodestar/logger"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; import {ChainConfig} from "@lodestar/config"; -import {Epoch, allForks, bellatrix} from "@lodestar/types"; +import {Epoch, SignedBeaconBlock, bellatrix} from "@lodestar/types"; import {ValidatorProposerConfig} from "@lodestar/validator"; import {routes} from "@lodestar/api"; @@ -213,7 +213,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { await new Promise((resolve, _reject) => { bn.chain.emitter.on(routes.events.EventType.block, async (blockData) => { const {data: fullOrBlindedBlock} = (await bn.api.beacon.getBlockV2({blockId: blockData.block})) as { - data: allForks.SignedBeaconBlock; + data: SignedBeaconBlock; }; if (fullOrBlindedBlock !== undefined) { const blockFeeRecipient = toHexString( diff --git a/packages/beacon-node/test/spec/presets/fork_choice.test.ts b/packages/beacon-node/test/spec/presets/fork_choice.test.ts index 67cb35972d77..92862c6cb03b 100644 --- a/packages/beacon-node/test/spec/presets/fork_choice.test.ts +++ b/packages/beacon-node/test/spec/presets/fork_choice.test.ts @@ -4,7 +4,7 @@ import {toHexString} from "@chainsafe/ssz"; import {BeaconStateAllForks, isExecutionStateType, signedBlockToSignedHeader} from "@lodestar/state-transition"; import {InputType} from "@lodestar/spec-test-util"; import {CheckpointWithHex, ForkChoice} from "@lodestar/fork-choice"; -import {phase0, allForks, bellatrix, ssz, RootHex, deneb} from "@lodestar/types"; +import {phase0, bellatrix, ssz, RootHex, deneb, BeaconBlock, SignedBeaconBlock} from "@lodestar/types"; import {bnToNum, fromHex} from "@lodestar/utils"; import {createBeaconConfig} from "@lodestar/config"; import {ACTIVE_PRESET, ForkSeq, isForkBlobs, ForkName} from "@lodestar/params"; @@ -345,7 +345,7 @@ const forkChoiceTest = }, mapToTestCase: (t: Record) => { // t has input file name as key - const blocks = new Map(); + const blocks = new Map(); const blobs = new Map(); const powBlocks = new Map(); const attestations = new Map(); @@ -487,9 +487,9 @@ type ForkChoiceTestCase = { bls_setting: bigint; }; anchorState: BeaconStateAllForks; - anchorBlock: allForks.BeaconBlock; + anchorBlock: BeaconBlock; steps: Step[]; - blocks: Map; + blocks: Map; blobs: Map; powBlocks: Map; attestations: Map; diff --git a/packages/beacon-node/test/spec/presets/genesis.test.ts b/packages/beacon-node/test/spec/presets/genesis.test.ts index ba3351a2103c..00ba11bc1e8e 100644 --- a/packages/beacon-node/test/spec/presets/genesis.test.ts +++ b/packages/beacon-node/test/spec/presets/genesis.test.ts @@ -1,6 +1,6 @@ import path from "node:path"; import {expect} from "vitest"; -import {phase0, Root, ssz, TimeSeconds, allForks, deneb} from "@lodestar/types"; +import {phase0, Root, ssz, TimeSeconds, ExecutionPayloadHeader} from "@lodestar/types"; import {InputType} from "@lodestar/spec-test-util"; import { BeaconStateAllForks, @@ -60,7 +60,9 @@ const genesisInitialization: TestRunnerFn + ) ); }, // eth1.yaml @@ -141,7 +143,7 @@ type GenesisInitSpecTest = { meta: { deposits_count: number; }; - execution_payload_header?: allForks.ExecutionPayloadHeader; + execution_payload_header?: ExecutionPayloadHeader; state: BeaconStateAllForks; }; diff --git a/packages/beacon-node/test/spec/presets/light_client/update_ranking.ts b/packages/beacon-node/test/spec/presets/light_client/update_ranking.ts index 9a38ddf36c8c..3ed71d6ff164 100644 --- a/packages/beacon-node/test/spec/presets/light_client/update_ranking.ts +++ b/packages/beacon-node/test/spec/presets/light_client/update_ranking.ts @@ -1,5 +1,5 @@ import {expect} from "vitest"; -import {altair, ssz, allForks} from "@lodestar/types"; +import {LightClientUpdate, altair, ssz} from "@lodestar/types"; import {isForkLightClient} from "@lodestar/params"; import {InputType} from "@lodestar/spec-test-util"; import {isBetterUpdate, LightClientUpdateSummary, toLightClientUpdateSummary} from "@lodestar/light-client/spec"; @@ -22,7 +22,7 @@ export const updateRanking: TestRunnerFn = (fork) = testFunction: (testcase) => { // Parse update files const updatesCount = Number(testcase.meta.updates_count as bigint); - const updates: allForks.LightClientUpdate[] = []; + const updates: LightClientUpdate[] = []; for (let i = 0; i < updatesCount; i++) { const update = (testcase as unknown as Record)[`updates_${i}`]; diff --git a/packages/beacon-node/test/spec/presets/sanity.test.ts b/packages/beacon-node/test/spec/presets/sanity.test.ts index 57afb8cf3d28..3ec1efb84fde 100644 --- a/packages/beacon-node/test/spec/presets/sanity.test.ts +++ b/packages/beacon-node/test/spec/presets/sanity.test.ts @@ -7,7 +7,7 @@ import { processSlots, stateTransition, } from "@lodestar/state-transition"; -import {allForks, deneb, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, deneb, ssz} from "@lodestar/types"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; import {bnToNum} from "@lodestar/utils"; import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; @@ -107,7 +107,7 @@ export function generateBlocksSZZTypeMapping(fork: ForkName, n: number): BlocksS } type SanityBlocksTestCase = { - [k: string]: allForks.SignedBeaconBlock | unknown | null | undefined; + [k: string]: SignedBeaconBlock | unknown | null | undefined; meta: { blocks_count: number; bls_setting: bigint; diff --git a/packages/beacon-node/test/spec/presets/transition.test.ts b/packages/beacon-node/test/spec/presets/transition.test.ts index 77919d76c3b1..d9925f292677 100644 --- a/packages/beacon-node/test/spec/presets/transition.test.ts +++ b/packages/beacon-node/test/spec/presets/transition.test.ts @@ -5,7 +5,7 @@ import { ExecutionPayloadStatus, stateTransition, } from "@lodestar/state-transition"; -import {allForks, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, ssz} from "@lodestar/types"; import {createChainForkConfig, ChainConfig} from "@lodestar/config"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; import {bnToNum} from "@lodestar/utils"; @@ -53,7 +53,7 @@ const transition = let state = createCachedBeaconStateTest(testcase.pre, testConfig); for (let i = 0; i < meta.blocks_count; i++) { - const signedBlock = testcase[`blocks_${i}`] as allForks.SignedBeaconBlock; + const signedBlock = testcase[`blocks_${i}`] as SignedBeaconBlock; state = stateTransition(state, signedBlock, { // Assume valid and available for this test executionPayloadStatus: ExecutionPayloadStatus.valid, @@ -108,7 +108,7 @@ function getTransitionConfig(fork: ForkName, forkEpoch: number): Partial; type TransitionTestCase = { - [k: string]: allForks.SignedBeaconBlock | unknown | null | undefined; + [k: string]: SignedBeaconBlock | unknown | null | undefined; meta: { post_fork: ForkName; fork_epoch: bigint; diff --git a/packages/beacon-node/test/spec/utils/expectEqualBeaconState.ts b/packages/beacon-node/test/spec/utils/expectEqualBeaconState.ts index 1f22c1b3d8cc..160310160054 100644 --- a/packages/beacon-node/test/spec/utils/expectEqualBeaconState.ts +++ b/packages/beacon-node/test/spec/utils/expectEqualBeaconState.ts @@ -1,6 +1,6 @@ import {expect} from "vitest"; -import {allForks, ssz} from "@lodestar/types"; -import {ForkName} from "@lodestar/params"; +import {SSZTypesFor, ssz} from "@lodestar/types"; +import {ForkAll, ForkName} from "@lodestar/params"; import {InputType} from "@lodestar/spec-test-util"; import {BeaconStateAllForks} from "@lodestar/state-transition"; @@ -14,7 +14,7 @@ export function expectEqualBeaconState( const expected = expectedView.toValue(); const actual = actualView.toValue(); - const stateType = ssz[fork].BeaconState as allForks.AllForksSSZTypes["BeaconState"]; + const stateType = ssz[fork].BeaconState as SSZTypesFor; if (!stateType.equals(actual, expected)) { expect(stateType.toJson(actual)).to.deep.equal(stateType.toJson(expected)); throw Error("Wrong state"); diff --git a/packages/beacon-node/test/unit/chain/blocks/verifyBlocksSanityChecks.test.ts b/packages/beacon-node/test/unit/chain/blocks/verifyBlocksSanityChecks.test.ts index e2af3ecd5cae..a45678e5bf48 100644 --- a/packages/beacon-node/test/unit/chain/blocks/verifyBlocksSanityChecks.test.ts +++ b/packages/beacon-node/test/unit/chain/blocks/verifyBlocksSanityChecks.test.ts @@ -4,7 +4,7 @@ import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice"; import {computeStartSlotAtEpoch} from "@lodestar/state-transition"; import {toHex} from "@lodestar/utils"; import {ChainForkConfig} from "@lodestar/config"; -import {allForks, Slot, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, Slot, ssz} from "@lodestar/types"; import {verifyBlocksSanityChecks as verifyBlocksImportSanityChecks} from "../../../../src/chain/blocks/verifyBlocksSanityChecks.js"; import {BlockErrorCode} from "../../../../src/chain/errors/index.js"; import {expectThrowsLodestarError} from "../../../utils/errors.js"; @@ -17,7 +17,7 @@ describe("chain / blocks / verifyBlocksSanityChecks", function () { let forkChoice: MockedBeaconChain["forkChoice"]; let clock: ClockStopped; let modules: {forkChoice: IForkChoice; clock: IClock; config: ChainForkConfig}; - let block: allForks.SignedBeaconBlock; + let block: SignedBeaconBlock; const currentSlot = 1; beforeEach(() => { @@ -121,9 +121,9 @@ describe("chain / blocks / verifyBlocksSanityChecks", function () { */ function verifyBlocksSanityChecks( modules: Parameters[0], - blocks: allForks.SignedBeaconBlock[], + blocks: SignedBeaconBlock[], opts: Parameters[2] -): {relevantBlocks: allForks.SignedBeaconBlock[]; parentSlots: Slot[]; parentBlock: ProtoBlock | null} { +): {relevantBlocks: SignedBeaconBlock[]; parentSlots: Slot[]; parentBlock: ProtoBlock | null} { const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksImportSanityChecks( modules, blocks.map((block) => getBlockInput.preData(config, block, BlockSource.byRange, null)), @@ -136,8 +136,8 @@ function verifyBlocksSanityChecks( }; } -function getValidChain(count: number, initialSlot = 0): allForks.SignedBeaconBlock[] { - const blocks: allForks.SignedBeaconBlock[] = []; +function getValidChain(count: number, initialSlot = 0): SignedBeaconBlock[] { + const blocks: SignedBeaconBlock[] = []; for (let i = 0; i < count; i++) { const block = ssz.phase0.SignedBeaconBlock.defaultValue(); @@ -154,7 +154,7 @@ function getValidChain(count: number, initialSlot = 0): allForks.SignedBeaconBlo return blocks; } -function getForkChoice(knownBlocks: allForks.SignedBeaconBlock[], finalizedEpoch = 0): IForkChoice { +function getForkChoice(knownBlocks: SignedBeaconBlock[], finalizedEpoch = 0): IForkChoice { const blocks = new Map(); for (const block of knownBlocks) { const protoBlock = toProtoBlock(block); @@ -174,7 +174,7 @@ function getForkChoice(knownBlocks: allForks.SignedBeaconBlock[], finalizedEpoch } as Partial as IForkChoice; } -function toProtoBlock(block: allForks.SignedBeaconBlock): ProtoBlock { +function toProtoBlock(block: SignedBeaconBlock): ProtoBlock { return { slot: block.message.slot, blockRoot: toHex(ssz.phase0.BeaconBlock.hashTreeRoot(block.message)), @@ -183,17 +183,17 @@ function toProtoBlock(block: allForks.SignedBeaconBlock): ProtoBlock { } as Partial as ProtoBlock; } -function slots(blocks: allForks.SignedBeaconBlock[]): Slot[] { +function slots(blocks: SignedBeaconBlock[]): Slot[] { return blocks.map((block) => block.message.slot); } /** Since blocks have no meaning compare the indexes against `allBlocks` */ function expectBlocks( - expectedBlocks: allForks.SignedBeaconBlock[], - actualBlocks: allForks.SignedBeaconBlock[], - allBlocks: allForks.SignedBeaconBlock[] + expectedBlocks: SignedBeaconBlock[], + actualBlocks: SignedBeaconBlock[], + allBlocks: SignedBeaconBlock[] ): void { - function indexOfBlocks(blocks: allForks.SignedBeaconBlock[]): number[] { + function indexOfBlocks(blocks: SignedBeaconBlock[]): number[] { return blocks.map((block) => allBlocks.indexOf(block)); } diff --git a/packages/beacon-node/test/unit/chain/lightclient/upgradeLightClientHeader.test.ts b/packages/beacon-node/test/unit/chain/lightclient/upgradeLightClientHeader.test.ts index 5ec991010466..a9a5edc9ec0e 100644 --- a/packages/beacon-node/test/unit/chain/lightclient/upgradeLightClientHeader.test.ts +++ b/packages/beacon-node/test/unit/chain/lightclient/upgradeLightClientHeader.test.ts @@ -1,11 +1,11 @@ import {describe, it, expect, beforeEach} from "vitest"; -import {ssz, allForks} from "@lodestar/types"; +import {LightClientHeader, ssz} from "@lodestar/types"; import {ForkName, ForkSeq} from "@lodestar/params"; import {createBeaconConfig, createChainForkConfig, defaultChainConfig} from "@lodestar/config"; import {upgradeLightClientHeader} from "@lodestar/light-client/spec"; describe("UpgradeLightClientHeader", function () { - let lcHeaderByFork: Record; + let lcHeaderByFork: Record; let testSlots: Record; /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/packages/beacon-node/test/unit/chain/validation/block.test.ts b/packages/beacon-node/test/unit/chain/validation/block.test.ts index ad2f2e690ddd..74f5248dd4b6 100644 --- a/packages/beacon-node/test/unit/chain/validation/block.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/block.test.ts @@ -2,7 +2,7 @@ import {Mock, Mocked, beforeEach, describe, it, vi} from "vitest"; import {config} from "@lodestar/config/default"; import {ProtoBlock} from "@lodestar/fork-choice"; import {ForkName} from "@lodestar/params"; -import {allForks, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, ssz} from "@lodestar/types"; import {MockedBeaconChain, getMockedBeaconChain} from "../../../mocks/mockedBeaconChain.js"; import {BlockErrorCode} from "../../../../src/chain/errors/index.js"; import {QueuedStateRegenerator} from "../../../../src/chain/regen/index.js"; @@ -17,7 +17,7 @@ describe("gossip block validation", function () { let forkChoice: MockedBeaconChain["forkChoice"]; let regen: Mocked; let verifySignature: Mock<[boolean]>; - let job: allForks.SignedBeaconBlock; + let job: SignedBeaconBlock; const proposerIndex = 0; const clockSlot = 32; const block = ssz.phase0.BeaconBlock.defaultValue(); diff --git a/packages/beacon-node/test/unit/network/reqresp/collectSequentialBlocksInRange.test.ts b/packages/beacon-node/test/unit/network/reqresp/collectSequentialBlocksInRange.test.ts index d577b6d6c3ee..fa9b47ebf026 100644 --- a/packages/beacon-node/test/unit/network/reqresp/collectSequentialBlocksInRange.test.ts +++ b/packages/beacon-node/test/unit/network/reqresp/collectSequentialBlocksInRange.test.ts @@ -1,5 +1,5 @@ import {describe, it, expect} from "vitest"; -import {allForks, phase0, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types"; import {ResponseIncoming} from "@lodestar/reqresp"; import {ForkName} from "@lodestar/params"; import { @@ -79,7 +79,7 @@ describe("beacon-node / network / reqresp / utils / collectSequentialBlocksInRan }); } - async function* arrToSource(arr: allForks.SignedBeaconBlock[]): AsyncGenerator { + async function* arrToSource(arr: SignedBeaconBlock[]): AsyncGenerator { for (const item of arr) { yield {data: ssz.phase0.SignedBeaconBlock.serialize(item), fork: ForkName.phase0, protocolVersion: 1}; } diff --git a/packages/beacon-node/test/utils/node/simTest.ts b/packages/beacon-node/test/utils/node/simTest.ts index 4bf922cfb377..e94c32cefb10 100644 --- a/packages/beacon-node/test/utils/node/simTest.ts +++ b/packages/beacon-node/test/utils/node/simTest.ts @@ -7,7 +7,7 @@ import { } from "@lodestar/state-transition"; import {BeaconConfig} from "@lodestar/config"; import {SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params"; -import {allForks, Epoch, Slot} from "@lodestar/types"; +import {BeaconBlock, Epoch, Slot} from "@lodestar/types"; import {Checkpoint} from "@lodestar/types/phase0"; import {Logger, mapValues} from "@lodestar/utils"; import {routes} from "@lodestar/api"; @@ -93,14 +93,14 @@ export function simTestInfoTracker(bn: BeaconNode, logger: Logger): () => void { }; } -function sumAttestationBits(block: allForks.BeaconBlock): number { +function sumAttestationBits(block: BeaconBlock): number { return Array.from(block.body.attestations).reduce( (total, att) => total + att.aggregationBits.getTrueBitIndexes().length, 0 ); } -function avgInclusionDelay(block: allForks.BeaconBlock): number { +function avgInclusionDelay(block: BeaconBlock): number { const inclDelay = Array.from(block.body.attestations).map((att) => block.slot - att.data.slot); return avg(inclDelay); } diff --git a/packages/beacon-node/test/utils/state.ts b/packages/beacon-node/test/utils/state.ts index af9ebd479340..10a653bf9498 100644 --- a/packages/beacon-node/test/utils/state.ts +++ b/packages/beacon-node/test/utils/state.ts @@ -8,7 +8,7 @@ import { CachedBeaconStateBellatrix, BeaconStateBellatrix, } from "@lodestar/state-transition"; -import {allForks, altair, bellatrix, ssz} from "@lodestar/types"; +import {BeaconState, altair, bellatrix, ssz} from "@lodestar/types"; import {createBeaconConfig, ChainForkConfig} from "@lodestar/config"; import {FAR_FUTURE_EPOCH, ForkName, ForkSeq, MAX_EFFECTIVE_BALANCE, SYNC_COMMITTEE_SIZE} from "@lodestar/params"; @@ -20,7 +20,7 @@ import {getConfig} from "./config.js"; /** * Copy of BeaconState, but all fields are marked optional to allow for swapping out variables as needed. */ -type TestBeaconState = Partial; +type TestBeaconState = Partial; /** * Generate beaconState, by default it will generate a mostly empty state with "just enough" to be valid-ish diff --git a/packages/cli/test/utils/crucible/interfaces.ts b/packages/cli/test/utils/crucible/interfaces.ts index c406d5a72432..44358561cea5 100644 --- a/packages/cli/test/utils/crucible/interfaces.ts +++ b/packages/cli/test/utils/crucible/interfaces.ts @@ -6,7 +6,7 @@ import {ApiClient} from "@lodestar/api"; import {ApiClient as KeyManagerApi} from "@lodestar/api/keymanager"; import {ChainForkConfig} from "@lodestar/config"; import {ForkName} from "@lodestar/params"; -import {Slot, allForks, Epoch} from "@lodestar/types"; +import {Slot, Epoch, SignedBeaconBlock} from "@lodestar/types"; import {LogLevel, Logger} from "@lodestar/logger"; import {BeaconArgs} from "../../../src/cmds/beacon/options.js"; import {IValidatorCliArgs} from "../../../src/cmds/validator/options.js"; @@ -317,7 +317,7 @@ export interface AssertionInput { } export interface CaptureInput> extends AssertionInput { - block: allForks.SignedBeaconBlock; + block: SignedBeaconBlock; dependantStores: D; } diff --git a/packages/cli/test/utils/crucible/utils/network.ts b/packages/cli/test/utils/crucible/utils/network.ts index 6042e91bc221..e3555fc149b9 100644 --- a/packages/cli/test/utils/crucible/utils/network.ts +++ b/packages/cli/test/utils/crucible/utils/network.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import {Slot, allForks} from "@lodestar/types"; +import {SignedBeaconBlock, Slot} from "@lodestar/types"; import {sleep} from "@lodestar/utils"; import {BeaconClient, BeaconNode, ExecutionClient, ExecutionNode, NodePair} from "../interfaces.js"; import {Simulation} from "../simulation.js"; @@ -155,7 +155,7 @@ export async function waitForSlot( export async function fetchBlock( node: NodePair, {tries, delay, slot, signal}: {slot: number; tries: number; delay: number; signal?: AbortSignal} -): Promise { +): Promise { for (let i = 0; i < tries; i++) { const res = await node.beacon.api.beacon.getBlockV2({blockId: slot}); if (!res.ok) { diff --git a/packages/cli/test/utils/crucible/utils/syncing.ts b/packages/cli/test/utils/crucible/utils/syncing.ts index 35b889fb3ac1..4a8913105335 100644 --- a/packages/cli/test/utils/crucible/utils/syncing.ts +++ b/packages/cli/test/utils/crucible/utils/syncing.ts @@ -1,6 +1,7 @@ import {routes} from "@lodestar/api"; -import {Slot} from "@lodestar/types"; +import {SignedBeaconBlock, Slot} from "@lodestar/types"; import {sleep, toHex} from "@lodestar/utils"; +import {ForkBlobs} from "@lodestar/params"; import type {Simulation} from "../simulation.js"; import {BeaconClient, ExecutionClient, NodePair} from "../interfaces.js"; import {connectNewCLNode, connectNewELNode, connectNewNode, waitForHead, waitForSlot} from "./network.js"; @@ -131,7 +132,7 @@ export async function assertUnknownBlockSync(env: Simulation): Promise { ( await unknownBlockSync.beacon.api.beacon.publishBlockV2({ signedBlockOrContents: { - signedBlock: currentHead, + signedBlock: currentHead as SignedBeaconBlock, blobs: currentSidecars.map((b) => b.blob), kzgProofs: currentSidecars.map((b) => b.kzgProof), }, diff --git a/packages/config/src/forkConfig/index.ts b/packages/config/src/forkConfig/index.ts index d630f1ddfc88..45213d0a611d 100644 --- a/packages/config/src/forkConfig/index.ts +++ b/packages/config/src/forkConfig/index.ts @@ -6,8 +6,12 @@ import { isForkLightClient, isForkExecution, isForkBlobs, + ForkExecution, + ForkAll, + ForkLightClient, + ForkBlobs, } from "@lodestar/params"; -import {Slot, allForks, Version, ssz} from "@lodestar/types"; +import {Slot, Version, ssz, SSZTypesFor, sszTypesFor} from "@lodestar/types"; import {ChainConfig} from "../chainConfig/index.js"; import {ForkConfig, ForkInfo} from "./types.js"; @@ -87,36 +91,36 @@ export function createForkConfig(config: ChainConfig): ForkConfig { getForkVersion(slot: Slot): Version { return this.getForkInfo(slot).version; }, - getForkTypes(slot: Slot): allForks.AllForksSSZTypes { - return ssz.allForks[this.getForkName(slot)] as allForks.AllForksSSZTypes; + getForkTypes(slot: Slot): SSZTypesFor { + return sszTypesFor(this.getForkName(slot)) as SSZTypesFor; }, - getExecutionForkTypes(slot: Slot): allForks.AllForksExecutionSSZTypes { + getExecutionForkTypes(slot: Slot): SSZTypesFor { const forkName = this.getForkName(slot); if (!isForkExecution(forkName)) { throw Error(`Invalid slot=${slot} fork=${forkName} for execution fork types`); } - return ssz.allForksExecution[forkName] as allForks.AllForksExecutionSSZTypes; + return ssz.allForksExecution[forkName]; }, - getBlindedForkTypes(slot: Slot): allForks.AllForksBlindedSSZTypes { + getBlindedForkTypes(slot: Slot): (typeof ssz.allForksBlinded)[ForkExecution] { const forkName = this.getForkName(slot); if (!isForkExecution(forkName)) { throw Error(`Invalid slot=${slot} fork=${forkName} for blinded fork types`); } - return ssz.allForksBlinded[forkName] as allForks.AllForksBlindedSSZTypes; + return ssz.allForksBlinded[forkName]; }, - getLightClientForkTypes(slot: Slot): allForks.AllForksLightClientSSZTypes { + getLightClientForkTypes(slot: Slot): SSZTypesFor { const forkName = this.getForkName(slot); if (!isForkLightClient(forkName)) { throw Error(`Invalid slot=${slot} fork=${forkName} for lightclient fork types`); } - return ssz.allForksLightClient[forkName] as allForks.AllForksLightClientSSZTypes; + return ssz.allForksLightClient[forkName]; }, - getBlobsForkTypes(slot: Slot): allForks.AllForksBlobsSSZTypes { + getBlobsForkTypes(slot: Slot): SSZTypesFor { const forkName = this.getForkName(slot); if (!isForkBlobs(forkName)) { throw Error(`Invalid slot=${slot} fork=${forkName} for blobs fork types`); } - return ssz.allForksBlobs[forkName] as allForks.AllForksBlobsSSZTypes; + return ssz.allForksBlobs[forkName]; }, }; } diff --git a/packages/config/src/forkConfig/types.ts b/packages/config/src/forkConfig/types.ts index b61752bf3698..93f34a62bc83 100644 --- a/packages/config/src/forkConfig/types.ts +++ b/packages/config/src/forkConfig/types.ts @@ -1,5 +1,5 @@ -import {ForkName, ForkSeq} from "@lodestar/params"; -import {allForks, Epoch, Slot, Version} from "@lodestar/types"; +import {ForkAll, ForkBlobs, ForkExecution, ForkLightClient, ForkName, ForkSeq} from "@lodestar/params"; +import {Epoch, SSZBlindedTypesFor, SSZTypesFor, Slot, Version} from "@lodestar/types"; export type ForkInfo = { name: ForkName; @@ -29,13 +29,13 @@ export type ForkConfig = { /** Get the hard-fork version at a given slot */ getForkVersion(slot: Slot): Version; /** Get SSZ types by hard-fork */ - getForkTypes(slot: Slot): allForks.AllForksSSZTypes; + getForkTypes(slot: Slot): SSZTypesFor; /** Get lightclient SSZ types by hard-fork*/ - getLightClientForkTypes(slot: Slot): allForks.AllForksLightClientSSZTypes; + getLightClientForkTypes(slot: Slot): SSZTypesFor; /** Get execution SSZ types by hard-fork*/ - getExecutionForkTypes(slot: Slot): allForks.AllForksExecutionSSZTypes; + getExecutionForkTypes(slot: Slot): SSZTypesFor; /** Get blinded SSZ types by hard-fork */ - getBlindedForkTypes(slot: Slot): allForks.AllForksBlindedSSZTypes; + getBlindedForkTypes(slot: Slot): SSZBlindedTypesFor; /** Get blobs SSZ types by hard-fork*/ - getBlobsForkTypes(slot: Slot): allForks.AllForksBlobsSSZTypes; + getBlobsForkTypes(slot: Slot): SSZTypesFor; }; diff --git a/packages/fork-choice/src/forkChoice/forkChoice.ts b/packages/fork-choice/src/forkChoice/forkChoice.ts index 2433d68224f2..2e2abed95eb7 100644 --- a/packages/fork-choice/src/forkChoice/forkChoice.ts +++ b/packages/fork-choice/src/forkChoice/forkChoice.ts @@ -1,7 +1,7 @@ import {toHexString} from "@chainsafe/ssz"; import {Logger, fromHex} from "@lodestar/utils"; import {SLOTS_PER_HISTORICAL_ROOT, SLOTS_PER_EPOCH, INTERVALS_PER_SLOT} from "@lodestar/params"; -import {bellatrix, Slot, ValidatorIndex, phase0, allForks, ssz, RootHex, Epoch, Root} from "@lodestar/types"; +import {bellatrix, Slot, ValidatorIndex, phase0, ssz, RootHex, Epoch, Root, BeaconBlock} from "@lodestar/types"; import { computeSlotsSinceEpochStart, computeStartSlotAtEpoch, @@ -464,7 +464,7 @@ export class ForkChoice implements IForkChoice { * This ensures that the forkchoice is never out of sync. */ onBlock( - block: allForks.BeaconBlock, + block: BeaconBlock, state: CachedBeaconStateAllForks, blockDelaySec: number, currentSlot: Slot, @@ -1088,7 +1088,7 @@ export class ForkChoice implements IForkChoice { * Return true if the block is timely for the current slot. * Child class can overwrite this for testing purpose. */ - protected isBlockTimely(block: allForks.BeaconBlock, blockDelaySec: number): boolean { + protected isBlockTimely(block: BeaconBlock, blockDelaySec: number): boolean { const isBeforeAttestingInterval = blockDelaySec < this.config.SECONDS_PER_SLOT / INTERVALS_PER_SLOT; return this.fcStore.currentSlot === block.slot && isBeforeAttestingInterval; } diff --git a/packages/fork-choice/src/forkChoice/interface.ts b/packages/fork-choice/src/forkChoice/interface.ts index aa5b86f0e64e..d0629c2125cc 100644 --- a/packages/fork-choice/src/forkChoice/interface.ts +++ b/packages/fork-choice/src/forkChoice/interface.ts @@ -1,6 +1,6 @@ import {EffectiveBalanceIncrements} from "@lodestar/state-transition"; import {CachedBeaconStateAllForks} from "@lodestar/state-transition"; -import {Epoch, Slot, ValidatorIndex, phase0, allForks, Root, RootHex} from "@lodestar/types"; +import {Epoch, Slot, ValidatorIndex, phase0, Root, RootHex, BeaconBlock} from "@lodestar/types"; import { ProtoBlock, MaybeValidExecutionStatus, @@ -131,7 +131,7 @@ export interface IForkChoice { * The supplied block **must** pass the `state_transition` function as it will not be run here. */ onBlock( - block: allForks.BeaconBlock, + block: BeaconBlock, state: CachedBeaconStateAllForks, blockDelaySec: number, currentSlot: Slot, diff --git a/packages/light-client/src/events.ts b/packages/light-client/src/events.ts index 9bb6bcb0b591..5b561718e89e 100644 --- a/packages/light-client/src/events.ts +++ b/packages/light-client/src/events.ts @@ -1,4 +1,4 @@ -import {allForks} from "@lodestar/types"; +import {LightClientHeader} from "@lodestar/types"; import {RunStatusCode} from "./index.js"; export enum LightclientEvent { @@ -8,8 +8,8 @@ export enum LightclientEvent { } export type LightclientEmitterEvents = { - [LightclientEvent.lightClientOptimisticHeader]: (newHeader: allForks.LightClientHeader) => void; - [LightclientEvent.lightClientFinalityHeader]: (newHeader: allForks.LightClientHeader) => void; + [LightclientEvent.lightClientOptimisticHeader]: (newHeader: LightClientHeader) => void; + [LightclientEvent.lightClientFinalityHeader]: (newHeader: LightClientHeader) => void; [LightclientEvent.statusChange]: (code: RunStatusCode) => void; }; diff --git a/packages/light-client/src/index.ts b/packages/light-client/src/index.ts index 63996a8fda18..16ecf1adf939 100644 --- a/packages/light-client/src/index.ts +++ b/packages/light-client/src/index.ts @@ -1,7 +1,17 @@ import mitt from "mitt"; import {fromHexString, toHexString} from "@chainsafe/ssz"; import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD} from "@lodestar/params"; -import {phase0, RootHex, Slot, SyncPeriod, allForks} from "@lodestar/types"; +import { + LightClientBootstrap, + LightClientFinalityUpdate, + LightClientHeader, + LightClientOptimisticUpdate, + LightClientUpdate, + phase0, + RootHex, + Slot, + SyncPeriod, +} from "@lodestar/types"; import {createBeaconConfig, BeaconConfig, ChainForkConfig} from "@lodestar/config"; import {isErrorAborted, sleep} from "@lodestar/utils"; import {getCurrentSlot, slotWithFutureTolerance, timeUntilNextEpoch} from "./utils/clock.js"; @@ -32,7 +42,7 @@ export type LightclientInitArgs = { opts?: LightclientOpts; genesisData: GenesisData; transport: LightClientTransport; - bootstrap: allForks.LightClientBootstrap; + bootstrap: LightClientBootstrap; }; /** Provides some protection against a server client sending header updates too far away in the future */ @@ -190,11 +200,11 @@ export class Lightclient { this.updateRunStatus({code: RunStatusCode.stopped}); } - getHead(): allForks.LightClientHeader { + getHead(): LightClientHeader { return this.lightclientSpec.store.optimisticHeader; } - getFinalized(): allForks.LightClientHeader { + getFinalized(): LightClientHeader { return this.lightclientSpec.store.finalizedHeader; } @@ -293,7 +303,7 @@ export class Lightclient { * Processes new optimistic header updates in only known synced sync periods. * This headerUpdate may update the head if there's enough participation. */ - private processOptimisticUpdate(optimisticUpdate: allForks.LightClientOptimisticUpdate): void { + private processOptimisticUpdate(optimisticUpdate: LightClientOptimisticUpdate): void { this.lightclientSpec.onOptimisticUpdate(this.currentSlotWithTolerance(), optimisticUpdate); } @@ -301,11 +311,11 @@ export class Lightclient { * Processes new header updates in only known synced sync periods. * This headerUpdate may update the head if there's enough participation. */ - private processFinalizedUpdate(finalizedUpdate: allForks.LightClientFinalityUpdate): void { + private processFinalizedUpdate(finalizedUpdate: LightClientFinalityUpdate): void { this.lightclientSpec.onFinalityUpdate(this.currentSlotWithTolerance(), finalizedUpdate); } - private processSyncCommitteeUpdate(update: allForks.LightClientUpdate): void { + private processSyncCommitteeUpdate(update: LightClientUpdate): void { this.lightclientSpec.onUpdate(this.currentSlotWithTolerance(), update); } diff --git a/packages/light-client/src/spec/index.ts b/packages/light-client/src/spec/index.ts index e81dba437dea..fc1a431129e8 100644 --- a/packages/light-client/src/spec/index.ts +++ b/packages/light-client/src/spec/index.ts @@ -1,6 +1,12 @@ import {BeaconConfig} from "@lodestar/config"; import {UPDATE_TIMEOUT} from "@lodestar/params"; -import {Slot, allForks} from "@lodestar/types"; +import { + LightClientBootstrap, + LightClientFinalityUpdate, + LightClientOptimisticUpdate, + LightClientUpdate, + Slot, +} from "@lodestar/types"; import {computeSyncPeriodAtSlot} from "../utils/index.js"; import {getSyncCommitteeAtPeriod, processLightClientUpdate, ProcessUpdateOpts} from "./processLightClientUpdate.js"; import {ILightClientStore, LightClientStore, LightClientStoreEvents} from "./store.js"; @@ -17,17 +23,17 @@ export class LightclientSpec { constructor( config: BeaconConfig, private readonly opts: ProcessUpdateOpts & LightClientStoreEvents, - bootstrap: allForks.LightClientBootstrap + bootstrap: LightClientBootstrap ) { this.store = new LightClientStore(config, bootstrap, opts); this.config = config; } - onUpdate(currentSlot: Slot, update: allForks.LightClientUpdate): void { + onUpdate(currentSlot: Slot, update: LightClientUpdate): void { processLightClientUpdate(this.config, this.store, currentSlot, this.opts, update); } - onFinalityUpdate(currentSlot: Slot, finalityUpdate: allForks.LightClientFinalityUpdate): void { + onFinalityUpdate(currentSlot: Slot, finalityUpdate: LightClientFinalityUpdate): void { this.onUpdate(currentSlot, { attestedHeader: finalityUpdate.attestedHeader, nextSyncCommittee: ZERO_SYNC_COMMITTEE, @@ -39,7 +45,7 @@ export class LightclientSpec { }); } - onOptimisticUpdate(currentSlot: Slot, optimisticUpdate: allForks.LightClientOptimisticUpdate): void { + onOptimisticUpdate(currentSlot: Slot, optimisticUpdate: LightClientOptimisticUpdate): void { this.onUpdate(currentSlot, { attestedHeader: optimisticUpdate.attestedHeader, nextSyncCommittee: ZERO_SYNC_COMMITTEE, diff --git a/packages/light-client/src/spec/isBetterUpdate.ts b/packages/light-client/src/spec/isBetterUpdate.ts index e149386cf97f..260d54434fba 100644 --- a/packages/light-client/src/spec/isBetterUpdate.ts +++ b/packages/light-client/src/spec/isBetterUpdate.ts @@ -1,5 +1,5 @@ import {SYNC_COMMITTEE_SIZE} from "@lodestar/params"; -import {Slot, allForks} from "@lodestar/types"; +import {LightClientUpdate, Slot} from "@lodestar/types"; import {computeSyncPeriodAtSlot} from "../utils/index.js"; import {isFinalityUpdate, isSyncCommitteeUpdate, sumBits} from "./utils.js"; @@ -82,7 +82,7 @@ export function isSafeLightClientUpdate(update: LightClientUpdateSummary): boole ); } -export function toLightClientUpdateSummary(update: allForks.LightClientUpdate): LightClientUpdateSummary { +export function toLightClientUpdateSummary(update: LightClientUpdate): LightClientUpdateSummary { return { activeParticipants: sumBits(update.syncAggregate.syncCommitteeBits), attestedHeaderSlot: update.attestedHeader.beacon.slot, diff --git a/packages/light-client/src/spec/processLightClientUpdate.ts b/packages/light-client/src/spec/processLightClientUpdate.ts index acd6a46d0175..644a956ab4b0 100644 --- a/packages/light-client/src/spec/processLightClientUpdate.ts +++ b/packages/light-client/src/spec/processLightClientUpdate.ts @@ -1,5 +1,5 @@ import {SYNC_COMMITTEE_SIZE} from "@lodestar/params"; -import {Slot, SyncPeriod, allForks} from "@lodestar/types"; +import {LightClientUpdate, Slot, SyncPeriod} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {pruneSetToMax} from "@lodestar/utils"; import {computeSyncPeriodAtSlot, deserializeSyncCommittee, sumBits} from "../utils/index.js"; @@ -18,7 +18,7 @@ export function processLightClientUpdate( store: ILightClientStore, currentSlot: Slot, opts: ProcessUpdateOpts, - update: allForks.LightClientUpdate + update: LightClientUpdate ): void { if (update.signatureSlot > currentSlot) { throw Error(`update slot ${update.signatureSlot} must not be in the future, current slot ${currentSlot}`); diff --git a/packages/light-client/src/spec/store.ts b/packages/light-client/src/spec/store.ts index 94c4510d90db..5b606883abcb 100644 --- a/packages/light-client/src/spec/store.ts +++ b/packages/light-client/src/spec/store.ts @@ -1,6 +1,6 @@ import type {PublicKey} from "@chainsafe/bls/types"; import {BeaconConfig} from "@lodestar/config"; -import {SyncPeriod, allForks} from "@lodestar/types"; +import {LightClientBootstrap, LightClientHeader, LightClientUpdate, SyncPeriod} from "@lodestar/types"; import {computeSyncPeriodAtSlot, deserializeSyncCommittee} from "../utils/index.js"; import {LightClientUpdateSummary} from "./isBetterUpdate.js"; @@ -18,29 +18,29 @@ export interface ILightClientStore { setActiveParticipants(period: SyncPeriod, activeParticipants: number): void; // Header that is finalized - finalizedHeader: allForks.LightClientHeader; + finalizedHeader: LightClientHeader; // Most recent available reasonably-safe header - optimisticHeader: allForks.LightClientHeader; + optimisticHeader: LightClientHeader; } export interface LightClientStoreEvents { - onSetFinalizedHeader?: (header: allForks.LightClientHeader) => void; - onSetOptimisticHeader?: (header: allForks.LightClientHeader) => void; + onSetFinalizedHeader?: (header: LightClientHeader) => void; + onSetOptimisticHeader?: (header: LightClientHeader) => void; } export class LightClientStore implements ILightClientStore { readonly syncCommittees = new Map(); readonly bestValidUpdates = new Map(); - private _finalizedHeader: allForks.LightClientHeader; - private _optimisticHeader: allForks.LightClientHeader; + private _finalizedHeader: LightClientHeader; + private _optimisticHeader: LightClientHeader; private readonly maxActiveParticipants = new Map(); constructor( readonly config: BeaconConfig, - bootstrap: allForks.LightClientBootstrap, + bootstrap: LightClientBootstrap, private readonly events: LightClientStoreEvents ) { const bootstrapPeriod = computeSyncPeriodAtSlot(bootstrap.header.beacon.slot); @@ -49,20 +49,20 @@ export class LightClientStore implements ILightClientStore { this._optimisticHeader = bootstrap.header; } - get finalizedHeader(): allForks.LightClientHeader { + get finalizedHeader(): LightClientHeader { return this._finalizedHeader; } - set finalizedHeader(value: allForks.LightClientHeader) { + set finalizedHeader(value: LightClientHeader) { this._finalizedHeader = value; this.events.onSetFinalizedHeader?.(value); } - get optimisticHeader(): allForks.LightClientHeader { + get optimisticHeader(): LightClientHeader { return this._optimisticHeader; } - set optimisticHeader(value: allForks.LightClientHeader) { + set optimisticHeader(value: LightClientHeader) { this._optimisticHeader = value; this.events.onSetOptimisticHeader?.(value); } @@ -95,7 +95,7 @@ export type SyncCommitteeFast = { }; export type LightClientUpdateWithSummary = { - update: allForks.LightClientUpdate; + update: LightClientUpdate; summary: LightClientUpdateSummary; }; diff --git a/packages/light-client/src/spec/utils.ts b/packages/light-client/src/spec/utils.ts index 2a5720a1f637..65d6f3e84c59 100644 --- a/packages/light-client/src/spec/utils.ts +++ b/packages/light-client/src/spec/utils.ts @@ -8,7 +8,16 @@ import { BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH as EXECUTION_PAYLOAD_DEPTH, BLOCK_BODY_EXECUTION_PAYLOAD_INDEX as EXECUTION_PAYLOAD_INDEX, } from "@lodestar/params"; -import {altair, phase0, ssz, allForks, capella, deneb, Slot} from "@lodestar/types"; +import { + ssz, + Slot, + LightClientFinalityUpdate, + LightClientHeader, + LightClientOptimisticUpdate, + LightClientUpdate, + BeaconBlockHeader, + SyncCommittee, +} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {isValidMerkleBranch, computeEpochAtSlot, computeSyncPeriodAtSlot} from "../utils/index.js"; @@ -32,7 +41,7 @@ export function getSafetyThreshold(maxActiveParticipants: number): number { return Math.floor(maxActiveParticipants / SAFETY_THRESHOLD_FACTOR); } -export function isSyncCommitteeUpdate(update: allForks.LightClientUpdate): boolean { +export function isSyncCommitteeUpdate(update: LightClientUpdate): boolean { return ( // Fast return for when constructing full LightClientUpdate from partial updates update.nextSyncCommitteeBranch !== ZERO_NEXT_SYNC_COMMITTEE_BRANCH && @@ -40,7 +49,7 @@ export function isSyncCommitteeUpdate(update: allForks.LightClientUpdate): boole ); } -export function isFinalityUpdate(update: allForks.LightClientUpdate): boolean { +export function isFinalityUpdate(update: LightClientUpdate): boolean { return ( // Fast return for when constructing full LightClientUpdate from partial updates update.finalityBranch !== ZERO_FINALITY_BRANCH && @@ -48,12 +57,12 @@ export function isFinalityUpdate(update: allForks.LightClientUpdate): boolean { ); } -export function isZeroedHeader(header: phase0.BeaconBlockHeader): boolean { +export function isZeroedHeader(header: BeaconBlockHeader): boolean { // Fast return for when constructing full LightClientUpdate from partial updates return header === ZERO_HEADER || byteArrayEquals(header.bodyRoot, ZERO_HASH); } -export function isZeroedSyncCommittee(syncCommittee: altair.SyncCommittee): boolean { +export function isZeroedSyncCommittee(syncCommittee: SyncCommittee): boolean { // Fast return for when constructing full LightClientUpdate from partial updates return syncCommittee === ZERO_SYNC_COMMITTEE || byteArrayEquals(syncCommittee.pubkeys[0], ZERO_PUBKEY); } @@ -61,8 +70,8 @@ export function isZeroedSyncCommittee(syncCommittee: altair.SyncCommittee): bool export function upgradeLightClientHeader( config: ChainForkConfig, targetFork: ForkName, - header: altair.LightClientHeader -): allForks.LightClientHeader { + header: LightClientHeader +): LightClientHeader { const headerFork = config.getForkName(header.beacon.slot); if (ForkSeq[headerFork] >= ForkSeq[targetFork]) { throw Error(`Invalid upgrade request from headerFork=${headerFork} to targetFork=${targetFork}`); @@ -70,7 +79,7 @@ export function upgradeLightClientHeader( // We are modifying the same header object, may be we could create a copy, but its // not required as of now - const upgradedHeader = header as allForks.LightClientHeader; + const upgradedHeader = header; const startUpgradeFromFork = Object.values(ForkName)[ForkSeq[headerFork] + 1]; switch (startUpgradeFromFork) { @@ -86,9 +95,9 @@ export function upgradeLightClientHeader( // eslint-disable-next-line no-fallthrough case ForkName.capella: - (upgradedHeader as capella.LightClientHeader).execution = + (upgradedHeader as LightClientHeader).execution = ssz.capella.LightClientHeader.fields.execution.defaultValue(); - (upgradedHeader as capella.LightClientHeader).executionBranch = + (upgradedHeader as LightClientHeader).executionBranch = ssz.capella.LightClientHeader.fields.executionBranch.defaultValue(); // Break if no further upgradation is required else fall through @@ -96,9 +105,9 @@ export function upgradeLightClientHeader( // eslint-disable-next-line no-fallthrough case ForkName.deneb: - (upgradedHeader as deneb.LightClientHeader).execution.blobGasUsed = + (upgradedHeader as LightClientHeader).execution.blobGasUsed = ssz.deneb.LightClientHeader.fields.execution.fields.blobGasUsed.defaultValue(); - (upgradedHeader as deneb.LightClientHeader).execution.excessBlobGas = + (upgradedHeader as LightClientHeader).execution.excessBlobGas = ssz.deneb.LightClientHeader.fields.execution.fields.excessBlobGas.defaultValue(); // Break if no further upgradation is required else fall through @@ -107,30 +116,30 @@ export function upgradeLightClientHeader( return upgradedHeader; } -export function isValidLightClientHeader(config: ChainForkConfig, header: allForks.LightClientHeader): boolean { +export function isValidLightClientHeader(config: ChainForkConfig, header: LightClientHeader): boolean { const epoch = computeEpochAtSlot(header.beacon.slot); if (epoch < config.CAPELLA_FORK_EPOCH) { return ( - ((header as capella.LightClientHeader).execution === undefined || + ((header as LightClientHeader).execution === undefined || ssz.capella.ExecutionPayloadHeader.equals( - (header as capella.LightClientHeader).execution, + (header as LightClientHeader).execution, ssz.capella.LightClientHeader.fields.execution.defaultValue() )) && - ((header as capella.LightClientHeader).executionBranch === undefined || + ((header as LightClientHeader).executionBranch === undefined || ssz.capella.LightClientHeader.fields.executionBranch.equals( ssz.capella.LightClientHeader.fields.executionBranch.defaultValue(), - (header as capella.LightClientHeader).executionBranch + (header as LightClientHeader).executionBranch )) ); } if (epoch < config.DENEB_FORK_EPOCH) { if ( - ((header as deneb.LightClientHeader).execution.blobGasUsed && - (header as deneb.LightClientHeader).execution.blobGasUsed !== BigInt(0)) || - ((header as deneb.LightClientHeader).execution.excessBlobGas && - (header as deneb.LightClientHeader).execution.excessBlobGas !== BigInt(0)) + ((header as LightClientHeader).execution.blobGasUsed && + (header as LightClientHeader).execution.blobGasUsed !== BigInt(0)) || + ((header as LightClientHeader).execution.excessBlobGas && + (header as LightClientHeader).execution.excessBlobGas !== BigInt(0)) ) { return false; } @@ -139,8 +148,8 @@ export function isValidLightClientHeader(config: ChainForkConfig, header: allFor return isValidMerkleBranch( config .getExecutionForkTypes(header.beacon.slot) - .ExecutionPayloadHeader.hashTreeRoot((header as capella.LightClientHeader).execution), - (header as capella.LightClientHeader).executionBranch, + .ExecutionPayloadHeader.hashTreeRoot((header as LightClientHeader).execution), + (header as LightClientHeader).executionBranch, EXECUTION_PAYLOAD_DEPTH, EXECUTION_PAYLOAD_INDEX, header.beacon.bodyRoot @@ -150,8 +159,8 @@ export function isValidLightClientHeader(config: ChainForkConfig, header: allFor export function upgradeLightClientUpdate( config: ChainForkConfig, targetFork: ForkName, - update: allForks.LightClientUpdate -): allForks.LightClientUpdate { + update: LightClientUpdate +): LightClientUpdate { update.attestedHeader = upgradeLightClientHeader(config, targetFork, update.attestedHeader); update.finalizedHeader = upgradeLightClientHeader(config, targetFork, update.finalizedHeader); @@ -161,8 +170,8 @@ export function upgradeLightClientUpdate( export function upgradeLightClientFinalityUpdate( config: ChainForkConfig, targetFork: ForkName, - finalityUpdate: allForks.LightClientFinalityUpdate -): allForks.LightClientFinalityUpdate { + finalityUpdate: LightClientFinalityUpdate +): LightClientFinalityUpdate { finalityUpdate.attestedHeader = upgradeLightClientHeader(config, targetFork, finalityUpdate.attestedHeader); finalityUpdate.finalizedHeader = upgradeLightClientHeader(config, targetFork, finalityUpdate.finalizedHeader); @@ -172,8 +181,8 @@ export function upgradeLightClientFinalityUpdate( export function upgradeLightClientOptimisticUpdate( config: ChainForkConfig, targetFork: ForkName, - optimisticUpdate: allForks.LightClientOptimisticUpdate -): allForks.LightClientOptimisticUpdate { + optimisticUpdate: LightClientOptimisticUpdate +): LightClientOptimisticUpdate { optimisticUpdate.attestedHeader = upgradeLightClientHeader(config, targetFork, optimisticUpdate.attestedHeader); return optimisticUpdate; diff --git a/packages/light-client/src/spec/validateLightClientBootstrap.ts b/packages/light-client/src/spec/validateLightClientBootstrap.ts index ab3660ee87ec..30540da24bd1 100644 --- a/packages/light-client/src/spec/validateLightClientBootstrap.ts +++ b/packages/light-client/src/spec/validateLightClientBootstrap.ts @@ -1,5 +1,5 @@ import {byteArrayEquals} from "@chainsafe/ssz"; -import {Root, ssz, allForks} from "@lodestar/types"; +import {LightClientBootstrap, Root, ssz} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {toHex} from "@lodestar/utils"; import {isValidMerkleBranch} from "../utils/verifyMerkleBranch.js"; @@ -11,7 +11,7 @@ const CURRENT_SYNC_COMMITTEE_DEPTH = 5; export function validateLightClientBootstrap( config: ChainForkConfig, trustedBlockRoot: Root, - bootstrap: allForks.LightClientBootstrap + bootstrap: LightClientBootstrap ): void { const headerRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(bootstrap.header.beacon); diff --git a/packages/light-client/src/spec/validateLightClientUpdate.ts b/packages/light-client/src/spec/validateLightClientUpdate.ts index 2629986e85f2..fde760da3b05 100644 --- a/packages/light-client/src/spec/validateLightClientUpdate.ts +++ b/packages/light-client/src/spec/validateLightClientUpdate.ts @@ -1,6 +1,6 @@ import bls from "@chainsafe/bls"; import type {PublicKey, Signature} from "@chainsafe/bls/types"; -import {Root, ssz, allForks} from "@lodestar/types"; +import {LightClientUpdate, Root, ssz} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import { FINALIZED_ROOT_INDEX, @@ -27,7 +27,7 @@ import {ILightClientStore} from "./store.js"; export function validateLightClientUpdate( config: ChainForkConfig, store: ILightClientStore, - update: allForks.LightClientUpdate, + update: LightClientUpdate, syncCommittee: SyncCommitteeFast ): void { // Verify sync committee has sufficient participants diff --git a/packages/light-client/src/transport/interface.ts b/packages/light-client/src/transport/interface.ts index dcd3210df70c..dc9c697c00be 100644 --- a/packages/light-client/src/transport/interface.ts +++ b/packages/light-client/src/transport/interface.ts @@ -1,4 +1,10 @@ -import {allForks, SyncPeriod} from "@lodestar/types"; +import { + LightClientBootstrap, + LightClientFinalityUpdate, + LightClientOptimisticUpdate, + LightClientUpdate, + SyncPeriod, +} from "@lodestar/types"; import {ForkName} from "@lodestar/params"; export interface LightClientTransport { @@ -8,24 +14,24 @@ export interface LightClientTransport { ): Promise< { version: ForkName; - data: allForks.LightClientUpdate; + data: LightClientUpdate; }[] >; /** * Returns the latest optimistic head update available. Clients should use the SSE type `light_client_optimistic_update` * unless to get the very first head update after syncing, or if SSE are not supported by the server. */ - getOptimisticUpdate(): Promise<{version: ForkName; data: allForks.LightClientOptimisticUpdate}>; - getFinalityUpdate(): Promise<{version: ForkName; data: allForks.LightClientFinalityUpdate}>; + getOptimisticUpdate(): Promise<{version: ForkName; data: LightClientOptimisticUpdate}>; + getFinalityUpdate(): Promise<{version: ForkName; data: LightClientFinalityUpdate}>; /** * Fetch a bootstrapping state with a proof to a trusted block root. * The trusted block root should be fetched with similar means to a weak subjectivity checkpoint. * Only block roots for checkpoints are guaranteed to be available. */ - getBootstrap(blockRoot: string): Promise<{version: ForkName; data: allForks.LightClientBootstrap}>; + getBootstrap(blockRoot: string): Promise<{version: ForkName; data: LightClientBootstrap}>; // registers handler for LightClientOptimisticUpdate. This can come either via sse or p2p - onOptimisticUpdate(handler: (optimisticUpdate: allForks.LightClientOptimisticUpdate) => void): void; + onOptimisticUpdate(handler: (optimisticUpdate: LightClientOptimisticUpdate) => void): void; // registers handler for LightClientFinalityUpdate. This can come either via sse or p2p - onFinalityUpdate(handler: (finalityUpdate: allForks.LightClientFinalityUpdate) => void): void; + onFinalityUpdate(handler: (finalityUpdate: LightClientFinalityUpdate) => void): void; } diff --git a/packages/light-client/src/transport/rest.ts b/packages/light-client/src/transport/rest.ts index bc19594e3930..c260e1aaeee3 100644 --- a/packages/light-client/src/transport/rest.ts +++ b/packages/light-client/src/transport/rest.ts @@ -1,13 +1,19 @@ import mitt from "mitt"; -import {type allForks, type SyncPeriod} from "@lodestar/types"; +import { + LightClientBootstrap, + LightClientFinalityUpdate, + LightClientOptimisticUpdate, + LightClientUpdate, + type SyncPeriod, +} from "@lodestar/types"; import {type ApiClient, routes} from "@lodestar/api"; import {type ForkName} from "@lodestar/params"; import {MittEmitter} from "../events.js"; import {type LightClientTransport} from "./interface.js"; export type LightClientRestEvents = { - [routes.events.EventType.lightClientFinalityUpdate]: (update: allForks.LightClientFinalityUpdate) => void; - [routes.events.EventType.lightClientOptimisticUpdate]: (update: allForks.LightClientOptimisticUpdate) => void; + [routes.events.EventType.lightClientFinalityUpdate]: (update: LightClientFinalityUpdate) => void; + [routes.events.EventType.lightClientOptimisticUpdate]: (update: LightClientOptimisticUpdate) => void; }; export type LightClientRestEmitter = MittEmitter; @@ -25,7 +31,7 @@ export class LightClientRestTransport implements LightClientTransport { ): Promise< { version: ForkName; - data: allForks.LightClientUpdate; + data: LightClientUpdate; }[] > { const res = await this.api.lightclient.getLightClientUpdatesByRange({startPeriod, count}); @@ -34,27 +40,27 @@ export class LightClientRestTransport implements LightClientTransport { return updates.map((data, i) => ({data, version: versions[i]})); } - async getOptimisticUpdate(): Promise<{version: ForkName; data: allForks.LightClientOptimisticUpdate}> { + async getOptimisticUpdate(): Promise<{version: ForkName; data: LightClientOptimisticUpdate}> { const res = await this.api.lightclient.getLightClientOptimisticUpdate(); return {version: res.meta().version, data: res.value()}; } - async getFinalityUpdate(): Promise<{version: ForkName; data: allForks.LightClientFinalityUpdate}> { + async getFinalityUpdate(): Promise<{version: ForkName; data: LightClientFinalityUpdate}> { const res = await this.api.lightclient.getLightClientFinalityUpdate(); return {version: res.meta().version, data: res.value()}; } - async getBootstrap(blockRoot: string): Promise<{version: ForkName; data: allForks.LightClientBootstrap}> { + async getBootstrap(blockRoot: string): Promise<{version: ForkName; data: LightClientBootstrap}> { const res = await this.api.lightclient.getLightClientBootstrap({blockRoot}); return {version: res.meta().version, data: res.value()}; } - onOptimisticUpdate(handler: (optimisticUpdate: allForks.LightClientOptimisticUpdate) => void): void { + onOptimisticUpdate(handler: (optimisticUpdate: LightClientOptimisticUpdate) => void): void { this.subscribeEventstream(); this.eventEmitter.on(routes.events.EventType.lightClientOptimisticUpdate, handler); } - onFinalityUpdate(handler: (finalityUpdate: allForks.LightClientFinalityUpdate) => void): void { + onFinalityUpdate(handler: (finalityUpdate: LightClientFinalityUpdate) => void): void { this.subscribeEventstream(); this.eventEmitter.on(routes.events.EventType.lightClientFinalityUpdate, handler); } diff --git a/packages/light-client/src/types.ts b/packages/light-client/src/types.ts index 6e6126f8a481..518edd349c63 100644 --- a/packages/light-client/src/types.ts +++ b/packages/light-client/src/types.ts @@ -1,14 +1,14 @@ import type {PublicKey} from "@chainsafe/bls/types"; -import {SyncPeriod, allForks} from "@lodestar/types"; +import {LightClientHeader, LightClientUpdate, SyncPeriod} from "@lodestar/types"; export type LightClientStoreFast = { snapshot: LightClientSnapshotFast; - bestUpdates: Map; + bestUpdates: Map; }; export type LightClientSnapshotFast = { /** Beacon block header */ - header: allForks.LightClientHeader; + header: LightClientHeader; /** Sync committees corresponding to the header */ currentSyncCommittee: SyncCommitteeFast; nextSyncCommittee: SyncCommitteeFast; diff --git a/packages/light-client/src/validation.ts b/packages/light-client/src/validation.ts index 85c5c35a2cea..e7839f115153 100644 --- a/packages/light-client/src/validation.ts +++ b/packages/light-client/src/validation.ts @@ -1,6 +1,6 @@ import bls from "@chainsafe/bls"; import type {PublicKey, Signature} from "@chainsafe/bls/types"; -import {altair, Root, Slot, ssz, allForks} from "@lodestar/types"; +import {altair, LightClientFinalityUpdate, LightClientUpdate, Root, Slot, ssz} from "@lodestar/types"; import { FINALIZED_ROOT_INDEX, FINALIZED_ROOT_DEPTH, @@ -24,7 +24,7 @@ import {computeSyncPeriodAtSlot} from "./utils/clock.js"; export function assertValidLightClientUpdate( config: BeaconConfig, syncCommittee: SyncCommitteeFast, - update: allForks.LightClientUpdate + update: LightClientUpdate ): void { // DIFF FROM SPEC: An update with the same header.slot can be valid and valuable to the lightclient // It may have more consensus and result in a better snapshot whilst not advancing the state @@ -64,7 +64,7 @@ export function assertValidLightClientUpdate( * * Where `hashTreeRoot(state) == update.finalityHeader.stateRoot` */ -export function assertValidFinalityProof(update: allForks.LightClientFinalityUpdate): void { +export function assertValidFinalityProof(update: LightClientFinalityUpdate): void { if ( !isValidMerkleBranch( ssz.phase0.BeaconBlockHeader.hashTreeRoot(update.finalizedHeader.beacon), @@ -94,7 +94,7 @@ export function assertValidFinalityProof(update: allForks.LightClientFinalityUpd * * Where `hashTreeRoot(state) == update.header.stateRoot` */ -export function assertValidSyncCommitteeProof(update: allForks.LightClientUpdate): void { +export function assertValidSyncCommitteeProof(update: LightClientUpdate): void { if ( !isValidMerkleBranch( ssz.altair.SyncCommittee.hashTreeRoot(update.nextSyncCommittee), diff --git a/packages/light-client/test/unit/isValidLightClientHeader.test.ts b/packages/light-client/test/unit/isValidLightClientHeader.test.ts index 40efa1293231..db27a8266103 100644 --- a/packages/light-client/test/unit/isValidLightClientHeader.test.ts +++ b/packages/light-client/test/unit/isValidLightClientHeader.test.ts @@ -1,6 +1,6 @@ import {describe, it, expect} from "vitest"; import {fromHexString} from "@chainsafe/ssz"; -import {ssz, allForks} from "@lodestar/types"; +import {LightClientHeader, ssz} from "@lodestar/types"; import {createBeaconConfig, createChainForkConfig, defaultChainConfig} from "@lodestar/config"; import {isValidLightClientHeader} from "../../src/spec/utils.js"; @@ -80,7 +80,7 @@ describe("isValidLightClientHeader", function () { executionBranch: capellaLCHeader.executionBranch, }; - const testCases: [string, allForks.LightClientHeader][] = [ + const testCases: [string, LightClientHeader][] = [ ["altair LC header", altairLCHeader], ["altair upgraded to capella", altairUpgradedCapellaLCHeader], ["altair upgraded to deneb", altairUpgradedDenebLCHeader], @@ -88,7 +88,7 @@ describe("isValidLightClientHeader", function () { ["capella upgraded to deneb LC header", capellaUpgradedDenebHeader], ]; - testCases.forEach(([name, header]: [string, allForks.LightClientHeader]) => { + testCases.forEach(([name, header]: [string, LightClientHeader]) => { it(name, function () { const isValid = isValidLightClientHeader(config, header); expect(isValid).toBe(true); diff --git a/packages/light-client/test/utils/utils.ts b/packages/light-client/test/utils/utils.ts index a99004345217..8364bcc7fc85 100644 --- a/packages/light-client/test/utils/utils.ts +++ b/packages/light-client/test/utils/utils.ts @@ -11,7 +11,7 @@ import { SLOTS_PER_EPOCH, SYNC_COMMITTEE_SIZE, } from "@lodestar/params"; -import {altair, phase0, Slot, ssz, SyncPeriod, allForks} from "@lodestar/types"; +import {altair, LightClientBootstrap, phase0, Slot, ssz, SyncPeriod} from "@lodestar/types"; import {SyncCommitteeFast} from "../../src/types.js"; import {computeSigningRoot} from "../../src/utils/domain.js"; import {getConsoleLogger} from "../../src/utils/logger.js"; @@ -156,7 +156,7 @@ export function computeLightclientUpdate(config: BeaconConfig, period: SyncPerio * Creates a LightClientBootstrap that passes validation */ export function computeLightClientSnapshot(period: SyncPeriod): { - snapshot: allForks.LightClientBootstrap; + snapshot: LightClientBootstrap; checkpointRoot: Uint8Array; } { const currentSyncCommittee = getInteropSyncCommittee(period).syncCommittee; diff --git a/packages/params/src/forkName.ts b/packages/params/src/forkName.ts index 142684c313f4..fa3be24bfae4 100644 --- a/packages/params/src/forkName.ts +++ b/packages/params/src/forkName.ts @@ -20,6 +20,8 @@ export enum ForkSeq { deneb = 4, } +export type ForkAll = ForkName; + export type ForkPreLightClient = ForkName.phase0; export type ForkLightClient = Exclude; export function isForkLightClient(fork: ForkName): fork is ForkLightClient { diff --git a/packages/prover/src/proof_provider/payload_store.ts b/packages/prover/src/proof_provider/payload_store.ts index 30d5b2126296..343ec63719f9 100644 --- a/packages/prover/src/proof_provider/payload_store.ts +++ b/packages/prover/src/proof_provider/payload_store.ts @@ -1,6 +1,7 @@ import {ApiClient} from "@lodestar/api"; -import {allForks, capella} from "@lodestar/types"; import {Logger} from "@lodestar/utils"; +import {ExecutionPayload, LightClientHeader} from "@lodestar/types"; +import {ForkName} from "@lodestar/params"; import {MAX_PAYLOAD_HISTORY} from "../constants.js"; import {fetchBlock, getExecutionPayloadForBlockNumber} from "../utils/consensus.js"; import {bufferToHex, hexToNumber} from "../utils/conversion.js"; @@ -28,13 +29,13 @@ export class PayloadStore { private unfinalizedRoots = new Map(); // Payloads store with BlockELRoot as key - private payloads = new Map(); + private payloads = new Map(); private latestBlockRoot: BlockELRoot | null = null; constructor(private opts: {api: ApiClient; logger: Logger}) {} - get finalized(): allForks.ExecutionPayload | undefined { + get finalized(): ExecutionPayload | undefined { const maxBlockNumberForFinalized = this.finalizedRoots.max; if (maxBlockNumberForFinalized === undefined) { @@ -49,7 +50,7 @@ export class PayloadStore { return undefined; } - get latest(): allForks.ExecutionPayload | undefined { + get latest(): ExecutionPayload | undefined { if (this.latestBlockRoot) { return this.payloads.get(this.latestBlockRoot); } @@ -57,7 +58,7 @@ export class PayloadStore { return undefined; } - async get(blockId: number | string): Promise { + async get(blockId: number | string): Promise { // Given block id is a block hash in hex (32 bytes root takes 64 hex chars + 2 for 0x prefix) if (typeof blockId === "string" && blockId.startsWith("0x") && blockId.length === 64 + 2) { return this.payloads.get(blockId); @@ -81,7 +82,7 @@ export class PayloadStore { return undefined; } - protected async getOrFetchFinalizedPayload(blockNumber: number): Promise { + protected async getOrFetchFinalizedPayload(blockNumber: number): Promise { const maxBlockNumberForFinalized = this.finalizedRoots.max; const minBlockNumberForFinalized = this.finalizedRoots.min; @@ -116,7 +117,7 @@ export class PayloadStore { return undefined; } - set(payload: allForks.ExecutionPayload, slot: number, finalized: boolean): void { + set(payload: ExecutionPayload, slot: number, finalized: boolean): void { const blockELRoot = bufferToHex(payload.blockHash); this.payloads.set(blockELRoot, payload); @@ -134,7 +135,7 @@ export class PayloadStore { } } - async processLCHeader(header: capella.LightClientHeader, finalized = false): Promise { + async processLCHeader(header: LightClientHeader, finalized = false): Promise { const blockSlot = header.beacon.slot; const blockNumber = header.execution.blockNumber; const blockELRoot = bufferToHex(header.execution.blockHash); diff --git a/packages/prover/src/proof_provider/proof_provider.ts b/packages/prover/src/proof_provider/proof_provider.ts index 4cced315da04..3a95b0e4e604 100644 --- a/packages/prover/src/proof_provider/proof_provider.ts +++ b/packages/prover/src/proof_provider/proof_provider.ts @@ -3,9 +3,9 @@ import {ChainForkConfig, createChainForkConfig} from "@lodestar/config"; import {NetworkName, networksChainConfig} from "@lodestar/config/networks"; import {Lightclient, LightclientEvent, RunStatusCode} from "@lodestar/light-client"; import {LightClientRestTransport} from "@lodestar/light-client/transport"; -import {isForkWithdrawals} from "@lodestar/params"; -import {allForks, capella} from "@lodestar/types"; +import {ForkName, isForkWithdrawals} from "@lodestar/params"; import {Logger} from "@lodestar/utils"; +import {ExecutionPayload, LightClientHeader} from "@lodestar/types"; import {LCTransport, RootProviderInitOptions} from "../interfaces.js"; import {assertLightClient} from "../utils/assertion.js"; import { @@ -146,7 +146,7 @@ export class ProofProvider { }; } - async getExecutionPayload(blockNumber: number | string | "finalized" | "latest"): Promise { + async getExecutionPayload(blockNumber: number | string | "finalized" | "latest"): Promise { assertLightClient(this.lightClient); if (typeof blockNumber === "string" && blockNumber === "finalized") { @@ -170,7 +170,7 @@ export class ProofProvider { throw new Error(`Invalid blockNumber "${blockNumber}"`); } - async processLCHeader(lcHeader: allForks.LightClientHeader, finalized = false): Promise { + async processLCHeader(lcHeader: LightClientHeader, finalized = false): Promise { const fork = this.opts.config.getForkName(lcHeader.beacon.slot); if (!isForkWithdrawals(fork)) { @@ -185,7 +185,7 @@ export class ProofProvider { throw new Error("Execution payload is required for execution fork"); } - await this.store.processLCHeader(lcHeader as capella.LightClientHeader, finalized); + await this.store.processLCHeader(lcHeader as LightClientHeader, finalized); } private registerEvents(): void { diff --git a/packages/prover/src/utils/consensus.ts b/packages/prover/src/utils/consensus.ts index c34558e96e6d..ac8fd24d139f 100644 --- a/packages/prover/src/utils/consensus.ts +++ b/packages/prover/src/utils/consensus.ts @@ -1,5 +1,5 @@ import {ApiClient} from "@lodestar/api/beacon"; -import {allForks, Bytes32, capella} from "@lodestar/types"; +import {Bytes32, ExecutionPayload, capella} from "@lodestar/types"; import {GenesisData, Lightclient} from "@lodestar/light-client"; import {Logger} from "@lodestar/utils"; import {MAX_PAYLOAD_HISTORY} from "../constants.js"; @@ -49,7 +49,7 @@ export async function getExecutionPayloads({ startSlot: number; endSlot: number; logger: Logger; -}): Promise> { +}): Promise> { [startSlot, endSlot] = [Math.min(startSlot, endSlot), Math.max(startSlot, endSlot)]; if (startSlot === endSlot) { logger.debug("Fetching EL payload", {slot: startSlot}); @@ -57,7 +57,7 @@ export async function getExecutionPayloads({ logger.debug("Fetching EL payloads", {startSlot, endSlot}); } - const payloads = new Map(); + const payloads = new Map(); let slot = endSlot; let block = await fetchNearestBlock(api, slot); @@ -82,8 +82,8 @@ export async function getExecutionPayloadForBlockNumber( api: ApiClient, startSlot: number, blockNumber: number -): Promise> { - const payloads = new Map(); +): Promise> { + const payloads = new Map(); let block = await fetchNearestBlock(api, startSlot); payloads.set(block.message.slot, block.message.body.executionPayload); diff --git a/packages/prover/src/utils/evm.ts b/packages/prover/src/utils/evm.ts index bbdba907c35f..19f43d9584c8 100644 --- a/packages/prover/src/utils/evm.ts +++ b/packages/prover/src/utils/evm.ts @@ -4,8 +4,8 @@ import {VM, RunTxResult} from "@ethereumjs/vm"; import {TransactionFactory} from "@ethereumjs/tx"; import {Block, BlockHeader} from "@ethereumjs/block"; import {NetworkName} from "@lodestar/config/networks"; -import {allForks} from "@lodestar/types"; import {Logger} from "@lodestar/utils"; +import {ExecutionPayload} from "@lodestar/types"; import {ZERO_ADDRESS} from "../constants.js"; import {ProofProvider} from "../proof_provider/proof_provider.js"; import {ELBlock, ELProof, ELTransaction, JsonRpcVersion} from "../types.js"; @@ -41,7 +41,7 @@ export async function getVMWithState({ }: { rpc: ELRpcProvider; vm: VM; - executionPayload: allForks.ExecutionPayload; + executionPayload: ExecutionPayload; tx: ELTransaction; logger: Logger; }): Promise { @@ -163,7 +163,7 @@ export async function executeVMCall({ rpc: ELRpcProvider; tx: ELTransaction; vm: VM; - executionPayload: allForks.ExecutionPayload; + executionPayload: ExecutionPayload; network: NetworkName; }): Promise { const {from, to, gas, gasPrice, maxPriorityFeePerGas, value, data, input} = tx; @@ -205,7 +205,7 @@ export async function executeVMTx({ rpc: ELRpcProvider; tx: ELTransaction; vm: VM; - executionPayload: allForks.ExecutionPayload; + executionPayload: ExecutionPayload; network: NetworkName; }): Promise { const {result: block} = await rpc.request("eth_getBlockByHash", [bufferToHex(executionPayload.blockHash), true], { @@ -258,7 +258,7 @@ export async function executeVMTx({ export function getVMBlockHeaderFromELBlock( block: ELBlock, - executionPayload: allForks.ExecutionPayload, + executionPayload: ExecutionPayload, network: NetworkName ): BlockHeader { const blockHeaderData = { diff --git a/packages/prover/src/utils/validation.ts b/packages/prover/src/utils/validation.ts index 1615df1db9b4..3adc37571db5 100644 --- a/packages/prover/src/utils/validation.ts +++ b/packages/prover/src/utils/validation.ts @@ -3,7 +3,7 @@ import {RLP} from "@ethereumjs/rlp"; import {Trie} from "@ethereumjs/trie"; import {Account, KECCAK256_NULL_S} from "@ethereumjs/util"; import {keccak256} from "ethereum-cryptography/keccak.js"; -import {Bytes32, allForks} from "@lodestar/types"; +import {Bytes32, ExecutionPayload} from "@lodestar/types"; import {Logger} from "@lodestar/utils"; import {ChainForkConfig} from "@lodestar/config"; import {ELBlock, ELProof, ELStorageProof, HexString} from "../types.js"; @@ -99,7 +99,7 @@ export async function isValidBlock({ logger, config, }: { - executionPayload: allForks.ExecutionPayload; + executionPayload: ExecutionPayload; block: ELBlock; logger: Logger; config: ChainForkConfig; diff --git a/packages/prover/test/unit/proof_provider/payload_store.test.ts b/packages/prover/test/unit/proof_provider/payload_store.test.ts index 81b8ffe3c16c..b482bb579e77 100644 --- a/packages/prover/test/unit/proof_provider/payload_store.test.ts +++ b/packages/prover/test/unit/proof_provider/payload_store.test.ts @@ -3,7 +3,7 @@ import {when} from "vitest-when"; import {ApiClient, ApiResponse, HttpStatusCode, routes} from "@lodestar/api"; import {hash} from "@lodestar/utils"; import {Logger} from "@lodestar/logger"; -import {allForks, capella} from "@lodestar/types"; +import {ExecutionPayload, SignedBeaconBlock, capella} from "@lodestar/types"; import {toHexString} from "@lodestar/utils"; import {ForkName} from "@lodestar/params"; import {PayloadStore} from "../../../src/proof_provider/payload_store.js"; @@ -12,12 +12,12 @@ import {MAX_PAYLOAD_HISTORY} from "../../../src/constants.js"; const slotNumber = 10; const createHash = (input: string): Uint8Array => hash(Buffer.from(input, "utf8")); -const buildPayload = ({blockNumber}: {blockNumber: number}): allForks.ExecutionPayload => +const buildPayload = ({blockNumber}: {blockNumber: number}): ExecutionPayload => ({ blockNumber, blockHash: createHash(`"block-hash-${blockNumber}`), parentHash: createHash(`"parent-hash-${blockNumber}`), - }) as unknown as allForks.ExecutionPayload; + }) as unknown as ExecutionPayload; const buildLCHeader = ({slot, blockNumber}: {slot: number; blockNumber: number}): capella.LightClientHeader => ({ @@ -25,7 +25,7 @@ const buildLCHeader = ({slot, blockNumber}: {slot: number; blockNumber: number}) execution: buildPayload({blockNumber}), }) as unknown as capella.LightClientHeader; -const buildBlock = ({slot, blockNumber}: {slot: number; blockNumber: number}): allForks.SignedBeaconBlock => +const buildBlock = ({slot, blockNumber}: {slot: number; blockNumber: number}): SignedBeaconBlock => ({ signature: createHash(`"beacon-block-signature-${slot}`), message: { @@ -37,7 +37,7 @@ const buildBlock = ({slot, blockNumber}: {slot: number; blockNumber: number}): a executionPayload: buildPayload({blockNumber}), }, }, - }) as unknown as allForks.SignedBeaconBlock; + }) as unknown as SignedBeaconBlock; const buildBlockResponse = ({ slot, diff --git a/packages/state-transition/src/block/index.ts b/packages/state-transition/src/block/index.ts index b235f7ca24ef..fdfc9e903518 100644 --- a/packages/state-transition/src/block/index.ts +++ b/packages/state-transition/src/block/index.ts @@ -1,5 +1,5 @@ import {ForkSeq} from "@lodestar/params"; -import {allForks, altair, capella} from "@lodestar/types"; +import {BeaconBlock, BlindedBeaconBlock, altair, capella} from "@lodestar/types"; import {getFullOrBlindedPayload, isExecutionEnabled} from "../util/execution.js"; import {CachedBeaconStateAllForks, CachedBeaconStateCapella, CachedBeaconStateBellatrix} from "../types.js"; import {processExecutionPayload} from "./processExecutionPayload.js"; @@ -31,7 +31,7 @@ export * from "./externalData.js"; export function processBlock( fork: ForkSeq, state: CachedBeaconStateAllForks, - block: allForks.FullOrBlindedBeaconBlock, + block: BeaconBlock | BlindedBeaconBlock, externalData: BlockExternalData & ProcessBlockOpts, opts?: ProcessBlockOpts ): void { diff --git a/packages/state-transition/src/block/processBlockHeader.ts b/packages/state-transition/src/block/processBlockHeader.ts index e37372e7b3ac..755850969b4f 100644 --- a/packages/state-transition/src/block/processBlockHeader.ts +++ b/packages/state-transition/src/block/processBlockHeader.ts @@ -1,5 +1,5 @@ import {toHexString, byteArrayEquals} from "@chainsafe/ssz"; -import {allForks, ssz} from "@lodestar/types"; +import {BeaconBlock, BlindedBeaconBlock, ssz} from "@lodestar/types"; import {CachedBeaconStateAllForks} from "../types.js"; import {ZERO_HASH} from "../constants/index.js"; import {blindedOrFullBlockToHeader} from "../util/index.js"; @@ -9,7 +9,7 @@ import {blindedOrFullBlockToHeader} from "../util/index.js"; * PERF: Fixed work independent of block contents. * NOTE: `block` body root MUST be pre-cached. */ -export function processBlockHeader(state: CachedBeaconStateAllForks, block: allForks.FullOrBlindedBeaconBlock): void { +export function processBlockHeader(state: CachedBeaconStateAllForks, block: BeaconBlock | BlindedBeaconBlock): void { const slot = state.slot; // verify that the slots match if (block.slot !== slot) { diff --git a/packages/state-transition/src/block/processExecutionPayload.ts b/packages/state-transition/src/block/processExecutionPayload.ts index b589436012a5..3c28a400d3bf 100644 --- a/packages/state-transition/src/block/processExecutionPayload.ts +++ b/packages/state-transition/src/block/processExecutionPayload.ts @@ -1,10 +1,9 @@ import {toHexString, byteArrayEquals} from "@chainsafe/ssz"; -import {allForks, deneb} from "@lodestar/types"; +import {BeaconBlockBody, BlindedBeaconBlockBody, deneb, isExecutionPayload} from "@lodestar/types"; import {ForkSeq, MAX_BLOBS_PER_BLOCK} from "@lodestar/params"; import {CachedBeaconStateBellatrix, CachedBeaconStateCapella} from "../types.js"; import {getRandaoMix} from "../util/index.js"; import { - isExecutionPayload, isMergeTransitionComplete, getFullOrBlindedPayloadFromBody, executionPayloadToPayloadHeader, @@ -14,7 +13,7 @@ import {BlockExternalData, ExecutionPayloadStatus} from "./externalData.js"; export function processExecutionPayload( fork: ForkSeq, state: CachedBeaconStateBellatrix | CachedBeaconStateCapella, - body: allForks.FullOrBlindedBeaconBlockBody, + body: BeaconBlockBody | BlindedBeaconBlockBody, externalData: Omit ): void { const payload = getFullOrBlindedPayloadFromBody(body); @@ -76,8 +75,8 @@ export function processExecutionPayload( const payloadHeader = isExecutionPayload(payload) ? executionPayloadToPayloadHeader(fork, payload) : payload; - // TODO Deneb: Types are not happy by default. Since it's a generic allForks type going through ViewDU - // transformation then into allForks, probably some weird intersection incompatibility happens + // TODO Deneb: Types are not happy by default. Since it's a generic type going through ViewDU + // transformation then into all forks compatible probably some weird intersection incompatibility happens state.latestExecutionPayloadHeader = state.config .getExecutionForkTypes(state.slot) .ExecutionPayloadHeader.toViewDU(payloadHeader) as typeof state.latestExecutionPayloadHeader; diff --git a/packages/state-transition/src/block/processOperations.ts b/packages/state-transition/src/block/processOperations.ts index c523f1dad246..38716bb42a40 100644 --- a/packages/state-transition/src/block/processOperations.ts +++ b/packages/state-transition/src/block/processOperations.ts @@ -1,4 +1,4 @@ -import {allForks, capella} from "@lodestar/types"; +import {BeaconBlockBody, capella} from "@lodestar/types"; import {ForkSeq, MAX_DEPOSITS} from "@lodestar/params"; import {CachedBeaconStateAllForks, CachedBeaconStateCapella} from "../types.js"; @@ -22,7 +22,7 @@ export { export function processOperations( fork: ForkSeq, state: CachedBeaconStateAllForks, - body: allForks.BeaconBlockBody, + body: BeaconBlockBody, opts: ProcessBlockOpts = {verifySignatures: true} ): void { // verify that outstanding deposits are processed up to the maximum number of deposits diff --git a/packages/state-transition/src/block/processRandao.ts b/packages/state-transition/src/block/processRandao.ts index effeb34390cb..65bcd60f52f6 100644 --- a/packages/state-transition/src/block/processRandao.ts +++ b/packages/state-transition/src/block/processRandao.ts @@ -1,5 +1,5 @@ import {digest} from "@chainsafe/as-sha256"; -import {allForks} from "@lodestar/types"; +import {BeaconBlock} from "@lodestar/types"; import {EPOCHS_PER_HISTORICAL_VECTOR} from "@lodestar/params"; import {getRandaoMix} from "../util/index.js"; import {verifyRandaoSignature} from "../signatureSets/index.js"; @@ -10,11 +10,7 @@ import {CachedBeaconStateAllForks} from "../types.js"; * * PERF: Fixed work independent of block contents. */ -export function processRandao( - state: CachedBeaconStateAllForks, - block: allForks.BeaconBlock, - verifySignature = true -): void { +export function processRandao(state: CachedBeaconStateAllForks, block: BeaconBlock, verifySignature = true): void { const {epochCtx} = state; const epoch = epochCtx.epoch; const randaoReveal = block.body.randaoReveal; diff --git a/packages/state-transition/src/cache/types.ts b/packages/state-transition/src/cache/types.ts index 39b1dbb4b45b..d6d8a3c37904 100644 --- a/packages/state-transition/src/cache/types.ts +++ b/packages/state-transition/src/cache/types.ts @@ -1,25 +1,15 @@ import {CompositeViewDU} from "@chainsafe/ssz"; -import {Epoch, RootHex, ssz} from "@lodestar/types"; +import {Epoch, RootHex, SSZTypesFor} from "@lodestar/types"; +import {ForkAll, ForkExecution, ForkName} from "@lodestar/params"; import {EpochShuffling} from "../util/epochShuffling.js"; -export type BeaconStatePhase0 = CompositeViewDU; -export type BeaconStateAltair = CompositeViewDU; -export type BeaconStateBellatrix = CompositeViewDU; -export type BeaconStateCapella = CompositeViewDU; -export type BeaconStateDeneb = CompositeViewDU; +export type BeaconStatePhase0 = CompositeViewDU>; +export type BeaconStateAltair = CompositeViewDU>; +export type BeaconStateBellatrix = CompositeViewDU>; +export type BeaconStateCapella = CompositeViewDU>; +export type BeaconStateDeneb = CompositeViewDU>; -// Union at the TreeViewDU level -// - Works well as function argument and as generic type for allForks functions -// -// Quasy equivalent to -// CompositeViewDU // + future forks -export type BeaconStateAllForks = - | BeaconStatePhase0 - | BeaconStateAltair - | BeaconStateBellatrix - | BeaconStateCapella - | BeaconStateDeneb; - -export type BeaconStateExecutions = BeaconStateBellatrix | BeaconStateCapella | BeaconStateDeneb; +export type BeaconStateAllForks = CompositeViewDU>; +export type BeaconStateExecutions = CompositeViewDU>; export type ShufflingGetter = (shufflingEpoch: Epoch, dependentRoot: RootHex) => EpochShuffling | null; diff --git a/packages/state-transition/src/signatureSets/attesterSlashings.ts b/packages/state-transition/src/signatureSets/attesterSlashings.ts index 370d48a7d2f0..f0de50e5d0b2 100644 --- a/packages/state-transition/src/signatureSets/attesterSlashings.ts +++ b/packages/state-transition/src/signatureSets/attesterSlashings.ts @@ -1,4 +1,4 @@ -import {allForks, phase0, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types"; import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params"; import {computeSigningRoot, computeStartSlotAtEpoch, ISignatureSet, SignatureSetType} from "../util/index.js"; import {CachedBeaconStateAllForks} from "../types.js"; @@ -6,7 +6,7 @@ import {CachedBeaconStateAllForks} from "../types.js"; /** Get signature sets from all AttesterSlashing objects in a block */ export function getAttesterSlashingsSignatureSets( state: CachedBeaconStateAllForks, - signedBlock: allForks.SignedBeaconBlock + signedBlock: SignedBeaconBlock ): ISignatureSet[] { return signedBlock.message.body.attesterSlashings .map((attesterSlashing) => getAttesterSlashingSignatureSets(state, attesterSlashing)) diff --git a/packages/state-transition/src/signatureSets/index.ts b/packages/state-transition/src/signatureSets/index.ts index 05e4ad4b197a..983e131e00e6 100644 --- a/packages/state-transition/src/signatureSets/index.ts +++ b/packages/state-transition/src/signatureSets/index.ts @@ -1,5 +1,5 @@ import {ForkSeq} from "@lodestar/params"; -import {allForks, altair, capella} from "@lodestar/types"; +import {SignedBeaconBlock, altair, capella} from "@lodestar/types"; import {ISignatureSet} from "../util/index.js"; import {CachedBeaconStateAllForks, CachedBeaconStateAltair} from "../types.js"; import {getSyncCommitteeSignatureSet} from "../block/processSyncCommittee.js"; @@ -25,7 +25,7 @@ export * from "./blsToExecutionChange.js"; */ export function getBlockSignatureSets( state: CachedBeaconStateAllForks, - signedBlock: allForks.SignedBeaconBlock, + signedBlock: SignedBeaconBlock, opts?: { /** Useful since block proposer signature is verified beforehand on gossip validation */ skipProposerSignature?: boolean; diff --git a/packages/state-transition/src/signatureSets/indexedAttestation.ts b/packages/state-transition/src/signatureSets/indexedAttestation.ts index b5c48a20c9d4..9ae6627d0b56 100644 --- a/packages/state-transition/src/signatureSets/indexedAttestation.ts +++ b/packages/state-transition/src/signatureSets/indexedAttestation.ts @@ -1,5 +1,5 @@ import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params"; -import {allForks, phase0, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types"; import { computeSigningRoot, computeStartSlotAtEpoch, @@ -39,7 +39,7 @@ export function getIndexedAttestationSignatureSet( export function getAttestationsSignatureSets( state: CachedBeaconStateAllForks, - signedBlock: allForks.SignedBeaconBlock + signedBlock: SignedBeaconBlock ): ISignatureSet[] { return signedBlock.message.body.attestations.map((attestation) => getIndexedAttestationSignatureSet(state, state.epochCtx.getIndexedAttestation(attestation)) diff --git a/packages/state-transition/src/signatureSets/proposer.ts b/packages/state-transition/src/signatureSets/proposer.ts index 135ac7ed5c7a..b5e501bd16c7 100644 --- a/packages/state-transition/src/signatureSets/proposer.ts +++ b/packages/state-transition/src/signatureSets/proposer.ts @@ -1,12 +1,12 @@ import {DOMAIN_BEACON_PROPOSER} from "@lodestar/params"; -import {allForks, isBlindedBeaconBlock, phase0, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, SignedBlindedBeaconBlock, isBlindedBeaconBlock, phase0, ssz} from "@lodestar/types"; import {computeSigningRoot} from "../util/index.js"; import {ISignatureSet, SignatureSetType, verifySignatureSet} from "../util/signatureSets.js"; import {CachedBeaconStateAllForks} from "../types.js"; export function verifyProposerSignature( state: CachedBeaconStateAllForks, - signedBlock: allForks.FullOrBlindedSignedBeaconBlock + signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock ): boolean { const signatureSet = getBlockProposerSignatureSet(state, signedBlock); return verifySignatureSet(signatureSet); @@ -14,7 +14,7 @@ export function verifyProposerSignature( export function getBlockProposerSignatureSet( state: CachedBeaconStateAllForks, - signedBlock: allForks.FullOrBlindedSignedBeaconBlock + signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock ): ISignatureSet { const {config, epochCtx} = state; const domain = config.getDomain(state.slot, DOMAIN_BEACON_PROPOSER, signedBlock.message.slot); diff --git a/packages/state-transition/src/signatureSets/proposerSlashings.ts b/packages/state-transition/src/signatureSets/proposerSlashings.ts index 46be30c0636e..8a004225111d 100644 --- a/packages/state-transition/src/signatureSets/proposerSlashings.ts +++ b/packages/state-transition/src/signatureSets/proposerSlashings.ts @@ -1,5 +1,5 @@ import {DOMAIN_BEACON_PROPOSER} from "@lodestar/params"; -import {allForks, phase0, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types"; import {computeSigningRoot, ISignatureSet, SignatureSetType} from "../util/index.js"; import {CachedBeaconStateAllForks} from "../types.js"; @@ -33,7 +33,7 @@ export function getProposerSlashingSignatureSets( export function getProposerSlashingsSignatureSets( state: CachedBeaconStateAllForks, - signedBlock: allForks.SignedBeaconBlock + signedBlock: SignedBeaconBlock ): ISignatureSet[] { return signedBlock.message.body.proposerSlashings .map((proposerSlashing) => getProposerSlashingSignatureSets(state, proposerSlashing)) diff --git a/packages/state-transition/src/signatureSets/randao.ts b/packages/state-transition/src/signatureSets/randao.ts index 4fc8b77e4e38..41422a9c1d00 100644 --- a/packages/state-transition/src/signatureSets/randao.ts +++ b/packages/state-transition/src/signatureSets/randao.ts @@ -1,5 +1,5 @@ import {DOMAIN_RANDAO} from "@lodestar/params"; -import {allForks, ssz} from "@lodestar/types"; +import {BeaconBlock, ssz} from "@lodestar/types"; import { computeEpochAtSlot, computeSigningRoot, @@ -9,17 +9,14 @@ import { } from "../util/index.js"; import {CachedBeaconStateAllForks} from "../types.js"; -export function verifyRandaoSignature(state: CachedBeaconStateAllForks, block: allForks.BeaconBlock): boolean { +export function verifyRandaoSignature(state: CachedBeaconStateAllForks, block: BeaconBlock): boolean { return verifySignatureSet(getRandaoRevealSignatureSet(state, block)); } /** * Extract signatures to allow validating all block signatures at once */ -export function getRandaoRevealSignatureSet( - state: CachedBeaconStateAllForks, - block: allForks.BeaconBlock -): ISignatureSet { +export function getRandaoRevealSignatureSet(state: CachedBeaconStateAllForks, block: BeaconBlock): ISignatureSet { const {epochCtx} = state; // should not get epoch from epochCtx const epoch = computeEpochAtSlot(block.slot); diff --git a/packages/state-transition/src/signatureSets/voluntaryExits.ts b/packages/state-transition/src/signatureSets/voluntaryExits.ts index bb86ef41777a..51dd20d671b6 100644 --- a/packages/state-transition/src/signatureSets/voluntaryExits.ts +++ b/packages/state-transition/src/signatureSets/voluntaryExits.ts @@ -1,4 +1,4 @@ -import {allForks, phase0, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types"; import { computeSigningRoot, computeStartSlotAtEpoch, @@ -36,7 +36,7 @@ export function getVoluntaryExitSignatureSet( export function getVoluntaryExitsSignatureSets( state: CachedBeaconStateAllForks, - signedBlock: allForks.SignedBeaconBlock + signedBlock: SignedBeaconBlock ): ISignatureSet[] { return signedBlock.message.body.voluntaryExits.map((voluntaryExit) => getVoluntaryExitSignatureSet(state, voluntaryExit) diff --git a/packages/state-transition/src/stateTransition.ts b/packages/state-transition/src/stateTransition.ts index b3f3b41eb865..3ecd24ea9813 100644 --- a/packages/state-transition/src/stateTransition.ts +++ b/packages/state-transition/src/stateTransition.ts @@ -1,5 +1,5 @@ import {toHexString} from "@chainsafe/ssz"; -import {allForks, Slot, ssz} from "@lodestar/types"; +import {SignedBeaconBlock, SignedBlindedBeaconBlock, Slot, ssz} from "@lodestar/types"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; import {BeaconStateTransitionMetrics, onPostStateMetrics, onStateCloneMetrics} from "./metrics.js"; import {beforeProcessEpoch, EpochTransitionCache, EpochTransitionCacheOpts} from "./cache/epochTransitionCache.js"; @@ -59,7 +59,7 @@ export enum StateHashTreeRootSource { */ export function stateTransition( state: CachedBeaconStateAllForks, - signedBlock: allForks.FullOrBlindedSignedBeaconBlock, + signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock, options: StateTransitionOpts = { // Assume default to be valid and available executionPayloadStatus: ExecutionPayloadStatus.valid, diff --git a/packages/state-transition/src/util/blindedBlock.ts b/packages/state-transition/src/util/blindedBlock.ts index 5b6cf42d3cef..2b9510bd1e99 100644 --- a/packages/state-transition/src/util/blindedBlock.ts +++ b/packages/state-transition/src/util/blindedBlock.ts @@ -1,12 +1,27 @@ import {ChainForkConfig} from "@lodestar/config"; -import {ForkSeq} from "@lodestar/params"; -import {allForks, phase0, Root, deneb, isBlindedBeaconBlock, isExecutionPayloadAndBlobsBundle} from "@lodestar/types"; +import {ForkExecution, ForkSeq} from "@lodestar/params"; +import { + Root, + isBlindedBeaconBlock, + isExecutionPayloadAndBlobsBundle, + BeaconBlock, + BeaconBlockHeader, + SignedBeaconBlock, + ExecutionPayload, + ExecutionPayloadAndBlobsBundle, + BlobsBundle, + SignedBeaconBlockOrContents, + Contents, + SignedBlindedBeaconBlock, + BlindedBeaconBlock, + ExecutionPayloadHeader, +} from "@lodestar/types"; import {executionPayloadToPayloadHeader} from "./execution.js"; export function blindedOrFullBlockHashTreeRoot( config: ChainForkConfig, - blindedOrFull: allForks.FullOrBlindedBeaconBlock + blindedOrFull: BeaconBlock | BlindedBeaconBlock ): Root { return isBlindedBeaconBlock(blindedOrFull) ? // Blinded @@ -17,8 +32,8 @@ export function blindedOrFullBlockHashTreeRoot( export function blindedOrFullBlockToHeader( config: ChainForkConfig, - blindedOrFull: allForks.FullOrBlindedBeaconBlock -): phase0.BeaconBlockHeader { + blindedOrFull: BeaconBlock | BlindedBeaconBlock +): BeaconBlockHeader { const bodyRoot = isBlindedBeaconBlock(blindedOrFull) ? // Blinded config.getBlindedForkTypes(blindedOrFull.slot).BeaconBlockBody.hashTreeRoot(blindedOrFull.body) @@ -34,20 +49,17 @@ export function blindedOrFullBlockToHeader( }; } -export function beaconBlockToBlinded( - config: ChainForkConfig, - block: allForks.AllForksExecution["BeaconBlock"] -): allForks.BlindedBeaconBlock { +export function beaconBlockToBlinded(config: ChainForkConfig, block: BeaconBlock): BlindedBeaconBlock { const fork = config.getForkName(block.slot); const executionPayloadHeader = executionPayloadToPayloadHeader(ForkSeq[fork], block.body.executionPayload); - const blindedBlock = {...block, body: {...block.body, executionPayloadHeader}} as allForks.BlindedBeaconBlock; + const blindedBlock = {...block, body: {...block.body, executionPayloadHeader}} as BlindedBeaconBlock; return blindedBlock; } export function signedBlindedBlockToFull( - signedBlindedBlock: allForks.SignedBlindedBeaconBlock, - executionPayload: allForks.ExecutionPayload | null -): allForks.SignedBeaconBlock { + signedBlindedBlock: SignedBlindedBeaconBlock, + executionPayload: ExecutionPayload | null +): SignedBeaconBlock { const signedBlock = { ...signedBlindedBlock, message: { @@ -58,18 +70,18 @@ export function signedBlindedBlockToFull( executionPayload: executionPayload ?? undefined, }, }, - } as allForks.SignedBeaconBlock; + } as SignedBeaconBlock; // state transition can't seem to handle executionPayloadHeader presense in merge block // so just delete the extra field we don't require - delete (signedBlock.message.body as {executionPayloadHeader?: allForks.ExecutionPayloadHeader}) - .executionPayloadHeader; + delete (signedBlock.message.body as {executionPayloadHeader?: ExecutionPayloadHeader}).executionPayloadHeader; return signedBlock; } -export function parseExecutionPayloadAndBlobsBundle( - data: allForks.ExecutionPayload | allForks.ExecutionPayloadAndBlobsBundle -): {executionPayload: allForks.ExecutionPayload; blobsBundle: deneb.BlobsBundle | null} { +export function parseExecutionPayloadAndBlobsBundle(data: ExecutionPayload | ExecutionPayloadAndBlobsBundle): { + executionPayload: ExecutionPayload; + blobsBundle: BlobsBundle | null; +} { if (isExecutionPayloadAndBlobsBundle(data)) { return data; } else { @@ -81,15 +93,15 @@ export function parseExecutionPayloadAndBlobsBundle( } export function reconstructFullBlockOrContents( - signedBlindedBlock: allForks.SignedBlindedBeaconBlock, + signedBlindedBlock: SignedBlindedBeaconBlock, { executionPayload, contents, }: { - executionPayload: allForks.ExecutionPayload | null; - contents: deneb.Contents | null; + executionPayload: ExecutionPayload | null; + contents: Contents | null; } -): allForks.SignedBeaconBlockOrContents { +): SignedBeaconBlockOrContents { const signedBlock = signedBlindedBlockToFull(signedBlindedBlock, executionPayload); if (contents !== null) { @@ -97,8 +109,8 @@ export function reconstructFullBlockOrContents( throw Error("Missing locally produced executionPayload for deneb+ publishBlindedBlock"); } - return {signedBlock, ...contents} as allForks.SignedBeaconBlockOrContents; + return {signedBlock, ...contents} as SignedBeaconBlockOrContents; } else { - return signedBlock as allForks.SignedBeaconBlockOrContents; + return signedBlock as SignedBeaconBlockOrContents; } } diff --git a/packages/state-transition/src/util/blockRoot.ts b/packages/state-transition/src/util/blockRoot.ts index 1e1df38ef4fe..54d96885e675 100644 --- a/packages/state-transition/src/util/blockRoot.ts +++ b/packages/state-transition/src/util/blockRoot.ts @@ -1,6 +1,13 @@ -import {Epoch, Slot, Root, phase0, allForks} from "@lodestar/types"; +import { + Epoch, + Slot, + Root, + BeaconBlock, + SignedBeaconBlock, + BeaconBlockHeader, + SignedBeaconBlockHeader, +} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; - import {SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params"; import {ZERO_HASH} from "../constants/index.js"; import {BeaconStateAllForks} from "../types.js"; @@ -28,10 +35,7 @@ export function getBlockRoot(state: BeaconStateAllForks, epoch: Epoch): Root { /** * Return the block header corresponding to a block with ``state_root`` set to ``ZERO_HASH``. */ -export function getTemporaryBlockHeader( - config: ChainForkConfig, - block: allForks.BeaconBlock -): phase0.BeaconBlockHeader { +export function getTemporaryBlockHeader(config: ChainForkConfig, block: BeaconBlock): BeaconBlockHeader { return { slot: block.slot, proposerIndex: block.proposerIndex, @@ -45,7 +49,7 @@ export function getTemporaryBlockHeader( /** * Receives a BeaconBlock, and produces the corresponding BeaconBlockHeader. */ -export function blockToHeader(config: ChainForkConfig, block: allForks.BeaconBlock): phase0.BeaconBlockHeader { +export function blockToHeader(config: ChainForkConfig, block: BeaconBlock): BeaconBlockHeader { return { stateRoot: block.stateRoot, proposerIndex: block.proposerIndex, @@ -57,8 +61,8 @@ export function blockToHeader(config: ChainForkConfig, block: allForks.BeaconBlo export function signedBlockToSignedHeader( config: ChainForkConfig, - signedBlock: allForks.SignedBeaconBlock -): phase0.SignedBeaconBlockHeader { + signedBlock: SignedBeaconBlock +): SignedBeaconBlockHeader { const message = blockToHeader(config, signedBlock.message); const signature = signedBlock.signature; return { diff --git a/packages/state-transition/src/util/epoch.ts b/packages/state-transition/src/util/epoch.ts index b7febf45c110..bb66fb04eb94 100644 --- a/packages/state-transition/src/util/epoch.ts +++ b/packages/state-transition/src/util/epoch.ts @@ -1,5 +1,5 @@ import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD, GENESIS_EPOCH, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params"; -import {allForks, Epoch, Slot, SyncPeriod} from "@lodestar/types"; +import {BeaconState, Epoch, Slot, SyncPeriod} from "@lodestar/types"; /** * Return the epoch number at the given slot. @@ -42,14 +42,14 @@ export function computeActivationExitEpoch(epoch: Epoch): Epoch { /** * Return the current epoch of the given state. */ -export function getCurrentEpoch(state: Pick): Epoch { +export function getCurrentEpoch(state: Pick): Epoch { return computeEpochAtSlot(state.slot); } /** * Return the previous epoch of the given state. */ -export function getPreviousEpoch(state: Pick): Epoch { +export function getPreviousEpoch(state: Pick): Epoch { const currentEpoch = getCurrentEpoch(state); if (currentEpoch === GENESIS_EPOCH) { return GENESIS_EPOCH; diff --git a/packages/state-transition/src/util/execution.ts b/packages/state-transition/src/util/execution.ts index 7ac4da4aeecb..1c5046354fcb 100644 --- a/packages/state-transition/src/util/execution.ts +++ b/packages/state-transition/src/util/execution.ts @@ -1,5 +1,18 @@ -import {allForks, bellatrix, capella, deneb, isBlindedBeaconBlockBody, ssz} from "@lodestar/types"; -import {ForkSeq} from "@lodestar/params"; +import { + bellatrix, + capella, + deneb, + isBlindedBeaconBlockBody, + ssz, + BeaconBlock, + BeaconBlockBody, + ExecutionPayload, + isExecutionPayload, + ExecutionPayloadHeader, + BlindedBeaconBlockBody, + BlindedBeaconBlock, +} from "@lodestar/types"; +import {ForkExecution, ForkName, ForkSeq} from "@lodestar/params"; import { BeaconStateBellatrix, @@ -14,7 +27,7 @@ import { * Execution enabled = merge is done. * When (A) state has execution data OR (B) block has execution data */ -export function isExecutionEnabled(state: BeaconStateExecutions, block: allForks.FullOrBlindedBeaconBlock): boolean { +export function isExecutionEnabled(state: BeaconStateExecutions, block: BeaconBlock | BlindedBeaconBlock): boolean { if (isMergeTransitionComplete(state)) { return true; } @@ -85,43 +98,33 @@ export function isExecutionCachedStateType(state: CachedBeaconStateAllForks): st return (state as CachedBeaconStateExecutions).latestExecutionPayloadHeader !== undefined; } -/** Type guard for allForks.ExecutionBlockBody */ -export function isExecutionBlockBodyType( - blockBody: allForks.BeaconBlockBody -): blockBody is allForks.ExecutionBlockBody { - return (blockBody as allForks.ExecutionBlockBody).executionPayload !== undefined; +/** Type guard for ExecutionBlockBody */ +export function isExecutionBlockBodyType(blockBody: BeaconBlockBody): blockBody is BeaconBlockBody { + return (blockBody as BeaconBlockBody).executionPayload !== undefined; } -export function getFullOrBlindedPayload( - block: allForks.FullOrBlindedBeaconBlock -): allForks.FullOrBlindedExecutionPayload { +export function getFullOrBlindedPayload(block: BeaconBlock): ExecutionPayload | ExecutionPayloadHeader { return getFullOrBlindedPayloadFromBody(block.body); } export function getFullOrBlindedPayloadFromBody( - body: allForks.FullOrBlindedBeaconBlockBody -): allForks.FullOrBlindedExecutionPayload { + body: BeaconBlockBody | BlindedBeaconBlockBody +): ExecutionPayload | ExecutionPayloadHeader { if (isBlindedBeaconBlockBody(body)) { return body.executionPayloadHeader; } else if ((body as bellatrix.BeaconBlockBody).executionPayload !== undefined) { return (body as bellatrix.BeaconBlockBody).executionPayload; } else { - throw Error("Ǹot allForks.FullOrBlindedBeaconBlock"); + throw Error("Not full or blinded beacon block"); } } -export function isExecutionPayload( - payload: allForks.FullOrBlindedExecutionPayload -): payload is allForks.ExecutionPayload { - return (payload as allForks.ExecutionPayload).transactions !== undefined; -} - export function isCapellaPayload( - payload: allForks.FullOrBlindedExecutionPayload -): payload is capella.FullOrBlindedExecutionPayload { + payload: ExecutionPayload | ExecutionPayloadHeader +): payload is ExecutionPayload | ExecutionPayloadHeader { return ( - (payload as capella.ExecutionPayload).withdrawals !== undefined || - (payload as capella.ExecutionPayloadHeader).withdrawalsRoot !== undefined + (payload as ExecutionPayload).withdrawals !== undefined || + (payload as ExecutionPayloadHeader).withdrawalsRoot !== undefined ); } @@ -131,13 +134,10 @@ export function isCapellaPayloadHeader( return (payload as capella.ExecutionPayloadHeader).withdrawalsRoot !== undefined; } -export function executionPayloadToPayloadHeader( - fork: ForkSeq, - payload: allForks.ExecutionPayload -): allForks.ExecutionPayloadHeader { +export function executionPayloadToPayloadHeader(fork: ForkSeq, payload: ExecutionPayload): ExecutionPayloadHeader { const transactionsRoot = ssz.bellatrix.Transactions.hashTreeRoot(payload.transactions); - const bellatrixPayloadFields: allForks.ExecutionPayloadHeader = { + const bellatrixPayloadFields: ExecutionPayloadHeader = { parentHash: payload.parentHash, feeRecipient: payload.feeRecipient, stateRoot: payload.stateRoot, diff --git a/packages/state-transition/src/util/sszBytes.ts b/packages/state-transition/src/util/sszBytes.ts index b5141e1673e5..fe3e5b69b892 100644 --- a/packages/state-transition/src/util/sszBytes.ts +++ b/packages/state-transition/src/util/sszBytes.ts @@ -1,6 +1,6 @@ import {ChainForkConfig} from "@lodestar/config"; -import {ForkSeq} from "@lodestar/params"; -import {Slot, allForks} from "@lodestar/types"; +import {ForkAll, ForkSeq} from "@lodestar/params"; +import {SSZTypesFor, Slot} from "@lodestar/types"; import {bytesToInt} from "@lodestar/utils"; /** @@ -42,10 +42,7 @@ export function getForkFromStateBytes(config: ChainForkConfig, bytes: Uint8Array return config.getForkSeq(slot); } -export function getStateTypeFromBytes( - config: ChainForkConfig, - bytes: Uint8Array -): allForks.AllForksSSZTypes["BeaconState"] { +export function getStateTypeFromBytes(config: ChainForkConfig, bytes: Uint8Array): SSZTypesFor { const slot = getStateSlotFromBytes(bytes); return config.getForkTypes(slot).BeaconState; } diff --git a/packages/state-transition/src/util/weakSubjectivity.ts b/packages/state-transition/src/util/weakSubjectivity.ts index 4614534bcb27..6bd6636c3e70 100644 --- a/packages/state-transition/src/util/weakSubjectivity.ts +++ b/packages/state-transition/src/util/weakSubjectivity.ts @@ -5,7 +5,7 @@ import {Epoch, Root} from "@lodestar/types"; import {ssz} from "@lodestar/types"; import {Checkpoint} from "@lodestar/types/phase0"; import {ZERO_HASH} from "../constants/constants.js"; -import {CachedBeaconStateAllForks, BeaconStateAllForks} from "../types.js"; +import {BeaconStateAllForks, CachedBeaconStateAllForks} from "../types.js"; import {computeEpochAtSlot, getCurrentEpoch, computeCheckpointEpochAtStateSlot} from "./epoch.js"; import {getCurrentSlot} from "./slot.js"; import {getActiveValidatorIndices, getChurnLimit} from "./validator.js"; diff --git a/packages/state-transition/test/perf/analyzeEpochs.ts b/packages/state-transition/test/perf/analyzeEpochs.ts index 4b793fe95e6e..c2f09fcc5521 100644 --- a/packages/state-transition/test/perf/analyzeEpochs.ts +++ b/packages/state-transition/test/perf/analyzeEpochs.ts @@ -143,7 +143,7 @@ async function analyzeEpochs(network: NetworkName, fromEpoch?: number): Promise< currentEpochAttestationsBits: countAttBits(currentEpochAttestations as phase0.PendingAttestation[]), }); - // -- allForks + // -- all forks // processEffectiveBalanceUpdates: function of effectiveBalance changes // processEth1DataReset: free // processHistoricalRootsUpdate: free diff --git a/packages/state-transition/test/perf/types.ts b/packages/state-transition/test/perf/types.ts index 23b9667716d9..8f4914238dba 100644 --- a/packages/state-transition/test/perf/types.ts +++ b/packages/state-transition/test/perf/types.ts @@ -1,4 +1,4 @@ -import {allForks} from "@lodestar/types"; +import {SignedBeaconBlock} from "@lodestar/types"; import {CachedBeaconStateAllForks, CachedBeaconStatePhase0, CachedBeaconStateAltair} from "../../src/index.js"; import {EpochTransitionCache} from "../../src/types.js"; @@ -6,7 +6,7 @@ import {EpochTransitionCache} from "../../src/types.js"; export type State = CachedBeaconStateAllForks; export type StateAltair = CachedBeaconStateAltair; -export type StateBlock = {state: CachedBeaconStateAllForks; block: allForks.SignedBeaconBlock}; +export type StateBlock = {state: CachedBeaconStateAllForks; block: SignedBeaconBlock}; export type StateEpoch = {state: CachedBeaconStateAllForks; cache: EpochTransitionCache}; export type StatePhase0Epoch = {state: CachedBeaconStatePhase0; cache: EpochTransitionCache}; export type StateAltairEpoch = {state: CachedBeaconStateAltair; cache: EpochTransitionCache}; diff --git a/packages/state-transition/test/perf/util.ts b/packages/state-transition/test/perf/util.ts index 4df6746ea938..a66797443d85 100644 --- a/packages/state-transition/test/perf/util.ts +++ b/packages/state-transition/test/perf/util.ts @@ -1,12 +1,13 @@ import {CoordType, PublicKey, SecretKey} from "@chainsafe/bls/types"; import bls from "@chainsafe/bls"; import {BitArray, fromHexString} from "@chainsafe/ssz"; -import {allForks, phase0, ssz, Slot, altair} from "@lodestar/types"; +import {phase0, ssz, Slot, BeaconState} from "@lodestar/types"; import {config} from "@lodestar/config/default"; import {createBeaconConfig, createChainForkConfig} from "@lodestar/config"; import { EPOCHS_PER_ETH1_VOTING_PERIOD, EPOCHS_PER_HISTORICAL_VECTOR, + ForkName, MAX_ATTESTATIONS, MAX_EFFECTIVE_BALANCE, SLOTS_PER_EPOCH, @@ -255,7 +256,7 @@ export function generatePerformanceStateAltair(pubkeysArg?: Uint8Array[]): Beaco if (!altairState) { const pubkeys = pubkeysArg || getPubkeys().pubkeys; const statePhase0 = buildPerformanceStatePhase0(pubkeys); - const state = statePhase0 as allForks.BeaconState as altair.BeaconState; + const state = statePhase0 as BeaconState as BeaconState; state.previousEpochParticipation = newFilledArray(pubkeys.length, 0b111); state.currentEpochParticipation = state.previousEpochParticipation; diff --git a/packages/state-transition/test/utils/testFileCache.ts b/packages/state-transition/test/utils/testFileCache.ts index 6e554d888d18..b894674f54f6 100644 --- a/packages/state-transition/test/utils/testFileCache.ts +++ b/packages/state-transition/test/utils/testFileCache.ts @@ -4,7 +4,7 @@ import got from "got"; import {getClient} from "@lodestar/api"; import {NetworkName, networksChainConfig} from "@lodestar/config/networks"; import {createChainForkConfig, ChainForkConfig} from "@lodestar/config"; -import {allForks} from "@lodestar/types"; +import {SignedBeaconBlock} from "@lodestar/types"; import {CachedBeaconStateAllForks} from "../../src/index.js"; import {testCachePath} from "../cache.js"; import {createCachedBeaconStateTest} from "../utils/state.js"; @@ -67,7 +67,7 @@ export async function getNetworkCachedBlock( network: NetworkName, slot: number, timeout?: number -): Promise { +): Promise { const config = getNetworkConfig(network); const fileId = `block_${network}_${slot}.ssz`; diff --git a/packages/types/README.md b/packages/types/README.md index 749f93321d39..59c1a02e122f 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -67,13 +67,13 @@ import {Epoch, ssz} from "@lodestar/types"; const epoch: Epoch = ssz.Epoch.defaultValue(); ``` -In some cases, we need interfaces that accept types across all forks, like when the fork is not known ahead of time. Typescript interfaces for this purpose are exported under the `allForks` namespace. SSZ Types typed to these interfaces are also provided under an `allForks` namespace, but keyed by `ForkName`. +In some cases, we need interfaces that accept types across all forks, like when the fork is not known ahead of time. SSZ Types typed to these interfaces are also provided under an `allForks` namespace, but keyed by `ForkName`. ```typescript import {ForkName} from "@lodestar/params"; -import {allForks, ssz} from "@lodestar/types"; +import {ssz, BeaconState} from "@lodestar/types"; -const state: allForks.BeaconState = ssz.allForks[ForkName.phase0].BeaconState.defaultValue(); +const state: BeaconState = ssz.allForks[ForkName.phase0].BeaconState.defaultValue(); ``` ## License diff --git a/packages/types/package.json b/packages/types/package.json index b892e6f0285a..037a3c46d71b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -17,9 +17,6 @@ ".": { "import": "./lib/index.js" }, - "./allForks": { - "import": "./lib/allForks/index.js" - }, "./altair": { "import": "./lib/altair/index.js" }, diff --git a/packages/types/src/allForks/index.ts b/packages/types/src/allForks/index.ts deleted file mode 100644 index 104de77d50c3..000000000000 --- a/packages/types/src/allForks/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./types.js"; - -// We have to use import->export because of the limitation in `@microsoft/api-extractor` -// which is used to bundle the package types -import * as ts from "./types.js"; -import * as ssz from "./sszTypes.js"; -export {ts, ssz}; diff --git a/packages/types/src/allForks/sszTypes.ts b/packages/types/src/allForks/sszTypes.ts deleted file mode 100644 index 7174bc52e89c..000000000000 --- a/packages/types/src/allForks/sszTypes.ts +++ /dev/null @@ -1,160 +0,0 @@ -import {ssz as phase0} from "../phase0/index.js"; -import {ssz as altair} from "../altair/index.js"; -import {ssz as bellatrix} from "../bellatrix/index.js"; -import {ssz as capella} from "../capella/index.js"; -import {ssz as deneb} from "../deneb/index.js"; - -/** - * Index the ssz types that differ by fork - * A record of AllForksSSZTypes indexed by fork - */ -export const allForks = { - phase0: { - BeaconBlockBody: phase0.BeaconBlockBody, - BeaconBlock: phase0.BeaconBlock, - SignedBeaconBlock: phase0.SignedBeaconBlock, - BeaconState: phase0.BeaconState, - Metadata: phase0.Metadata, - }, - altair: { - BeaconBlockBody: altair.BeaconBlockBody, - BeaconBlock: altair.BeaconBlock, - SignedBeaconBlock: altair.SignedBeaconBlock, - BeaconState: altair.BeaconState, - Metadata: altair.Metadata, - }, - bellatrix: { - BeaconBlockBody: bellatrix.BeaconBlockBody, - BeaconBlock: bellatrix.BeaconBlock, - SignedBeaconBlock: bellatrix.SignedBeaconBlock, - BeaconState: bellatrix.BeaconState, - Metadata: altair.Metadata, - }, - capella: { - BeaconBlockBody: capella.BeaconBlockBody, - BeaconBlock: capella.BeaconBlock, - SignedBeaconBlock: capella.SignedBeaconBlock, - BeaconState: capella.BeaconState, - Metadata: altair.Metadata, - }, - deneb: { - BeaconBlockBody: deneb.BeaconBlockBody, - BeaconBlock: deneb.BeaconBlock, - SignedBeaconBlock: deneb.SignedBeaconBlock, - BeaconState: deneb.BeaconState, - Metadata: altair.Metadata, - }, -}; - -/** - * Index the execution ssz types that differ by fork - * A record of AllForksExecutionSSZTypes indexed by fork - */ -export const allForksExecution = { - bellatrix: { - BeaconBlockBody: bellatrix.BeaconBlockBody, - BeaconBlock: bellatrix.BeaconBlock, - SignedBeaconBlock: bellatrix.SignedBeaconBlock, - BeaconState: bellatrix.BeaconState, - ExecutionPayload: bellatrix.ExecutionPayload, - ExecutionPayloadHeader: bellatrix.ExecutionPayloadHeader, - BuilderBid: bellatrix.BuilderBid, - SignedBuilderBid: bellatrix.SignedBuilderBid, - SSEPayloadAttributes: bellatrix.SSEPayloadAttributes, - }, - capella: { - BeaconBlockBody: capella.BeaconBlockBody, - BeaconBlock: capella.BeaconBlock, - SignedBeaconBlock: capella.SignedBeaconBlock, - BeaconState: capella.BeaconState, - // Not used in phase0 but added for type consitency - ExecutionPayload: capella.ExecutionPayload, - ExecutionPayloadHeader: capella.ExecutionPayloadHeader, - BuilderBid: capella.BuilderBid, - SignedBuilderBid: capella.SignedBuilderBid, - SSEPayloadAttributes: capella.SSEPayloadAttributes, - }, - deneb: { - BeaconBlockBody: deneb.BeaconBlockBody, - BeaconBlock: deneb.BeaconBlock, - SignedBeaconBlock: deneb.SignedBeaconBlock, - BeaconState: deneb.BeaconState, - ExecutionPayload: deneb.ExecutionPayload, - ExecutionPayloadHeader: deneb.ExecutionPayloadHeader, - BuilderBid: deneb.BuilderBid, - SignedBuilderBid: deneb.SignedBuilderBid, - SSEPayloadAttributes: deneb.SSEPayloadAttributes, - }, -}; - -/** - * Index the blinded ssz types that differ by fork - * A record of AllForksBlindedSSZTypes indexed by fork - */ -export const allForksBlinded = { - bellatrix: { - BeaconBlockBody: bellatrix.BlindedBeaconBlockBody, - BeaconBlock: bellatrix.BlindedBeaconBlock, - SignedBeaconBlock: bellatrix.SignedBlindedBeaconBlock, - }, - capella: { - BeaconBlockBody: capella.BlindedBeaconBlockBody, - BeaconBlock: capella.BlindedBeaconBlock, - SignedBeaconBlock: capella.SignedBlindedBeaconBlock, - }, - deneb: { - BeaconBlockBody: deneb.BlindedBeaconBlockBody, - BeaconBlock: deneb.BlindedBeaconBlock, - SignedBeaconBlock: deneb.SignedBlindedBeaconBlock, - }, -}; - -export const allForksLightClient = { - altair: { - BeaconBlock: altair.BeaconBlock, - BeaconBlockBody: altair.BeaconBlockBody, - LightClientHeader: altair.LightClientHeader, - LightClientBootstrap: altair.LightClientBootstrap, - LightClientUpdate: altair.LightClientUpdate, - LightClientFinalityUpdate: altair.LightClientFinalityUpdate, - LightClientOptimisticUpdate: altair.LightClientOptimisticUpdate, - LightClientStore: altair.LightClientStore, - }, - bellatrix: { - BeaconBlock: bellatrix.BeaconBlock, - BeaconBlockBody: bellatrix.BeaconBlockBody, - LightClientHeader: altair.LightClientHeader, - LightClientBootstrap: altair.LightClientBootstrap, - LightClientUpdate: altair.LightClientUpdate, - LightClientFinalityUpdate: altair.LightClientFinalityUpdate, - LightClientOptimisticUpdate: altair.LightClientOptimisticUpdate, - LightClientStore: altair.LightClientStore, - }, - capella: { - BeaconBlock: capella.BeaconBlock, - BeaconBlockBody: capella.BeaconBlockBody, - LightClientHeader: capella.LightClientHeader, - LightClientBootstrap: capella.LightClientBootstrap, - LightClientUpdate: capella.LightClientUpdate, - LightClientFinalityUpdate: capella.LightClientFinalityUpdate, - LightClientOptimisticUpdate: capella.LightClientOptimisticUpdate, - LightClientStore: capella.LightClientStore, - }, - deneb: { - BeaconBlock: deneb.BeaconBlock, - BeaconBlockBody: deneb.BeaconBlockBody, - LightClientHeader: deneb.LightClientHeader, - LightClientBootstrap: deneb.LightClientBootstrap, - LightClientUpdate: deneb.LightClientUpdate, - LightClientFinalityUpdate: deneb.LightClientFinalityUpdate, - LightClientOptimisticUpdate: deneb.LightClientOptimisticUpdate, - LightClientStore: deneb.LightClientStore, - }, -}; - -export const allForksBlobs = { - deneb: { - BlobSidecar: deneb.BlobSidecar, - ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle, - }, -}; diff --git a/packages/types/src/allForks/types.ts b/packages/types/src/allForks/types.ts deleted file mode 100644 index 59768a5a3308..000000000000 --- a/packages/types/src/allForks/types.ts +++ /dev/null @@ -1,298 +0,0 @@ -import {CompositeType, ContainerType, ValueOf, CompositeView, CompositeViewDU} from "@chainsafe/ssz"; -import {ts as phase0} from "../phase0/index.js"; -import {ts as altair} from "../altair/index.js"; -import {ts as bellatrix} from "../bellatrix/index.js"; -import {ts as capella} from "../capella/index.js"; -import {ts as deneb} from "../deneb/index.js"; - -import {ssz as phase0Ssz} from "../phase0/index.js"; -import {ssz as altairSsz} from "../altair/index.js"; -import {ssz as bellatrixSsz} from "../bellatrix/index.js"; -import {ssz as capellaSsz} from "../capella/index.js"; -import {ssz as denebSsz} from "../deneb/index.js"; - -// Re-export union types for types that are _known_ to differ - -export type BeaconBlockBody = - | phase0.BeaconBlockBody - | altair.BeaconBlockBody - | bellatrix.BeaconBlockBody - | capella.BeaconBlockBody - | deneb.BeaconBlockBody; -export type BeaconBlock = - | phase0.BeaconBlock - | altair.BeaconBlock - | bellatrix.BeaconBlock - | capella.BeaconBlock - | deneb.BeaconBlock; -export type SignedBeaconBlock = - | phase0.SignedBeaconBlock - | altair.SignedBeaconBlock - | bellatrix.SignedBeaconBlock - | capella.SignedBeaconBlock - | deneb.SignedBeaconBlock; -export type BeaconState = - | phase0.BeaconState - | altair.BeaconState - | bellatrix.BeaconState - | capella.BeaconState - | deneb.BeaconState; -export type Metadata = phase0.Metadata | altair.Metadata; - -// For easy reference in the assemble block for building payloads -export type ExecutionBlockBody = bellatrix.BeaconBlockBody | capella.BeaconBlockBody | deneb.BeaconBlockBody; - -// These two additional types will also change bellatrix forward -export type ExecutionPayload = bellatrix.ExecutionPayload | capella.ExecutionPayload | deneb.ExecutionPayload; -export type ExecutionPayloadHeader = - | bellatrix.ExecutionPayloadHeader - | capella.ExecutionPayloadHeader - | deneb.ExecutionPayloadHeader; - -// Blinded types that will change across forks -export type BlindedBeaconBlockBody = - | bellatrix.BlindedBeaconBlockBody - | capella.BlindedBeaconBlockBody - | deneb.BlindedBeaconBlockBody; -export type BlindedBeaconBlock = bellatrix.BlindedBeaconBlock | capella.BlindedBeaconBlock | deneb.BlindedBeaconBlock; -export type SignedBlindedBeaconBlock = - | bellatrix.SignedBlindedBeaconBlock - | capella.SignedBlindedBeaconBlock - | deneb.SignedBlindedBeaconBlock; - -// Full or blinded types -export type FullOrBlindedExecutionPayload = - | bellatrix.FullOrBlindedExecutionPayload - | capella.FullOrBlindedExecutionPayload; -export type FullOrBlindedBeaconBlockBody = BeaconBlockBody | BlindedBeaconBlockBody; -export type FullOrBlindedBeaconBlock = BeaconBlock | BlindedBeaconBlock; -export type FullOrBlindedSignedBeaconBlock = SignedBeaconBlock | SignedBlindedBeaconBlock; - -export type BlockContents = {block: BeaconBlock; kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs}; -export type SignedBlockContents = { - signedBlock: SignedBeaconBlock; - kzgProofs: deneb.KZGProofs; - blobs: deneb.Blobs; -}; - -export type BeaconBlockOrContents = BeaconBlock | BlockContents; -export type SignedBeaconBlockOrContents = SignedBeaconBlock | SignedBlockContents; - -export type FullOrBlindedBeaconBlockOrContents = BeaconBlockOrContents | BlindedBeaconBlock; - -export type BuilderBid = bellatrix.BuilderBid | capella.BuilderBid | deneb.BuilderBid; -export type SignedBuilderBid = bellatrix.SignedBuilderBid | capella.SignedBuilderBid | deneb.SignedBuilderBid; -export type ExecutionPayloadAndBlobsBundle = deneb.ExecutionPayloadAndBlobsBundle; - -export type LightClientHeader = altair.LightClientHeader | capella.LightClientHeader | deneb.LightClientHeader; -export type LightClientBootstrap = - | altair.LightClientBootstrap - | capella.LightClientBootstrap - | deneb.LightClientBootstrap; -export type LightClientUpdate = altair.LightClientUpdate | capella.LightClientUpdate | deneb.LightClientUpdate; -export type LightClientFinalityUpdate = - | altair.LightClientFinalityUpdate - | capella.LightClientFinalityUpdate - | deneb.LightClientFinalityUpdate; -export type LightClientOptimisticUpdate = - | altair.LightClientOptimisticUpdate - | capella.LightClientOptimisticUpdate - | deneb.LightClientOptimisticUpdate; -export type LightClientStore = altair.LightClientStore | capella.LightClientStore | deneb.LightClientStore; - -export type SSEPayloadAttributes = - | bellatrix.SSEPayloadAttributes - | capella.SSEPayloadAttributes - | deneb.SSEPayloadAttributes; - -/** - * Types known to change between forks - */ -export type AllForksTypes = { - BeaconBlockBody: BeaconBlockBody; - BeaconBlock: BeaconBlock; - SignedBeaconBlock: SignedBeaconBlock; - BeaconState: BeaconState; - Metadata: Metadata; - ExecutionPayload: ExecutionPayload; - ExecutionPayloadHeader: ExecutionPayloadHeader; - LightClientHeader: LightClientHeader; - BuilderBid: BuilderBid; - SignedBuilderBid: SignedBuilderBid; -}; - -export type AllForksBlindedTypes = { - BeaconBlockBody: BlindedBeaconBlockBody; - BeaconBlock: BlindedBeaconBlock; - SignedBeaconBlock: SignedBlindedBeaconBlock; -}; - -export type AllForksLightClient = { - BeaconBlock: altair.BeaconBlock | bellatrix.BeaconBlock | capella.BeaconBlock | deneb.BeaconBlock; - LightClientHeader: LightClientHeader; - LightClientBootstrap: LightClientBootstrap; - LightClientUpdate: LightClientUpdate; - LightClientFinalityUpdate: LightClientFinalityUpdate; - LightClientOptimisticUpdate: LightClientOptimisticUpdate; - LightClientStore: LightClientStore; -}; - -export type AllForksExecution = { - BeaconBlock: bellatrix.BeaconBlock | capella.BeaconBlock | deneb.BeaconBlock; - BeaconBlockBody: bellatrix.BeaconBlockBody | capella.BeaconBlockBody | deneb.BeaconBlockBody; -}; - -/** - * An AllForks type must accept as any parameter the UNION of all fork types. - * The generic argument of `AllForksTypeOf` must be the union of the fork types: - * - * - * For example, `allForks.BeaconState.defaultValue()` must return - * ``` - * phase0.BeaconState | altair.BeaconState | bellatrix.BeaconState - * ``` - * - * And `allForks.BeaconState.serialize()` must accept as parameter - * ``` - * phase0.BeaconState | altair.BeaconState | bellatrix.BeaconState - * ``` - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type AllForksTypeOf> = CompositeType< - ValueOf, - CompositeView, - CompositeViewDU ->; - -/** - * SSZ Types known to change between forks. - * - * Re-wrapping a union of fields in a new ContainerType allows to pass a generic block to .serialize() - * - .serialize() requires a value with ONLY the common fork fields - * - .deserialize() and ValueOf return a value with ONLY the general fork fields - */ -export type AllForksSSZTypes = { - BeaconBlockBody: AllForksTypeOf< - | typeof phase0Ssz.BeaconBlockBody - | typeof altairSsz.BeaconBlockBody - | typeof bellatrixSsz.BeaconBlockBody - | typeof capellaSsz.BeaconBlockBody - | typeof denebSsz.BeaconBlockBody - >; - BeaconBlock: AllForksTypeOf< - | typeof phase0Ssz.BeaconBlock - | typeof altairSsz.BeaconBlock - | typeof bellatrixSsz.BeaconBlock - | typeof capellaSsz.BeaconBlock - | typeof denebSsz.BeaconBlock - >; - SignedBeaconBlock: AllForksTypeOf< - | typeof phase0Ssz.SignedBeaconBlock - | typeof altairSsz.SignedBeaconBlock - | typeof bellatrixSsz.SignedBeaconBlock - | typeof capellaSsz.SignedBeaconBlock - | typeof denebSsz.SignedBeaconBlock - >; - BeaconState: AllForksTypeOf< - | typeof phase0Ssz.BeaconState - | typeof altairSsz.BeaconState - | typeof bellatrixSsz.BeaconState - | typeof capellaSsz.BeaconState - | typeof denebSsz.BeaconState - >; - Metadata: AllForksTypeOf; -}; - -export type AllForksExecutionSSZTypes = { - BeaconBlockBody: AllForksTypeOf< - typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody | typeof denebSsz.BeaconBlockBody - >; - BeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof denebSsz.BeaconBlock - >; - SignedBeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.SignedBeaconBlock | typeof capellaSsz.SignedBeaconBlock | typeof denebSsz.SignedBeaconBlock - >; - BeaconState: AllForksTypeOf< - typeof bellatrixSsz.BeaconState | typeof capellaSsz.BeaconState | typeof denebSsz.BeaconState - >; - ExecutionPayload: AllForksTypeOf< - typeof bellatrixSsz.ExecutionPayload | typeof capellaSsz.ExecutionPayload | typeof denebSsz.ExecutionPayload - >; - ExecutionPayloadHeader: AllForksTypeOf< - | typeof bellatrixSsz.ExecutionPayloadHeader - | typeof capellaSsz.ExecutionPayloadHeader - | typeof denebSsz.ExecutionPayloadHeader - >; - BuilderBid: AllForksTypeOf< - typeof bellatrixSsz.BuilderBid | typeof capellaSsz.BuilderBid | typeof denebSsz.BuilderBid - >; - SignedBuilderBid: AllForksTypeOf< - typeof bellatrixSsz.SignedBuilderBid | typeof capellaSsz.SignedBuilderBid | typeof denebSsz.SignedBuilderBid - >; - SSEPayloadAttributes: AllForksTypeOf< - | typeof bellatrixSsz.SSEPayloadAttributes - | typeof capellaSsz.SSEPayloadAttributes - | typeof denebSsz.SSEPayloadAttributes - >; -}; - -export type AllForksBlindedSSZTypes = { - BeaconBlockBody: AllForksTypeOf< - | typeof bellatrixSsz.BlindedBeaconBlockBody - | typeof capellaSsz.BlindedBeaconBlock - | typeof denebSsz.BlindedBeaconBlock - >; - BeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.BlindedBeaconBlock | typeof capellaSsz.BlindedBeaconBlock | typeof denebSsz.BlindedBeaconBlock - >; - SignedBeaconBlock: AllForksTypeOf< - | typeof bellatrixSsz.SignedBlindedBeaconBlock - | typeof capellaSsz.SignedBlindedBeaconBlock - | typeof denebSsz.SignedBlindedBeaconBlock - >; -}; - -export type AllForksLightClientSSZTypes = { - BeaconBlock: AllForksTypeOf< - | typeof altairSsz.BeaconBlock - | typeof bellatrixSsz.BeaconBlock - | typeof capellaSsz.BeaconBlock - | typeof denebSsz.BeaconBlock - >; - BeaconBlockBody: AllForksTypeOf< - | typeof altairSsz.BeaconBlockBody - | typeof bellatrixSsz.BeaconBlockBody - | typeof capellaSsz.BeaconBlockBody - | typeof denebSsz.BeaconBlockBody - >; - LightClientHeader: AllForksTypeOf< - typeof altairSsz.LightClientHeader | typeof capellaSsz.LightClientHeader | typeof denebSsz.LightClientHeader - >; - LightClientBootstrap: AllForksTypeOf< - | typeof altairSsz.LightClientBootstrap - | typeof capellaSsz.LightClientBootstrap - | typeof denebSsz.LightClientBootstrap - >; - LightClientUpdate: AllForksTypeOf< - typeof altairSsz.LightClientUpdate | typeof capellaSsz.LightClientUpdate | typeof denebSsz.LightClientUpdate - >; - LightClientFinalityUpdate: AllForksTypeOf< - | typeof altairSsz.LightClientFinalityUpdate - | typeof capellaSsz.LightClientFinalityUpdate - | typeof denebSsz.LightClientFinalityUpdate - >; - LightClientOptimisticUpdate: AllForksTypeOf< - | typeof altairSsz.LightClientOptimisticUpdate - | typeof capellaSsz.LightClientOptimisticUpdate - | typeof denebSsz.LightClientOptimisticUpdate - >; - LightClientStore: AllForksTypeOf< - typeof altairSsz.LightClientStore | typeof capellaSsz.LightClientStore | typeof denebSsz.LightClientStore - >; -}; - -export type AllForksBlobsSSZTypes = { - BlobSidecar: AllForksTypeOf; - ExecutionPayloadAndBlobsBundle: AllForksTypeOf; -}; diff --git a/packages/types/src/deneb/types.ts b/packages/types/src/deneb/types.ts index 0921ae2428e7..9a901c9a1a81 100644 --- a/packages/types/src/deneb/types.ts +++ b/packages/types/src/deneb/types.ts @@ -1,5 +1,6 @@ import {ValueOf} from "@chainsafe/ssz"; -import {BlockContents} from "../allForks/types.js"; +import {ForkName} from "@lodestar/params"; +import type {BlockContents} from "../types.js"; import * as ssz from "./sszTypes.js"; export type KZGProof = ValueOf; @@ -48,4 +49,4 @@ export type LightClientOptimisticUpdate = ValueOf; export type ProducedBlobSidecars = Omit; -export type Contents = Omit; +export type Contents = Omit, "block">; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index bfd0f6abb6f9..7838a02d06e2 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,6 +1,8 @@ export * from "./types.js"; import * as ssz from "./sszTypes.js"; -export {ssz}; +import {sszTypesFor} from "./sszTypes.js"; +import type {SSZBlindedTypesFor, SSZTypesFor} from "./sszTypes.js"; +export {sszTypesFor, SSZBlindedTypesFor, SSZTypesFor, ssz}; // Typeguards export * from "./utils/typeguards.js"; // String type diff --git a/packages/types/src/sszTypes.ts b/packages/types/src/sszTypes.ts index 2a7df948a447..1ee5b9340630 100644 --- a/packages/types/src/sszTypes.ts +++ b/packages/types/src/sszTypes.ts @@ -1,13 +1,185 @@ +import {CompositeType, CompositeView, CompositeViewDU, ContainerType, ValueOf} from "@chainsafe/ssz"; +import {ForkBlobs, ForkExecution, ForkLightClient, ForkName} from "@lodestar/params"; +import {ssz as phase0} from "./phase0/index.js"; +import {ssz as altair} from "./altair/index.js"; +import {ssz as bellatrix} from "./bellatrix/index.js"; +import {ssz as capella} from "./capella/index.js"; +import {ssz as deneb} from "./deneb/index.js"; + export * from "./primitive/sszTypes.js"; -export {ssz as phase0} from "./phase0/index.js"; -export {ssz as altair} from "./altair/index.js"; -export {ssz as bellatrix} from "./bellatrix/index.js"; -export {ssz as capella} from "./capella/index.js"; -export {ssz as deneb} from "./deneb/index.js"; - -import {ssz as allForksSsz} from "./allForks/index.js"; -export const allForks = allForksSsz.allForks; -export const allForksBlinded = allForksSsz.allForksBlinded; -export const allForksExecution = allForksSsz.allForksExecution; -export const allForksBlobs = allForksSsz.allForksBlobs; -export const allForksLightClient = allForksSsz.allForksLightClient; +export {phase0, altair, bellatrix, capella, deneb}; + +/** + * Index the ssz types that differ by fork + * A record of AllForksSSZTypes indexed by fork + */ +const typesByFork = { + [ForkName.phase0]: { + BeaconBlock: phase0.BeaconBlock, + BeaconBlockBody: phase0.BeaconBlockBody, + BeaconState: phase0.BeaconState, + SignedBeaconBlock: phase0.SignedBeaconBlock, + Metadata: phase0.Metadata, + }, + [ForkName.altair]: { + BeaconBlock: altair.BeaconBlock, + BeaconBlockBody: altair.BeaconBlockBody, + BeaconState: altair.BeaconState, + SignedBeaconBlock: altair.SignedBeaconBlock, + Metadata: altair.Metadata, + LightClientHeader: altair.LightClientHeader, + LightClientBootstrap: altair.LightClientBootstrap, + LightClientUpdate: altair.LightClientUpdate, + LightClientFinalityUpdate: altair.LightClientFinalityUpdate, + LightClientOptimisticUpdate: altair.LightClientOptimisticUpdate, + LightClientStore: altair.LightClientStore, + }, + [ForkName.bellatrix]: { + BeaconBlock: bellatrix.BeaconBlock, + BeaconBlockBody: bellatrix.BeaconBlockBody, + BeaconState: bellatrix.BeaconState, + SignedBeaconBlock: bellatrix.SignedBeaconBlock, + Metadata: altair.Metadata, + LightClientHeader: altair.LightClientHeader, + LightClientBootstrap: altair.LightClientBootstrap, + LightClientUpdate: altair.LightClientUpdate, + LightClientFinalityUpdate: altair.LightClientFinalityUpdate, + LightClientOptimisticUpdate: altair.LightClientOptimisticUpdate, + LightClientStore: altair.LightClientStore, + BlindedBeaconBlock: bellatrix.BlindedBeaconBlock, + BlindedBeaconBlockBody: bellatrix.BlindedBeaconBlockBody, + SignedBlindedBeaconBlock: bellatrix.SignedBlindedBeaconBlock, + ExecutionPayload: bellatrix.ExecutionPayload, + ExecutionPayloadHeader: bellatrix.ExecutionPayloadHeader, + BuilderBid: bellatrix.BuilderBid, + SignedBuilderBid: bellatrix.SignedBuilderBid, + SSEPayloadAttributes: bellatrix.SSEPayloadAttributes, + }, + [ForkName.capella]: { + BeaconBlock: capella.BeaconBlock, + BeaconBlockBody: capella.BeaconBlockBody, + BeaconState: capella.BeaconState, + SignedBeaconBlock: capella.SignedBeaconBlock, + Metadata: altair.Metadata, + LightClientHeader: capella.LightClientHeader, + LightClientBootstrap: capella.LightClientBootstrap, + LightClientUpdate: capella.LightClientUpdate, + LightClientFinalityUpdate: capella.LightClientFinalityUpdate, + LightClientOptimisticUpdate: capella.LightClientOptimisticUpdate, + LightClientStore: capella.LightClientStore, + BlindedBeaconBlock: capella.BlindedBeaconBlock, + BlindedBeaconBlockBody: capella.BlindedBeaconBlockBody, + SignedBlindedBeaconBlock: capella.SignedBlindedBeaconBlock, + ExecutionPayload: capella.ExecutionPayload, + ExecutionPayloadHeader: capella.ExecutionPayloadHeader, + BuilderBid: capella.BuilderBid, + SignedBuilderBid: capella.SignedBuilderBid, + SSEPayloadAttributes: capella.SSEPayloadAttributes, + }, + [ForkName.deneb]: { + BeaconBlock: deneb.BeaconBlock, + BeaconBlockBody: deneb.BeaconBlockBody, + BeaconState: deneb.BeaconState, + SignedBeaconBlock: deneb.SignedBeaconBlock, + Metadata: altair.Metadata, + LightClientHeader: deneb.LightClientHeader, + LightClientBootstrap: deneb.LightClientBootstrap, + LightClientUpdate: deneb.LightClientUpdate, + LightClientFinalityUpdate: deneb.LightClientFinalityUpdate, + LightClientOptimisticUpdate: deneb.LightClientOptimisticUpdate, + LightClientStore: deneb.LightClientStore, + BlindedBeaconBlock: deneb.BlindedBeaconBlock, + BlindedBeaconBlockBody: deneb.BlindedBeaconBlockBody, + SignedBlindedBeaconBlock: deneb.SignedBlindedBeaconBlock, + ExecutionPayload: deneb.ExecutionPayload, + ExecutionPayloadHeader: deneb.ExecutionPayloadHeader, + BuilderBid: deneb.BuilderBid, + SignedBuilderBid: deneb.SignedBuilderBid, + SSEPayloadAttributes: deneb.SSEPayloadAttributes, + ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle, + }, +}; + +const pick = , K extends keyof T>(obj: T, ...keys: K[]): Pick => + Object.fromEntries(keys.filter((key) => key in obj).map((key) => [key, obj[key]])) as Pick; + +const executionForks: ForkExecution[] = [ForkName.bellatrix, ForkName.capella, ForkName.deneb]; +const lightCLientForks: ForkLightClient[] = [ForkName.altair, ForkName.bellatrix, ForkName.capella, ForkName.deneb]; +const blobsForks: ForkBlobs[] = [ForkName.deneb]; + +export const allForksExecution = pick(typesByFork, ...executionForks); +export const allForksLightClient = pick(typesByFork, ...lightCLientForks); +export const allForksBlobs = pick(typesByFork, ...blobsForks); +export const allForksBlinded = { + bellatrix: { + BeaconBlockBody: bellatrix.BlindedBeaconBlockBody, + BeaconBlock: bellatrix.BlindedBeaconBlock, + SignedBeaconBlock: bellatrix.SignedBlindedBeaconBlock, + }, + capella: { + BeaconBlockBody: capella.BlindedBeaconBlockBody, + BeaconBlock: capella.BlindedBeaconBlock, + SignedBeaconBlock: capella.SignedBlindedBeaconBlock, + }, + deneb: { + BeaconBlockBody: deneb.BlindedBeaconBlockBody, + BeaconBlock: deneb.BlindedBeaconBlock, + SignedBeaconBlock: deneb.SignedBlindedBeaconBlock, + }, +}; + +// TODO: These helpers should be removed along with `allForksBlinded` +type SSZBlindedTypesByFork = { + [F in keyof typeof allForksBlinded]: { + [T in keyof (typeof allForksBlinded)[F]]: (typeof allForksBlinded)[F][T]; + }; +}; + +// TODO: These helpers should be removed along with `allForksBlinded` +export type SSZBlindedTypesFor< + F extends ForkExecution, + K extends keyof SSZBlindedTypesByFork[F] | void = void, +> = K extends void + ? // It compiles fine, need to debug the error + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + {[K2 in keyof SSZBlindedTypesByFork[F]]: UnionSSZForksTypeOf} + : // It compiles fine, need to debug the error + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + UnionSSZForksTypeOf]>; + +/** + * A type of union of forks must accept as any parameter the UNION of all fork types. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type UnionSSZForksTypeOf> = CompositeType< + ValueOf, + CompositeView, + CompositeViewDU +>; + +type SSZTypesByFork = { + [F in keyof typeof typesByFork]: { + [T in keyof (typeof typesByFork)[F]]: (typeof typesByFork)[F][T]; + }; +}; + +export type SSZTypesFor = K extends void + ? // It compiles fine, need to debug the error + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + {[K2 in keyof SSZTypesByFork[F]]: UnionSSZForksTypeOf} + : // It compiles fine, need to debug the error + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + UnionSSZForksTypeOf]>; + +export function sszTypesFor( + fork: F, + typeName?: K +): SSZTypesFor { + return ( + typeName === undefined ? typesByFork[fork] : typesByFork[fork][typeName as keyof SSZTypesByFork[F]] + ) as SSZTypesFor; +} diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index e2e416fa3667..4f844884b88e 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -1,3 +1,9 @@ +import {ForkAll, ForkBlobs, ForkExecution, ForkLightClient, ForkName, ForkPreBlobs} from "@lodestar/params"; +import {ts as phase0} from "./phase0/index.js"; +import {ts as altair} from "./altair/index.js"; +import {ts as bellatrix} from "./bellatrix/index.js"; +import {ts as capella} from "./capella/index.js"; +import {ts as deneb} from "./deneb/index.js"; import {Slot} from "./primitive/types.js"; export * from "./primitive/types.js"; @@ -7,8 +13,6 @@ export {ts as bellatrix} from "./bellatrix/index.js"; export {ts as capella} from "./capella/index.js"; export {ts as deneb} from "./deneb/index.js"; -export {ts as allForks} from "./allForks/index.js"; - /** Common non-spec type to represent roots as strings */ export type RootHex = string; @@ -20,3 +24,172 @@ export enum ProducedBlockSource { export type SlotRootHex = {slot: Slot; root: RootHex}; export type SlotOptionalRoot = {slot: Slot; root?: RootHex}; + +type TypesByFork = { + [ForkName.phase0]: { + BeaconBlockHeader: phase0.BeaconBlockHeader; + SignedBeaconBlockHeader: phase0.SignedBeaconBlockHeader; + BeaconBlock: phase0.BeaconBlock; + BeaconBlockBody: phase0.BeaconBlockBody; + BeaconState: phase0.BeaconState; + SignedBeaconBlock: phase0.SignedBeaconBlock; + Metadata: phase0.Metadata; + }; + [ForkName.altair]: { + BeaconBlockHeader: phase0.BeaconBlockHeader; + SignedBeaconBlockHeader: phase0.SignedBeaconBlockHeader; + BeaconBlock: altair.BeaconBlock; + BeaconBlockBody: altair.BeaconBlockBody; + BeaconState: altair.BeaconState; + SignedBeaconBlock: altair.SignedBeaconBlock; + Metadata: altair.Metadata; + LightClientHeader: altair.LightClientHeader; + LightClientBootstrap: altair.LightClientBootstrap; + LightClientUpdate: altair.LightClientUpdate; + LightClientFinalityUpdate: altair.LightClientFinalityUpdate; + LightClientOptimisticUpdate: altair.LightClientOptimisticUpdate; + LightClientStore: altair.LightClientStore; + SyncCommittee: altair.SyncCommittee; + SyncAggregate: altair.SyncAggregate; + }; + [ForkName.bellatrix]: { + BeaconBlockHeader: phase0.BeaconBlockHeader; + SignedBeaconBlockHeader: phase0.SignedBeaconBlockHeader; + BeaconBlock: bellatrix.BeaconBlock; + BeaconBlockBody: bellatrix.BeaconBlockBody; + BeaconState: bellatrix.BeaconState; + SignedBeaconBlock: bellatrix.SignedBeaconBlock; + Metadata: altair.Metadata; + LightClientHeader: altair.LightClientHeader; + LightClientBootstrap: altair.LightClientBootstrap; + LightClientUpdate: altair.LightClientUpdate; + LightClientFinalityUpdate: altair.LightClientFinalityUpdate; + LightClientOptimisticUpdate: altair.LightClientOptimisticUpdate; + LightClientStore: altair.LightClientStore; + BlindedBeaconBlock: bellatrix.BlindedBeaconBlock; + BlindedBeaconBlockBody: bellatrix.BlindedBeaconBlockBody; + SignedBlindedBeaconBlock: bellatrix.SignedBlindedBeaconBlock; + ExecutionPayload: bellatrix.ExecutionPayload; + ExecutionPayloadHeader: bellatrix.ExecutionPayloadHeader; + BuilderBid: bellatrix.BuilderBid; + SignedBuilderBid: bellatrix.SignedBuilderBid; + SSEPayloadAttributes: bellatrix.SSEPayloadAttributes; + SyncCommittee: altair.SyncCommittee; + SyncAggregate: altair.SyncAggregate; + }; + [ForkName.capella]: { + BeaconBlockHeader: phase0.BeaconBlockHeader; + SignedBeaconBlockHeader: phase0.SignedBeaconBlockHeader; + BeaconBlock: capella.BeaconBlock; + BeaconBlockBody: capella.BeaconBlockBody; + BeaconState: capella.BeaconState; + SignedBeaconBlock: capella.SignedBeaconBlock; + Metadata: altair.Metadata; + LightClientHeader: capella.LightClientHeader; + LightClientBootstrap: capella.LightClientBootstrap; + LightClientUpdate: capella.LightClientUpdate; + LightClientFinalityUpdate: capella.LightClientFinalityUpdate; + LightClientOptimisticUpdate: capella.LightClientOptimisticUpdate; + LightClientStore: capella.LightClientStore; + BlindedBeaconBlock: capella.BlindedBeaconBlock; + BlindedBeaconBlockBody: capella.BlindedBeaconBlockBody; + SignedBlindedBeaconBlock: capella.SignedBlindedBeaconBlock; + ExecutionPayload: capella.ExecutionPayload; + ExecutionPayloadHeader: capella.ExecutionPayloadHeader; + BuilderBid: capella.BuilderBid; + SignedBuilderBid: capella.SignedBuilderBid; + SSEPayloadAttributes: capella.SSEPayloadAttributes; + SyncCommittee: altair.SyncCommittee; + SyncAggregate: altair.SyncAggregate; + }; + [ForkName.deneb]: { + BeaconBlockHeader: phase0.BeaconBlockHeader; + SignedBeaconBlockHeader: phase0.SignedBeaconBlockHeader; + BeaconBlock: deneb.BeaconBlock; + BeaconBlockBody: deneb.BeaconBlockBody; + BeaconState: deneb.BeaconState; + SignedBeaconBlock: deneb.SignedBeaconBlock; + Metadata: altair.Metadata; + LightClientHeader: deneb.LightClientHeader; + LightClientBootstrap: deneb.LightClientBootstrap; + LightClientUpdate: deneb.LightClientUpdate; + LightClientFinalityUpdate: deneb.LightClientFinalityUpdate; + LightClientOptimisticUpdate: deneb.LightClientOptimisticUpdate; + LightClientStore: deneb.LightClientStore; + BlindedBeaconBlock: deneb.BlindedBeaconBlock; + BlindedBeaconBlockBody: deneb.BlindedBeaconBlockBody; + SignedBlindedBeaconBlock: deneb.SignedBlindedBeaconBlock; + ExecutionPayload: deneb.ExecutionPayload; + ExecutionPayloadHeader: deneb.ExecutionPayloadHeader; + BuilderBid: deneb.BuilderBid; + SignedBuilderBid: deneb.SignedBuilderBid; + SSEPayloadAttributes: deneb.SSEPayloadAttributes; + BlockContents: {block: BeaconBlock; kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs}; + SignedBlockContents: { + signedBlock: SignedBeaconBlock; + kzgProofs: deneb.KZGProofs; + blobs: deneb.Blobs; + }; + ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle; + BlobsBundle: deneb.BlobsBundle; + Contents: deneb.Contents; + SyncCommittee: altair.SyncCommittee; + SyncAggregate: altair.SyncAggregate; + }; +}; + +export type TypesFor = K extends void + ? TypesByFork[F] + : TypesByFork[F][Exclude]; + +export type BeaconBlockHeader = TypesByFork[F]["BeaconBlockHeader"]; +export type SignedBeaconBlockHeader = TypesByFork[F]["SignedBeaconBlockHeader"]; + +export type BeaconBlock = TypesByFork[F]["BeaconBlock"]; +export type BlindedBeaconBlock = TypesByFork[F]["BlindedBeaconBlock"]; + +export type SignedBeaconBlock = TypesByFork[F]["SignedBeaconBlock"]; +export type SignedBlindedBeaconBlock = + TypesByFork[F]["SignedBlindedBeaconBlock"]; + +export type BeaconBlockBody = TypesByFork[F]["BeaconBlockBody"]; +export type BlindedBeaconBlockBody = TypesByFork[F]["BlindedBeaconBlockBody"]; + +export type BlockContents = TypesByFork[F]["BlockContents"]; +export type SignedBlockContents = TypesByFork[F]["SignedBlockContents"]; +export type SignedOrUnsignedBlockContents = BlockContents | SignedBlockContents; + +export type BeaconBlockOrContents = + | BeaconBlock + | BlockContents; + +export type SignedBeaconBlockOrContents = + | SignedBeaconBlock + | SignedBlockContents; + +export type ExecutionPayload = TypesByFork[F]["ExecutionPayload"]; +export type ExecutionPayloadHeader = TypesByFork[F]["ExecutionPayloadHeader"]; + +export type BlobsBundle = TypesByFork[F]["BlobsBundle"]; +export type Contents = TypesByFork[F]["Contents"]; +export type ExecutionPayloadAndBlobsBundle = + TypesByFork[F]["ExecutionPayloadAndBlobsBundle"]; + +export type LightClientHeader = TypesByFork[F]["LightClientHeader"]; +export type LightClientBootstrap = TypesByFork[F]["LightClientBootstrap"]; +export type LightClientUpdate = TypesByFork[F]["LightClientUpdate"]; +export type LightClientFinalityUpdate = + TypesByFork[F]["LightClientFinalityUpdate"]; +export type LightClientOptimisticUpdate = + TypesByFork[F]["LightClientOptimisticUpdate"]; +export type LightClientStore = TypesByFork[F]["LightClientStore"]; +export type SyncCommittee = TypesByFork[F]["SyncCommittee"]; +export type SyncAggregate = TypesByFork[F]["SyncAggregate"]; + +export type BeaconState = TypesByFork[F]["BeaconState"]; + +export type Metadata = TypesByFork[F]["Metadata"]; + +export type BuilderBid = TypesByFork[F]["BuilderBid"]; +export type SignedBuilderBid = TypesByFork[F]["SignedBuilderBid"]; +export type SSEPayloadAttributes = TypesByFork[F]["SSEPayloadAttributes"]; diff --git a/packages/types/src/utils/typeguards.ts b/packages/types/src/utils/typeguards.ts index 781738c3dbad..7848be700b00 100644 --- a/packages/types/src/utils/typeguards.ts +++ b/packages/types/src/utils/typeguards.ts @@ -1,51 +1,68 @@ +import {ForkBlobs, ForkExecution} from "@lodestar/params"; import { - FullOrBlindedBeaconBlockOrContents, - FullOrBlindedBeaconBlock, - FullOrBlindedSignedBeaconBlock, - FullOrBlindedBeaconBlockBody, - FullOrBlindedExecutionPayload, - ExecutionPayloadHeader, - BlindedBeaconBlockBody, - BlindedBeaconBlock, BlockContents, - SignedBlindedBeaconBlock, - SignedBlockContents, SignedBeaconBlock, ExecutionPayload, ExecutionPayloadAndBlobsBundle, -} from "../allForks/types.js"; + BeaconBlockBody, + BeaconBlockOrContents, + SignedBeaconBlockOrContents, + ExecutionPayloadHeader, + BlindedBeaconBlock, + SignedBlindedBeaconBlock, + BlindedBeaconBlockBody, + SignedBlockContents, + BeaconBlock, +} from "../types.js"; -export function isBlindedExecution(payload: FullOrBlindedExecutionPayload): payload is ExecutionPayloadHeader { - // we just check transactionsRoot for determinging as it the base field +export function isExecutionPayload( + payload: ExecutionPayload | ExecutionPayloadHeader +): payload is ExecutionPayload { + // we just check transactionsRoot for determining as it the base field // that is present and differs from ExecutionPayload for all forks - return (payload as ExecutionPayloadHeader).transactionsRoot !== undefined; + return (payload as ExecutionPayload).transactions !== undefined; } -export function isBlindedBeaconBlock(block: FullOrBlindedBeaconBlockOrContents): block is BlindedBeaconBlock { - const body = (block as FullOrBlindedBeaconBlock).body; - return body !== undefined && isBlindedBeaconBlockBody(body); +export function isBlindedExecutionPayload( + payload: ExecutionPayload | ExecutionPayloadHeader +): payload is ExecutionPayloadHeader { + // we just check transactionsRoot for determining as it the base field + // that is present and differs from ExecutionPayload for all forks + return (payload as ExecutionPayloadHeader).transactionsRoot !== undefined; } -export function isBlindedBeaconBlockBody(body: FullOrBlindedBeaconBlockBody): body is BlindedBeaconBlockBody { - return (body as BlindedBeaconBlockBody).executionPayloadHeader !== undefined; +export function isExecutionPayloadAndBlobsBundle( + data: ExecutionPayload | ExecutionPayloadAndBlobsBundle +): data is ExecutionPayloadAndBlobsBundle { + return (data as ExecutionPayloadAndBlobsBundle).blobsBundle !== undefined; } -export function isBlindedSignedBeaconBlock( - signedBlock: FullOrBlindedSignedBeaconBlock -): signedBlock is SignedBlindedBeaconBlock { - return (signedBlock as SignedBlindedBeaconBlock).message.body.executionPayloadHeader !== undefined; +export function isBlindedBeaconBlock( + block: BeaconBlockOrContents | SignedBeaconBlockOrContents +): block is BlindedBeaconBlock { + return (block as BeaconBlock).body !== null && isBlindedBeaconBlockBody((block as BeaconBlock).body); } -export function isBlockContents(data: FullOrBlindedBeaconBlockOrContents): data is BlockContents { - return (data as BlockContents).kzgProofs !== undefined; +export function isBlindedSignedBeaconBlock( + signedBlock: SignedBeaconBlock | SignedBeaconBlockOrContents +): signedBlock is SignedBlindedBeaconBlock { + return (signedBlock as SignedBlindedBeaconBlock).message.body.executionPayloadHeader !== undefined; +} + +export function isBlindedBeaconBlockBody( + body: BeaconBlockBody | BlindedBeaconBlockBody +): body is BlindedBeaconBlockBody { + return (body as BlindedBeaconBlockBody).executionPayloadHeader !== undefined; } -export function isSignedBlockContents(data: SignedBeaconBlock | SignedBlockContents): data is SignedBlockContents { - return (data as SignedBlockContents).kzgProofs !== undefined; +export function isBlockContents( + data: BeaconBlockOrContents | SignedBeaconBlockOrContents +): data is BlockContents { + return (data as BlockContents).kzgProofs !== undefined; } -export function isExecutionPayloadAndBlobsBundle( - data: ExecutionPayload | ExecutionPayloadAndBlobsBundle -): data is ExecutionPayloadAndBlobsBundle { - return (data as ExecutionPayloadAndBlobsBundle).blobsBundle !== undefined; +export function isSignedBlockContents( + data: SignedBeaconBlockOrContents | BeaconBlockOrContents +): data is SignedBlockContents { + return (data as SignedBlockContents).kzgProofs !== undefined; } diff --git a/packages/validator/src/services/block.ts b/packages/validator/src/services/block.ts index ab855e264447..661ff47440db 100644 --- a/packages/validator/src/services/block.ts +++ b/packages/validator/src/services/block.ts @@ -3,11 +3,15 @@ import { BLSPubkey, Slot, BLSSignature, - allForks, - isBlindedSignedBeaconBlock, ProducedBlockSource, deneb, isBlockContents, + BeaconBlock, + BeaconBlockOrContents, + isBlindedSignedBeaconBlock, + BlindedBeaconBlock, + SignedBeaconBlock, + SignedBlindedBeaconBlock, } from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {ForkPreBlobs, ForkBlobs, ForkSeq, ForkExecution, ForkName} from "@lodestar/params"; @@ -26,14 +30,14 @@ import {BlockDutiesService, GENESIS_SLOT} from "./blockDuties.js"; type FullOrBlindedBlockWithContents = | { version: ForkPreBlobs; - block: allForks.BeaconBlock; + block: BeaconBlock; contents: null; executionPayloadBlinded: false; executionPayloadSource: ProducedBlockSource.engine; } | { version: ForkBlobs; - block: allForks.BeaconBlock; + block: BeaconBlock; contents: { kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs; @@ -43,7 +47,7 @@ type FullOrBlindedBlockWithContents = } | { version: ForkExecution; - block: allForks.BlindedBeaconBlock; + block: BlindedBeaconBlock; contents: null; executionPayloadBlinded: true; executionPayloadSource: ProducedBlockSource; @@ -174,7 +178,7 @@ export class BlockProposingService { } private publishBlockWrapper = async ( - signedBlock: allForks.FullOrBlindedSignedBeaconBlock, + signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock, contents: {kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs} | null, opts: {broadcastValidation?: routes.beacon.BroadcastValidation} = {} ): Promise => { @@ -189,7 +193,12 @@ export class BlockProposingService { if (contents === null) { (await this.api.beacon.publishBlockV2({signedBlockOrContents: signedBlock, ...opts})).assertOk(); } else { - (await this.api.beacon.publishBlockV2({signedBlockOrContents: {...contents, signedBlock}, ...opts})).assertOk(); + ( + await this.api.beacon.publishBlockV2({ + signedBlockOrContents: {...contents, signedBlock: signedBlock as SignedBeaconBlock}, + ...opts, + }) + ).assertOk(); } } }; @@ -273,7 +282,7 @@ export class BlockProposingService { } function parseProduceBlockResponse( - response: {data: allForks.FullOrBlindedBeaconBlockOrContents} & { + response: {data: BeaconBlockOrContents | BlindedBeaconBlock} & { executionPayloadSource: ProducedBlockSource; executionPayloadBlinded: boolean; version: ForkName; @@ -304,10 +313,11 @@ function parseProduceBlockResponse( debugLogCtx, } as FullOrBlindedBlockWithContents & DebugLogCtx; } else { - if (isBlockContents(response.data)) { + const data = response.data; + if (isBlockContents(data)) { return { - block: response.data.block, - contents: {blobs: response.data.blobs, kzgProofs: response.data.kzgProofs}, + block: data.block, + contents: {blobs: data.blobs, kzgProofs: data.kzgProofs}, version: response.version, executionPayloadBlinded: false, executionPayloadSource, diff --git a/packages/validator/src/services/validatorStore.ts b/packages/validator/src/services/validatorStore.ts index 6cd9ed8dc065..ef675ff4a001 100644 --- a/packages/validator/src/services/validatorStore.ts +++ b/packages/validator/src/services/validatorStore.ts @@ -21,14 +21,17 @@ import { DOMAIN_APPLICATION_BUILDER, } from "@lodestar/params"; import { - allForks, altair, + BeaconBlock, bellatrix, + BlindedBeaconBlock, BLSPubkey, BLSSignature, Epoch, phase0, Root, + SignedBeaconBlock, + SignedBlindedBeaconBlock, Slot, ssz, ValidatorIndex, @@ -430,10 +433,10 @@ export class ValidatorStore { async signBlock( pubkey: BLSPubkey, - blindedOrFull: allForks.FullOrBlindedBeaconBlock, + blindedOrFull: BeaconBlock | BlindedBeaconBlock, currentSlot: Slot, logger?: LoggerVc - ): Promise { + ): Promise { // Make sure the block slot is not higher than the current slot to avoid potential attacks. if (blindedOrFull.slot > currentSlot) { throw Error(`Not signing block with slot ${blindedOrFull.slot} greater than current slot ${currentSlot}`); @@ -469,7 +472,7 @@ export class ValidatorStore { return { message: blindedOrFull, signature: await this.getSignature(pubkey, signingRoot, signingSlot, signableMessage), - } as allForks.FullOrBlindedSignedBeaconBlock; + } as SignedBeaconBlock | SignedBlindedBeaconBlock; } async signRandao(pubkey: BLSPubkey, slot: Slot): Promise { diff --git a/packages/validator/src/util/externalSignerClient.ts b/packages/validator/src/util/externalSignerClient.ts index 374da79b7989..dc1d0d0f1dd5 100644 --- a/packages/validator/src/util/externalSignerClient.ts +++ b/packages/validator/src/util/externalSignerClient.ts @@ -1,11 +1,11 @@ import {ContainerType, toHexString, ValueOf} from "@chainsafe/ssz"; import {fetch} from "@lodestar/api"; -import {phase0, altair, capella} from "@lodestar/types"; +import {phase0, altair, capella, BeaconBlock, BlindedBeaconBlock} from "@lodestar/types"; import {ForkSeq} from "@lodestar/params"; import {ValidatorRegistrationV1} from "@lodestar/types/bellatrix"; import {BeaconConfig} from "@lodestar/config"; import {computeEpochAtSlot, blindedOrFullBlockToHeader} from "@lodestar/state-transition"; -import {allForks, Epoch, Root, RootHex, Slot, ssz} from "@lodestar/types"; +import {Epoch, Root, RootHex, Slot, ssz} from "@lodestar/types"; import {PubkeyHex} from "../types.js"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -63,7 +63,7 @@ export type SignableMessage = | {type: SignableMessageType.AGGREGATION_SLOT; data: {slot: Slot}} | {type: SignableMessageType.AGGREGATE_AND_PROOF; data: phase0.AggregateAndProof} | {type: SignableMessageType.ATTESTATION; data: phase0.AttestationData} - | {type: SignableMessageType.BLOCK_V2; data: allForks.FullOrBlindedBeaconBlock} + | {type: SignableMessageType.BLOCK_V2; data: BeaconBlock | BlindedBeaconBlock} | {type: SignableMessageType.DEPOSIT; data: ValueOf} | {type: SignableMessageType.RANDAO_REVEAL; data: {epoch: Epoch}} | {type: SignableMessageType.VOLUNTARY_EXIT; data: phase0.VoluntaryExit} From 3cc1cb40261d2a38df79f705b5a9a2cac0279505 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Mon, 24 Jun 2024 12:47:21 +0200 Subject: [PATCH 09/17] chore: cleanup usage of blinded types (#6903) * Cleanup blinded types * Fix lint error --- .../api/src/beacon/routes/beacon/block.ts | 18 ++++----- packages/api/src/beacon/routes/validator.ts | 6 +-- packages/api/src/builder/routes.ts | 6 +-- packages/api/src/utils/fork.ts | 9 +---- .../src/api/impl/beacon/blocks/index.ts | 4 +- .../src/api/impl/validator/index.ts | 2 +- packages/beacon-node/src/chain/chain.ts | 6 +-- packages/config/src/forkConfig/index.ts | 7 ---- packages/config/src/forkConfig/types.ts | 4 +- .../src/signatureSets/proposer.ts | 2 +- .../state-transition/src/util/blindedBlock.ts | 4 +- packages/types/src/index.ts | 4 +- packages/types/src/sszTypes.ts | 38 ------------------- 13 files changed, 28 insertions(+), 82 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/block.ts b/packages/api/src/beacon/routes/beacon/block.ts index dcf0d07c7a9b..facd3da55059 100644 --- a/packages/api/src/beacon/routes/beacon/block.ts +++ b/packages/api/src/beacon/routes/beacon/block.ts @@ -22,7 +22,7 @@ import { ExecutionOptimisticFinalizedAndVersionMeta, MetaHeader, } from "../../../utils/metadata.js"; -import {getBlindedForkTypes, toForkName} from "../../../utils/fork.js"; +import {getExecutionForkTypes, toForkName} from "../../../utils/fork.js"; import {fromHeaders} from "../../../utils/headers.js"; import {WireFormat} from "../../../utils/wireFormat.js"; @@ -414,7 +414,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = config.getForkName(signedBlindedBlock.message.slot); return { - body: getBlindedForkTypes(fork).SignedBeaconBlock.toJson(signedBlindedBlock), + body: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.toJson(signedBlindedBlock), headers: { [MetaHeader.Version]: fork, }, @@ -423,13 +423,13 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - signedBlindedBlock: getBlindedForkTypes(fork).SignedBeaconBlock.fromJson(body), + signedBlindedBlock: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.fromJson(body), }; }, writeReqSsz: ({signedBlindedBlock}) => { const fork = config.getForkName(signedBlindedBlock.message.slot); return { - body: getBlindedForkTypes(fork).SignedBeaconBlock.serialize(signedBlindedBlock), + body: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.serialize(signedBlindedBlock), headers: { [MetaHeader.Version]: fork, }, @@ -438,7 +438,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - signedBlindedBlock: getBlindedForkTypes(fork).SignedBeaconBlock.deserialize(body), + signedBlindedBlock: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.deserialize(body), }; }, schema: { @@ -458,7 +458,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = config.getForkName(signedBlindedBlock.message.slot); return { - body: getBlindedForkTypes(fork).SignedBeaconBlock.toJson(signedBlindedBlock), + body: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.toJson(signedBlindedBlock), headers: { [MetaHeader.Version]: fork, @@ -469,14 +469,14 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - signedBlindedBlock: getBlindedForkTypes(fork).SignedBeaconBlock.fromJson(body), + signedBlindedBlock: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.fromJson(body), broadcastValidation: query.broadcast_validation as BroadcastValidation, }; }, writeReqSsz: ({signedBlindedBlock, broadcastValidation}) => { const fork = config.getForkName(signedBlindedBlock.message.slot); return { - body: getBlindedForkTypes(fork).SignedBeaconBlock.serialize(signedBlindedBlock), + body: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.serialize(signedBlindedBlock), headers: { [MetaHeader.Version]: fork, }, @@ -486,7 +486,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - signedBlindedBlock: getBlindedForkTypes(fork).SignedBeaconBlock.deserialize(body), + signedBlindedBlock: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.deserialize(body), broadcastValidation: query.broadcast_validation as BroadcastValidation, }; }, diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index cd26f8997764..32a76a536d81 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -20,7 +20,7 @@ import { } from "@lodestar/types"; import {Endpoint, RouteDefinitions, Schema} from "../../utils/index.js"; import {fromGraffitiHex, toBoolean, toGraffitiHex} from "../../utils/serdes.js"; -import {getBlindedForkTypes, toForkName} from "../../utils/fork.js"; +import {getExecutionForkTypes, toForkName} from "../../utils/fork.js"; import { ArrayOf, EmptyMeta, @@ -685,7 +685,7 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions (executionPayloadBlinded - ? getBlindedForkTypes(version).BeaconBlock + ? getExecutionForkTypes(version).BlindedBeaconBlock : isForkBlobs(version) ? BlockContentsType : ssz[version].BeaconBlock) as Type @@ -753,7 +753,7 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions getBlindedForkTypes(fork).BeaconBlock), + data: WithVersion((fork) => getExecutionForkTypes(fork).BlindedBeaconBlock), meta: VersionCodec, }, }, diff --git a/packages/api/src/builder/routes.ts b/packages/api/src/builder/routes.ts index 7e6a6e24b1b7..971f4a1e63fe 100644 --- a/packages/api/src/builder/routes.ts +++ b/packages/api/src/builder/routes.ts @@ -27,7 +27,7 @@ import { JsonOnlyReq, WithVersion, } from "../utils/codecs.js"; -import {getBlindedForkTypes, getExecutionForkTypes, toForkName} from "../utils/fork.js"; +import {getExecutionForkTypes, toForkName} from "../utils/fork.js"; import {fromHeaders} from "../utils/headers.js"; // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes @@ -130,7 +130,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = config.getForkName(signedBlindedBlock.message.slot); return { - body: getBlindedForkTypes(fork).SignedBeaconBlock.toJson(signedBlindedBlock), + body: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.toJson(signedBlindedBlock), headers: { [MetaHeader.Version]: fork, }, @@ -139,7 +139,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - signedBlindedBlock: getBlindedForkTypes(fork).SignedBeaconBlock.fromJson(body), + signedBlindedBlock: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.fromJson(body), }; }, schema: { diff --git a/packages/api/src/utils/fork.ts b/packages/api/src/utils/fork.ts index 0925e750719a..5854a0867d02 100644 --- a/packages/api/src/utils/fork.ts +++ b/packages/api/src/utils/fork.ts @@ -7,7 +7,7 @@ import { isForkExecution, isForkLightClient, } from "@lodestar/params"; -import {SSZBlindedTypesFor, SSZTypesFor, ssz, sszTypesFor} from "@lodestar/types"; +import {SSZTypesFor, sszTypesFor} from "@lodestar/types"; export function toForkName(version: string): ForkName { // Teku returns fork as UPPERCASE @@ -35,13 +35,6 @@ export function getExecutionForkTypes(fork: ForkName): SSZTypesFor { - if (!isForkExecution(fork)) { - throw Error(`Invalid fork=${fork} for blinded fork types`); - } - return ssz.allForksBlinded[fork]; -} - export function getBlobsForkTypes(fork: ForkName): SSZTypesFor { if (!isForkBlobs(fork)) { throw Error(`Invalid fork=${fork} for blobs fork types`); diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts index d4675de21185..274d88e5d508 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -240,8 +240,8 @@ export function getBeaconBlockApi({ const slot = signedBlindedBlock.message.slot; const blockRoot = toHex( chain.config - .getBlindedForkTypes(signedBlindedBlock.message.slot) - .BeaconBlock.hashTreeRoot(signedBlindedBlock.message) + .getExecutionForkTypes(signedBlindedBlock.message.slot) + .BlindedBeaconBlock.hashTreeRoot(signedBlindedBlock.message) ); // Either the payload/blobs are cached from i) engine locally or ii) they are from the builder diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 8fb40e4ecb6b..486fc6e8062f 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -416,7 +416,7 @@ export function getValidatorApi({ slot, executionPayloadValue, consensusBlockValue, - root: toHex(config.getBlindedForkTypes(slot).BeaconBlock.hashTreeRoot(block)), + root: toHex(config.getExecutionForkTypes(slot).BlindedBeaconBlock.hashTreeRoot(block)), }); if (chain.opts.persistProducedBlocks) { diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index ccd10f7b4d6f..a6912d952b68 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -620,7 +620,7 @@ export class BeaconChain implements IBeaconChain { const bodyRoot = blockType === BlockType.Full ? this.config.getForkTypes(slot).BeaconBlockBody.hashTreeRoot(body) - : this.config.getBlindedForkTypes(slot).BeaconBlockBody.hashTreeRoot(body as BlindedBeaconBlockBody); + : this.config.getExecutionForkTypes(slot).BlindedBeaconBlockBody.hashTreeRoot(body as BlindedBeaconBlockBody); this.logger.debug("Computing block post state from the produced body", { slot, bodyRoot: toHexString(bodyRoot), @@ -640,7 +640,7 @@ export class BeaconChain implements IBeaconChain { const blockRoot = blockType === BlockType.Full ? this.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block) - : this.config.getBlindedForkTypes(slot).BeaconBlock.hashTreeRoot(block as BlindedBeaconBlock); + : this.config.getExecutionForkTypes(slot).BlindedBeaconBlock.hashTreeRoot(block as BlindedBeaconBlock); const blockRootHex = toHex(blockRoot); // track the produced block for consensus broadcast validations @@ -777,7 +777,7 @@ export class BeaconChain implements IBeaconChain { persistBlock(data: BeaconBlock | BlindedBeaconBlock, suffix?: string): void { const slot = data.slot; if (isBlindedBeaconBlock(data)) { - const sszType = this.config.getBlindedForkTypes(slot).BeaconBlock; + const sszType = this.config.getExecutionForkTypes(slot).BlindedBeaconBlock; void this.persistSszObject("BlindedBeaconBlock", sszType.serialize(data), sszType.hashTreeRoot(data), suffix); } else { const sszType = this.config.getForkTypes(slot).BeaconBlock; diff --git a/packages/config/src/forkConfig/index.ts b/packages/config/src/forkConfig/index.ts index 45213d0a611d..358c2d752001 100644 --- a/packages/config/src/forkConfig/index.ts +++ b/packages/config/src/forkConfig/index.ts @@ -101,13 +101,6 @@ export function createForkConfig(config: ChainConfig): ForkConfig { } return ssz.allForksExecution[forkName]; }, - getBlindedForkTypes(slot: Slot): (typeof ssz.allForksBlinded)[ForkExecution] { - const forkName = this.getForkName(slot); - if (!isForkExecution(forkName)) { - throw Error(`Invalid slot=${slot} fork=${forkName} for blinded fork types`); - } - return ssz.allForksBlinded[forkName]; - }, getLightClientForkTypes(slot: Slot): SSZTypesFor { const forkName = this.getForkName(slot); if (!isForkLightClient(forkName)) { diff --git a/packages/config/src/forkConfig/types.ts b/packages/config/src/forkConfig/types.ts index 93f34a62bc83..2905e6f03c34 100644 --- a/packages/config/src/forkConfig/types.ts +++ b/packages/config/src/forkConfig/types.ts @@ -1,5 +1,5 @@ import {ForkAll, ForkBlobs, ForkExecution, ForkLightClient, ForkName, ForkSeq} from "@lodestar/params"; -import {Epoch, SSZBlindedTypesFor, SSZTypesFor, Slot, Version} from "@lodestar/types"; +import {Epoch, SSZTypesFor, Slot, Version} from "@lodestar/types"; export type ForkInfo = { name: ForkName; @@ -34,8 +34,6 @@ export type ForkConfig = { getLightClientForkTypes(slot: Slot): SSZTypesFor; /** Get execution SSZ types by hard-fork*/ getExecutionForkTypes(slot: Slot): SSZTypesFor; - /** Get blinded SSZ types by hard-fork */ - getBlindedForkTypes(slot: Slot): SSZBlindedTypesFor; /** Get blobs SSZ types by hard-fork*/ getBlobsForkTypes(slot: Slot): SSZTypesFor; }; diff --git a/packages/state-transition/src/signatureSets/proposer.ts b/packages/state-transition/src/signatureSets/proposer.ts index b5e501bd16c7..e5ae7fd1f6f1 100644 --- a/packages/state-transition/src/signatureSets/proposer.ts +++ b/packages/state-transition/src/signatureSets/proposer.ts @@ -20,7 +20,7 @@ export function getBlockProposerSignatureSet( const domain = config.getDomain(state.slot, DOMAIN_BEACON_PROPOSER, signedBlock.message.slot); const blockType = isBlindedBeaconBlock(signedBlock.message) - ? config.getBlindedForkTypes(signedBlock.message.slot).BeaconBlock + ? config.getExecutionForkTypes(signedBlock.message.slot).BlindedBeaconBlock : config.getForkTypes(signedBlock.message.slot).BeaconBlock; return { diff --git a/packages/state-transition/src/util/blindedBlock.ts b/packages/state-transition/src/util/blindedBlock.ts index 2b9510bd1e99..d63b40a9fe88 100644 --- a/packages/state-transition/src/util/blindedBlock.ts +++ b/packages/state-transition/src/util/blindedBlock.ts @@ -25,7 +25,7 @@ export function blindedOrFullBlockHashTreeRoot( ): Root { return isBlindedBeaconBlock(blindedOrFull) ? // Blinded - config.getBlindedForkTypes(blindedOrFull.slot).BeaconBlock.hashTreeRoot(blindedOrFull) + config.getExecutionForkTypes(blindedOrFull.slot).BlindedBeaconBlock.hashTreeRoot(blindedOrFull) : // Full config.getForkTypes(blindedOrFull.slot).BeaconBlock.hashTreeRoot(blindedOrFull); } @@ -36,7 +36,7 @@ export function blindedOrFullBlockToHeader( ): BeaconBlockHeader { const bodyRoot = isBlindedBeaconBlock(blindedOrFull) ? // Blinded - config.getBlindedForkTypes(blindedOrFull.slot).BeaconBlockBody.hashTreeRoot(blindedOrFull.body) + config.getExecutionForkTypes(blindedOrFull.slot).BlindedBeaconBlockBody.hashTreeRoot(blindedOrFull.body) : // Full config.getForkTypes(blindedOrFull.slot).BeaconBlockBody.hashTreeRoot(blindedOrFull.body); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 7838a02d06e2..e0745834c7d1 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,8 +1,8 @@ export * from "./types.js"; import * as ssz from "./sszTypes.js"; import {sszTypesFor} from "./sszTypes.js"; -import type {SSZBlindedTypesFor, SSZTypesFor} from "./sszTypes.js"; -export {sszTypesFor, SSZBlindedTypesFor, SSZTypesFor, ssz}; +import type {SSZTypesFor} from "./sszTypes.js"; +export {sszTypesFor, SSZTypesFor, ssz}; // Typeguards export * from "./utils/typeguards.js"; // String type diff --git a/packages/types/src/sszTypes.ts b/packages/types/src/sszTypes.ts index 1ee5b9340630..eae1bbcdeaba 100644 --- a/packages/types/src/sszTypes.ts +++ b/packages/types/src/sszTypes.ts @@ -110,44 +110,6 @@ const blobsForks: ForkBlobs[] = [ForkName.deneb]; export const allForksExecution = pick(typesByFork, ...executionForks); export const allForksLightClient = pick(typesByFork, ...lightCLientForks); export const allForksBlobs = pick(typesByFork, ...blobsForks); -export const allForksBlinded = { - bellatrix: { - BeaconBlockBody: bellatrix.BlindedBeaconBlockBody, - BeaconBlock: bellatrix.BlindedBeaconBlock, - SignedBeaconBlock: bellatrix.SignedBlindedBeaconBlock, - }, - capella: { - BeaconBlockBody: capella.BlindedBeaconBlockBody, - BeaconBlock: capella.BlindedBeaconBlock, - SignedBeaconBlock: capella.SignedBlindedBeaconBlock, - }, - deneb: { - BeaconBlockBody: deneb.BlindedBeaconBlockBody, - BeaconBlock: deneb.BlindedBeaconBlock, - SignedBeaconBlock: deneb.SignedBlindedBeaconBlock, - }, -}; - -// TODO: These helpers should be removed along with `allForksBlinded` -type SSZBlindedTypesByFork = { - [F in keyof typeof allForksBlinded]: { - [T in keyof (typeof allForksBlinded)[F]]: (typeof allForksBlinded)[F][T]; - }; -}; - -// TODO: These helpers should be removed along with `allForksBlinded` -export type SSZBlindedTypesFor< - F extends ForkExecution, - K extends keyof SSZBlindedTypesByFork[F] | void = void, -> = K extends void - ? // It compiles fine, need to debug the error - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - {[K2 in keyof SSZBlindedTypesByFork[F]]: UnionSSZForksTypeOf} - : // It compiles fine, need to debug the error - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - UnionSSZForksTypeOf]>; /** * A type of union of forks must accept as any parameter the UNION of all fork types. From f20ec4efe8c5e6d37965d6ab51c4dea6d9007e22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 09:53:22 -0400 Subject: [PATCH 10/17] chore(deps): bump ws from 7.5.9 to 7.5.10 in /docs (#6896) Bumps [ws](https://github.com/websockets/ws) from 7.5.9 to 7.5.10. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.9...7.5.10) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/yarn.lock | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/docs/yarn.lock b/docs/yarn.lock index ad6053d02f35..a2c90a3ace6c 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2512,7 +2512,7 @@ "@docusaurus/theme-search-algolia" "3.4.0" "@docusaurus/types" "3.4.0" -"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": +"@docusaurus/react-loadable@5.5.2": version "5.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== @@ -9359,6 +9359,14 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1: dependencies: "@babel/runtime" "^7.10.3" +"react-loadable@npm:@docusaurus/react-loadable@5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" + integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== + dependencies: + "@types/react" "*" + prop-types "^15.6.2" + "react-loadable@npm:@docusaurus/react-loadable@6.0.0": version "6.0.0" resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz#de6c7f73c96542bd70786b8e522d535d69069dc4" @@ -10853,14 +10861,14 @@ write-file-atomic@^3.0.3: typedarray-to-buffer "^3.1.5" ws@^7.3.1: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== ws@^8.13.0: - version "8.16.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" - integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== xdg-basedir@^5.0.1, xdg-basedir@^5.1.0: version "5.1.0" From b453b37d53a946b6faf73277fe1d75becff3ae8f Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Mon, 24 Jun 2024 15:52:24 +0100 Subject: [PATCH 11/17] feat: add endpoint to fetch blinded blocks (#6905) * feat: add endpoint to fetch blinded blocks * Reorder test assertions --- .../api/src/beacon/routes/beacon/block.ts | 25 ++++- .../api/test/unit/beacon/oapiSpec.test.ts | 1 - .../api/test/unit/beacon/testData/beacon.ts | 7 ++ .../src/api/impl/beacon/blocks/index.ts | 24 ++++- .../api/impl/beacon/block/endpoint.test.ts | 101 ++++++++++++++++++ .../state-transition/src/util/blindedBlock.ts | 12 ++- 6 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 packages/beacon-node/test/e2e/api/impl/beacon/block/endpoint.test.ts diff --git a/packages/api/src/beacon/routes/beacon/block.ts b/packages/api/src/beacon/routes/beacon/block.ts index facd3da55059..cbf206e51a4a 100644 --- a/packages/api/src/beacon/routes/beacon/block.ts +++ b/packages/api/src/beacon/routes/beacon/block.ts @@ -12,7 +12,7 @@ import { SignedBeaconBlockOrContents, SignedBlindedBeaconBlock, } from "@lodestar/types"; -import {ForkName, ForkSeq} from "@lodestar/params"; +import {ForkName, ForkPreExecution, ForkSeq, isForkExecution} from "@lodestar/params"; import {Endpoint, RequestCodec, RouteDefinitions, Schema} from "../../../utils/index.js"; import {EmptyMeta, EmptyResponseCodec, EmptyResponseData, WithVersion} from "../../../utils/codecs.js"; import { @@ -88,6 +88,18 @@ export type Endpoints = { ExecutionOptimisticFinalizedAndVersionMeta >; + /** + * Get blinded block + * Retrieves blinded block for given block id. + */ + getBlindedBlock: Endpoint< + "GET", + BlockArgs, + {params: {block_id: string}}, + SignedBlindedBeaconBlock | SignedBeaconBlock, + ExecutionOptimisticFinalizedAndVersionMeta + >; + /** * Get block attestations * Retrieves attestation included in requested block. @@ -226,6 +238,17 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions + isForkExecution(fork) ? ssz[fork].SignedBlindedBeaconBlock : ssz[fork].SignedBeaconBlock + ), + meta: ExecutionOptimisticFinalizedAndVersionCodec, + }, + }, getBlockAttestations: { url: "/eth/v1/beacon/blocks/{block_id}/attestations", method: "GET", diff --git a/packages/api/test/unit/beacon/oapiSpec.test.ts b/packages/api/test/unit/beacon/oapiSpec.test.ts index 6b238bf8cd36..e5d473ab6a55 100644 --- a/packages/api/test/unit/beacon/oapiSpec.test.ts +++ b/packages/api/test/unit/beacon/oapiSpec.test.ts @@ -56,7 +56,6 @@ const testDatas = { const ignoredOperations = [ /* missing route */ "getDepositSnapshot", // Won't fix for now, see https://github.com/ChainSafe/lodestar/issues/5697 - "getBlindedBlock", // https://github.com/ChainSafe/lodestar/issues/5699 "getNextWithdrawals", // https://github.com/ChainSafe/lodestar/issues/5696 "getDebugForkChoice", // https://github.com/ChainSafe/lodestar/issues/5700 /* Must support ssz response body */ diff --git a/packages/api/test/unit/beacon/testData/beacon.ts b/packages/api/test/unit/beacon/testData/beacon.ts index a6b48320bf8c..9a89abd68a14 100644 --- a/packages/api/test/unit/beacon/testData/beacon.ts +++ b/packages/api/test/unit/beacon/testData/beacon.ts @@ -38,6 +38,13 @@ export const testData: GenericServerTestCases = { meta: {executionOptimistic: true, finalized: false, version: ForkName.bellatrix}, }, }, + getBlindedBlock: { + args: {blockId: "head"}, + res: { + data: ssz.deneb.SignedBlindedBeaconBlock.defaultValue(), + meta: {executionOptimistic: true, finalized: false, version: ForkName.deneb}, + }, + }, getBlockAttestations: { args: {blockId: "head"}, res: {data: [ssz.phase0.Attestation.defaultValue()], meta: {executionOptimistic: true, finalized: false}}, diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts index 274d88e5d508..177f58aebb95 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -1,7 +1,12 @@ import {routes} from "@lodestar/api"; import {ApplicationMethods} from "@lodestar/api/server"; -import {computeEpochAtSlot, computeTimeAtSlot, reconstructFullBlockOrContents} from "@lodestar/state-transition"; -import {SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params"; +import { + computeEpochAtSlot, + computeTimeAtSlot, + reconstructFullBlockOrContents, + signedBeaconBlockToBlinded, +} from "@lodestar/state-transition"; +import {ForkExecution, SLOTS_PER_HISTORICAL_ROOT, isForkExecution} from "@lodestar/params"; import {sleep, fromHex, toHex} from "@lodestar/utils"; import { deneb, @@ -385,6 +390,21 @@ export function getBeaconBlockApi({ }; }, + async getBlindedBlock({blockId}) { + const {block, executionOptimistic, finalized} = await resolveBlockId(chain, blockId); + const fork = config.getForkName(block.message.slot); + return { + data: isForkExecution(fork) + ? signedBeaconBlockToBlinded(config, block as SignedBeaconBlock) + : block, + meta: { + executionOptimistic, + finalized, + version: fork, + }, + }; + }, + async getBlockAttestations({blockId}) { const {block, executionOptimistic, finalized} = await resolveBlockId(chain, blockId); return { diff --git a/packages/beacon-node/test/e2e/api/impl/beacon/block/endpoint.test.ts b/packages/beacon-node/test/e2e/api/impl/beacon/block/endpoint.test.ts new file mode 100644 index 000000000000..46b0e10580aa --- /dev/null +++ b/packages/beacon-node/test/e2e/api/impl/beacon/block/endpoint.test.ts @@ -0,0 +1,101 @@ +import {describe, beforeAll, afterAll, it, expect, vi} from "vitest"; +import {createBeaconConfig} from "@lodestar/config"; +import {ApiClient, WireFormat, getClient} from "@lodestar/api"; +import { + SignedBeaconBlock, + SignedBlindedBeaconBlock, + isBlindedExecutionPayload, + isBlindedSignedBeaconBlock, + isExecutionPayload, +} from "@lodestar/types"; +import {ForkName} from "@lodestar/params"; +import {LogLevel, testLogger} from "../../../../../utils/logger.js"; +import {getDevBeaconNode} from "../../../../../utils/node/beacon.js"; +import {BeaconNode} from "../../../../../../src/node/nodejs.js"; +import {getConfig} from "../../../../../utils/config.js"; + +describe("beacon block api", function () { + vi.setConfig({testTimeout: 60_000, hookTimeout: 60_000}); + + const restPort = 9596; + const fork = ForkName.deneb; + const config = createBeaconConfig(getConfig(fork), Buffer.alloc(32, 0xaa)); + const validatorCount = 8; + + let bn: BeaconNode; + let client: ApiClient["beacon"]; + + beforeAll(async () => { + bn = await getDevBeaconNode({ + params: config, + options: { + sync: {isSingleNode: true}, + network: {allowPublishToZeroPeers: true}, + api: { + rest: { + enabled: true, + port: restPort, + }, + }, + chain: {blsVerifyAllMainThread: true}, + }, + validatorCount, + logger: testLogger("Node-A", {level: LogLevel.info}), + }); + client = getClient({baseUrl: `http://127.0.0.1:${restPort}`}, {config}).beacon; + }); + + afterAll(async () => { + await bn.close(); + }); + + describe("getBlockV2", () => { + it("should return signed beacon block", async () => { + const res = await client.getBlockV2({blockId: "head"}); + + expect(res.meta().version).toBe(fork); + expect(res.wireFormat()).toBe(WireFormat.ssz); + + const beaconBlock = res.value() as SignedBeaconBlock; + + expect(isBlindedSignedBeaconBlock(beaconBlock)).toBe(false); + expect(isExecutionPayload(beaconBlock.message.body.executionPayload)).toBe(true); + expect(beaconBlock.message.body).not.toHaveProperty("executionPayloadHeader"); + }); + + it("should return 400 if block id is invalid", async () => { + const res = await client.getBlockV2({blockId: "current"}); + expect(res.status).toBe(400); + }); + + it("should return 404 if block not found", async () => { + const res = await client.getBlockV2({blockId: 999}); + expect(res.status).toBe(404); + }); + }); + + describe("getBlindedBlock", () => { + it("should return signed blinded block", async () => { + const res = await client.getBlindedBlock({blockId: "head"}); + + expect(res.meta().version).toBe(fork); + expect(res.wireFormat()).toBe(WireFormat.ssz); + + const blindedBlock = res.value() as SignedBlindedBeaconBlock; + + expect(isBlindedSignedBeaconBlock(blindedBlock)).toBe(true); + expect(isBlindedExecutionPayload(blindedBlock.message.body.executionPayloadHeader)).toBe(true); + expect(blindedBlock.message.body).not.toHaveProperty("executionPayload"); + }); + + it("should return 400 if block id is invalid", async () => { + const res = await client.getBlindedBlock({blockId: "current"}); + expect(res.status).toBe(400); + }); + + it("should return 404 if block not found", async () => { + const res = await client.getBlindedBlock({blockId: 999}); + expect(res.status).toBe(404); + }); + }); +}); diff --git a/packages/state-transition/src/util/blindedBlock.ts b/packages/state-transition/src/util/blindedBlock.ts index d63b40a9fe88..2e4e4d590817 100644 --- a/packages/state-transition/src/util/blindedBlock.ts +++ b/packages/state-transition/src/util/blindedBlock.ts @@ -52,10 +52,20 @@ export function blindedOrFullBlockToHeader( export function beaconBlockToBlinded(config: ChainForkConfig, block: BeaconBlock): BlindedBeaconBlock { const fork = config.getForkName(block.slot); const executionPayloadHeader = executionPayloadToPayloadHeader(ForkSeq[fork], block.body.executionPayload); - const blindedBlock = {...block, body: {...block.body, executionPayloadHeader}} as BlindedBeaconBlock; + const blindedBlock: BlindedBeaconBlock = {...block, body: {...block.body, executionPayloadHeader}}; return blindedBlock; } +export function signedBeaconBlockToBlinded( + config: ChainForkConfig, + signedBlock: SignedBeaconBlock +): SignedBlindedBeaconBlock { + return { + message: beaconBlockToBlinded(config, signedBlock.message), + signature: signedBlock.signature, + }; +} + export function signedBlindedBlockToFull( signedBlindedBlock: SignedBlindedBeaconBlock, executionPayload: ExecutionPayload | null From 0f3109f322a352308ecdd47ac0d125ac33360d9d Mon Sep 17 00:00:00 2001 From: g11tech Date: Tue, 25 Jun 2024 17:03:16 +0530 Subject: [PATCH 12/17] fix: update blockcontents typing to be more precise (#6907) --- packages/types/src/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 4f844884b88e..46641d55667e 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -124,9 +124,9 @@ type TypesByFork = { BuilderBid: deneb.BuilderBid; SignedBuilderBid: deneb.SignedBuilderBid; SSEPayloadAttributes: deneb.SSEPayloadAttributes; - BlockContents: {block: BeaconBlock; kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs}; + BlockContents: {block: BeaconBlock; kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs}; SignedBlockContents: { - signedBlock: SignedBeaconBlock; + signedBlock: SignedBeaconBlock; kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs; }; From fe23f68998cff78ee1e68c0701e9f38405268a36 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Tue, 25 Jun 2024 14:08:12 +0100 Subject: [PATCH 13/17] chore: rename execution payload header type guard (#6906) --- .../test/e2e/api/impl/beacon/block/endpoint.test.ts | 4 ++-- packages/types/src/utils/typeguards.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/beacon-node/test/e2e/api/impl/beacon/block/endpoint.test.ts b/packages/beacon-node/test/e2e/api/impl/beacon/block/endpoint.test.ts index 46b0e10580aa..ee0b17348099 100644 --- a/packages/beacon-node/test/e2e/api/impl/beacon/block/endpoint.test.ts +++ b/packages/beacon-node/test/e2e/api/impl/beacon/block/endpoint.test.ts @@ -4,9 +4,9 @@ import {ApiClient, WireFormat, getClient} from "@lodestar/api"; import { SignedBeaconBlock, SignedBlindedBeaconBlock, - isBlindedExecutionPayload, isBlindedSignedBeaconBlock, isExecutionPayload, + isExecutionPayloadHeader, } from "@lodestar/types"; import {ForkName} from "@lodestar/params"; import {LogLevel, testLogger} from "../../../../../utils/logger.js"; @@ -84,7 +84,7 @@ describe("beacon block api", function () { const blindedBlock = res.value() as SignedBlindedBeaconBlock; expect(isBlindedSignedBeaconBlock(blindedBlock)).toBe(true); - expect(isBlindedExecutionPayload(blindedBlock.message.body.executionPayloadHeader)).toBe(true); + expect(isExecutionPayloadHeader(blindedBlock.message.body.executionPayloadHeader)).toBe(true); expect(blindedBlock.message.body).not.toHaveProperty("executionPayload"); }); diff --git a/packages/types/src/utils/typeguards.ts b/packages/types/src/utils/typeguards.ts index 7848be700b00..f006227e03c9 100644 --- a/packages/types/src/utils/typeguards.ts +++ b/packages/types/src/utils/typeguards.ts @@ -23,7 +23,7 @@ export function isExecutionPayload( return (payload as ExecutionPayload).transactions !== undefined; } -export function isBlindedExecutionPayload( +export function isExecutionPayloadHeader( payload: ExecutionPayload | ExecutionPayloadHeader ): payload is ExecutionPayloadHeader { // we just check transactionsRoot for determining as it the base field From 08884f8f114655249b38adfb00af5f8db786f660 Mon Sep 17 00:00:00 2001 From: harkamal Date: Wed, 24 Jan 2024 17:38:11 +0530 Subject: [PATCH 14/17] feat: placeholder PR for electra add types stub and epoch config fix types --- .../beacon-node/src/chain/blocks/types.ts | 2 +- .../test/spec/presets/fork.test.ts | 2 + .../test/spec/presets/transition.test.ts | 8 + .../upgradeLightClientHeader.test.ts | 33 +++- .../test/unit/network/fork.test.ts | 13 +- packages/beacon-node/test/utils/config.ts | 8 + .../config/src/chainConfig/configs/mainnet.ts | 4 + .../config/src/chainConfig/configs/minimal.ts | 4 + packages/config/src/chainConfig/types.ts | 6 + packages/config/src/forkConfig/index.ts | 10 +- packages/light-client/src/spec/utils.ts | 4 + packages/params/src/forkName.ts | 2 + .../test/unit/upgradeState.test.ts | 8 + packages/types/src/electra/index.ts | 3 + packages/types/src/electra/sszTypes.ts | 148 ++++++++++++++++++ packages/types/src/electra/types.ts | 29 ++++ packages/types/src/sszTypes.ts | 37 ++++- packages/types/src/types.ts | 36 +++++ packages/validator/src/util/params.ts | 5 + 19 files changed, 353 insertions(+), 9 deletions(-) create mode 100644 packages/types/src/electra/index.ts create mode 100644 packages/types/src/electra/sszTypes.ts create mode 100644 packages/types/src/electra/types.ts diff --git a/packages/beacon-node/src/chain/blocks/types.ts b/packages/beacon-node/src/chain/blocks/types.ts index da573bb76334..5eb5eebc7840 100644 --- a/packages/beacon-node/src/chain/blocks/types.ts +++ b/packages/beacon-node/src/chain/blocks/types.ts @@ -36,7 +36,7 @@ export enum GossipedInputType { type BlobsCacheMap = Map; -type ForkBlobsInfo = {fork: ForkName.deneb}; +type ForkBlobsInfo = {fork: ForkName.deneb | ForkName.electra}; type BlobsData = {blobs: deneb.BlobSidecars; blobsBytes: (Uint8Array | null)[]; blobsSource: BlobsSource}; export type BlockInputDataBlobs = ForkBlobsInfo & BlobsData; export type BlockInputData = BlockInputDataBlobs; diff --git a/packages/beacon-node/test/spec/presets/fork.test.ts b/packages/beacon-node/test/spec/presets/fork.test.ts index 228ab6a38935..c880d24bbbe3 100644 --- a/packages/beacon-node/test/spec/presets/fork.test.ts +++ b/packages/beacon-node/test/spec/presets/fork.test.ts @@ -35,6 +35,8 @@ const fork: TestRunnerFn = (forkNext) => { return slotFns.upgradeStateToCapella(preState as CachedBeaconStateBellatrix); case ForkName.deneb: return slotFns.upgradeStateToDeneb(preState as CachedBeaconStateCapella); + case ForkName.electra: + throw Error("not Implemented"); } }, options: { diff --git a/packages/beacon-node/test/spec/presets/transition.test.ts b/packages/beacon-node/test/spec/presets/transition.test.ts index d9925f292677..cae7c667b590 100644 --- a/packages/beacon-node/test/spec/presets/transition.test.ts +++ b/packages/beacon-node/test/spec/presets/transition.test.ts @@ -102,6 +102,14 @@ function getTransitionConfig(fork: ForkName, forkEpoch: number): Partial${toFork}`, function () { + lcHeaderByFork[fromFork].beacon.slot = testSlots[fromFork]; + lcHeaderByFork[toFork].beacon.slot = testSlots[fromFork]; + + expect(() => { + upgradeLightClientHeader(config, toFork, lcHeaderByFork[fromFork]); + }).toThrow("Not Implemented"); + }); + } + + // Since electra is not implemented for loop is till deneb (Object.values(ForkName).length-1) + // Once electra is implemnted run for loop till Object.values(ForkName).length + + // for (let i = ForkSeq.altair; i < Object.values(ForkName).length; i++) { + + for (let i = ForkSeq.altair; i < Object.values(ForkName).length - 1; i++) { for (let j = i; j > 0; j--) { const fromFork = ForkName[ForkSeq[i] as ForkName]; const toFork = ForkName[ForkSeq[j] as ForkName]; diff --git a/packages/beacon-node/test/unit/network/fork.test.ts b/packages/beacon-node/test/unit/network/fork.test.ts index be748d2e8185..bbe1c0870d30 100644 --- a/packages/beacon-node/test/unit/network/fork.test.ts +++ b/packages/beacon-node/test/unit/network/fork.test.ts @@ -9,12 +9,14 @@ function getForkConfig({ bellatrix, capella, deneb, + electra, }: { phase0: number; altair: number; bellatrix: number; capella: number; deneb: number; + electra: number; }): BeaconConfig { const forks: Record = { phase0: { @@ -57,6 +59,14 @@ function getForkConfig({ prevVersion: Buffer.from([0, 0, 0, 3]), prevForkName: ForkName.capella, }, + electra: { + name: ForkName.electra, + seq: ForkSeq.electra, + epoch: electra, + version: Buffer.from([0, 0, 0, 5]), + prevVersion: Buffer.from([0, 0, 0, 4]), + prevForkName: ForkName.deneb, + }, }; const forksAscendingEpochOrder = Object.values(forks); const forksDescendingEpochOrder = Object.values(forks).reverse(); @@ -133,9 +143,10 @@ const testScenarios = [ for (const testScenario of testScenarios) { const {phase0, altair, bellatrix, capella, testCases} = testScenario; const deneb = Infinity; + const electra = Infinity; describe(`network / fork: phase0: ${phase0}, altair: ${altair}, bellatrix: ${bellatrix} capella: ${capella}`, () => { - const forkConfig = getForkConfig({phase0, altair, bellatrix, capella, deneb}); + const forkConfig = getForkConfig({phase0, altair, bellatrix, capella, deneb, electra}); const forks = forkConfig.forks; for (const testCase of testCases) { const {epoch, currentFork, nextFork, activeForks} = testCase; diff --git a/packages/beacon-node/test/utils/config.ts b/packages/beacon-node/test/utils/config.ts index 54c058d30722..2aad1c14c03e 100644 --- a/packages/beacon-node/test/utils/config.ts +++ b/packages/beacon-node/test/utils/config.ts @@ -31,5 +31,13 @@ export function getConfig(fork: ForkName, forkEpoch = 0): ChainForkConfig { CAPELLA_FORK_EPOCH: 0, DENEB_FORK_EPOCH: forkEpoch, }); + case ForkName.electra: + return createChainForkConfig({ + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + CAPELLA_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: forkEpoch, + }); } } diff --git a/packages/config/src/chainConfig/configs/mainnet.ts b/packages/config/src/chainConfig/configs/mainnet.ts index 883688ca821b..0de1bee666ec 100644 --- a/packages/config/src/chainConfig/configs/mainnet.ts +++ b/packages/config/src/chainConfig/configs/mainnet.ts @@ -49,6 +49,10 @@ export const chainConfig: ChainConfig = { DENEB_FORK_VERSION: b("0x04000000"), DENEB_FORK_EPOCH: 269568, // March 13, 2024, 01:55:35pm UTC + // Electra + ELECTRA_FORK_VERSION: b("0x05000000"), + ELECTRA_FORK_EPOCH: Infinity, + // Time parameters // --------------------------------------------------------------- // 12 seconds diff --git a/packages/config/src/chainConfig/configs/minimal.ts b/packages/config/src/chainConfig/configs/minimal.ts index 23cd14e763ec..c99a76d1ee40 100644 --- a/packages/config/src/chainConfig/configs/minimal.ts +++ b/packages/config/src/chainConfig/configs/minimal.ts @@ -46,6 +46,10 @@ export const chainConfig: ChainConfig = { DENEB_FORK_VERSION: b("0x04000001"), DENEB_FORK_EPOCH: Infinity, + // Electra + ELECTRA_FORK_VERSION: b("0x05000001"), + ELECTRA_FORK_EPOCH: Infinity, + // Time parameters // --------------------------------------------------------------- // [customized] Faster for testing purposes diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index 45f05bfaa724..234a08558be5 100644 --- a/packages/config/src/chainConfig/types.ts +++ b/packages/config/src/chainConfig/types.ts @@ -40,6 +40,9 @@ export type ChainConfig = { // DENEB DENEB_FORK_VERSION: Uint8Array; DENEB_FORK_EPOCH: number; + // ELECTRA + ELECTRA_FORK_VERSION: Uint8Array; + ELECTRA_FORK_EPOCH: number; // Time parameters SECONDS_PER_SLOT: number; @@ -99,6 +102,9 @@ export const chainConfigTypes: SpecTypes = { // DENEB DENEB_FORK_VERSION: "bytes", DENEB_FORK_EPOCH: "number", + // ELECTRA + ELECTRA_FORK_VERSION: "bytes", + ELECTRA_FORK_EPOCH: "number", // Time parameters SECONDS_PER_SLOT: "number", diff --git a/packages/config/src/forkConfig/index.ts b/packages/config/src/forkConfig/index.ts index 358c2d752001..c854d87d5eb8 100644 --- a/packages/config/src/forkConfig/index.ts +++ b/packages/config/src/forkConfig/index.ts @@ -59,10 +59,18 @@ export function createForkConfig(config: ChainConfig): ForkConfig { prevVersion: config.CAPELLA_FORK_VERSION, prevForkName: ForkName.capella, }; + const electra: ForkInfo = { + name: ForkName.electra, + seq: ForkSeq.electra, + epoch: config.ELECTRA_FORK_EPOCH, + version: config.ELECTRA_FORK_VERSION, + prevVersion: config.DENEB_FORK_VERSION, + prevForkName: ForkName.deneb, + }; /** Forks in order order of occurence, `phase0` first */ // Note: Downstream code relies on proper ordering. - const forks = {phase0, altair, bellatrix, capella, deneb}; + const forks = {phase0, altair, bellatrix, capella, deneb, electra}; // Prevents allocating an array on every getForkInfo() call const forksAscendingEpochOrder = Object.values(forks); diff --git a/packages/light-client/src/spec/utils.ts b/packages/light-client/src/spec/utils.ts index 65d6f3e84c59..aafd81c9250a 100644 --- a/packages/light-client/src/spec/utils.ts +++ b/packages/light-client/src/spec/utils.ts @@ -112,6 +112,10 @@ export function upgradeLightClientHeader( // Break if no further upgradation is required else fall through if (ForkSeq[targetFork] <= ForkSeq.deneb) break; + + // eslint-disable-next-line no-fallthrough + case ForkName.electra: + throw Error("Not Implemented"); } return upgradedHeader; } diff --git a/packages/params/src/forkName.ts b/packages/params/src/forkName.ts index fa3be24bfae4..d6505bf85dc4 100644 --- a/packages/params/src/forkName.ts +++ b/packages/params/src/forkName.ts @@ -7,6 +7,7 @@ export enum ForkName { bellatrix = "bellatrix", capella = "capella", deneb = "deneb", + electra = "electra", } /** @@ -18,6 +19,7 @@ export enum ForkSeq { bellatrix = 2, capella = 3, deneb = 4, + electra = 5, } export type ForkAll = ForkName; diff --git a/packages/state-transition/test/unit/upgradeState.test.ts b/packages/state-transition/test/unit/upgradeState.test.ts index 2ea8eef182ac..75ba415c1bea 100644 --- a/packages/state-transition/test/unit/upgradeState.test.ts +++ b/packages/state-transition/test/unit/upgradeState.test.ts @@ -55,5 +55,13 @@ function getConfig(fork: ForkName, forkEpoch = 0): ChainForkConfig { CAPELLA_FORK_EPOCH: 0, DENEB_FORK_EPOCH: forkEpoch, }); + case ForkName.electra: + return createChainForkConfig({ + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + CAPELLA_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: forkEpoch, + }); } } diff --git a/packages/types/src/electra/index.ts b/packages/types/src/electra/index.ts new file mode 100644 index 000000000000..7856cd729620 --- /dev/null +++ b/packages/types/src/electra/index.ts @@ -0,0 +1,3 @@ +export * from "./types.js"; +export * as ts from "./types.js"; +export * as ssz from "./sszTypes.js"; diff --git a/packages/types/src/electra/sszTypes.ts b/packages/types/src/electra/sszTypes.ts new file mode 100644 index 000000000000..30690a499845 --- /dev/null +++ b/packages/types/src/electra/sszTypes.ts @@ -0,0 +1,148 @@ +import {ContainerType} from "@chainsafe/ssz"; +import {ssz as primitiveSsz} from "../primitive/index.js"; +import {ssz as denebSsz} from "../deneb/index.js"; + +const {BLSSignature} = primitiveSsz; + +export const ExecutionPayload = new ContainerType( + { + ...denebSsz.ExecutionPayload.fields, + }, + {typeName: "ExecutionPayload", jsonCase: "eth2"} +); + +export const ExecutionPayloadHeader = new ContainerType( + { + ...denebSsz.ExecutionPayloadHeader.fields, + }, + {typeName: "ExecutionPayloadHeader", jsonCase: "eth2"} +); + +export const BeaconBlockBody = new ContainerType( + { + ...denebSsz.BeaconBlockBody.fields, + }, + {typeName: "BeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const BeaconBlock = new ContainerType( + { + ...denebSsz.BeaconBlock.fields, + }, + {typeName: "BeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const SignedBeaconBlock = new ContainerType( + { + message: BeaconBlock, + signature: BLSSignature, + }, + {typeName: "SignedBeaconBlock", jsonCase: "eth2"} +); + +export const BlobSidecar = new ContainerType( + { + ...denebSsz.BlobSidecar.fields, + }, + {typeName: "BlobSidecar", jsonCase: "eth2"} +); + +export const BlindedBeaconBlockBody = new ContainerType( + { + ...denebSsz.BlindedBeaconBlockBody.fields, + }, + {typeName: "BlindedBeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const BlindedBeaconBlock = new ContainerType( + { + ...denebSsz.BlindedBeaconBlock.fields, + }, + {typeName: "BlindedBeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const SignedBlindedBeaconBlock = new ContainerType( + { + message: BlindedBeaconBlock, + signature: BLSSignature, + }, + {typeName: "SignedBlindedBeaconBlock", jsonCase: "eth2"} +); + +export const BuilderBid = new ContainerType( + { + ...denebSsz.BuilderBid.fields, + }, + {typeName: "BuilderBid", jsonCase: "eth2"} +); + +export const SignedBuilderBid = new ContainerType( + { + message: BuilderBid, + signature: BLSSignature, + }, + {typeName: "SignedBuilderBid", jsonCase: "eth2"} +); + +export const ExecutionPayloadAndBlobsBundle = new ContainerType( + { + ...denebSsz.ExecutionPayloadAndBlobsBundle.fields, + }, + {typeName: "ExecutionPayloadAndBlobsBundle", jsonCase: "eth2"} +); + +export const BeaconState = new ContainerType( + { + ...denebSsz.BeaconState.fields, + }, + {typeName: "BeaconState", jsonCase: "eth2"} +); + +export const LightClientHeader = new ContainerType( + { + ...denebSsz.LightClientHeader.fields, + }, + {typeName: "LightClientHeader", jsonCase: "eth2"} +); + +export const LightClientBootstrap = new ContainerType( + { + ...denebSsz.LightClientBootstrap.fields, + }, + {typeName: "LightClientBootstrap", jsonCase: "eth2"} +); + +export const LightClientUpdate = new ContainerType( + { + ...denebSsz.LightClientUpdate.fields, + }, + {typeName: "LightClientUpdate", jsonCase: "eth2"} +); + +export const LightClientFinalityUpdate = new ContainerType( + { + ...denebSsz.LightClientFinalityUpdate.fields, + }, + {typeName: "LightClientFinalityUpdate", jsonCase: "eth2"} +); + +export const LightClientOptimisticUpdate = new ContainerType( + { + ...denebSsz.LightClientOptimisticUpdate.fields, + }, + {typeName: "LightClientOptimisticUpdate", jsonCase: "eth2"} +); + +export const LightClientStore = new ContainerType( + { + ...denebSsz.LightClientStore.fields, + }, + {typeName: "LightClientStore", jsonCase: "eth2"} +); + +export const SSEPayloadAttributes = new ContainerType( + { + ...denebSsz.SSEPayloadAttributes.fields, + }, + {typeName: "SSEPayloadAttributes", jsonCase: "eth2"} +); diff --git a/packages/types/src/electra/types.ts b/packages/types/src/electra/types.ts new file mode 100644 index 000000000000..198259eed1dd --- /dev/null +++ b/packages/types/src/electra/types.ts @@ -0,0 +1,29 @@ +import {ValueOf} from "@chainsafe/ssz"; +import * as ssz from "./sszTypes.js"; + +export type BlobSidecar = ValueOf; +export type ExecutionPayloadAndBlobsBundle = ValueOf; + +export type ExecutionPayload = ValueOf; +export type ExecutionPayloadHeader = ValueOf; + +export type BeaconBlockBody = ValueOf; +export type BeaconBlock = ValueOf; +export type SignedBeaconBlock = ValueOf; + +export type BeaconState = ValueOf; + +export type BlindedBeaconBlockBody = ValueOf; +export type BlindedBeaconBlock = ValueOf; +export type SignedBlindedBeaconBlock = ValueOf; + +export type BuilderBid = ValueOf; +export type SignedBuilderBid = ValueOf; +export type SSEPayloadAttributes = ValueOf; + +export type LightClientHeader = ValueOf; +export type LightClientBootstrap = ValueOf; +export type LightClientUpdate = ValueOf; +export type LightClientFinalityUpdate = ValueOf; +export type LightClientOptimisticUpdate = ValueOf; +export type LightClientStore = ValueOf; diff --git a/packages/types/src/sszTypes.ts b/packages/types/src/sszTypes.ts index eae1bbcdeaba..fcfb951111e9 100644 --- a/packages/types/src/sszTypes.ts +++ b/packages/types/src/sszTypes.ts @@ -5,9 +5,10 @@ import {ssz as altair} from "./altair/index.js"; import {ssz as bellatrix} from "./bellatrix/index.js"; import {ssz as capella} from "./capella/index.js"; import {ssz as deneb} from "./deneb/index.js"; +import {ssz as electra} from "./electra/index.js"; export * from "./primitive/sszTypes.js"; -export {phase0, altair, bellatrix, capella, deneb}; +export {phase0, altair, bellatrix, capella, deneb, electra}; /** * Index the ssz types that differ by fork @@ -98,14 +99,42 @@ const typesByFork = { SSEPayloadAttributes: deneb.SSEPayloadAttributes, ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle, }, + [ForkName.electra]: { + BeaconBlock: electra.BeaconBlock, + BeaconBlockBody: electra.BeaconBlockBody, + BeaconState: electra.BeaconState, + SignedBeaconBlock: electra.SignedBeaconBlock, + Metadata: altair.Metadata, + LightClientHeader: electra.LightClientHeader, + LightClientBootstrap: electra.LightClientBootstrap, + LightClientUpdate: electra.LightClientUpdate, + LightClientFinalityUpdate: electra.LightClientFinalityUpdate, + LightClientOptimisticUpdate: electra.LightClientOptimisticUpdate, + LightClientStore: electra.LightClientStore, + BlindedBeaconBlock: electra.BlindedBeaconBlock, + BlindedBeaconBlockBody: electra.BlindedBeaconBlockBody, + SignedBlindedBeaconBlock: electra.SignedBlindedBeaconBlock, + ExecutionPayload: electra.ExecutionPayload, + ExecutionPayloadHeader: electra.ExecutionPayloadHeader, + BuilderBid: electra.BuilderBid, + SignedBuilderBid: electra.SignedBuilderBid, + SSEPayloadAttributes: electra.SSEPayloadAttributes, + ExecutionPayloadAndBlobsBundle: electra.ExecutionPayloadAndBlobsBundle, + }, }; const pick = , K extends keyof T>(obj: T, ...keys: K[]): Pick => Object.fromEntries(keys.filter((key) => key in obj).map((key) => [key, obj[key]])) as Pick; -const executionForks: ForkExecution[] = [ForkName.bellatrix, ForkName.capella, ForkName.deneb]; -const lightCLientForks: ForkLightClient[] = [ForkName.altair, ForkName.bellatrix, ForkName.capella, ForkName.deneb]; -const blobsForks: ForkBlobs[] = [ForkName.deneb]; +const executionForks: ForkExecution[] = [ForkName.bellatrix, ForkName.capella, ForkName.deneb, ForkName.electra]; +const lightCLientForks: ForkLightClient[] = [ + ForkName.altair, + ForkName.bellatrix, + ForkName.capella, + ForkName.deneb, + ForkName.electra, +]; +const blobsForks: ForkBlobs[] = [ForkName.deneb, ForkName.electra]; export const allForksExecution = pick(typesByFork, ...executionForks); export const allForksLightClient = pick(typesByFork, ...lightCLientForks); diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 46641d55667e..58bf373c3ff8 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -4,6 +4,7 @@ import {ts as altair} from "./altair/index.js"; import {ts as bellatrix} from "./bellatrix/index.js"; import {ts as capella} from "./capella/index.js"; import {ts as deneb} from "./deneb/index.js"; +import {ts as electra} from "./electra/index.js"; import {Slot} from "./primitive/types.js"; export * from "./primitive/types.js"; @@ -12,6 +13,7 @@ export {ts as altair} from "./altair/index.js"; export {ts as bellatrix} from "./bellatrix/index.js"; export {ts as capella} from "./capella/index.js"; export {ts as deneb} from "./deneb/index.js"; +export {ts as electra} from "./electra/index.js"; /** Common non-spec type to represent roots as strings */ export type RootHex = string; @@ -136,6 +138,40 @@ type TypesByFork = { SyncCommittee: altair.SyncCommittee; SyncAggregate: altair.SyncAggregate; }; + [ForkName.electra]: { + BeaconBlockHeader: phase0.BeaconBlockHeader; + SignedBeaconBlockHeader: phase0.SignedBeaconBlockHeader; + BeaconBlock: electra.BeaconBlock; + BeaconBlockBody: electra.BeaconBlockBody; + BeaconState: electra.BeaconState; + SignedBeaconBlock: electra.SignedBeaconBlock; + Metadata: altair.Metadata; + LightClientHeader: electra.LightClientHeader; + LightClientBootstrap: electra.LightClientBootstrap; + LightClientUpdate: electra.LightClientUpdate; + LightClientFinalityUpdate: electra.LightClientFinalityUpdate; + LightClientOptimisticUpdate: electra.LightClientOptimisticUpdate; + LightClientStore: electra.LightClientStore; + BlindedBeaconBlock: electra.BlindedBeaconBlock; + BlindedBeaconBlockBody: electra.BlindedBeaconBlockBody; + SignedBlindedBeaconBlock: electra.SignedBlindedBeaconBlock; + ExecutionPayload: electra.ExecutionPayload; + ExecutionPayloadHeader: electra.ExecutionPayloadHeader; + BuilderBid: electra.BuilderBid; + SignedBuilderBid: electra.SignedBuilderBid; + SSEPayloadAttributes: electra.SSEPayloadAttributes; + BlockContents: {block: BeaconBlock; kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs}; + SignedBlockContents: { + signedBlock: SignedBeaconBlock; + kzgProofs: deneb.KZGProofs; + blobs: deneb.Blobs; + }; + ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle; + BlobsBundle: deneb.BlobsBundle; + Contents: deneb.Contents; + SyncCommittee: altair.SyncCommittee; + SyncAggregate: altair.SyncAggregate; + }; }; export type TypesFor = K extends void diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 8ccaf9fe75ba..0afede39b951 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -73,6 +73,7 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record Date: Wed, 24 Jan 2024 18:40:25 +0530 Subject: [PATCH 15/17] feat: implement peerDAS on electra add some presets add further params and types add data column to types repo and network move to max request data columns to preset add the datacolumns data in blockinput and fix breaking errors in seen gossip blockinput handle data columns in gossip and the seengossip further propagate forkaware blockdata and resolve build/type issues further handle datacolumns sync by range by root and forkaware data handling fix issues chore: update c-kzg to peerDas version feat: add peerDas ckzg functions to interface fix the lookups handle the publishing flow various sync try fixes fixes compute blob side car various misl debuggings and fixes debug and apply fixes and get range and by root sync to work will full custody enable syncing with lower custody requirement use node peerid rather than a dummy string get and use the nodeid from enr and correctly compute subnets and column indexes filterout and connect to peers only matching out custody requiremnt try adding custody requirement add protection for subnet calc get the sync working with devnet 0 correctly set the enr with custody subnet info rebase fixes small refactor --- packages/beacon-node/package.json | 2 +- .../src/api/impl/beacon/blocks/index.ts | 49 ++- .../src/chain/blocks/importBlock.ts | 31 +- .../beacon-node/src/chain/blocks/types.ts | 54 ++- .../blocks/verifyBlocksDataAvailability.ts | 47 ++- .../src/chain/blocks/writeBlockInputToDb.ts | 76 ++++- packages/beacon-node/src/chain/chain.ts | 8 +- .../chain/errors/dataColumnSidecarError.ts | 17 + .../beacon-node/src/chain/errors/index.ts | 1 + .../chain/seenCache/seenGossipBlockInput.ts | 320 ++++++++++++++---- .../src/chain/validation/dataColumnSidecar.ts | 66 ++++ packages/beacon-node/src/db/beacon.ts | 6 + packages/beacon-node/src/db/buckets.ts | 3 + packages/beacon-node/src/db/interface.ts | 4 + .../src/db/repositories/dataColumnSidecars.ts | 50 +++ .../repositories/dataColumnSidecarsArchive.ts | 28 ++ .../beacon-node/src/db/repositories/index.ts | 2 + packages/beacon-node/src/network/events.ts | 4 +- .../src/network/gossip/interface.ts | 5 + .../beacon-node/src/network/gossip/topic.ts | 20 ++ packages/beacon-node/src/network/interface.ts | 15 + packages/beacon-node/src/network/metadata.ts | 1 + packages/beacon-node/src/network/network.ts | 58 +++- .../beacon-node/src/network/peers/discover.ts | 56 ++- .../src/network/peers/peerManager.ts | 17 +- .../src/network/peers/peersData.ts | 3 + .../network/processor/extractSlotRootFns.ts | 9 + .../src/network/processor/gossipHandlers.ts | 130 ++++++- .../network/processor/gossipQueues/index.ts | 5 + .../src/network/processor/index.ts | 8 +- .../src/network/reqresp/ReqRespBeaconNode.ts | 7 + .../reqresp/beaconBlocksMaybeBlobsByRange.ts | 159 ++++++++- .../reqresp/beaconBlocksMaybeBlobsByRoot.ts | 221 +++++++++--- .../reqresp/handlers/beaconBlocksByRange.ts | 2 +- .../handlers/dataColumnSidecarsByRange.ts | 125 +++++++ .../handlers/dataColumnSidecarsByRoot.ts | 90 +++++ .../src/network/reqresp/handlers/index.ts | 11 + .../src/network/reqresp/protocols.ts | 12 + .../src/network/reqresp/rateLimit.ts | 12 + .../beacon-node/src/network/reqresp/types.ts | 16 +- packages/beacon-node/src/node/nodejs.ts | 5 + packages/beacon-node/src/sync/range/chain.ts | 21 +- packages/beacon-node/src/sync/unknownBlock.ts | 17 +- packages/beacon-node/src/util/blobs.ts | 49 ++- packages/beacon-node/src/util/dataColumns.ts | 74 ++++ packages/beacon-node/src/util/kzg.ts | 18 + packages/beacon-node/src/util/sszBytes.ts | 20 ++ .../db/api/repositories/dataColumn.test.ts | 103 ++++++ .../test/unit/network/gossip/topic.test.ts | 6 + .../test/unit/util/dataColumn.test.ts | 14 + .../beacon-node/test/utils/node/beacon.ts | 7 +- packages/cli/src/cmds/beacon/handler.ts | 8 +- .../cli/src/cmds/beacon/initPeerIdAndEnr.ts | 21 +- packages/cli/src/cmds/bootnode/handler.ts | 2 +- .../config/src/chainConfig/configs/mainnet.ts | 4 + .../config/src/chainConfig/configs/minimal.ts | 4 + packages/config/src/chainConfig/types.ts | 6 + packages/params/src/forkName.ts | 6 + packages/params/src/index.ts | 15 + packages/params/src/presets/mainnet.ts | 8 + packages/params/src/presets/minimal.ts | 8 + packages/params/src/types.ts | 16 + packages/types/src/electra/sszTypes.ts | 58 +++- packages/types/src/electra/types.ts | 12 +- packages/types/src/primitive/sszTypes.ts | 1 + packages/types/src/primitive/types.ts | 1 + packages/validator/src/util/params.ts | 12 + yarn.lock | 39 +-- 68 files changed, 2048 insertions(+), 257 deletions(-) create mode 100644 packages/beacon-node/src/chain/errors/dataColumnSidecarError.ts create mode 100644 packages/beacon-node/src/chain/validation/dataColumnSidecar.ts create mode 100644 packages/beacon-node/src/db/repositories/dataColumnSidecars.ts create mode 100644 packages/beacon-node/src/db/repositories/dataColumnSidecarsArchive.ts create mode 100644 packages/beacon-node/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts create mode 100644 packages/beacon-node/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts create mode 100644 packages/beacon-node/src/util/dataColumns.ts create mode 100644 packages/beacon-node/test/unit/db/api/repositories/dataColumn.test.ts create mode 100644 packages/beacon-node/test/unit/util/dataColumn.test.ts diff --git a/packages/beacon-node/package.json b/packages/beacon-node/package.json index ee17ff0c0c08..c6202f3178f9 100644 --- a/packages/beacon-node/package.json +++ b/packages/beacon-node/package.json @@ -133,7 +133,7 @@ "@lodestar/utils": "^1.19.0", "@lodestar/validator": "^1.19.0", "@multiformats/multiaddr": "^12.1.3", - "c-kzg": "^2.1.2", + "c-kzg": "matthewkeil/c-kzg-4844#67bf9367817f0fa5ebd390aeb8c3ae88bdbc170e", "datastore-core": "^9.1.1", "datastore-level": "^10.1.1", "deepmerge": "^4.3.1", diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts index 177f58aebb95..e0d59836e17f 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -6,9 +6,10 @@ import { reconstructFullBlockOrContents, signedBeaconBlockToBlinded, } from "@lodestar/state-transition"; -import {ForkExecution, SLOTS_PER_HISTORICAL_ROOT, isForkExecution} from "@lodestar/params"; +import {ForkExecution, SLOTS_PER_HISTORICAL_ROOT, isForkExecution, ForkName} from "@lodestar/params"; import {sleep, fromHex, toHex} from "@lodestar/utils"; import { + electra, deneb, isSignedBlockContents, ProducedBlockSource, @@ -23,10 +24,13 @@ import { BlockInput, BlobsSource, BlockInputDataBlobs, + BlockInputDataDataColumns, + DataColumnsSource, + BlockInputData, } from "../../../../chain/blocks/types.js"; import {promiseAllMaybeAsync} from "../../../../util/promises.js"; import {isOptimisticBlock} from "../../../../util/forkChoice.js"; -import {computeBlobSidecars} from "../../../../util/blobs.js"; +import {computeBlobSidecars, computeDataColumnSidecars} from "../../../../util/blobs.js"; import {BlockError, BlockErrorCode, BlockGossipError} from "../../../../chain/errors/index.js"; import {OpSource} from "../../../../metrics/validatorMonitor.js"; import {NetworkEvent} from "../../../../network/index.js"; @@ -65,17 +69,40 @@ export function getBeaconBlockApi({ opts: PublishBlockOpts = {} ) => { const seenTimestampSec = Date.now() / 1000; - let blockForImport: BlockInput, signedBlock: SignedBeaconBlock, blobSidecars: deneb.BlobSidecars; + let blockForImport: BlockInput, + signedBlock: SignedBeaconBlock, + blobSidecars: deneb.BlobSidecars, + dataColumnSidecars: electra.DataColumnSidecars; if (isSignedBlockContents(signedBlockOrContents)) { ({signedBlock} = signedBlockOrContents); - blobSidecars = computeBlobSidecars(config, signedBlock, signedBlockOrContents); - const blockData = { - fork: config.getForkName(signedBlock.message.slot), - blobs: blobSidecars, - blobsSource: BlobsSource.api, - blobsBytes: blobSidecars.map(() => null), - } as BlockInputDataBlobs; + const fork = config.getForkName(signedBlock.message.slot); + let blockData: BlockInputData; + if (fork === ForkName.electra) { + dataColumnSidecars = computeDataColumnSidecars(config, signedBlock, signedBlockOrContents); + blockData = { + fork, + dataColumnsLen: dataColumnSidecars.length, + // custodyColumns is a 1 based index of ith column present in dataColumns[custodyColumns[i-1]] + dataColumnsIndex: new Uint8Array(Array.from({length: dataColumnSidecars.length}, (_, j) => 1 + j)), + dataColumns: dataColumnSidecars, + dataColumnsBytes: dataColumnSidecars.map(() => null), + dataColumnsSource: DataColumnsSource.api, + } as BlockInputDataDataColumns; + blobSidecars = []; + } else if (fork === ForkName.deneb) { + blobSidecars = computeBlobSidecars(config, signedBlock, signedBlockOrContents); + blockData = { + fork, + blobs: blobSidecars, + blobsSource: BlobsSource.api, + blobsBytes: blobSidecars.map(() => null), + } as BlockInputDataBlobs; + dataColumnSidecars = []; + } else { + throw Error(`Invalid data fork=${fork} for publish`); + } + blockForImport = getBlockInput.availableData( config, signedBlock, @@ -87,6 +114,7 @@ export function getBeaconBlockApi({ } else { signedBlock = signedBlockOrContents; blobSidecars = []; + dataColumnSidecars = []; blockForImport = getBlockInput.preData(config, signedBlock, BlockSource.api, context?.sszBytes ?? null); } @@ -221,6 +249,7 @@ export function getBeaconBlockApi({ // b) they might require more hops to reach recipients in peerDAS kind of setup where // blobs might need to hop between nodes because of partial subnet subscription ...blobSidecars.map((blobSidecar) => () => network.publishBlobSidecar(blobSidecar)), + ...dataColumnSidecars.map((dataColumnSidecar) => () => network.publishDataColumnSidecar(dataColumnSidecar)), () => network.publishBeaconBlock(signedBlock) as Promise, () => // there is no rush to persist block since we published it to gossip anyway diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index e67b6d1e9dbc..1c1386c3dfed 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -1,6 +1,6 @@ import {toHexString} from "@chainsafe/ssz"; import {capella, ssz, altair, BeaconBlock} from "@lodestar/types"; -import {ForkLightClient, ForkSeq, INTERVALS_PER_SLOT, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params"; +import {ForkName, ForkLightClient, ForkSeq, INTERVALS_PER_SLOT, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params"; import { CachedBeaconStateAltair, computeEpochAtSlot, @@ -113,18 +113,23 @@ export async function importBlock( // out of data range blocks and import then in forkchoice although one would not be able to // attest and propose with such head similar to optimistic sync if (blockInput.type === BlockInputType.availableData) { - const {blobsSource, blobs} = blockInput.blockData; - - this.metrics?.importBlock.blobsBySource.inc({blobsSource}); - for (const blobSidecar of blobs) { - const {index, kzgCommitment} = blobSidecar; - this.emitter.emit(routes.events.EventType.blobSidecar, { - blockRoot: blockRootHex, - slot: blockSlot, - index, - kzgCommitment: toHexString(kzgCommitment), - versionedHash: toHexString(kzgCommitmentToVersionedHash(kzgCommitment)), - }); + const {blockData} = blockInput; + if (blockData.fork === ForkName.deneb) { + const {blobsSource, blobs} = blockData; + + this.metrics?.importBlock.blobsBySource.inc({blobsSource}); + for (const blobSidecar of blobs) { + const {index, kzgCommitment} = blobSidecar; + this.emitter.emit(routes.events.EventType.blobSidecar, { + blockRoot: blockRootHex, + slot: blockSlot, + index, + kzgCommitment: toHexString(kzgCommitment), + versionedHash: toHexString(kzgCommitmentToVersionedHash(kzgCommitment)), + }); + } + } else if (blockData.fork === ForkName.electra) { + // TODO peerDAS build and emit the event for the datacolumns } } }); diff --git a/packages/beacon-node/src/chain/blocks/types.ts b/packages/beacon-node/src/chain/blocks/types.ts index 5eb5eebc7840..9166bfbd59fb 100644 --- a/packages/beacon-node/src/chain/blocks/types.ts +++ b/packages/beacon-node/src/chain/blocks/types.ts @@ -1,6 +1,6 @@ import {CachedBeaconStateAllForks, computeEpochAtSlot} from "@lodestar/state-transition"; import {MaybeValidExecutionStatus, DataAvailabilityStatus} from "@lodestar/fork-choice"; -import {deneb, Slot, RootHex, SignedBeaconBlock} from "@lodestar/types"; +import {deneb, Slot, RootHex, SignedBeaconBlock, electra, ColumnIndex} from "@lodestar/types"; import {ForkSeq, ForkName} from "@lodestar/params"; import {ChainForkConfig} from "@lodestar/config"; @@ -29,23 +29,45 @@ export enum BlobsSource { byRoot = "req_resp_by_root", } +export enum DataColumnsSource { + gossip = "gossip", + api = "api", + byRange = "req_resp_by_range", + byRoot = "req_resp_by_root", +} + export enum GossipedInputType { block = "block", blob = "blob", + dataColumn = "dataColumn", } -type BlobsCacheMap = Map; +export type BlobsCacheMap = Map; +export type DataColumnsCacheMap = Map< + number, + {dataColumnSidecar: electra.DataColumnSidecar; dataColumnBytes: Uint8Array | null} +>; type ForkBlobsInfo = {fork: ForkName.deneb | ForkName.electra}; type BlobsData = {blobs: deneb.BlobSidecars; blobsBytes: (Uint8Array | null)[]; blobsSource: BlobsSource}; export type BlockInputDataBlobs = ForkBlobsInfo & BlobsData; -export type BlockInputData = BlockInputDataBlobs; -export type BlockInputBlobs = {blobs: deneb.BlobSidecars; blobsBytes: (Uint8Array | null)[]; blobsSource: BlobsSource}; -type Availability = {availabilityPromise: Promise; resolveAvailability: (data: T) => void}; +type ForkDataColumnsInfo = {fork: ForkName.electra}; +type DataColumnsData = { + // marker of that columns are to be custodied + dataColumnsLen: number; + dataColumnsIndex: Uint8Array; + dataColumns: electra.DataColumnSidecars; + dataColumnsBytes: (Uint8Array | null)[]; + dataColumnsSource: DataColumnsSource; +}; +export type BlockInputDataDataColumns = ForkDataColumnsInfo & DataColumnsData; +export type BlockInputData = BlockInputDataBlobs | BlockInputDataDataColumns; +type Availability = {availabilityPromise: Promise; resolveAvailability: (data: T) => void}; type CachedBlobs = {blobsCache: BlobsCacheMap} & Availability; -export type CachedData = ForkBlobsInfo & CachedBlobs; +type CachedDataColumns = {dataColumnsCache: DataColumnsCacheMap} & Availability; +export type CachedData = (ForkBlobsInfo & CachedBlobs) | (ForkDataColumnsInfo & CachedDataColumns); export type BlockInput = {block: SignedBeaconBlock; source: BlockSource; blockBytes: Uint8Array | null} & ( | {type: BlockInputType.preData | BlockInputType.outOfRangeData} @@ -161,6 +183,26 @@ export function getBlockInputBlobs(blobsCache: BlobsCacheMap): Omit { + const dataColumns = []; + const dataColumnsBytes = []; + + for (const index of columnIndexes) { + const dataColumnCache = dataColumnsCache.get(index); + if (dataColumnCache === undefined) { + // check if the index is correct as per the custody columns + throw Error(`Missing dataColumnCache at index=${index}`); + } + const {dataColumnSidecar, dataColumnBytes} = dataColumnCache; + dataColumns.push(dataColumnSidecar); + dataColumnsBytes.push(dataColumnBytes); + } + return {dataColumns, dataColumnsBytes}; +} + export enum AttestationImportOpt { Skip, Force, diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksDataAvailability.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksDataAvailability.ts index 8393c91063de..9147ecbd82d1 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksDataAvailability.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksDataAvailability.ts @@ -3,10 +3,19 @@ import {DataAvailabilityStatus} from "@lodestar/fork-choice"; import {ChainForkConfig} from "@lodestar/config"; import {deneb, UintNum64} from "@lodestar/types"; import {Logger} from "@lodestar/utils"; +import {ForkName} from "@lodestar/params"; import {BlockError, BlockErrorCode} from "../errors/index.js"; import {validateBlobSidecars} from "../validation/blobSidecar.js"; +import {validateDataColumnsSidecars} from "../validation/dataColumnSidecar.js"; import {Metrics} from "../../metrics/metrics.js"; -import {BlockInput, BlockInputType, ImportBlockOpts, BlobSidecarValidation, getBlockInput} from "./types.js"; +import { + BlockInput, + BlockInputType, + ImportBlockOpts, + BlobSidecarValidation, + getBlockInput, + BlockInputData, +} from "./types.js"; // we can now wait for full 12 seconds because unavailable block sync will try pulling // the blobs from the network anyway after 500ms of seeing the block @@ -88,27 +97,37 @@ async function maybeValidateBlobs( // run full validation const {block} = blockInput; const blockSlot = block.message.slot; - - const blobsData = - blockInput.type === BlockInputType.availableData - ? blockInput.blockData - : await raceWithCutoff(chain, blockInput, blockInput.cachedData.availabilityPromise); - const {blobs} = blobsData; - const {blobKzgCommitments} = (block as deneb.SignedBeaconBlock).message.body; const beaconBlockRoot = chain.config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(block.message); - - // if the blob siddecars have been individually verified then we can skip kzg proof check - // but other checks to match blobs with block data still need to be performed - const skipProofsCheck = opts.validBlobSidecars === BlobSidecarValidation.Individual; - validateBlobSidecars(blockSlot, beaconBlockRoot, blobKzgCommitments, blobs, {skipProofsCheck}); + const blockData = + blockInput.type === BlockInputType.availableData + ? blockInput.blockData + : await raceWithCutoff( + chain, + blockInput, + blockInput.cachedData.availabilityPromise as Promise + ); + + if (blockData.fork === ForkName.deneb) { + const {blobs} = blockData; + + // if the blob siddecars have been individually verified then we can skip kzg proof check + // but other checks to match blobs with block data still need to be performed + const skipProofsCheck = opts.validBlobSidecars === BlobSidecarValidation.Individual; + validateBlobSidecars(blockSlot, beaconBlockRoot, blobKzgCommitments, blobs, {skipProofsCheck}); + } else if (blockData.fork === ForkName.electra) { + const {dataColumns} = blockData; + const skipProofsCheck = opts.validBlobSidecars === BlobSidecarValidation.Individual; + // might require numColumns, custodyColumns from blockData as input to below + validateDataColumnsSidecars(blockSlot, beaconBlockRoot, blobKzgCommitments, dataColumns, {skipProofsCheck}); + } const availableBlockInput = getBlockInput.availableData( chain.config, blockInput.block, blockInput.source, blockInput.blockBytes, - blobsData + blockData ); return {dataAvailabilityStatus: DataAvailabilityStatus.Available, availableBlockInput: availableBlockInput}; } diff --git a/packages/beacon-node/src/chain/blocks/writeBlockInputToDb.ts b/packages/beacon-node/src/chain/blocks/writeBlockInputToDb.ts index b0f5ab159591..8c6c4cb4af26 100644 --- a/packages/beacon-node/src/chain/blocks/writeBlockInputToDb.ts +++ b/packages/beacon-node/src/chain/blocks/writeBlockInputToDb.ts @@ -1,4 +1,6 @@ +import {ForkName} from "@lodestar/params"; import {toHex} from "@lodestar/utils"; +import {electra, ssz} from "@lodestar/types"; import {BeaconChain} from "../chain.js"; import {BlockInput, BlockInputType} from "./types.js"; @@ -30,19 +32,44 @@ export async function writeBlockInputToDb(this: BeaconChain, blocksInput: BlockI }); if (blockInput.type === BlockInputType.availableData || blockInput.type === BlockInputType.dataPromise) { - const blobSidecars = - blockInput.type == BlockInputType.availableData - ? blockInput.blockData.blobs - : // At this point of import blobs are available and can be safely awaited - (await blockInput.cachedData.availabilityPromise).blobs; + const blockData = + blockInput.type === BlockInputType.availableData + ? blockInput.blockData + : await blockInput.cachedData.availabilityPromise; - // NOTE: Old blobs are pruned on archive - fnPromises.push(this.db.blobSidecars.add({blockRoot, slot: block.message.slot, blobSidecars})); - this.logger.debug("Persisted blobSidecars to hot DB", { - blobsLen: blobSidecars.length, - slot: block.message.slot, - root: blockRootHex, - }); + // NOTE: Old data is pruned on archive + if (blockData.fork === ForkName.deneb) { + const blobSidecars = blockData.blobs; + fnPromises.push(this.db.blobSidecars.add({blockRoot, slot: block.message.slot, blobSidecars})); + this.logger.debug("Persisted blobSidecars to hot DB", { + blobsLen: blobSidecars.length, + slot: block.message.slot, + root: blockRootHex, + }); + } else { + const {dataColumnsLen, dataColumnsIndex, dataColumns: dataColumnSidecars} = blockData; + const blobsLen = (block.message as electra.BeaconBlock).body.blobKzgCommitments.length; + + const dataColumnsSize = + ssz.electra.DataColumnSidecar.minSize + + blobsLen * (ssz.electra.Cell.fixedSize + ssz.deneb.KZGCommitment.fixedSize + ssz.deneb.KZGProof.fixedSize); + const slot = block.message.slot; + const writeData = { + blockRoot, + slot, + dataColumnsLen, + dataColumnsSize, + dataColumnsIndex, + dataColumnSidecars, + }; + fnPromises.push(this.db.dataColumnSidecars.add(writeData)); + + this.logger.debug("Persisted dataColumnSidecars to hot DB", { + dataColumnsLen: dataColumnSidecars.length, + slot: block.message.slot, + root: blockRootHex, + }); + } } } @@ -55,17 +82,35 @@ export async function writeBlockInputToDb(this: BeaconChain, blocksInput: BlockI export async function removeEagerlyPersistedBlockInputs(this: BeaconChain, blockInputs: BlockInput[]): Promise { const blockToRemove = []; const blobsToRemove = []; + const dataColumnsToRemove = []; for (const blockInput of blockInputs) { const {block, type} = blockInput; - const blockRoot = this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message); + const slot = block.message.slot; + const blockRoot = this.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message); const blockRootHex = toHex(blockRoot); if (!this.forkChoice.hasBlockHex(blockRootHex)) { blockToRemove.push(block); if (type === BlockInputType.availableData) { - const blobSidecars = blockInput.blockData.blobs; - blobsToRemove.push({blockRoot, slot: block.message.slot, blobSidecars}); + const {blockData} = blockInput; + if (blockData.fork === ForkName.deneb) { + const blobSidecars = blockData.blobs; + blobsToRemove.push({blockRoot, slot, blobSidecars}); + } else { + const {dataColumnsLen, dataColumnsIndex, dataColumns: dataColumnSidecars} = blockData; + const blobsLen = (block.message as electra.BeaconBlock).body.blobKzgCommitments.length; + const dataColumnsSize = ssz.electra.Cell.fixedSize * blobsLen; + + dataColumnsToRemove.push({ + blockRoot, + slot, + dataColumnsLen, + dataColumnsSize, + dataColumnsIndex, + dataColumnSidecars, + }); + } } } } @@ -74,5 +119,6 @@ export async function removeEagerlyPersistedBlockInputs(this: BeaconChain, block // TODO: Batch DB operations not with Promise.all but with level db ops this.db.block.batchRemove(blockToRemove), this.db.blobSidecars.batchRemove(blobsToRemove), + this.db.dataColumnSidecars.batchRemove(dataColumnsToRemove), ]); } diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index a6912d952b68..3e323ac5d0a5 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -47,6 +47,8 @@ import {Clock, ClockEvent, IClock} from "../util/clock.js"; import {ensureDir, writeIfNotExist} from "../util/file.js"; import {isOptimisticBlock} from "../util/forkChoice.js"; import {BufferPool} from "../util/bufferPool.js"; +import {NodeId} from "../network/subnets/interface.js"; +import {getCustodyConfig} from "../util/dataColumns.js"; import {BlockProcessor, ImportBlockOpts} from "./blocks/index.js"; import {ChainEventEmitter, ChainEvent} from "./emitter.js"; import { @@ -144,7 +146,7 @@ export class BeaconChain implements IBeaconChain { readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages(); readonly seenContributionAndProof: SeenContributionAndProof; readonly seenAttestationDatas: SeenAttestationDatas; - readonly seenGossipBlockInput = new SeenGossipBlockInput(); + readonly seenGossipBlockInput: SeenGossipBlockInput; // Seen cache for liveness checks readonly seenBlockAttesters = new SeenBlockAttesters(); @@ -175,6 +177,7 @@ export class BeaconChain implements IBeaconChain { constructor( opts: IChainOptions, { + nodeId, config, db, logger, @@ -186,6 +189,7 @@ export class BeaconChain implements IBeaconChain { executionEngine, executionBuilder, }: { + nodeId: NodeId; config: BeaconConfig; db: IBeaconDb; logger: Logger; @@ -231,6 +235,8 @@ export class BeaconChain implements IBeaconChain { this.seenAggregatedAttestations = new SeenAggregatedAttestations(metrics); this.seenContributionAndProof = new SeenContributionAndProof(metrics); this.seenAttestationDatas = new SeenAttestationDatas(metrics, this.opts?.attDataCacheSlotDistance); + const custodyConfig = getCustodyConfig(nodeId, config); + this.seenGossipBlockInput = new SeenGossipBlockInput(custodyConfig); this.beaconProposerCache = new BeaconProposerCache(opts); this.checkpointBalancesCache = new CheckpointBalancesCache(); diff --git a/packages/beacon-node/src/chain/errors/dataColumnSidecarError.ts b/packages/beacon-node/src/chain/errors/dataColumnSidecarError.ts new file mode 100644 index 000000000000..cc3d27e4652c --- /dev/null +++ b/packages/beacon-node/src/chain/errors/dataColumnSidecarError.ts @@ -0,0 +1,17 @@ +import {Slot, RootHex} from "@lodestar/types"; +import {GossipActionError} from "./gossipValidation.js"; + +export enum DataColumnSidecarErrorCode { + INVALID_INDEX = "DATA_COLUMN_SIDECAR_ERROR_INVALID_INDEX", + + // following errors are adapted from the block errors + FUTURE_SLOT = "DATA_COLUMN_SIDECAR_ERROR_FUTURE_SLOT", + PARENT_UNKNOWN = "DATA_COLUMN_SIDECAR_ERROR_PARENT_UNKNOWN", +} + +export type DataColumnSidecarErrorType = + | {code: DataColumnSidecarErrorCode.INVALID_INDEX; columnIndex: number; gossipIndex: number} + | {code: DataColumnSidecarErrorCode.FUTURE_SLOT; blockSlot: Slot; currentSlot: Slot} + | {code: DataColumnSidecarErrorCode.PARENT_UNKNOWN; parentRoot: RootHex}; + +export class DataColumnSidecarGossipError extends GossipActionError {} diff --git a/packages/beacon-node/src/chain/errors/index.ts b/packages/beacon-node/src/chain/errors/index.ts index 1bd8f8577305..2159b1562e2d 100644 --- a/packages/beacon-node/src/chain/errors/index.ts +++ b/packages/beacon-node/src/chain/errors/index.ts @@ -1,6 +1,7 @@ export * from "./attestationError.js"; export * from "./attesterSlashingError.js"; export * from "./blobSidecarError.js"; +export * from "./dataColumnSidecarError.js"; export * from "./blockError.js"; export * from "./gossipValidation.js"; export * from "./proposerSlashingError.js"; diff --git a/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts b/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts index 6b51332353f2..4a68c5c27f00 100644 --- a/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts +++ b/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts @@ -1,8 +1,8 @@ import {toHexString} from "@chainsafe/ssz"; -import {deneb, RootHex, SignedBeaconBlock, ssz} from "@lodestar/types"; +import {deneb, RootHex, SignedBeaconBlock, ssz, electra} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {pruneSetToMax} from "@lodestar/utils"; -import {BLOBSIDECAR_FIXED_SIZE, isForkBlobs, ForkName} from "@lodestar/params"; +import {BLOBSIDECAR_FIXED_SIZE, isForkBlobs, ForkName, NUMBER_OF_COLUMNS} from "@lodestar/params"; import { BlockInput, @@ -14,8 +14,12 @@ import { GossipedInputType, getBlockInputBlobs, BlobsSource, + DataColumnsSource, + getBlockInputDataColumns, + BlockInputDataDataColumns, } from "../blocks/types.js"; import {Metrics} from "../../metrics/index.js"; +import {CustodyConfig} from "../../util/dataColumns.js"; export enum BlockInputAvailabilitySource { GOSSIP = "gossip", @@ -24,7 +28,12 @@ export enum BlockInputAvailabilitySource { type GossipedBlockInput = | {type: GossipedInputType.block; signedBlock: SignedBeaconBlock; blockBytes: Uint8Array | null} - | {type: GossipedInputType.blob; blobSidecar: deneb.BlobSidecar; blobBytes: Uint8Array | null}; + | {type: GossipedInputType.blob; blobSidecar: deneb.BlobSidecar; blobBytes: Uint8Array | null} + | { + type: GossipedInputType.dataColumn; + dataColumnSidecar: electra.DataColumnSidecar; + dataColumnBytes: Uint8Array | null; + }; type BlockInputCacheType = { fork: ForkName; @@ -51,6 +60,7 @@ const MAX_GOSSIPINPUT_CACHE = 5; */ export class SeenGossipBlockInput { private blockInputCache = new Map(); + constructor(private custodyConfig: CustodyConfig) {} prune(): void { pruneSetToMax(this.blockInputCache, MAX_GOSSIPINPUT_CACHE); @@ -67,11 +77,16 @@ export class SeenGossipBlockInput { ): | { blockInput: BlockInput; - blockInputMeta: {pending: GossipedInputType.blob | null; haveBlobs: number; expectedBlobs: number}; + blockInputMeta: + | {pending: GossipedInputType.blob | null; haveBlobs: number; expectedBlobs: number} + | {pending: GossipedInputType.dataColumn | null; haveColumns: number; expectedColumns: number}; } | { blockInput: NullBlockInput; - blockInputMeta: {pending: GossipedInputType.block; haveBlobs: number; expectedBlobs: null}; + blockInputMeta: {pending: GossipedInputType.block} & ( + | {haveBlobs: number; expectedBlobs: null} + | {haveColumns: number; expectedColumns: null} + ); } { let blockHex; let blockCache; @@ -88,13 +103,16 @@ export class SeenGossipBlockInput { blockCache.block = signedBlock; blockCache.blockBytes = blockBytes; - } else { + } else if (gossipedInput.type === GossipedInputType.blob) { const {blobSidecar, blobBytes} = gossipedInput; const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blobSidecar.signedBlockHeader.message); fork = config.getForkName(blobSidecar.signedBlockHeader.message.slot); blockHex = toHexString(blockRoot); blockCache = this.blockInputCache.get(blockHex) ?? getEmptyBlockInputCacheEntry(fork); + if (blockCache.cachedData?.fork !== ForkName.deneb) { + throw Error(`blob data at non deneb fork=${blockCache.fork}`); + } // TODO: freetheblobs check if its the same blob or a duplicate and throw/take actions blockCache.cachedData?.blobsCache.set(blobSidecar.index, { @@ -102,6 +120,26 @@ export class SeenGossipBlockInput { // easily splice out the unsigned message as blob is a fixed length type blobBytes: blobBytes?.slice(0, BLOBSIDECAR_FIXED_SIZE) ?? null, }); + } else if (gossipedInput.type === GossipedInputType.dataColumn) { + const {dataColumnSidecar, dataColumnBytes} = gossipedInput; + const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(dataColumnSidecar.signedBlockHeader.message); + fork = config.getForkName(dataColumnSidecar.signedBlockHeader.message.slot); + + blockHex = toHexString(blockRoot); + blockCache = this.blockInputCache.get(blockHex) ?? getEmptyBlockInputCacheEntry(fork); + if (blockCache.cachedData?.fork !== ForkName.electra) { + throw Error(`blob data at non electra fork=${blockCache.fork}`); + } + + // TODO: freetheblobs check if its the same blob or a duplicate and throw/take actions + blockCache.cachedData?.dataColumnsCache.set(dataColumnSidecar.index, { + dataColumnSidecar, + // easily splice out the unsigned message as blob is a fixed length type + dataColumnBytes: dataColumnBytes?.slice(0, dataColumnBytes.length) ?? null, + }); + } else { + // somehow helps resolve typescript that all types have been exausted + throw Error("Invalid gossipedInput type"); } if (!this.blockInputCache.has(blockHex)) { @@ -121,72 +159,192 @@ export class SeenGossipBlockInput { if (cachedData === undefined || !isForkBlobs(cachedData.fork)) { throw Error("Missing or Invalid fork cached Data for deneb+ block"); } - const {blobsCache, resolveAvailability} = cachedData; - // block is available, check if all blobs have shown up - const {slot, body} = signedBlock.message; - const {blobKzgCommitments} = body as deneb.BeaconBlockBody; - const blockInfo = `blockHex=${blockHex}, slot=${slot}`; + if (cachedData.fork === ForkName.deneb) { + const {blobsCache} = cachedData; - if (blobKzgCommitments.length < blobsCache.size) { - throw Error( - `Received more blobs=${blobsCache.size} than commitments=${blobKzgCommitments.length} for ${blockInfo}` - ); + // block is available, check if all blobs have shown up + const {slot, body} = signedBlock.message; + const {blobKzgCommitments} = body as deneb.BeaconBlockBody; + const blockInfo = `blockHex=${blockHex}, slot=${slot}`; + + if (blobKzgCommitments.length < blobsCache.size) { + throw Error( + `Received more blobs=${blobsCache.size} than commitments=${blobKzgCommitments.length} for ${blockInfo}` + ); + } + + if (blobKzgCommitments.length === blobsCache.size) { + const allBlobs = getBlockInputBlobs(blobsCache); + metrics?.syncUnknownBlock.resolveAvailabilitySource.inc({source: BlockInputAvailabilitySource.GOSSIP}); + const {blobs} = allBlobs; + const blockData = { + fork: cachedData.fork, + ...allBlobs, + blobsSource: BlobsSource.gossip, + }; + const blockInput = getBlockInput.availableData( + config, + signedBlock, + BlockSource.gossip, + blockBytes ?? null, + blockData + ); + + resolveBlockInput(blockInput); + return { + blockInput, + blockInputMeta: {pending: null, haveBlobs: blobs.length, expectedBlobs: blobKzgCommitments.length}, + }; + } else { + const blockInput = getBlockInput.dataPromise( + config, + signedBlock, + BlockSource.gossip, + blockBytes ?? null, + cachedData + ); + + resolveBlockInput(blockInput); + return { + blockInput, + blockInputMeta: { + pending: GossipedInputType.blob, + haveBlobs: blobsCache.size, + expectedBlobs: blobKzgCommitments.length, + }, + }; + } + } else if (cachedData.fork === ForkName.electra) { + const {dataColumnsCache} = cachedData; + + // block is available, check if all blobs have shown up + const {slot} = signedBlock.message; + const blockInfo = `blockHex=${blockHex}, slot=${slot}`; + + if (NUMBER_OF_COLUMNS < dataColumnsCache.size) { + throw Error( + `Received more dataColumns=${dataColumnsCache.size} than columns=${NUMBER_OF_COLUMNS} for ${blockInfo}` + ); + } + + // get the custody columns and see if we have got all the requisite columns + const blobKzgCommitmentsLen = (signedBlock.message.body as deneb.BeaconBlockBody).blobKzgCommitments.length; + if (blobKzgCommitmentsLen === 0) { + const blockData = { + fork: cachedData.fork, + dataColumns: [], + dataColumnsBytes: [], + dataColumnsLen: 0, + dataColumnsIndex: new Uint8Array(NUMBER_OF_COLUMNS), + dataColumnsSource: DataColumnsSource.gossip, + }; + + const blockInput = getBlockInput.availableData( + config, + signedBlock, + BlockSource.gossip, + blockBytes ?? null, + blockData + ); + + resolveBlockInput(blockInput); + return { + blockInput, + blockInputMeta: {pending: null, haveColumns: 0, expectedColumns: 0}, + }; + } + + const custodyIndexesPresent = + dataColumnsCache.size >= this.custodyConfig.custodyColumnsLen && + this.custodyConfig.custodyColumns.reduce( + (acc, columnIndex) => acc && dataColumnsCache.has(columnIndex), + true + ); + + if (custodyIndexesPresent) { + const allDataColumns = getBlockInputDataColumns(dataColumnsCache, this.custodyConfig.custodyColumns); + metrics?.syncUnknownBlock.resolveAvailabilitySource.inc({source: BlockInputAvailabilitySource.GOSSIP}); + const {dataColumns} = allDataColumns; + const blockData = { + fork: cachedData.fork, + ...allDataColumns, + dataColumnsLen: this.custodyConfig.custodyColumnsLen, + dataColumnsIndex: this.custodyConfig.custodyColumnsIndex, + dataColumnsSource: DataColumnsSource.gossip, + }; + const blockInput = getBlockInput.availableData( + config, + signedBlock, + BlockSource.gossip, + blockBytes ?? null, + blockData + ); + + resolveBlockInput(blockInput); + return { + blockInput, + blockInputMeta: { + pending: null, + haveColumns: dataColumns.length, + expectedColumns: this.custodyConfig.custodyColumnsLen, + }, + }; + } else { + const blockInput = getBlockInput.dataPromise( + config, + signedBlock, + BlockSource.gossip, + blockBytes ?? null, + cachedData + ); + + resolveBlockInput(blockInput); + return { + blockInput, + blockInputMeta: { + pending: GossipedInputType.dataColumn, + haveColumns: dataColumnsCache.size, + expectedColumns: this.custodyConfig.custodyColumnsLen, + }, + }; + } + } else { + throw Error(`Invalid fork=${fork}`); + } + } else { + // will need to wait for the block to showup + if (cachedData === undefined) { + throw Error("Missing cachedData for deneb+ blobs"); } - if (blobKzgCommitments.length === blobsCache.size) { - const allBlobs = getBlockInputBlobs(blobsCache); - const blockData = {...allBlobs, blobsSource: BlobsSource.gossip, fork: cachedData.fork}; - resolveAvailability(blockData); - metrics?.syncUnknownBlock.resolveAvailabilitySource.inc({source: BlockInputAvailabilitySource.GOSSIP}); - const blockInput = getBlockInput.availableData( - config, - signedBlock, - BlockSource.gossip, - blockBytes ?? null, - blockData - ); + if (cachedData.fork === ForkName.deneb) { + const {blobsCache} = cachedData; - resolveBlockInput(blockInput); return { - blockInput, - blockInputMeta: {pending: null, haveBlobs: allBlobs.blobs.length, expectedBlobs: blobKzgCommitments.length}, + blockInput: { + block: null, + blockRootHex: blockHex, + cachedData, + blockInputPromise, + }, + blockInputMeta: {pending: GossipedInputType.block, haveBlobs: blobsCache.size, expectedBlobs: null}, }; - } else { - const blockInput = getBlockInput.dataPromise( - config, - signedBlock, - BlockSource.gossip, - blockBytes ?? null, - cachedData - ); + } else if (fork === ForkName.electra) { + const {dataColumnsCache} = cachedData; - resolveBlockInput(blockInput); return { - blockInput, - blockInputMeta: { - pending: GossipedInputType.blob, - haveBlobs: blobsCache.size, - expectedBlobs: blobKzgCommitments.length, + blockInput: { + block: null, + blockRootHex: blockHex, + cachedData, + blockInputPromise, }, + blockInputMeta: {pending: GossipedInputType.block, haveColumns: dataColumnsCache.size, expectedColumns: null}, }; + } else { + throw Error(`invalid fork=${fork} data not implemented`); } - } else { - // will need to wait for the block to showup - if (cachedData === undefined) { - throw Error("Missing cachedData for deneb+ blobs"); - } - const {blobsCache} = cachedData; - - return { - blockInput: { - block: null, - blockRootHex: blockHex, - cachedData, - blockInputPromise, - }, - blockInputMeta: {pending: GossipedInputType.block, haveBlobs: blobsCache.size, expectedBlobs: null}, - }; } } } @@ -205,16 +363,38 @@ function getEmptyBlockInputCacheEntry(fork: ForkName): BlockInputCacheType { return {fork, blockInputPromise, resolveBlockInput}; } - let resolveAvailability: ((blobs: BlockInputDataBlobs) => void) | null = null; - const availabilityPromise = new Promise((resolveCB) => { - resolveAvailability = resolveCB; - }); + if (fork === ForkName.deneb) { + let resolveAvailability: ((blobs: BlockInputDataBlobs) => void) | null = null; + const availabilityPromise = new Promise((resolveCB) => { + resolveAvailability = resolveCB; + }); - if (resolveAvailability === null) { - throw Error("Promise Constructor was not executed immediately"); - } + if (resolveAvailability === null) { + throw Error("Promise Constructor was not executed immediately"); + } + + const blobsCache = new Map(); + const cachedData: CachedData = {fork, blobsCache, availabilityPromise, resolveAvailability}; + return {fork, blockInputPromise, resolveBlockInput, cachedData}; + } else if (fork === ForkName.electra) { + let resolveAvailability: ((blobs: BlockInputDataDataColumns) => void) | null = null; + const availabilityPromise = new Promise((resolveCB) => { + resolveAvailability = resolveCB; + }); - const blobsCache = new Map(); - const cachedData: CachedData = {fork, blobsCache, availabilityPromise, resolveAvailability}; - return {fork, blockInputPromise, resolveBlockInput, cachedData}; + if (resolveAvailability === null) { + throw Error("Promise Constructor was not executed immediately"); + } + + const dataColumnsCache = new Map(); + const cachedData: CachedData = { + fork, + dataColumnsCache, + availabilityPromise, + resolveAvailability, + }; + return {fork, blockInputPromise, resolveBlockInput, cachedData}; + } else { + throw Error(`Invalid fork=${fork} for getEmptyBlockInputCacheEntry`); + } } diff --git a/packages/beacon-node/src/chain/validation/dataColumnSidecar.ts b/packages/beacon-node/src/chain/validation/dataColumnSidecar.ts new file mode 100644 index 000000000000..004a4a57c25e --- /dev/null +++ b/packages/beacon-node/src/chain/validation/dataColumnSidecar.ts @@ -0,0 +1,66 @@ +import { + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH, + KZG_COMMITMENTS_SUBTREE_INDEX, + DATA_COLUMN_SIDECAR_SUBNET_COUNT, + NUMBER_OF_COLUMNS, +} from "@lodestar/params"; +import {ssz, deneb, electra, Slot, Root} from "@lodestar/types"; +import {verifyMerkleBranch} from "@lodestar/utils"; + +import {DataColumnSidecarGossipError, DataColumnSidecarErrorCode} from "../errors/dataColumnSidecarError.js"; +import {GossipAction} from "../errors/gossipValidation.js"; +import {IBeaconChain} from "../interface.js"; + +export async function validateGossipDataColumnSidecar( + chain: IBeaconChain, + dataColumnSideCar: electra.DataColumnSidecar, + gossipIndex: number +): Promise { + const dataColumnSlot = dataColumnSideCar.signedBlockHeader.message.slot; + + if ( + dataColumnSideCar.index > NUMBER_OF_COLUMNS || + dataColumnSideCar.index % DATA_COLUMN_SIDECAR_SUBNET_COUNT !== gossipIndex + ) { + throw new DataColumnSidecarGossipError(GossipAction.REJECT, { + code: DataColumnSidecarErrorCode.INVALID_INDEX, + columnIndex: dataColumnSideCar.index, + gossipIndex, + }); + } + + // [IGNORE] The sidecar is not from a future slot (with a MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance) -- + // i.e. validate that sidecar.slot <= current_slot (a client MAY queue future blocks for processing at + // the appropriate slot). + const currentSlotWithGossipDisparity = chain.clock.currentSlotWithGossipDisparity; + if (currentSlotWithGossipDisparity < dataColumnSlot) { + throw new DataColumnSidecarGossipError(GossipAction.IGNORE, { + code: DataColumnSidecarErrorCode.FUTURE_SLOT, + currentSlot: currentSlotWithGossipDisparity, + blockSlot: dataColumnSlot, + }); + } + + validateInclusionProof(dataColumnSideCar); +} + +export function validateDataColumnsSidecars( + _blockSlot: Slot, + _blockRoot: Root, + _expectedKzgCommitments: deneb.BlobKzgCommitments, + _dataColumnSidecars: electra.DataColumnSidecars, + _opts: {skipProofsCheck: boolean} = {skipProofsCheck: false} +): void { + // stubbed + return; +} + +function validateInclusionProof(dataColumnSidecar: electra.DataColumnSidecar): boolean { + return verifyMerkleBranch( + ssz.deneb.BlobKzgCommitments.hashTreeRoot(dataColumnSidecar.kzgCommitments), + dataColumnSidecar.kzgCommitmentsInclusionProof, + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH, + KZG_COMMITMENTS_SUBTREE_INDEX, + dataColumnSidecar.signedBlockHeader.message.bodyRoot + ); +} diff --git a/packages/beacon-node/src/db/beacon.ts b/packages/beacon-node/src/db/beacon.ts index 07cc47fa54d8..41b6daccd42d 100644 --- a/packages/beacon-node/src/db/beacon.ts +++ b/packages/beacon-node/src/db/beacon.ts @@ -18,6 +18,8 @@ import { BackfilledRanges, BlobSidecarsRepository, BlobSidecarsArchiveRepository, + DataColumnSidecarsRepository, + DataColumnSidecarsArchiveRepository, BLSToExecutionChangeRepository, } from "./repositories/index.js"; import {PreGenesisState, PreGenesisStateLastProcessedBlock} from "./single/index.js"; @@ -34,6 +36,8 @@ export class BeaconDb implements IBeaconDb { blobSidecars: BlobSidecarsRepository; blobSidecarsArchive: BlobSidecarsArchiveRepository; + dataColumnSidecars: DataColumnSidecarsRepository; + dataColumnSidecarsArchive: DataColumnSidecarsArchiveRepository; stateArchive: StateArchiveRepository; checkpointState: CheckpointStateRepository; @@ -67,6 +71,8 @@ export class BeaconDb implements IBeaconDb { this.blobSidecars = new BlobSidecarsRepository(config, db); this.blobSidecarsArchive = new BlobSidecarsArchiveRepository(config, db); + this.dataColumnSidecars = new DataColumnSidecarsRepository(config, db); + this.dataColumnSidecarsArchive = new DataColumnSidecarsArchiveRepository(config, db); this.stateArchive = new StateArchiveRepository(config, db); this.checkpointState = new CheckpointStateRepository(config, db); diff --git a/packages/beacon-node/src/db/buckets.ts b/packages/beacon-node/src/db/buckets.ts index eff123879037..5df8d50abc11 100644 --- a/packages/beacon-node/src/db/buckets.ts +++ b/packages/beacon-node/src/db/buckets.ts @@ -61,6 +61,9 @@ export enum Bucket { // 54 was for bestPartialLightClientUpdate, allocate a fresh one // lightClient_bestLightClientUpdate = 55, // SyncPeriod -> LightClientUpdate // DEPRECATED on v1.5.0 lightClient_bestLightClientUpdate = 56, // SyncPeriod -> [Slot, LightClientUpdate] + + allForks_dataColumnSidecars = 57, // ELECTRA BeaconBlockRoot -> DataColumnSidecars + allForks_dataColumnSidecarsArchive = 58, // ELECTRA BeaconBlockSlot -> DataColumnSidecars } export function getBucketNameByValue(enumValue: T): keyof typeof Bucket { diff --git a/packages/beacon-node/src/db/interface.ts b/packages/beacon-node/src/db/interface.ts index 6ffb8992f635..cf55d3d95a44 100644 --- a/packages/beacon-node/src/db/interface.ts +++ b/packages/beacon-node/src/db/interface.ts @@ -16,6 +16,8 @@ import { BackfilledRanges, BlobSidecarsRepository, BlobSidecarsArchiveRepository, + DataColumnSidecarsRepository, + DataColumnSidecarsArchiveRepository, BLSToExecutionChangeRepository, } from "./repositories/index.js"; import {PreGenesisState, PreGenesisStateLastProcessedBlock} from "./single/index.js"; @@ -34,6 +36,8 @@ export interface IBeaconDb { blobSidecars: BlobSidecarsRepository; blobSidecarsArchive: BlobSidecarsArchiveRepository; + dataColumnSidecars: DataColumnSidecarsRepository; + dataColumnSidecarsArchive: DataColumnSidecarsArchiveRepository; // finalized states stateArchive: StateArchiveRepository; diff --git a/packages/beacon-node/src/db/repositories/dataColumnSidecars.ts b/packages/beacon-node/src/db/repositories/dataColumnSidecars.ts new file mode 100644 index 000000000000..fe420c7ab81d --- /dev/null +++ b/packages/beacon-node/src/db/repositories/dataColumnSidecars.ts @@ -0,0 +1,50 @@ +import {ValueOf, ContainerType, ByteVectorType} from "@chainsafe/ssz"; +import {ChainForkConfig} from "@lodestar/config"; +import {Db, Repository} from "@lodestar/db"; +import {ssz} from "@lodestar/types"; +import {NUMBER_OF_COLUMNS} from "@lodestar/params"; + +import {Bucket, getBucketNameByValue} from "../buckets.js"; + +export const dataColumnSidecarsWrapperSsz = new ContainerType( + { + blockRoot: ssz.Root, + slot: ssz.Slot, + dataColumnsLen: ssz.Uint16, + dataColumnsSize: ssz.UintNum64, + // // each byte[i] tells what index (1 based) the column i is stored, 0 means not custodied + // max value to represent will be 128 which can be represented in a byte + dataColumnsIndex: new ByteVectorType(NUMBER_OF_COLUMNS), + dataColumnSidecars: ssz.electra.DataColumnSidecars, + }, + {typeName: "DataColumnSidecarsWrapper", jsonCase: "eth2"} +); + +export type DataColumnSidecarsWrapper = ValueOf; +export const BLOCK_ROOT_IN_WRAPPER_INDEX = 0; +export const BLOCK_SLOT_IN_WRAPPER_INDEX = 32; +export const NUM_COLUMNS_IN_WRAPPER_INDEX = 40; +export const COLUMN_SIZE_IN_WRAPPER_INDEX = 42; +export const CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX = 50; +export const DATA_COLUMN_SIDECARS_IN_WRAPPER_INDEX = + CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX + NUMBER_OF_COLUMNS + 4 + 4 * NUMBER_OF_COLUMNS; + +/** + * dataColumnSidecarsWrapper by block root (= hash_tree_root(SignedBeaconBlock.message)) + * + * Used to store unfinalized DataColumnSidecars + */ +export class DataColumnSidecarsRepository extends Repository { + constructor(config: ChainForkConfig, db: Db) { + const bucket = Bucket.allForks_dataColumnSidecars; + super(config, db, bucket, dataColumnSidecarsWrapperSsz, getBucketNameByValue(bucket)); + } + + /** + * Id is hashTreeRoot of unsigned BeaconBlock + */ + getId(value: DataColumnSidecarsWrapper): Uint8Array { + const {blockRoot} = value; + return blockRoot; + } +} diff --git a/packages/beacon-node/src/db/repositories/dataColumnSidecarsArchive.ts b/packages/beacon-node/src/db/repositories/dataColumnSidecarsArchive.ts new file mode 100644 index 000000000000..08a71dcbf646 --- /dev/null +++ b/packages/beacon-node/src/db/repositories/dataColumnSidecarsArchive.ts @@ -0,0 +1,28 @@ +import {ChainForkConfig} from "@lodestar/config"; +import {Db, Repository} from "@lodestar/db"; +import {Slot} from "@lodestar/types"; +import {bytesToInt} from "@lodestar/utils"; +import {Bucket, getBucketNameByValue} from "../buckets.js"; +import {dataColumnSidecarsWrapperSsz, DataColumnSidecarsWrapper} from "./dataColumnSidecars.js"; + +/** + * dataColumnSidecarsWrapper by slot + * + * Used to store finalized DataColumnSidecars + */ +export class DataColumnSidecarsArchiveRepository extends Repository { + constructor(config: ChainForkConfig, db: Db) { + const bucket = Bucket.allForks_dataColumnSidecarsArchive; + super(config, db, bucket, dataColumnSidecarsWrapperSsz, getBucketNameByValue(bucket)); + } + + // Handle key as slot + + getId(value: DataColumnSidecarsWrapper): Slot { + return value.slot; + } + + decodeKey(data: Uint8Array): number { + return bytesToInt(super.decodeKey(data) as unknown as Uint8Array, "be"); + } +} diff --git a/packages/beacon-node/src/db/repositories/index.ts b/packages/beacon-node/src/db/repositories/index.ts index 4a66a0ba9876..72e0c7224148 100644 --- a/packages/beacon-node/src/db/repositories/index.ts +++ b/packages/beacon-node/src/db/repositories/index.ts @@ -1,5 +1,7 @@ export {BlobSidecarsRepository} from "./blobSidecars.js"; export {BlobSidecarsArchiveRepository} from "./blobSidecarsArchive.js"; +export {DataColumnSidecarsRepository} from "./dataColumnSidecars.js"; +export {DataColumnSidecarsArchiveRepository} from "./dataColumnSidecarsArchive.js"; export {BlockRepository} from "./block.js"; export {BlockArchiveRepository} from "./blockArchive.js"; diff --git a/packages/beacon-node/src/network/events.ts b/packages/beacon-node/src/network/events.ts index 45759de61073..d285f121c0de 100644 --- a/packages/beacon-node/src/network/events.ts +++ b/packages/beacon-node/src/network/events.ts @@ -1,6 +1,6 @@ import {EventEmitter} from "events"; import {PeerId, TopicValidatorResult} from "@libp2p/interface"; -import {phase0, RootHex} from "@lodestar/types"; +import {phase0, RootHex, ColumnIndex} from "@lodestar/types"; import {BlockInput, NullBlockInput} from "../chain/blocks/types.js"; import {StrictEventEmitterSingleArg} from "../util/strictEvents.js"; import {PeerIdStr} from "../util/peerId.js"; @@ -27,7 +27,7 @@ export enum NetworkEvent { } export type NetworkEventData = { - [NetworkEvent.peerConnected]: {peer: PeerIdStr; status: phase0.Status}; + [NetworkEvent.peerConnected]: {peer: PeerIdStr; status: phase0.Status; dataColumns: ColumnIndex[]}; [NetworkEvent.peerDisconnected]: {peer: PeerIdStr}; [NetworkEvent.reqRespRequest]: {request: RequestTypedContainer; peer: PeerId}; [NetworkEvent.unknownBlockParent]: {blockInput: BlockInput; peer: PeerIdStr}; diff --git a/packages/beacon-node/src/network/gossip/interface.ts b/packages/beacon-node/src/network/gossip/interface.ts index 25a871b4e2a0..0a63354d0ae1 100644 --- a/packages/beacon-node/src/network/gossip/interface.ts +++ b/packages/beacon-node/src/network/gossip/interface.ts @@ -11,6 +11,7 @@ import { phase0, SignedBeaconBlock, Slot, + electra } from "@lodestar/types"; import {BeaconConfig} from "@lodestar/config"; import {Logger} from "@lodestar/utils"; @@ -22,6 +23,7 @@ import {GossipActionError} from "../../chain/errors/gossipValidation.js"; export enum GossipType { beacon_block = "beacon_block", blob_sidecar = "blob_sidecar", + data_column_sidecar = "data_column_sidecar", beacon_aggregate_and_proof = "beacon_aggregate_and_proof", beacon_attestation = "beacon_attestation", voluntary_exit = "voluntary_exit", @@ -50,6 +52,7 @@ export interface IGossipTopic { export type GossipTopicTypeMap = { [GossipType.beacon_block]: {type: GossipType.beacon_block}; [GossipType.blob_sidecar]: {type: GossipType.blob_sidecar; index: number}; + [GossipType.data_column_sidecar]: {type: GossipType.data_column_sidecar; index: number}; [GossipType.beacon_aggregate_and_proof]: {type: GossipType.beacon_aggregate_and_proof}; [GossipType.beacon_attestation]: {type: GossipType.beacon_attestation; subnet: number}; [GossipType.voluntary_exit]: {type: GossipType.voluntary_exit}; @@ -80,6 +83,7 @@ export type SSZTypeOfGossipTopic = T extends {type: infer export type GossipTypeMap = { [GossipType.beacon_block]: SignedBeaconBlock; [GossipType.blob_sidecar]: deneb.BlobSidecar; + [GossipType.data_column_sidecar]: electra.DataColumnSidecar; [GossipType.beacon_aggregate_and_proof]: phase0.SignedAggregateAndProof; [GossipType.beacon_attestation]: phase0.Attestation; [GossipType.voluntary_exit]: phase0.SignedVoluntaryExit; @@ -95,6 +99,7 @@ export type GossipTypeMap = { export type GossipFnByType = { [GossipType.beacon_block]: (signedBlock: SignedBeaconBlock) => Promise | void; [GossipType.blob_sidecar]: (blobSidecar: deneb.BlobSidecar) => Promise | void; + [GossipType.data_column_sidecar]: (blobSidecar: electra.DataColumnSidecar) => Promise | void; [GossipType.beacon_aggregate_and_proof]: (aggregateAndProof: phase0.SignedAggregateAndProof) => Promise | void; [GossipType.beacon_attestation]: (attestation: phase0.Attestation) => Promise | void; [GossipType.voluntary_exit]: (voluntaryExit: phase0.SignedVoluntaryExit) => Promise | void; diff --git a/packages/beacon-node/src/network/gossip/topic.ts b/packages/beacon-node/src/network/gossip/topic.ts index c5cd68ffa1de..7896d9abed25 100644 --- a/packages/beacon-node/src/network/gossip/topic.ts +++ b/packages/beacon-node/src/network/gossip/topic.ts @@ -7,6 +7,7 @@ import { SYNC_COMMITTEE_SUBNET_COUNT, isForkLightClient, MAX_BLOBS_PER_BLOCK, + DATA_COLUMN_SIDECAR_SUBNET_COUNT, } from "@lodestar/params"; import {GossipAction, GossipActionError, GossipErrorCode} from "../../chain/errors/gossipValidation.js"; @@ -75,6 +76,8 @@ function stringifyGossipTopicType(topic: GossipTopic): string { return `${topic.type}_${topic.subnet}`; case GossipType.blob_sidecar: return `${topic.type}_${topic.index}`; + case GossipType.data_column_sidecar: + return `${topic.type}_${topic.index}`; } } @@ -86,6 +89,8 @@ export function getGossipSSZType(topic: GossipTopic) { return ssz[topic.fork].SignedBeaconBlock; case GossipType.blob_sidecar: return ssz.deneb.BlobSidecar; + case GossipType.data_column_sidecar: + return ssz.electra.DataColumnSidecar; case GossipType.beacon_aggregate_and_proof: return ssz.phase0.SignedAggregateAndProof; case GossipType.beacon_attestation: @@ -189,6 +194,13 @@ export function parseGossipTopic(forkDigestContext: ForkDigestContext, topicStr: return {type: GossipType.blob_sidecar, index, fork, encoding}; } + if (gossipTypeStr.startsWith(GossipType.data_column_sidecar)) { + const indexStr = gossipTypeStr.slice(GossipType.data_column_sidecar.length + 1); // +1 for '_' concatenating the topic name and the index + const index = parseInt(indexStr, 10); + if (Number.isNaN(index)) throw Error(`index ${indexStr} is not a number`); + return {type: GossipType.data_column_sidecar, index, fork, encoding}; + } + throw Error(`Unknown gossip type ${gossipTypeStr}`); } catch (e) { (e as Error).message = `Invalid gossip topic ${topicStr}: ${(e as Error).message}`; @@ -212,6 +224,13 @@ export function getCoreTopicsAtFork( {type: GossipType.attester_slashing}, ]; + // After Electra also track data_column_sidecar_{index} + if (ForkSeq[fork] >= ForkSeq.electra) { + for (let index = 0; index < DATA_COLUMN_SIDECAR_SUBNET_COUNT; index++) { + topics.push({type: GossipType.data_column_sidecar, index}); + } + } + // After Deneb also track blob_sidecar_{index} if (ForkSeq[fork] >= ForkSeq.deneb) { for (let index = 0; index < MAX_BLOBS_PER_BLOCK; index++) { @@ -262,6 +281,7 @@ function parseEncodingStr(encodingStr: string): GossipEncoding { export const gossipTopicIgnoreDuplicatePublishError: Record = { [GossipType.beacon_block]: true, [GossipType.blob_sidecar]: true, + [GossipType.data_column_sidecar]: true, [GossipType.beacon_aggregate_and_proof]: true, [GossipType.beacon_attestation]: true, [GossipType.voluntary_exit]: true, diff --git a/packages/beacon-node/src/network/interface.ts b/packages/beacon-node/src/network/interface.ts index 5012650e229a..c3dbfe12342b 100644 --- a/packages/beacon-node/src/network/interface.ts +++ b/packages/beacon-node/src/network/interface.ts @@ -26,13 +26,16 @@ import { capella, deneb, phase0, + electra } from "@lodestar/types"; import {PeerIdStr} from "../util/peerId.js"; +import {CustodyConfig} from "../util/dataColumns.js"; import {INetworkEventBus} from "./events.js"; import {INetworkCorePublic} from "./core/types.js"; import {GossipType} from "./gossip/interface.js"; import {PendingGossipsubMessage} from "./processor/types.js"; import {PeerAction} from "./peers/index.js"; +import {NodeId} from "./subnets/interface.js"; export type WithBytes = {data: T; bytes: Uint8Array}; @@ -46,6 +49,9 @@ export type WithBytes = {data: T; bytes: Uint8Array}; */ export interface INetwork extends INetworkCorePublic { + readonly nodeId: NodeId; + readonly peerId: PeerId; + readonly custodyConfig: CustodyConfig; readonly closed: boolean; events: INetworkEventBus; @@ -67,10 +73,19 @@ export interface INetwork extends INetworkCorePublic { ): Promise[]>; sendBlobSidecarsByRange(peerId: PeerIdStr, request: deneb.BlobSidecarsByRangeRequest): Promise; sendBlobSidecarsByRoot(peerId: PeerIdStr, request: deneb.BlobSidecarsByRootRequest): Promise; + sendDataColumnSidecarsByRange( + peerId: PeerIdStr, + request: electra.DataColumnSidecarsByRangeRequest + ): Promise; + sendDataColumnSidecarsByRoot( + peerId: PeerIdStr, + request: electra.DataColumnSidecarsByRootRequest + ): Promise; // Gossip publishBeaconBlock(signedBlock: SignedBeaconBlock): Promise; publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise; + publishDataColumnSidecar(dataColumnSideCar: electra.DataColumnSidecar): Promise; publishBeaconAggregateAndProof(aggregateAndProof: phase0.SignedAggregateAndProof): Promise; publishBeaconAttestation(attestation: phase0.Attestation, subnet: number): Promise; publishVoluntaryExit(voluntaryExit: phase0.SignedVoluntaryExit): Promise; diff --git a/packages/beacon-node/src/network/metadata.ts b/packages/beacon-node/src/network/metadata.ts index fab220c1ebf8..6c3cd92a69d3 100644 --- a/packages/beacon-node/src/network/metadata.ts +++ b/packages/beacon-node/src/network/metadata.ts @@ -11,6 +11,7 @@ export enum ENRKey { eth2 = "eth2", attnets = "attnets", syncnets = "syncnets", + custody_subnet_count = "custody_subnet_count", } export enum SubnetType { attnets = "attnets", diff --git a/packages/beacon-node/src/network/network.ts b/packages/beacon-node/src/network/network.ts index 52b9d85c0064..d70004222265 100644 --- a/packages/beacon-node/src/network/network.ts +++ b/packages/beacon-node/src/network/network.ts @@ -17,22 +17,25 @@ import { LightClientFinalityUpdate, LightClientOptimisticUpdate, LightClientUpdate, + electra, + ColumnIndex, } from "@lodestar/types"; import {routes} from "@lodestar/api"; import {ResponseIncoming} from "@lodestar/reqresp"; -import {ForkSeq, MAX_BLOBS_PER_BLOCK} from "@lodestar/params"; +import {ForkSeq, MAX_BLOBS_PER_BLOCK, NUMBER_OF_COLUMNS, DATA_COLUMN_SIDECAR_SUBNET_COUNT} from "@lodestar/params"; import {Metrics, RegistryMetricCreator} from "../metrics/index.js"; import {IBeaconChain} from "../chain/index.js"; import {IBeaconDb} from "../db/interface.js"; import {PeerIdStr, peerIdToString} from "../util/peerId.js"; import {IClock} from "../util/clock.js"; +import {getCustodyConfig, CustodyConfig} from "../util/dataColumns.js"; import {NetworkOptions} from "./options.js"; import {WithBytes, INetwork} from "./interface.js"; import {ReqRespMethod} from "./reqresp/index.js"; import {GossipHandlers, GossipTopicMap, GossipType, GossipTypeMap} from "./gossip/index.js"; import {PeerAction, PeerScoreStats} from "./peers/index.js"; import {INetworkEventBus, NetworkEvent, NetworkEventBus, NetworkEventData} from "./events.js"; -import {CommitteeSubscription} from "./subnets/index.js"; +import {CommitteeSubscription, NodeId} from "./subnets/index.js"; import {isPublishToZeroPeersError} from "./util.js"; import {NetworkProcessor, PendingGossipsubMessage} from "./processor/index.js"; import {INetworkCore, NetworkCore, WorkerNetworkCore} from "./core/index.js"; @@ -50,6 +53,7 @@ import {getActiveForks} from "./forks.js"; type NetworkModules = { opts: NetworkOptions; peerId: PeerId; + nodeId: NodeId; config: BeaconConfig; logger: LoggerNode; chain: IBeaconChain; @@ -63,6 +67,7 @@ export type NetworkInitModules = { opts: NetworkOptions; config: BeaconConfig; peerId: PeerId; + nodeId: NodeId; peerStoreDir?: string; logger: LoggerNode; metrics: Metrics | null; @@ -83,6 +88,8 @@ export type NetworkInitModules = { */ export class Network implements INetwork { readonly peerId: PeerId; + readonly nodeId: NodeId; + readonly custodyConfig: CustodyConfig; // TODO: Make private readonly events: INetworkEventBus; @@ -99,12 +106,14 @@ export class Network implements INetwork { private readonly aggregatorTracker: AggregatorTracker; private subscribedToCoreTopics = false; - private connectedPeers = new Set(); + private connectedPeers = new Map(); private regossipBlsChangesPromise: Promise | null = null; constructor(modules: NetworkModules) { this.peerId = modules.peerId; + this.nodeId = modules.nodeId; this.config = modules.config; + this.custodyConfig = getCustodyConfig(modules.nodeId, modules.config); this.logger = modules.logger; this.chain = modules.chain; this.clock = modules.chain.clock; @@ -134,6 +143,7 @@ export class Network implements INetwork { db, gossipHandlers, peerId, + nodeId, peerStoreDir, getReqRespHandler, }: NetworkInitModules): Promise { @@ -189,6 +199,7 @@ export class Network implements INetwork { return new Network({ opts, peerId, + nodeId, config, logger, chain, @@ -257,7 +268,7 @@ export class Network implements INetwork { // REST API queries getConnectedPeers(): PeerIdStr[] { - return Array.from(this.connectedPeers.values()); + return Array.from(this.connectedPeers.keys()); } getConnectedPeerCount(): number { return this.connectedPeers.size; @@ -316,6 +327,20 @@ export class Network implements INetwork { }); } + async publishDataColumnSidecar(dataColumnSidecar: electra.DataColumnSidecar): Promise { + const slot = dataColumnSidecar.signedBlockHeader.message.slot; + const fork = this.config.getForkName(slot); + const index = dataColumnSidecar.index % DATA_COLUMN_SIDECAR_SUBNET_COUNT; + + return this.publishGossip( + {type: GossipType.data_column_sidecar, fork, index}, + dataColumnSidecar, + { + ignoreDuplicatePublishError: true, + } + ); + } + async publishBeaconAggregateAndProof(aggregateAndProof: phase0.SignedAggregateAndProof): Promise { const fork = this.config.getForkName(aggregateAndProof.message.aggregate.data.slot); return this.publishGossip( @@ -516,6 +541,29 @@ export class Network implements INetwork { ); } + async sendDataColumnSidecarsByRange( + peerId: PeerIdStr, + request: electra.DataColumnSidecarsByRangeRequest + ): Promise { + return collectMaxResponseTyped( + this.sendReqRespRequest(peerId, ReqRespMethod.DataColumnSidecarsByRange, [Version.V1], request), + // request's count represent the slots, so the actual max count received could be slots * blobs per slot + request.count * NUMBER_OF_COLUMNS, + responseSszTypeByMethod[ReqRespMethod.DataColumnSidecarsByRange] + ); + } + + async sendDataColumnSidecarsByRoot( + peerId: PeerIdStr, + request: electra.DataColumnSidecarsByRootRequest + ): Promise { + return collectMaxResponseTyped( + this.sendReqRespRequest(peerId, ReqRespMethod.DataColumnSidecarsByRoot, [Version.V1], request), + request.length, + responseSszTypeByMethod[ReqRespMethod.DataColumnSidecarsByRoot] + ); + } + private sendReqRespRequest( peerId: PeerIdStr, method: ReqRespMethod, @@ -628,7 +676,7 @@ export class Network implements INetwork { }; private onPeerConnected = (data: NetworkEventData[NetworkEvent.peerConnected]): void => { - this.connectedPeers.add(data.peer); + this.connectedPeers.set(data.peer, data.dataColumns); }; private onPeerDisconnected = (data: NetworkEventData[NetworkEvent.peerDisconnected]): void => { diff --git a/packages/beacon-node/src/network/peers/discover.ts b/packages/beacon-node/src/network/peers/discover.ts index 1cb084846f61..bd1dcd38a1f4 100644 --- a/packages/beacon-node/src/network/peers/discover.ts +++ b/packages/beacon-node/src/network/peers/discover.ts @@ -1,16 +1,20 @@ import {Multiaddr} from "@multiformats/multiaddr"; import type {PeerId, PeerInfo} from "@libp2p/interface"; import {ENR} from "@chainsafe/enr"; +import {fromHexString, toHexString} from "@chainsafe/ssz"; import {BeaconConfig} from "@lodestar/config"; import {pruneSetToMax, sleep} from "@lodestar/utils"; import {ATTESTATION_SUBNET_COUNT, SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params"; import {LoggerNode} from "@lodestar/logger/node"; +import {ssz} from "@lodestar/types"; import {NetworkCoreMetrics} from "../core/metrics.js"; import {Libp2p} from "../interface.js"; import {ENRKey, SubnetType} from "../metadata.js"; import {getConnectionsMap, prettyPrintPeerId} from "../util.js"; import {Discv5Worker} from "../discv5/index.js"; import {LodestarDiscv5Opts} from "../discv5/types.js"; +import {NodeId} from "../subnets/interface.js"; +import {getCustodyColumnSubnets} from "../../util/dataColumns.js"; import {deserializeEnrSubnets, zeroAttnets, zeroSyncnets} from "./utils/enrSubnetsDeserialize.js"; import {IPeerRpcScoreStore, ScoreState} from "./score/index.js"; @@ -19,6 +23,8 @@ const MAX_CACHED_ENRS = 100; /** Max age a cached ENR will be considered for dial */ const MAX_CACHED_ENR_AGE_MS = 5 * 60 * 1000; +const MAX_CACHED_NODEIDS = 10000; + export type PeerDiscoveryOpts = { maxPeers: number; discv5FirstQueryDelayMs: number; @@ -76,6 +82,7 @@ type CachedENR = { multiaddrTCP: Multiaddr; subnets: Record; addedUnixMs: number; + custodySubnetCount: number; }; /** @@ -85,11 +92,15 @@ type CachedENR = { export class PeerDiscovery { readonly discv5: Discv5Worker; private libp2p: Libp2p; + private nodeId: NodeId; + private custodySubnets: number[]; private peerRpcScores: IPeerRpcScoreStore; private metrics: NetworkCoreMetrics | null; private logger: LoggerNode; private config: BeaconConfig; private cachedENRs = new Map(); + private peerIdToNodeId = new Map(); + private peerIdToCustodySubnetCount = new Map(); private randomNodeQuery: QueryStatus = {code: QueryStatusCode.NotActive}; private peersToConnect = 0; private subnetRequests: Record> = { @@ -112,6 +123,10 @@ export class PeerDiscovery { this.logger = logger; this.config = config; this.discv5 = discv5; + this.nodeId = fromHexString(ENR.decodeTxt(opts.discv5.enr).nodeId); + // we will only connect to peers that can provide us custody + this.custodySubnets = getCustodyColumnSubnets(this.nodeId, config.CUSTODY_REQUIREMENT); + this.maxPeers = opts.maxPeers; this.discv5StartMs = 0; this.discv5StartMs = Date.now(); @@ -304,7 +319,9 @@ export class PeerDiscovery { const attnets = zeroAttnets; const syncnets = zeroSyncnets; - const status = this.handleDiscoveredPeer(id, multiaddrs[0], attnets, syncnets); + const custodySubnetCount = 0; + + const status = this.handleDiscoveredPeer(id, multiaddrs[0], attnets, syncnets, custodySubnetCount); this.metrics?.discovery.discoveredStatus.inc({status}); }; @@ -317,6 +334,11 @@ export class PeerDiscovery { } // async due to some crypto that's no longer necessary const peerId = await enr.peerId(); + + const nodeId = fromHexString(enr.nodeId); + this.peerIdToNodeId.set(peerId.toString(), nodeId); + pruneSetToMax(this.peerIdToNodeId, MAX_CACHED_NODEIDS); + // tcp multiaddr is known to be be present, checked inside the worker const multiaddrTCP = enr.getLocationMultiaddr(ENRKey.tcp); if (!multiaddrTCP) { @@ -327,6 +349,7 @@ export class PeerDiscovery { // Are this fields mandatory? const attnetsBytes = enr.kvs.get(ENRKey.attnets); // 64 bits const syncnetsBytes = enr.kvs.get(ENRKey.syncnets); // 4 bits + const custodySubnetCountBytes = enr.kvs.get(ENRKey.custody_subnet_count); // 64 bits // Use faster version than ssz's implementation that leverages pre-cached. // Some nodes don't serialize the bitfields properly, encoding the syncnets as attnets, @@ -334,8 +357,10 @@ export class PeerDiscovery { // never throw and treat too long or too short bitfields as zero-ed const attnets = attnetsBytes ? deserializeEnrSubnets(attnetsBytes, ATTESTATION_SUBNET_COUNT) : zeroAttnets; const syncnets = syncnetsBytes ? deserializeEnrSubnets(syncnetsBytes, SYNC_COMMITTEE_SUBNET_COUNT) : zeroSyncnets; + const custodySubnetCount = custodySubnetCountBytes ? ssz.UintNum64.deserialize(custodySubnetCountBytes) : 1; + this.peerIdToCustodySubnetCount.set(peerId.toString(), custodySubnetCount); - const status = this.handleDiscoveredPeer(peerId, multiaddrTCP, attnets, syncnets); + const status = this.handleDiscoveredPeer(peerId, multiaddrTCP, attnets, syncnets, custodySubnetCount); this.metrics?.discovery.discoveredStatus.inc({status}); }; @@ -346,8 +371,11 @@ export class PeerDiscovery { peerId: PeerId, multiaddrTCP: Multiaddr, attnets: boolean[], - syncnets: boolean[] + syncnets: boolean[], + custodySubnetCount: number ): DiscoveredPeerStatus { + const nodeId = this.peerIdToNodeId.get(peerId.toString()); + this.logger.warn("handleDiscoveredPeer", {nodeId: nodeId ? toHexString(nodeId) : null, peerId: peerId.toString()}); try { // Check if peer is not banned or disconnected if (this.peerRpcScores.getScoreState(peerId) !== ScoreState.Healthy) { @@ -374,6 +402,7 @@ export class PeerDiscovery { multiaddrTCP, subnets: {attnets, syncnets}, addedUnixMs: Date.now(), + custodySubnetCount, }; // Only dial peer if necessary @@ -394,6 +423,27 @@ export class PeerDiscovery { } private shouldDialPeer(peer: CachedENR): boolean { + const nodeId = this.peerIdToNodeId.get(peer.peerId.toString()); + if (nodeId === undefined) { + return false; + } + const peerCustodySubnetCount = peer.custodySubnetCount; + const peerCustodySubnets = getCustodyColumnSubnets(nodeId, peerCustodySubnetCount); + const hasAllColumns = this.custodySubnets.reduce((acc, elem) => acc && peerCustodySubnets.includes(elem), true); + + this.logger.debug("peerCustodySubnets", { + peerId: peer.peerId.toString(), + peerNodeId: toHexString(nodeId), + hasAllColumns, + peerCustodySubnetCount, + peerCustodySubnets: peerCustodySubnets.join(","), + custodySubnets: this.custodySubnets.join(","), + nodeId: `${toHexString(this.nodeId)}`, + }); + if (!hasAllColumns) { + return false; + } + for (const type of [SubnetType.attnets, SubnetType.syncnets]) { for (const [subnet, {toUnixMs, peersToConnect}] of this.subnetRequests[type].entries()) { if (toUnixMs < Date.now() || peersToConnect === 0) { diff --git a/packages/beacon-node/src/network/peers/peerManager.ts b/packages/beacon-node/src/network/peers/peerManager.ts index 43fbee723f0e..8279e0578ebb 100644 --- a/packages/beacon-node/src/network/peers/peerManager.ts +++ b/packages/beacon-node/src/network/peers/peerManager.ts @@ -1,5 +1,5 @@ import {Connection, PeerId} from "@libp2p/interface"; -import {BitArray} from "@chainsafe/ssz"; +import {BitArray, toHexString} from "@chainsafe/ssz"; import {SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params"; import {BeaconConfig} from "@lodestar/config"; import {Metadata, altair, phase0} from "@lodestar/types"; @@ -17,6 +17,7 @@ import {Eth2Gossipsub} from "../gossip/gossipsub.js"; import {StatusCache} from "../statusCache.js"; import {NetworkCoreMetrics} from "../core/metrics.js"; import {LodestarDiscv5Opts} from "../discv5/types.js"; +import {getCustodyColumns} from "../../util/dataColumns.js"; import {PeerDiscovery, SubnetDiscvQueryMs} from "./discover.js"; import {PeersData, PeerData} from "./peersData.js"; import {getKnownClientFromAgentVersion, ClientKind} from "./client.js"; @@ -376,7 +377,15 @@ export class PeerManager { peerData.relevantStatus = RelevantPeerStatus.relevant; } if (getConnection(this.libp2p, peer.toString())) { - this.networkEventBus.emit(NetworkEvent.peerConnected, {peer: peer.toString(), status}); + const nodeId = peerData?.nodeId ?? this.discovery?.["peerIdToNodeId"].get(peer.toString()); + const custodySubnetCount = + peerData?.custodySubnetCount ?? this.discovery?.["peerIdToCustodySubnetCount"].get(peer.toString()); + this.logger.warn("onStatus", {nodeId: nodeId ? toHexString(nodeId) : undefined, peerId: peer.toString()}); + + if (nodeId !== undefined && custodySubnetCount !== undefined) { + const dataColumns = getCustodyColumns(nodeId, custodySubnetCount); + this.networkEventBus.emit(NetworkEvent.peerConnected, {peer: peer.toString(), status, dataColumns}); + } } } @@ -586,6 +595,8 @@ export class PeerManager { // NOTE: libp2p may emit two "peer:connect" events: One for inbound, one for outbound // If that happens, it's okay. Only the "outbound" connection triggers immediate action const now = Date.now(); + const nodeId = this.discovery?.["peerIdToNodeId"].get(remotePeer.toString()) ?? null; + const custodySubnetCount = this.discovery?.["peerIdToCustodySubnetCount"].get(remotePeer.toString()) ?? null; const peerData: PeerData = { lastReceivedMsgUnixTsMs: direction === "outbound" ? 0 : now, // If inbound, request after STATUS_INBOUND_GRACE_PERIOD @@ -593,11 +604,13 @@ export class PeerManager { connectedUnixTsMs: now, relevantStatus: RelevantPeerStatus.Unknown, direction, + nodeId, peerId: remotePeer, metadata: null, agentVersion: null, agentClient: null, encodingPreference: null, + custodySubnetCount, }; this.connectedPeers.set(remotePeer.toString(), peerData); diff --git a/packages/beacon-node/src/network/peers/peersData.ts b/packages/beacon-node/src/network/peers/peersData.ts index 4f96548c73e4..3ea776917f73 100644 --- a/packages/beacon-node/src/network/peers/peersData.ts +++ b/packages/beacon-node/src/network/peers/peersData.ts @@ -1,6 +1,7 @@ import {PeerId} from "@libp2p/interface"; import {altair} from "@lodestar/types"; import {Encoding} from "@lodestar/reqresp"; +import {NodeId} from "../subnets/interface.js"; import {ClientKind} from "./client.js"; type PeerIdStr = string; @@ -18,10 +19,12 @@ export type PeerData = { relevantStatus: RelevantPeerStatus; direction: "inbound" | "outbound"; peerId: PeerId; + nodeId: NodeId | null; metadata: altair.Metadata | null; agentVersion: string | null; agentClient: ClientKind | null; encodingPreference: Encoding | null; + custodySubnetCount: number | null; }; /** diff --git a/packages/beacon-node/src/network/processor/extractSlotRootFns.ts b/packages/beacon-node/src/network/processor/extractSlotRootFns.ts index d31cb3e2d7f9..2c8d7462b121 100644 --- a/packages/beacon-node/src/network/processor/extractSlotRootFns.ts +++ b/packages/beacon-node/src/network/processor/extractSlotRootFns.ts @@ -6,6 +6,7 @@ import { getSlotFromSignedAggregateAndProofSerialized, getSlotFromBlobSidecarSerialized, getSlotFromSignedBeaconBlockSerialized, + getSlotFromDataColumnSidecarSerialized, } from "../../util/sszBytes.js"; import {GossipType} from "../gossip/index.js"; import {ExtractSlotRootFns} from "./types.js"; @@ -45,6 +46,14 @@ export function createExtractBlockSlotRootFns(): ExtractSlotRootFns { [GossipType.blob_sidecar]: (data: Uint8Array): SlotOptionalRoot | null => { const slot = getSlotFromBlobSidecarSerialized(data); + if (slot === null) { + return null; + } + return {slot}; + }, + [GossipType.data_column_sidecar]: (data: Uint8Array): SlotOptionalRoot | null => { + const slot = getSlotFromDataColumnSidecarSerialized(data); + if (slot === null) { return null; } diff --git a/packages/beacon-node/src/network/processor/gossipHandlers.ts b/packages/beacon-node/src/network/processor/gossipHandlers.ts index 82fe7d8db358..a9d9f22103be 100644 --- a/packages/beacon-node/src/network/processor/gossipHandlers.ts +++ b/packages/beacon-node/src/network/processor/gossipHandlers.ts @@ -1,7 +1,7 @@ import {toHexString} from "@chainsafe/ssz"; import {BeaconConfig, ChainForkConfig} from "@lodestar/config"; import {LogLevel, Logger, prettyBytes} from "@lodestar/utils"; -import {Root, Slot, ssz, deneb, UintNum64, SignedBeaconBlock} from "@lodestar/types"; +import {Root, Slot, ssz, deneb, UintNum64, SignedBeaconBlock, electra} from "@lodestar/types"; import {ForkName, ForkSeq} from "@lodestar/params"; import {routes} from "@lodestar/api"; import {computeTimeAtSlot} from "@lodestar/state-transition"; @@ -15,6 +15,8 @@ import { BlockGossipError, BlobSidecarErrorCode, BlobSidecarGossipError, + DataColumnSidecarGossipError, + DataColumnSidecarErrorCode, GossipAction, GossipActionError, SyncCommitteeError, @@ -46,6 +48,7 @@ import {PeerAction} from "../peers/index.js"; import {validateLightClientFinalityUpdate} from "../../chain/validation/lightClientFinalityUpdate.js"; import {validateLightClientOptimisticUpdate} from "../../chain/validation/lightClientOptimisticUpdate.js"; import {validateGossipBlobSidecar} from "../../chain/validation/blobSidecar.js"; +import {validateGossipDataColumnSidecar} from "../../chain/validation/dataColumnSidecar.js"; import { BlockInput, GossipedInputType, @@ -252,6 +255,74 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler } } + async function validateBeaconDataColumn( + dataColumnSidecar: electra.DataColumnSidecar, + dataColumnBytes: Uint8Array, + gossipIndex: number, + peerIdStr: string, + seenTimestampSec: number + ): Promise { + const dataColumnBlockHeader = dataColumnSidecar.signedBlockHeader.message; + const slot = dataColumnBlockHeader.slot; + const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(dataColumnBlockHeader); + const blockHex = prettyBytes(blockRoot); + + const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec); + const recvToValLatency = Date.now() / 1000 - seenTimestampSec; + + const {blockInput, blockInputMeta} = chain.seenGossipBlockInput.getGossipBlockInput( + config, + { + type: GossipedInputType.dataColumn, + dataColumnSidecar, + dataColumnBytes, + }, + metrics + ); + + try { + await validateGossipDataColumnSidecar(chain, dataColumnSidecar, gossipIndex); + const recvToValidation = Date.now() / 1000 - seenTimestampSec; + const validationTime = recvToValidation - recvToValLatency; + + metrics?.gossipBlob.recvToValidation.observe(recvToValidation); + metrics?.gossipBlob.validationTime.observe(validationTime); + + logger.debug("Received gossip dataColumn", { + slot: slot, + root: blockHex, + curentSlot: chain.clock.currentSlot, + peerId: peerIdStr, + delaySec, + gossipIndex, + ...blockInputMeta, + recvToValLatency, + recvToValidation, + validationTime, + }); + + return blockInput; + } catch (e) { + if (e instanceof DataColumnSidecarGossipError) { + // Don't trigger this yet if full block and blobs haven't arrived yet + if (e.type.code === DataColumnSidecarErrorCode.PARENT_UNKNOWN && blockInput.block !== null) { + logger.debug("Gossip dataColumn has error", {slot, root: blockHex, code: e.type.code}); + events.emit(NetworkEvent.unknownBlockParent, {blockInput, peer: peerIdStr}); + } + + if (e.action === GossipAction.REJECT) { + chain.persistInvalidSszValue( + ssz.electra.DataColumnSidecar, + dataColumnSidecar, + `gossip_reject_slot_${slot}_index_${dataColumnSidecar.index}` + ); + } + } + + throw e; + } + } + function handleValidBeaconBlock(blockInput: BlockInput, peerIdStr: string, seenTimestampSec: number): void { const signedBlock = blockInput.block; @@ -408,6 +479,63 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler } }, + [GossipType.data_column_sidecar]: async ({ + gossipData, + topic, + peerIdStr, + seenTimestampSec, + }: GossipHandlerParamGeneric) => { + const {serializedData} = gossipData; + const dataColumnSidecar = sszDeserialize(topic, serializedData); + const blobSlot = dataColumnSidecar.signedBlockHeader.message.slot; + const index = dataColumnSidecar.index; + + if (config.getForkSeq(blobSlot) < ForkSeq.deneb) { + throw new GossipActionError(GossipAction.REJECT, {code: "PRE_DENEB_BLOCK"}); + } + const blockInput = await validateBeaconDataColumn( + dataColumnSidecar, + serializedData, + topic.index, + peerIdStr, + seenTimestampSec + ); + if (blockInput.block !== null) { + // we can just queue up the blockInput in the processor, but block gossip handler would have already + // queued it up. + // + // handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec); + } else { + // wait for the block to arrive till some cutoff else emit unknownBlockInput event + chain.logger.debug("Block not yet available, racing with cutoff", {blobSlot, index}); + const normalBlockInput = await raceWithCutoff( + chain, + blobSlot, + blockInput.blockInputPromise, + BLOCK_AVAILABILITY_CUTOFF_MS + ).catch((_e) => { + return null; + }); + + if (normalBlockInput !== null) { + chain.logger.debug("Block corresponding to blob is now available for processing", {blobSlot, index}); + // we can directly send it for processing but block gossip handler will queue it up anyway + // if we see any issues later, we can send it to handleValidBeaconBlock + // + // handleValidBeaconBlock(normalBlockInput, peerIdStr, seenTimestampSec); + // + // however we can emit the event which will atleast add the peer to the list of peers to pull + // data from + if (normalBlockInput.type === BlockInputType.dataPromise) { + events.emit(NetworkEvent.unknownBlockInput, {blockInput: normalBlockInput, peer: peerIdStr}); + } + } else { + chain.logger.debug("Block not available till BLOCK_AVAILABILITY_CUTOFF_MS", {blobSlot, index}); + events.emit(NetworkEvent.unknownBlockInput, {blockInput, peer: peerIdStr}); + } + } + }, + [GossipType.beacon_aggregate_and_proof]: async ({ gossipData, topic, diff --git a/packages/beacon-node/src/network/processor/gossipQueues/index.ts b/packages/beacon-node/src/network/processor/gossipQueues/index.ts index 366b23b30679..8d38e0e1e14a 100644 --- a/packages/beacon-node/src/network/processor/gossipQueues/index.ts +++ b/packages/beacon-node/src/network/processor/gossipQueues/index.ts @@ -39,6 +39,11 @@ const defaultGossipQueueOpts: { type: QueueType.FIFO, dropOpts: {type: DropType.count, count: 1}, }, + [GossipType.data_column_sidecar]: { + maxLength: 4096, + type: QueueType.FIFO, + dropOpts: {type: DropType.count, count: 1}, + }, // lighthoue has aggregate_queue 4096 and unknown_block_aggregate_queue 1024, we use single queue [GossipType.beacon_aggregate_and_proof]: { maxLength: 5120, diff --git a/packages/beacon-node/src/network/processor/index.ts b/packages/beacon-node/src/network/processor/index.ts index 9a1dcfb32fa0..2a88054b1498 100644 --- a/packages/beacon-node/src/network/processor/index.ts +++ b/packages/beacon-node/src/network/processor/index.ts @@ -66,6 +66,7 @@ type WorkOpts = { const executeGossipWorkOrderObj: Record = { [GossipType.beacon_block]: {bypassQueue: true}, [GossipType.blob_sidecar]: {bypassQueue: true}, + [GossipType.data_column_sidecar]: {bypassQueue: true}, [GossipType.beacon_aggregate_and_proof]: {}, [GossipType.voluntary_exit]: {}, [GossipType.bls_to_execution_change]: {}, @@ -268,7 +269,12 @@ export class NetworkProcessor { }); return; } - if (slot === clockSlot && (topicType === GossipType.beacon_block || topicType === GossipType.blob_sidecar)) { + if ( + slot === clockSlot && + (topicType === GossipType.beacon_block || + topicType === GossipType.blob_sidecar || + topicType === GossipType.data_column_sidecar) + ) { // in the worse case if the current slot block is not valid, this will be reset in the next slot this.isProcessingCurrentSlotBlock = true; } diff --git a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts index 8d131e9e9945..50126898ee06 100644 --- a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts +++ b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts @@ -253,6 +253,13 @@ export class ReqRespBeaconNode extends ReqResp { ); } + if (ForkSeq[fork] >= ForkSeq.electra) { + protocolsAtFork.push( + [protocols.DataColumnSidecarsByRoot(this.config), this.getHandler(ReqRespMethod.DataColumnSidecarsByRoot)], + [protocols.DataColumnSidecarsByRange(this.config), this.getHandler(ReqRespMethod.DataColumnSidecarsByRange)] + ); + } + return protocolsAtFork; } diff --git a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts index 4dae4831d716..0479e6f2c3c1 100644 --- a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts +++ b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts @@ -1,11 +1,20 @@ import {ChainForkConfig} from "@lodestar/config"; -import {deneb, Epoch, phase0, SignedBeaconBlock, Slot} from "@lodestar/types"; -import {ForkSeq} from "@lodestar/params"; +import {deneb, Epoch, phase0, SignedBeaconBlock, Slot, electra} from "@lodestar/types"; +import {ForkSeq, NUMBER_OF_COLUMNS} from "@lodestar/params"; import {computeEpochAtSlot} from "@lodestar/state-transition"; -import {BlobsSource, BlockInput, BlockSource, getBlockInput, BlockInputDataBlobs} from "../../chain/blocks/types.js"; +import { + BlobsSource, + BlockInput, + BlockSource, + getBlockInput, + BlockInputDataBlobs, + BlockInputDataDataColumns, + DataColumnsSource, +} from "../../chain/blocks/types.js"; import {PeerIdStr} from "../../util/peerId.js"; import {INetwork, WithBytes} from "../interface.js"; +import {CustodyConfig} from "../../util/dataColumns.js"; export async function beaconBlocksMaybeBlobsByRange( config: ChainForkConfig, @@ -30,20 +39,44 @@ export async function beaconBlocksMaybeBlobsByRange( ); } + const forkSeq = config.getForkSeq(startSlot); + // Note: Assumes all blocks in the same epoch - if (config.getForkSeq(startSlot) < ForkSeq.deneb) { + if (forkSeq < ForkSeq.deneb) { const blocks = await network.sendBeaconBlocksByRange(peerId, request); return blocks.map((block) => getBlockInput.preData(config, block.data, BlockSource.byRange, block.bytes)); } // Only request blobs if they are recent enough else if (computeEpochAtSlot(startSlot) >= currentEpoch - config.MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS) { - const [allBlocks, allBlobSidecars] = await Promise.all([ - network.sendBeaconBlocksByRange(peerId, request), - network.sendBlobSidecarsByRange(peerId, request), - ]); + if (forkSeq < ForkSeq.electra) { + const [allBlocks, allBlobSidecars] = await Promise.all([ + network.sendBeaconBlocksByRange(peerId, request), + network.sendBlobSidecarsByRange(peerId, request), + ]); + + return matchBlockWithBlobs(config, allBlocks, allBlobSidecars, endSlot, BlockSource.byRange, BlobsSource.byRange); + } else { + const {custodyConfig} = network; + // get columns + const {custodyColumns: columns} = custodyConfig; + const dataColumnRequest = {...request, columns}; + const [allBlocks, allDataColumnSidecars] = await Promise.all([ + network.sendBeaconBlocksByRange(peerId, request), + network.sendDataColumnSidecarsByRange(peerId, dataColumnRequest), + ]); - return matchBlockWithBlobs(config, allBlocks, allBlobSidecars, endSlot, BlockSource.byRange, BlobsSource.byRange); + return matchBlockWithDataColumns( + peerId, + config, + custodyConfig, + allBlocks, + allDataColumnSidecars, + endSlot, + BlockSource.byRange, + DataColumnsSource.byRange + ); + } } // Post Deneb but old blobs @@ -125,3 +158,111 @@ export function matchBlockWithBlobs( } return blockInputs; } + +export function matchBlockWithDataColumns( + peerId: PeerIdStr, + config: ChainForkConfig, + custodyConfig: CustodyConfig, + allBlocks: WithBytes[], + allDataColumnSidecars: electra.DataColumnSidecar[], + endSlot: Slot, + blockSource: BlockSource, + dataColumnsSource: DataColumnsSource +): BlockInput[] { + const blockInputs: BlockInput[] = []; + let dataColumnSideCarIndex = 0; + let lastMatchedSlot = -1; + + // Match dataColumnSideCar with the block as some blocks would have no dataColumns and hence + // would be omitted from the response. If there are any inconsitencies in the + // response, the validations during import will reject the block and hence this + // entire segment. + // + // Assuming that the blocks and blobs will come in same sorted order + for (let i = 0; i < allBlocks.length; i++) { + const block = allBlocks[i]; + + const forkSeq = config.getForkSeq(block.data.message.slot); + if (forkSeq < ForkSeq.electra) { + throw Error(`Invalid block forkSeq=${forkSeq} < ForSeq.electra for matchBlockWithDataColumns`); + } else { + const dataColumnSidecars: electra.DataColumnSidecar[] = []; + let dataColumnSidecar: electra.DataColumnSidecar; + while ( + (dataColumnSidecar = allDataColumnSidecars[dataColumnSideCarIndex])?.signedBlockHeader.message.slot === + block.data.message.slot + ) { + dataColumnSidecars.push(dataColumnSidecar); + lastMatchedSlot = block.data.message.slot; + dataColumnSideCarIndex++; + } + + const blobKzgCommitmentsLen = (block.data.message.body as deneb.BeaconBlockBody).blobKzgCommitments.length; + if (blobKzgCommitmentsLen === 0) { + if (dataColumnSidecars.length > 0) { + throw Error( + `Missing or mismatching dataColumnSidecars from peerId=${peerId} for blockSlot=${block.data.message.slot} with blobKzgCommitmentsLen=0 dataColumnSidecars=${dataColumnSidecars.length}>0` + ); + } + + const blockData = { + fork: config.getForkName(block.data.message.slot), + dataColumnsLen: 0, + dataColumnsIndex: new Uint8Array(NUMBER_OF_COLUMNS), + dataColumns: [], + dataColumnsBytes: [], + dataColumnsSource, + } as BlockInputDataDataColumns; + blockInputs.push(getBlockInput.availableData(config, block.data, blockSource, null, blockData)); + } else { + // Quick inspect how many blobSidecars was expected + const {custodyColumns: columnIndexes, custodyColumnsLen, custodyColumnsIndex} = custodyConfig; + const dataColumnIndexes = dataColumnSidecars.map((dataColumnSidecar) => dataColumnSidecar.index); + const custodyIndexesPresent = columnIndexes.reduce( + (acc, columnIndex) => acc && dataColumnIndexes.includes(columnIndex), + true + ); + + if ( + dataColumnSidecars.length < custodyColumnsLen || + dataColumnSidecars.length > NUMBER_OF_COLUMNS || + !custodyIndexesPresent + ) { + throw Error( + `Missing or mismatching dataColumnSidecars from peerId=${peerId} for blockSlot=${block.data.message.slot} with numColumns=${columnIndexes.length} dataColumnSidecars=${dataColumnSidecars.length} custodyIndexesPresent=${custodyIndexesPresent} received dataColumnIndexes=${dataColumnIndexes.join(",")} expected=${columnIndexes.join(",")}` + ); + } + + const blockData = { + fork: config.getForkName(block.data.message.slot), + dataColumnsLen: custodyColumnsLen, + dataColumnsIndex: custodyColumnsIndex, + dataColumns: dataColumnSidecars, + dataColumnsSource, + dataColumnsBytes: Array.from({length: dataColumnSidecars.length}, () => null), + } as BlockInputDataDataColumns; + + // TODO DENEB: instead of null, pass payload in bytes + blockInputs.push(getBlockInput.availableData(config, block.data, blockSource, null, blockData)); + } + } + } + + // If there are still unconsumed blobs this means that the response was inconsistent + // and matching was wrong and hence we should throw error + if ( + allDataColumnSidecars[dataColumnSideCarIndex] !== undefined && + // If there are no data columns, the data columns request can give 1 block outside the requested range + allDataColumnSidecars[dataColumnSideCarIndex].signedBlockHeader.message.slot <= endSlot + ) { + throw Error( + `Unmatched blobSidecars, blocks=${allBlocks.length}, blobs=${ + allDataColumnSidecars.length + } lastMatchedSlot=${lastMatchedSlot}, pending blobSidecars slots=${allDataColumnSidecars + .slice(dataColumnSideCarIndex) + .map((blb) => blb.signedBlockHeader.message.slot) + .join(",")}` + ); + } + return blockInputs; +} diff --git a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRoot.ts b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRoot.ts index 2b802ab1edd9..ec5ac60fd599 100644 --- a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRoot.ts +++ b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRoot.ts @@ -1,7 +1,7 @@ import {fromHexString} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; -import {phase0, deneb} from "@lodestar/types"; -import {ForkSeq} from "@lodestar/params"; +import {phase0, deneb, electra} from "@lodestar/types"; +import {ForkName, ForkSeq, NUMBER_OF_COLUMNS} from "@lodestar/params"; import { BlockInput, BlockInputType, @@ -11,12 +11,15 @@ import { NullBlockInput, BlobsSource, BlockInputDataBlobs, + DataColumnsSource, + getBlockInputDataColumns, + BlockInputDataDataColumns, } from "../../chain/blocks/types.js"; import {PeerIdStr} from "../../util/peerId.js"; import {INetwork} from "../interface.js"; import {BlockInputAvailabilitySource} from "../../chain/seenCache/seenGossipBlockInput.js"; import {Metrics} from "../../metrics/index.js"; -import {matchBlockWithBlobs} from "./beaconBlocksMaybeBlobsByRange.js"; +import {matchBlockWithBlobs, matchBlockWithDataColumns} from "./beaconBlocksMaybeBlobsByRange.js"; export async function beaconBlocksMaybeBlobsByRoot( config: ChainForkConfig, @@ -25,31 +28,86 @@ export async function beaconBlocksMaybeBlobsByRoot( request: phase0.BeaconBlocksByRootRequest ): Promise { const allBlocks = await network.sendBeaconBlocksByRoot(peerId, request); + const preDataBlocks = []; + const blobsDataBlocks = []; + const dataColumnsDataBlocks = []; + const blobIdentifiers: deneb.BlobIdentifier[] = []; + const dataColumnIdentifiers: electra.DataColumnIdentifier[] = []; for (const block of allBlocks) { const slot = block.data.message.slot; const blockRoot = config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.data.message); const fork = config.getForkName(slot); - - if (ForkSeq[fork] >= ForkSeq.deneb) { + if (ForkSeq[fork] < ForkSeq.deneb) { + preDataBlocks.push(block); + } else if (fork === ForkName.deneb) { + blobsDataBlocks.push(block); const blobKzgCommitmentsLen = (block.data.message.body as deneb.BeaconBlockBody).blobKzgCommitments.length; for (let index = 0; index < blobKzgCommitmentsLen; index++) { blobIdentifiers.push({blockRoot, index}); } + } else if (fork === ForkName.electra) { + dataColumnsDataBlocks.push(block); + const blobKzgCommitmentsLen = (block.data.message.body as deneb.BeaconBlockBody).blobKzgCommitments.length; + const custodyColumnIndexes = blobKzgCommitmentsLen > 0 ? network.custodyConfig.custodyColumns : []; + for (const columnIndex of custodyColumnIndexes) { + dataColumnIdentifiers.push({blockRoot, index: columnIndex}); + } + } else { + throw Error(`Invalid fork=${fork} in beaconBlocksMaybeBlobsByRoot`); } } - let allBlobSidecars: deneb.BlobSidecar[]; - if (blobIdentifiers.length > 0) { - allBlobSidecars = await network.sendBlobSidecarsByRoot(peerId, blobIdentifiers); - } else { - allBlobSidecars = []; + let blockInputs = preDataBlocks.map((block) => + getBlockInput.preData(config, block.data, BlockSource.byRoot, block.bytes) + ); + + if (blobsDataBlocks.length > 0) { + let allBlobSidecars: deneb.BlobSidecar[]; + if (blobIdentifiers.length > 0) { + allBlobSidecars = await network.sendBlobSidecarsByRoot(peerId, blobIdentifiers); + } else { + allBlobSidecars = []; + } + + // The last arg is to provide slot to which all blobs should be exausted in matching + // and here it should be infinity since all bobs should match + const blockInputWithBlobs = matchBlockWithBlobs( + config, + allBlocks, + allBlobSidecars, + Infinity, + BlockSource.byRoot, + BlobsSource.byRoot + ); + blockInputs = [...blockInputs, ...blockInputWithBlobs]; + } + + if (dataColumnsDataBlocks.length > 0) { + let allDataColumnsSidecars: electra.DataColumnSidecar[]; + if (dataColumnIdentifiers.length > 0) { + allDataColumnsSidecars = await network.sendDataColumnSidecarsByRoot(peerId, dataColumnIdentifiers); + } else { + allDataColumnsSidecars = []; + } + + // The last arg is to provide slot to which all blobs should be exausted in matching + // and here it should be infinity since all bobs should match + const blockInputWithBlobs = matchBlockWithDataColumns( + peerId, + config, + network.custodyConfig, + allBlocks, + allDataColumnsSidecars, + Infinity, + BlockSource.byRoot, + DataColumnsSource.byRoot + ); + blockInputs = [...blockInputs, ...blockInputWithBlobs]; } - // The last arg is to provide slot to which all blobs should be exausted in matching - // and here it should be infinity since all bobs should match - return matchBlockWithBlobs(config, allBlocks, allBlobSidecars, Infinity, BlockSource.byRoot, BlobsSource.byRoot); + return blockInputs; } export async function unavailableBeaconBlobsByRoot( @@ -64,51 +122,122 @@ export async function unavailableBeaconBlobsByRoot( } // resolve the block if thats unavailable - let block, blobsCache, blockBytes, resolveAvailability, cachedData; + let block, blockBytes, cachedData; if (unavailableBlockInput.block === null) { const allBlocks = await network.sendBeaconBlocksByRoot(peerId, [fromHexString(unavailableBlockInput.blockRootHex)]); block = allBlocks[0].data; blockBytes = allBlocks[0].bytes; cachedData = unavailableBlockInput.cachedData; - ({blobsCache, resolveAvailability} = cachedData); } else { ({block, cachedData, blockBytes} = unavailableBlockInput); - ({blobsCache, resolveAvailability} = cachedData); } - // resolve missing blobs - const blobIdentifiers: deneb.BlobIdentifier[] = []; - const slot = block.message.slot; - const blockRoot = config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message); + let availableBlockInput; + if (cachedData.fork === ForkName.deneb) { + const {blobsCache, resolveAvailability} = cachedData; - const blobKzgCommitmentsLen = (block.message.body as deneb.BeaconBlockBody).blobKzgCommitments.length; - for (let index = 0; index < blobKzgCommitmentsLen; index++) { - if (blobsCache.has(index) === false) blobIdentifiers.push({blockRoot, index}); - } + // resolve missing blobs + const blobIdentifiers: deneb.BlobIdentifier[] = []; + const slot = block.message.slot; + const blockRoot = config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message); - let allBlobSidecars: deneb.BlobSidecar[]; - if (blobIdentifiers.length > 0) { - allBlobSidecars = await network.sendBlobSidecarsByRoot(peerId, blobIdentifiers); - } else { - allBlobSidecars = []; - } + const blobKzgCommitmentsLen = (block.message.body as deneb.BeaconBlockBody).blobKzgCommitments.length; + for (let index = 0; index < blobKzgCommitmentsLen; index++) { + if (blobsCache.has(index) === false) blobIdentifiers.push({blockRoot, index}); + } - // add them in cache so that its reflected in all the blockInputs that carry this - // for e.g. a blockInput that might be awaiting blobs promise fullfillment in - // verifyBlocksDataAvailability - for (const blobSidecar of allBlobSidecars) { - blobsCache.set(blobSidecar.index, {blobSidecar, blobBytes: null}); - } + let allBlobSidecars: deneb.BlobSidecar[]; + if (blobIdentifiers.length > 0) { + allBlobSidecars = await network.sendBlobSidecarsByRoot(peerId, blobIdentifiers); + } else { + allBlobSidecars = []; + } + + // add them in cache so that its reflected in all the blockInputs that carry this + // for e.g. a blockInput that might be awaiting blobs promise fullfillment in + // verifyBlocksDataAvailability + for (const blobSidecar of allBlobSidecars) { + blobsCache.set(blobSidecar.index, {blobSidecar, blobBytes: null}); + } + + // check and see if all blobs are now available and in that case resolve availability + // if not this will error and the leftover blobs will be tried from another peer + const allBlobs = getBlockInputBlobs(blobsCache); + const {blobs} = allBlobs; + if (blobs.length !== blobKzgCommitmentsLen) { + throw Error(`Not all blobs fetched missingBlobs=${blobKzgCommitmentsLen - blobs.length}`); + } + const blockData = {fork: cachedData.fork, ...allBlobs, blobsSource: BlobsSource.byRoot} as BlockInputDataBlobs; + resolveAvailability(blockData); + metrics?.syncUnknownBlock.resolveAvailabilitySource.inc({source: BlockInputAvailabilitySource.UNKNOWN_SYNC}); + availableBlockInput = getBlockInput.availableData(config, block, BlockSource.byRoot, blockBytes, blockData); + } else if (cachedData.fork === ForkName.electra) { + const {dataColumnsCache, resolveAvailability} = cachedData; + + // resolve missing blobs + const dataColumnIdentifiers: electra.DataColumnIdentifier[] = []; + const slot = block.message.slot; + const blockRoot = config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message); + + const blobKzgCommitmentsLen = (block.message.body as deneb.BeaconBlockBody).blobKzgCommitments.length; + if (blobKzgCommitmentsLen === 0) { + const blockData = { + fork: cachedData.fork, + dataColumns: [], + dataColumnsBytes: [], + dataColumnsLen: 0, + dataColumnsIndex: new Uint8Array(NUMBER_OF_COLUMNS), + dataColumnsSource: DataColumnsSource.gossip, + } as BlockInputDataDataColumns; + + resolveAvailability(blockData); + metrics?.syncUnknownBlock.resolveAvailabilitySource.inc({source: BlockInputAvailabilitySource.UNKNOWN_SYNC}); + availableBlockInput = getBlockInput.availableData(config, block, BlockSource.byRoot, blockBytes, blockData); + } else { + const custodyColumnIndexes = network.custodyConfig.custodyColumns; + for (const columnIndex of custodyColumnIndexes) { + if (dataColumnsCache.has(columnIndex) === false) { + dataColumnIdentifiers.push({blockRoot, index: columnIndex}); + } + } - // check and see if all blobs are now available and in that case resolve availability - // if not this will error and the leftover blobs will be tried from another peer - const allBlobs = getBlockInputBlobs(blobsCache); - const {blobs} = allBlobs; - if (blobs.length !== blobKzgCommitmentsLen) { - throw Error(`Not all blobs fetched missingBlobs=${blobKzgCommitmentsLen - blobs.length}`); + let allDataColumnSidecars: electra.DataColumnSidecar[]; + if (dataColumnIdentifiers.length > 0) { + allDataColumnSidecars = await network.sendDataColumnSidecarsByRoot(peerId, dataColumnIdentifiers); + } else { + allDataColumnSidecars = []; + } + + // add them in cache so that its reflected in all the blockInputs that carry this + // for e.g. a blockInput that might be awaiting blobs promise fullfillment in + // verifyBlocksDataAvailability + for (const dataColumnSidecar of allDataColumnSidecars) { + dataColumnsCache.set(dataColumnSidecar.index, {dataColumnSidecar, dataColumnBytes: null}); + } + + // check and see if all blobs are now available and in that case resolve availability + // if not this will error and the leftover blobs will be tried from another peer + const allDataColumns = getBlockInputDataColumns(dataColumnsCache, custodyColumnIndexes); + const {dataColumns} = allDataColumns; + if (dataColumns.length !== network.custodyConfig.custodyColumnsLen) { + throw Error( + `Not all dataColumns fetched missingColumns=${network.custodyConfig.custodyColumnsLen - dataColumns.length}` + ); + } + const blockData = { + fork: cachedData.fork, + ...allDataColumns, + dataColumnsLen: network.custodyConfig.custodyColumnsLen, + dataColumnsIndex: network.custodyConfig.custodyColumnsIndex, + dataColumnsSource: DataColumnsSource.byRoot, + } as BlockInputDataDataColumns; + resolveAvailability(blockData); + metrics?.syncUnknownBlock.resolveAvailabilitySource.inc({source: BlockInputAvailabilitySource.UNKNOWN_SYNC}); + availableBlockInput = getBlockInput.availableData(config, block, BlockSource.byRoot, blockBytes, blockData); + } + } else { + throw Error(`Invalid cachedData fork=${cachedData.fork} for unavailableBeaconBlobsByRoot`); } - const blockData = {fork: cachedData.fork, ...allBlobs, blobsSource: BlobsSource.byRoot} as BlockInputDataBlobs; - resolveAvailability(blockData); - metrics?.syncUnknownBlock.resolveAvailabilitySource.inc({source: BlockInputAvailabilitySource.UNKNOWN_SYNC}); - return getBlockInput.availableData(config, block, BlockSource.byRoot, blockBytes, blockData); + + return availableBlockInput; } diff --git a/packages/beacon-node/src/network/reqresp/handlers/beaconBlocksByRange.ts b/packages/beacon-node/src/network/reqresp/handlers/beaconBlocksByRange.ts index d1046db9651d..e892b2f412dd 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/beaconBlocksByRange.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/beaconBlocksByRange.ts @@ -69,7 +69,7 @@ export async function* onBeaconBlocksByRange( } export function validateBeaconBlocksByRangeRequest( - request: deneb.BlobSidecarsByRangeRequest + request: phase0.BeaconBlocksByRangeRequest ): deneb.BlobSidecarsByRangeRequest { const {startSlot} = request; let {count} = request; diff --git a/packages/beacon-node/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts b/packages/beacon-node/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts new file mode 100644 index 000000000000..903c4a64b69f --- /dev/null +++ b/packages/beacon-node/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts @@ -0,0 +1,125 @@ +import {GENESIS_SLOT, MAX_REQUEST_BLOCKS_DENEB} from "@lodestar/params"; +import {ResponseError, ResponseOutgoing, RespStatus} from "@lodestar/reqresp"; +import {electra, Slot, ssz, ColumnIndex} from "@lodestar/types"; +import {fromHex} from "@lodestar/utils"; +import {IBeaconChain} from "../../../chain/index.js"; +import {IBeaconDb} from "../../../db/index.js"; +import { + DATA_COLUMN_SIDECARS_IN_WRAPPER_INDEX, + COLUMN_SIZE_IN_WRAPPER_INDEX, + CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX, +} from "../../../db/repositories/dataColumnSidecars.js"; + +export async function* onDataColumnSidecarsByRange( + request: electra.DataColumnSidecarsByRangeRequest, + chain: IBeaconChain, + db: IBeaconDb +): AsyncIterable { + // Non-finalized range of blobs + const {startSlot, count, columns} = validateDataColumnSidecarsByRangeRequest(request); + const endSlot = startSlot + count; + + const finalized = db.dataColumnSidecarsArchive; + const unfinalized = db.dataColumnSidecars; + const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot; + + // Finalized range of blobs + if (startSlot <= finalizedSlot) { + // Chain of blobs won't change + for await (const {key, value: dataColumnSideCarsBytesWrapped} of finalized.binaryEntriesStream({ + gte: startSlot, + lt: endSlot, + })) { + yield* iterateDataColumnBytesFromWrapper( + chain, + dataColumnSideCarsBytesWrapped, + finalized.decodeKey(key), + columns + ); + } + } + + // Non-finalized range of blobs + if (endSlot > finalizedSlot) { + const headRoot = chain.forkChoice.getHeadRoot(); + // TODO DENEB: forkChoice should mantain an array of canonical blocks, and change only on reorg + const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot); + + // Iterate head chain with ascending block numbers + for (let i = headChain.length - 1; i >= 0; i--) { + const block = headChain[i]; + + // Must include only blobs in the range requested + if (block.slot >= startSlot && block.slot < endSlot) { + // Note: Here the forkChoice head may change due to a re-org, so the headChain reflects the canonical chain + // at the time of the start of the request. Spec is clear the chain of blobs must be consistent, but on + // re-org there's no need to abort the request + // Spec: https://github.com/ethereum/consensus-specs/blob/a1e46d1ae47dd9d097725801575b46907c12a1f8/specs/eip4844/p2p-interface.md#blobssidecarsbyrange-v1 + + const blobSideCarsBytesWrapped = await unfinalized.getBinary(fromHex(block.blockRoot)); + if (!blobSideCarsBytesWrapped) { + // Handle the same to onBeaconBlocksByRange + throw new ResponseError(RespStatus.SERVER_ERROR, `No item for root ${block.blockRoot} slot ${block.slot}`); + } + yield* iterateDataColumnBytesFromWrapper(chain, blobSideCarsBytesWrapped, block.slot, columns); + } + + // If block is after endSlot, stop iterating + else if (block.slot >= endSlot) { + break; + } + } + } +} + +export function* iterateDataColumnBytesFromWrapper( + chain: IBeaconChain, + dataColumnSidecarsBytesWrapped: Uint8Array, + blockSlot: Slot, + // use the columns include to see if you want to yield in response + _columns: ColumnIndex[] +): Iterable { + const retrievedColumnsSizeBytes = dataColumnSidecarsBytesWrapped.slice( + COLUMN_SIZE_IN_WRAPPER_INDEX, + CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX + ); + const columnsSize = ssz.UintNum64.deserialize(retrievedColumnsSizeBytes); + const allDataColumnSidecarsBytes = dataColumnSidecarsBytesWrapped.slice(DATA_COLUMN_SIDECARS_IN_WRAPPER_INDEX); + + const columnsLen = allDataColumnSidecarsBytes.length / columnsSize; + + for (let index = 0; index < columnsLen; index++) { + const dataColumnSidecarBytes = allDataColumnSidecarsBytes.slice(index * columnsSize, (index + 1) * columnsSize); + if (dataColumnSidecarBytes.length !== columnsSize) { + throw new ResponseError( + RespStatus.SERVER_ERROR, + `Invalid dataColumnSidecar index=${index} bytes length=${dataColumnSidecarBytes.length} expected=${columnsSize} for slot ${blockSlot} blobsLen=${columnsLen}` + ); + } + yield { + data: dataColumnSidecarBytes, + fork: chain.config.getForkName(blockSlot), + }; + } +} + +export function validateDataColumnSidecarsByRangeRequest( + request: electra.DataColumnSidecarsByRangeRequest +): electra.DataColumnSidecarsByRangeRequest { + const {startSlot, columns} = 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 > MAX_REQUEST_BLOCKS_DENEB) { + count = MAX_REQUEST_BLOCKS_DENEB; + } + + return {startSlot, count, columns}; +} diff --git a/packages/beacon-node/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts b/packages/beacon-node/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts new file mode 100644 index 000000000000..dea728cd62ef --- /dev/null +++ b/packages/beacon-node/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts @@ -0,0 +1,90 @@ +import {ResponseError, ResponseOutgoing, RespStatus} from "@lodestar/reqresp"; +import {NUMBER_OF_COLUMNS} from "@lodestar/params"; +import {electra, RootHex, ssz} from "@lodestar/types"; +import {toHex, fromHex} from "@lodestar/utils"; +import {IBeaconChain} from "../../../chain/index.js"; +import {IBeaconDb} from "../../../db/index.js"; +import { + DATA_COLUMN_SIDECARS_IN_WRAPPER_INDEX, + CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX, + COLUMN_SIZE_IN_WRAPPER_INDEX, +} from "../../../db/repositories/dataColumnSidecars.js"; + +export async function* onDataColumnSidecarsByRoot( + requestBody: electra.DataColumnSidecarsByRootRequest, + chain: IBeaconChain, + db: IBeaconDb +): AsyncIterable { + const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot; + + // In sidecars by root request, it can be expected that sidecar requests will be come + // clustured by blockroots, and this helps us save db lookups once we load sidecars + // for a root + let lastFetchedSideCars: { + blockRoot: RootHex; + bytes: Uint8Array; + custodyColumns: Uint8Array; + columnsSize: number; + } | null = null; + + for (const dataColumnIdentifier of requestBody) { + const {blockRoot, index} = dataColumnIdentifier; + const blockRootHex = toHex(blockRoot); + const block = chain.forkChoice.getBlockHex(blockRootHex); + + // NOTE: Only support non-finalized blocks. + // SPEC: Clients MUST support requesting blocks and sidecars since the latest finalized epoch. + // https://github.com/ethereum/consensus-specs/blob/11a037fd9227e29ee809c9397b09f8cc3383a8c0/specs/eip4844/p2p-interface.md#beaconblockandblobssidecarbyroot-v1 + if (!block || block.slot <= finalizedSlot) { + continue; + } + + // Check if we need to load sidecars for a new block root + if (lastFetchedSideCars === null || lastFetchedSideCars.blockRoot !== blockRootHex) { + const dataColumnSidecarsBytesWrapped = await db.dataColumnSidecars.getBinary(fromHex(block.blockRoot)); + if (!dataColumnSidecarsBytesWrapped) { + // Handle the same to onBeaconBlocksByRange + throw new ResponseError(RespStatus.SERVER_ERROR, `No item for root ${block.blockRoot} slot ${block.slot}`); + } + + const retrievedColumnsSizeBytes = dataColumnSidecarsBytesWrapped.slice( + COLUMN_SIZE_IN_WRAPPER_INDEX, + CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX + ); + const columnsSize = ssz.UintNum64.deserialize(retrievedColumnsSizeBytes); + const dataColumnSidecarsBytes = dataColumnSidecarsBytesWrapped.slice(DATA_COLUMN_SIDECARS_IN_WRAPPER_INDEX); + + const custodyColumns = dataColumnSidecarsBytesWrapped.slice( + CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX, + CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX + NUMBER_OF_COLUMNS + ); + + lastFetchedSideCars = {blockRoot: blockRootHex, bytes: dataColumnSidecarsBytes, columnsSize, custodyColumns}; + } + + const dataIndex = lastFetchedSideCars.custodyColumns[index]; + const {columnsSize} = lastFetchedSideCars; + + if (dataIndex === undefined || dataIndex === 0) { + throw Error( + `Missing dataColumnSidecar blockRoot=${blockRootHex} index=${index} calculated dataIndex=${dataIndex}` + ); + } + + // dataIndex is 1 based index + const dataColumnSidecarBytes = lastFetchedSideCars.bytes.slice( + (dataIndex - 1) * columnsSize, + dataIndex * columnsSize + ); + if (dataColumnSidecarBytes.length !== columnsSize) { + throw Error( + `Inconsistent state, dataColumnSidecar blockRoot=${blockRootHex} index=${index} dataColumnSidecarBytes=${dataColumnSidecarBytes.length} expected=${columnsSize}` + ); + } + + yield { + data: dataColumnSidecarBytes, + fork: chain.config.getForkName(block.slot), + }; + } +} diff --git a/packages/beacon-node/src/network/reqresp/handlers/index.ts b/packages/beacon-node/src/network/reqresp/handlers/index.ts index 50b8cc870844..0c4732310641 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/index.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/index.ts @@ -7,6 +7,8 @@ import {onBeaconBlocksByRange} from "./beaconBlocksByRange.js"; import {onBeaconBlocksByRoot} from "./beaconBlocksByRoot.js"; import {onBlobSidecarsByRoot} from "./blobSidecarsByRoot.js"; import {onBlobSidecarsByRange} from "./blobSidecarsByRange.js"; +import {onDataColumnSidecarsByRange} from "./dataColumnSidecarsByRange.js"; +import {onDataColumnSidecarsByRoot} from "./dataColumnSidecarsByRoot.js"; import {onLightClientBootstrap} from "./lightClientBootstrap.js"; import {onLightClientFinalityUpdate} from "./lightClientFinalityUpdate.js"; import {onLightClientOptimisticUpdate} from "./lightClientOptimisticUpdate.js"; @@ -44,6 +46,15 @@ export function getReqRespHandlers({db, chain}: {db: IBeaconDb; chain: IBeaconCh const body = ssz.deneb.BlobSidecarsByRangeRequest.deserialize(req.data); return onBlobSidecarsByRange(body, chain, db); }, + [ReqRespMethod.DataColumnSidecarsByRange]: (req) => { + const body = ssz.electra.DataColumnSidecarsByRangeRequest.deserialize(req.data); + return onDataColumnSidecarsByRange(body, chain, db); + }, + [ReqRespMethod.DataColumnSidecarsByRoot]: (req) => { + const body = ssz.electra.DataColumnSidecarsByRootRequest.deserialize(req.data); + return onDataColumnSidecarsByRoot(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 a0fa9576c93c..4fd96f6be711 100644 --- a/packages/beacon-node/src/network/reqresp/protocols.ts +++ b/packages/beacon-node/src/network/reqresp/protocols.ts @@ -71,6 +71,18 @@ export const BlobSidecarsByRoot = toProtocol({ contextBytesType: ContextBytesType.ForkDigest, }); +export const DataColumnSidecarsByRange = toProtocol({ + method: ReqRespMethod.DataColumnSidecarsByRange, + version: Version.V1, + contextBytesType: ContextBytesType.ForkDigest, +}); + +export const DataColumnSidecarsByRoot = toProtocol({ + method: ReqRespMethod.DataColumnSidecarsByRoot, + 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 881ab36bc05d..cf48d9d138fe 100644 --- a/packages/beacon-node/src/network/reqresp/rateLimit.ts +++ b/packages/beacon-node/src/network/reqresp/rateLimit.ts @@ -3,6 +3,8 @@ import { MAX_REQUEST_LIGHT_CLIENT_UPDATES, MAX_BLOBS_PER_BLOCK, MAX_REQUEST_BLOB_SIDECARS, + MAX_REQUEST_BLOCKS_DENEB, + NUMBER_OF_COLUMNS, } from "@lodestar/params"; import {InboundRateLimitQuota} from "@lodestar/reqresp"; import {ReqRespMethod, RequestBodyByMethod} from "./types.js"; @@ -46,6 +48,16 @@ export const rateLimitQuotas: Record = { byPeer: {quota: 128 * MAX_BLOBS_PER_BLOCK, quotaTimeMs: 10_000}, getRequestCount: getRequestCountFn(ReqRespMethod.BlobSidecarsByRoot, (req) => req.length), }, + [ReqRespMethod.DataColumnSidecarsByRange]: { + // Rationale: MAX_REQUEST_BLOCKS_DENEB * NUMBER_OF_COLUMNS + byPeer: {quota: MAX_REQUEST_BLOCKS_DENEB * NUMBER_OF_COLUMNS, quotaTimeMs: 10_000}, + getRequestCount: getRequestCountFn(ReqRespMethod.DataColumnSidecarsByRange, (req) => req.count), + }, + [ReqRespMethod.DataColumnSidecarsByRoot]: { + // Rationale: quota of BeaconBlocksByRoot * NUMBER_OF_COLUMNS + byPeer: {quota: 128 * NUMBER_OF_COLUMNS, quotaTimeMs: 10_000}, + getRequestCount: getRequestCountFn(ReqRespMethod.DataColumnSidecarsByRoot, (req) => req.length), + }, [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/types.ts b/packages/beacon-node/src/network/reqresp/types.ts index 78625f6ea414..77be3bd2d78a 100644 --- a/packages/beacon-node/src/network/reqresp/types.ts +++ b/packages/beacon-node/src/network/reqresp/types.ts @@ -1,7 +1,7 @@ import {Type} from "@chainsafe/ssz"; import {ForkLightClient, ForkName, isForkLightClient} from "@lodestar/params"; import {Protocol, ProtocolHandler, ReqRespRequest} from "@lodestar/reqresp"; -import {Metadata, Root, SignedBeaconBlock, altair, deneb, phase0, ssz} from "@lodestar/types"; +import {Metadata, Root, SignedBeaconBlock, altair, deneb, phase0, ssz, electra} from "@lodestar/types"; export type ProtocolNoHandler = Omit; @@ -16,6 +16,8 @@ export enum ReqRespMethod { BeaconBlocksByRoot = "beacon_blocks_by_root", BlobSidecarsByRange = "blob_sidecars_by_range", BlobSidecarsByRoot = "blob_sidecars_by_root", + DataColumnSidecarsByRange = "data_column_sidecars_by_range", + DataColumnSidecarsByRoot = "data_column_sidecars_by_root", LightClientBootstrap = "light_client_bootstrap", LightClientUpdatesByRange = "light_client_updates_by_range", LightClientFinalityUpdate = "light_client_finality_update", @@ -32,6 +34,8 @@ export type RequestBodyByMethod = { [ReqRespMethod.BeaconBlocksByRoot]: phase0.BeaconBlocksByRootRequest; [ReqRespMethod.BlobSidecarsByRange]: deneb.BlobSidecarsByRangeRequest; [ReqRespMethod.BlobSidecarsByRoot]: deneb.BlobSidecarsByRootRequest; + [ReqRespMethod.DataColumnSidecarsByRange]: electra.DataColumnSidecarsByRangeRequest; + [ReqRespMethod.DataColumnSidecarsByRoot]: electra.DataColumnSidecarsByRootRequest; [ReqRespMethod.LightClientBootstrap]: Root; [ReqRespMethod.LightClientUpdatesByRange]: altair.LightClientUpdatesByRange; [ReqRespMethod.LightClientFinalityUpdate]: null; @@ -48,6 +52,9 @@ type ResponseBodyByMethod = { [ReqRespMethod.BeaconBlocksByRoot]: SignedBeaconBlock; [ReqRespMethod.BlobSidecarsByRange]: deneb.BlobSidecar; [ReqRespMethod.BlobSidecarsByRoot]: deneb.BlobSidecar; + [ReqRespMethod.DataColumnSidecarsByRange]: electra.DataColumnSidecar; + [ReqRespMethod.DataColumnSidecarsByRoot]: electra.DataColumnSidecar; + [ReqRespMethod.LightClientBootstrap]: altair.LightClientBootstrap; [ReqRespMethod.LightClientUpdatesByRange]: altair.LightClientUpdate; [ReqRespMethod.LightClientFinalityUpdate]: altair.LightClientFinalityUpdate; @@ -62,10 +69,14 @@ export const requestSszTypeByMethod: { [ReqRespMethod.Goodbye]: ssz.phase0.Goodbye, [ReqRespMethod.Ping]: ssz.phase0.Ping, [ReqRespMethod.Metadata]: null, + [ReqRespMethod.BeaconBlocksByRange]: ssz.phase0.BeaconBlocksByRangeRequest, [ReqRespMethod.BeaconBlocksByRoot]: ssz.phase0.BeaconBlocksByRootRequest, [ReqRespMethod.BlobSidecarsByRange]: ssz.deneb.BlobSidecarsByRangeRequest, [ReqRespMethod.BlobSidecarsByRoot]: ssz.deneb.BlobSidecarsByRootRequest, + [ReqRespMethod.DataColumnSidecarsByRange]: ssz.electra.DataColumnSidecarsByRangeRequest, + [ReqRespMethod.DataColumnSidecarsByRoot]: ssz.electra.DataColumnSidecarsByRootRequest, + [ReqRespMethod.LightClientBootstrap]: ssz.Root, [ReqRespMethod.LightClientUpdatesByRange]: ssz.altair.LightClientUpdatesByRange, [ReqRespMethod.LightClientFinalityUpdate]: null, @@ -91,6 +102,9 @@ export const responseSszTypeByMethod: {[K in ReqRespMethod]: ResponseTypeGetter< [ReqRespMethod.BeaconBlocksByRoot]: blocksResponseType, [ReqRespMethod.BlobSidecarsByRange]: () => ssz.deneb.BlobSidecar, [ReqRespMethod.BlobSidecarsByRoot]: () => ssz.deneb.BlobSidecar, + [ReqRespMethod.DataColumnSidecarsByRange]: () => ssz.electra.DataColumnSidecar, + [ReqRespMethod.DataColumnSidecarsByRoot]: () => ssz.electra.DataColumnSidecar, + [ReqRespMethod.LightClientBootstrap]: (fork) => ssz.allForksLightClient[onlyLightclientFork(fork)].LightClientBootstrap, [ReqRespMethod.LightClientUpdatesByRange]: (fork) => diff --git a/packages/beacon-node/src/node/nodejs.ts b/packages/beacon-node/src/node/nodejs.ts index a1147b60bea2..2e7ee756c336 100644 --- a/packages/beacon-node/src/node/nodejs.ts +++ b/packages/beacon-node/src/node/nodejs.ts @@ -21,6 +21,7 @@ import {getApi, BeaconRestApiServer} from "../api/index.js"; import {initializeExecutionEngine, initializeExecutionBuilder} from "../execution/index.js"; import {initializeEth1ForBlockProduction} from "../eth1/index.js"; import {initCKZG, loadEthereumTrustedSetup, TrustedFileMode} from "../util/kzg.js"; +import {NodeId} from "../network/subnets/interface.js"; import {IBeaconNodeOptions} from "./options.js"; import {runNodeNotifier} from "./notifier.js"; @@ -49,6 +50,7 @@ export type BeaconNodeInitModules = { logger: LoggerNode; processShutdownCallback: ProcessShutdownCallback; peerId: PeerId; + nodeId: NodeId; peerStoreDir?: string; anchorState: BeaconStateAllForks; wsCheckpoint?: phase0.Checkpoint; @@ -146,6 +148,7 @@ export class BeaconNode { logger, processShutdownCallback, peerId, + nodeId, peerStoreDir, anchorState, wsCheckpoint, @@ -195,6 +198,7 @@ export class BeaconNode { : null; const chain = new BeaconChain(opts.chain, { + nodeId, config, db, logger: logger.child({module: LoggerModule.chain}), @@ -231,6 +235,7 @@ export class BeaconNode { chain, db, peerId, + nodeId, peerStoreDir, getReqRespHandler: getReqRespHandlers({db, chain}), }); diff --git a/packages/beacon-node/src/sync/range/chain.ts b/packages/beacon-node/src/sync/range/chain.ts index 41bbce3da820..f100fe2153a2 100644 --- a/packages/beacon-node/src/sync/range/chain.ts +++ b/packages/beacon-node/src/sync/range/chain.ts @@ -2,6 +2,8 @@ import {toHexString} from "@chainsafe/ssz"; import {Epoch, Root, Slot, phase0} from "@lodestar/types"; import {ErrorAborted, Logger} from "@lodestar/utils"; import {ChainForkConfig} from "@lodestar/config"; +import {ForkName} from "@lodestar/params"; + import {BlockInput, BlockInputType} from "../../chain/blocks/types.js"; import {PeerAction} from "../../network/index.js"; import {ItTrigger} from "../../util/itTrigger.js"; @@ -404,14 +406,27 @@ export class SyncChain { const blobs = res.result.reduce((acc, blockInput) => { hasPostDenebBlocks ||= blockInput.type === BlockInputType.availableData; return hasPostDenebBlocks - ? acc + (blockInput.type === BlockInputType.availableData ? blockInput.blockData.blobs.length : 0) + ? acc + + (blockInput.type === BlockInputType.availableData && blockInput.blockData.fork === ForkName.deneb + ? blockInput.blockData.blobs.length + : 0) + : 0; + }, 0); + const dataColumns = res.result.reduce((acc, blockInput) => { + hasPostDenebBlocks ||= blockInput.type === BlockInputType.availableData; + return hasPostDenebBlocks + ? acc + + (blockInput.type === BlockInputType.availableData && blockInput.blockData.fork === ForkName.electra + ? blockInput.blockData.dataColumns.length + : 0) : 0; }, 0); + const downloadInfo = {blocks: res.result.length}; if (hasPostDenebBlocks) { - Object.assign(downloadInfo, {blobs}); + Object.assign(downloadInfo, {blobs, dataColumns}); } - this.logger.debug("Downloaded batch", {id: this.logId, ...batch.getMetadata(), ...downloadInfo}); + this.logger.debug("Downloaded batch", {id: this.logId, ...batch.getMetadata(), ...downloadInfo, peer}); this.triggerBatchProcessor(); } else { this.logger.verbose("Batch download error", {id: this.logId, ...batch.getMetadata()}, res.err); diff --git a/packages/beacon-node/src/sync/unknownBlock.ts b/packages/beacon-node/src/sync/unknownBlock.ts index 3c15b32eb8d8..20c56e1f1a17 100644 --- a/packages/beacon-node/src/sync/unknownBlock.ts +++ b/packages/beacon-node/src/sync/unknownBlock.ts @@ -2,7 +2,7 @@ import {fromHexString, toHexString} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; import {Logger, pruneSetToMax} from "@lodestar/utils"; import {Root, RootHex, deneb} from "@lodestar/types"; -import {INTERVALS_PER_SLOT} from "@lodestar/params"; +import {INTERVALS_PER_SLOT, ForkName} from "@lodestar/params"; import {sleep} from "@lodestar/utils"; import {INetwork, NetworkEvent, NetworkEventData, PeerAction} from "../network/index.js"; import {PeerIdStr} from "../util/peerId.js"; @@ -515,21 +515,28 @@ export class UnknownBlockSync { const shuffledPeers = shuffle(connectedPeers); let blockRootHex; - let pendingBlobs; let blobKzgCommitmentsLen; let blockRoot; + const dataMeta: Record = {}; if (unavailableBlockInput.block === null) { blockRootHex = unavailableBlockInput.blockRootHex; blockRoot = fromHexString(blockRootHex); } else { - const unavailableBlock = unavailableBlockInput.block; + const {cachedData, block: unavailableBlock} = unavailableBlockInput; blockRoot = this.config .getForkTypes(unavailableBlock.message.slot) .BeaconBlock.hashTreeRoot(unavailableBlock.message); blockRootHex = toHexString(blockRoot); blobKzgCommitmentsLen = (unavailableBlock.message.body as deneb.BeaconBlockBody).blobKzgCommitments.length; - pendingBlobs = blobKzgCommitmentsLen - unavailableBlockInput.cachedData.blobsCache.size; + + if (cachedData.fork === ForkName.deneb) { + const pendingBlobs = blobKzgCommitmentsLen - cachedData.blobsCache.size; + Object.assign(dataMeta, {pendingBlobs}); + } else if (cachedData.fork === ForkName.electra) { + const pendingColumns = this.network.custodyConfig.custodyColumnsLen - cachedData.dataColumnsCache.size; + Object.assign(dataMeta, {pendingColumns}); + } } let lastError: Error | null = null; @@ -559,7 +566,7 @@ export class UnknownBlockSync { if (unavailableBlockInput.block === null) { this.logger.debug("Fetched NullBlockInput", {attempts: i, blockRootHex}); } else { - this.logger.debug("Fetched UnavailableBlockInput", {attempts: i, pendingBlobs, blobKzgCommitmentsLen}); + this.logger.debug("Fetched UnavailableBlockInput", {attempts: i, ...dataMeta, blobKzgCommitmentsLen}); } return {blockInput, peerIdStr: peer}; diff --git a/packages/beacon-node/src/util/blobs.ts b/packages/beacon-node/src/util/blobs.ts index fcc31092464f..5923ba8ab1a5 100644 --- a/packages/beacon-node/src/util/blobs.ts +++ b/packages/beacon-node/src/util/blobs.ts @@ -1,9 +1,10 @@ import {digest as sha256Digest} from "@chainsafe/as-sha256"; import {Tree} from "@chainsafe/persistent-merkle-tree"; -import {VERSIONED_HASH_VERSION_KZG, KZG_COMMITMENT_GINDEX0, ForkName, ForkAll} from "@lodestar/params"; +import {VERSIONED_HASH_VERSION_KZG, KZG_COMMITMENT_GINDEX0, KZG_COMMITMENTS_GINDEX, ForkName, ForkAll, NUMBER_OF_COLUMNS} from "@lodestar/params"; import {deneb, ssz, BeaconBlockBody, SignedBeaconBlock, SSZTypesFor} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {signedBlockToSignedHeader} from "@lodestar/state-transition"; +import {ckzg} from "./kzg.js"; type VersionHash = Uint8Array; @@ -24,6 +25,14 @@ export function computeInclusionProof( return new Tree(bodyView.node).getSingleProof(BigInt(commitmentGindex)); } +export function computeKzgCommitmentsInclusionProof( + fork: ForkName, + body: BeaconBlockBody +): electra.KzgCommitmentsInclusionProof { + const bodyView = (ssz[fork].BeaconBlockBody as SSZTypesFor).toView(body); + return new Tree(bodyView.node).getSingleProof(BigInt(KZG_COMMITMENTS_GINDEX)); +} + export function computeBlobSidecars( config: ChainForkConfig, signedBlock: SignedBeaconBlock, @@ -46,3 +55,41 @@ export function computeBlobSidecars( return {index, blob, kzgCommitment, kzgProof, signedBlockHeader, kzgCommitmentInclusionProof}; }); } + +export function computeDataColumnSidecars( + config: ChainForkConfig, + signedBlock: SignedBeaconBlock, + contents: deneb.Contents & {kzgCommitmentsInclusionProof?: electra.KzgCommitmentsInclusionProof} +): electra.DataColumnSidecars { + const blobKzgCommitments = (signedBlock as deneb.SignedBeaconBlock).message.body.blobKzgCommitments; + if (blobKzgCommitments === undefined) { + throw Error("Invalid block with missing blobKzgCommitments for computeBlobSidecars"); + } + if (blobKzgCommitments.length === 0) { + return []; + } + + const signedBlockHeader = signedBlockToSignedHeader(config, signedBlock); + const fork = config.getForkName(signedBlockHeader.message.slot); + const kzgCommitmentsInclusionProof = + contents.kzgCommitmentsInclusionProof ?? computeKzgCommitmentsInclusionProof(fork, signedBlock.message.body); + const cellsAndProofs = contents.blobs.map((blob) => ckzg.computeCellsAndKzgProofs(blob)); + const dataColumnSidecars = Array.from({length: NUMBER_OF_COLUMNS}, (_, j) => { + // j'th column + const column = Array.from({length: contents.blobs.length}, (_, i) => cellsAndProofs[i][0][j]); + const kzgProofs = Array.from({length: contents.blobs.length}, (_, i) => cellsAndProofs[i][1][j]); + + const dataColumnSidecar = { + index: j, + column, + kzgCommitments: blobKzgCommitments, + kzgProofs, + signedBlockHeader, + kzgCommitmentsInclusionProof, + } as electra.DataColumnSidecar; + + return dataColumnSidecar; + }); + + return dataColumnSidecars; +} diff --git a/packages/beacon-node/src/util/dataColumns.ts b/packages/beacon-node/src/util/dataColumns.ts new file mode 100644 index 000000000000..9efec2a02ba6 --- /dev/null +++ b/packages/beacon-node/src/util/dataColumns.ts @@ -0,0 +1,74 @@ +import {digest} from "@chainsafe/as-sha256"; +import {NUMBER_OF_COLUMNS, DATA_COLUMN_SIDECAR_SUBNET_COUNT} from "@lodestar/params"; +import {ColumnIndex} from "@lodestar/types"; +import {ChainForkConfig} from "@lodestar/config"; +import {ssz} from "@lodestar/types"; +import {NodeId} from "../network/subnets/index.js"; + +export type CustodyConfig = {custodyColumnsIndex: Uint8Array; custodyColumnsLen: number; custodyColumns: ColumnIndex[]}; + +export function getCustodyConfig(nodeId: NodeId, config: ChainForkConfig): CustodyConfig { + const custodyColumns = getCustodyColumns(nodeId, config.CUSTODY_REQUIREMENT); + const custodyMeta = getCustodyColumnsMeta(custodyColumns); + return {...custodyMeta, custodyColumns}; +} + +export function getCustodyColumnsMeta(custodyColumns: ColumnIndex[]): { + custodyColumnsIndex: Uint8Array; + custodyColumnsLen: number; +} { + // custody columns map which column maps to which index in the array of columns custodied + // with zero representing it is not custodied + const custodyColumnsIndex = new Uint8Array(NUMBER_OF_COLUMNS); + let custodyAtIndex = 1; + for (const columnIndex of custodyColumns) { + custodyColumns[columnIndex] = custodyAtIndex; + custodyAtIndex++; + } + return {custodyColumnsIndex, custodyColumnsLen: custodyColumns.length}; +} + +// optimize by having a size limited index/map +export function getCustodyColumns(nodeId: NodeId, custodySubnetCount: number): ColumnIndex[] { + const subnetIds = getCustodyColumnSubnets(nodeId, custodySubnetCount); + const columnsPerSubnet = Number(NUMBER_OF_COLUMNS / DATA_COLUMN_SIDECAR_SUBNET_COUNT); + + const columnIndexes = []; + for (const subnetId of subnetIds) { + for (let i = 0; i < columnsPerSubnet; i++) { + const columnIndex = DATA_COLUMN_SIDECAR_SUBNET_COUNT * i + subnetId; + columnIndexes.push(columnIndex); + } + } + + columnIndexes.sort((a, b) => a - b); + return columnIndexes; +} + +export function getCustodyColumnSubnets(nodeId: NodeId, custodySubnetCount: number): number[] { + const subnetIds: number[] = []; + if (custodySubnetCount > DATA_COLUMN_SIDECAR_SUBNET_COUNT) { + custodySubnetCount = DATA_COLUMN_SIDECAR_SUBNET_COUNT; + } + + let currentId = ssz.UintBn256.deserialize(nodeId); + while (subnetIds.length < custodySubnetCount) { + // could be optimized + const currentIdBytes = ssz.UintBn256.serialize(currentId); + const subnetId = Number( + ssz.UintBn64.deserialize(digest(currentIdBytes).slice(0, 8)) % BigInt(DATA_COLUMN_SIDECAR_SUBNET_COUNT) + ); + if (!subnetIds.includes(subnetId)) { + subnetIds.push(subnetId); + } + + const willOverflow = currentIdBytes.reduce((acc, elem) => acc && elem === 0xff, true); + if (willOverflow) { + currentId = BigInt(0); + } else { + currentId++; + } + } + + return subnetIds; +} diff --git a/packages/beacon-node/src/util/kzg.ts b/packages/beacon-node/src/util/kzg.ts index e20a379d62ff..0fc45eff04fe 100644 --- a/packages/beacon-node/src/util/kzg.ts +++ b/packages/beacon-node/src/util/kzg.ts @@ -19,6 +19,18 @@ export let ckzg: { computeBlobKzgProof(blob: Uint8Array, commitment: Uint8Array): Uint8Array; verifyBlobKzgProof(blob: Uint8Array, commitment: Uint8Array, proof: Uint8Array): boolean; verifyBlobKzgProofBatch(blobs: Uint8Array[], expectedKzgCommitments: Uint8Array[], kzgProofs: Uint8Array[]): boolean; + computeCells(blob: Uint8Array): Uint8Array[]; + computeCellsAndKzgProofs(blob: Uint8Array): [Uint8Array[], Uint8Array[]]; + cellsToBlob(cells: Uint8Array[]): Uint8Array; + recoverAllCells(cellIds: number[], cells: Uint8Array[]): Uint8Array[]; + verifyCellKzgProof(commitmentBytes: Uint8Array, cellId: number, cell: Uint8Array, proofBytes: Uint8Array): boolean; + verifyCellKzgProofBatch( + commitmentsBytes: Uint8Array[], + rowIndices: number[], + columnIndices: number[], + cells: Uint8Array[], + proofsBytes: Uint8Array[] + ): boolean; } = { freeTrustedSetup: ckzgNotLoaded, loadTrustedSetup: ckzgNotLoaded, @@ -26,6 +38,12 @@ export let ckzg: { computeBlobKzgProof: ckzgNotLoaded, verifyBlobKzgProof: ckzgNotLoaded, verifyBlobKzgProofBatch: ckzgNotLoaded, + computeCells: ckzgNotLoaded, + computeCellsAndKzgProofs: ckzgNotLoaded, + cellsToBlob: ckzgNotLoaded, + recoverAllCells: ckzgNotLoaded, + verifyCellKzgProof: ckzgNotLoaded, + verifyCellKzgProofBatch: ckzgNotLoaded, }; // Global variable __dirname no longer available in ES6 modules. diff --git a/packages/beacon-node/src/util/sszBytes.ts b/packages/beacon-node/src/util/sszBytes.ts index 802b9a266ab1..df0a087b482b 100644 --- a/packages/beacon-node/src/util/sszBytes.ts +++ b/packages/beacon-node/src/util/sszBytes.ts @@ -198,6 +198,26 @@ export function getSlotFromBlobSidecarSerialized(data: Uint8Array): Slot | null return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_SIGNED_BLOB_SIDECAR); } +/** + * { + index: ColumnIndex [ fixed - 8 bytes], + column: DataColumn BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_CELL * , + kzgCommitments: denebSsz.BlobKzgCommitments, + kzgProofs: denebSsz.KZGProofs, + signedBlockHeader: phase0Ssz.SignedBeaconBlockHeader, + kzgCommitmentsInclusionProof: KzgCommitmentsInclusionProof, + } + */ + +const SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR = 20; +export function getSlotFromDataColumnSidecarSerialized(data: Uint8Array): Slot | null { + if (data.length < SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR + SLOT_SIZE) { + return null; + } + + return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR); +} + function getSlotFromOffset(data: Uint8Array, offset: number): Slot { // TODO: Optimize const dv = new DataView(data.buffer, data.byteOffset, data.byteLength); diff --git a/packages/beacon-node/test/unit/db/api/repositories/dataColumn.test.ts b/packages/beacon-node/test/unit/db/api/repositories/dataColumn.test.ts new file mode 100644 index 000000000000..93ac6f4cde1e --- /dev/null +++ b/packages/beacon-node/test/unit/db/api/repositories/dataColumn.test.ts @@ -0,0 +1,103 @@ +import {rimraf} from "rimraf"; +import {describe, it, expect, beforeEach, afterEach, beforeAll} from "vitest"; +import {ssz} from "@lodestar/types"; +import {createChainForkConfig} from "@lodestar/config"; +import {LevelDbController} from "@lodestar/db"; +import {NUMBER_OF_COLUMNS} from "@lodestar/params"; + +import { + DataColumnSidecarsRepository, + dataColumnSidecarsWrapperSsz, + DATA_COLUMN_SIDECARS_IN_WRAPPER_INDEX, + COLUMN_SIZE_IN_WRAPPER_INDEX, + CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX, +} from "../../../../../src/db/repositories/dataColumnSidecars.js"; +import {testLogger} from "../../../../utils/logger.js"; +import {computeDataColumnSidecars} from "../../../../../src/util/blobs.js"; +import {loadEthereumTrustedSetup, initCKZG} from "../../../../../src/util/kzg.js"; + +/* eslint-disable @typescript-eslint/naming-convention */ +const config = createChainForkConfig({ + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: 0, +}); +describe("block archive repository", function () { + const testDir = "./.tmp"; + let dataColumnRepo: DataColumnSidecarsRepository; + let db: LevelDbController; + + beforeEach(async function () { + db = await LevelDbController.create({name: testDir}, {logger: testLogger()}); + dataColumnRepo = new DataColumnSidecarsRepository(config, db); + }); + afterEach(async function () { + await db.close(); + rimraf.sync(testDir); + }); + + beforeAll(async function () { + await initCKZG(); + loadEthereumTrustedSetup(); + }); + + it("should get block by parent root", async function () { + const dataColumn = ssz.electra.DataColumnSidecar.defaultValue(); + const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(dataColumn.signedBlockHeader.message); + const slot = dataColumn.signedBlockHeader.message.slot; + const blob = ssz.deneb.Blob.defaultValue(); + const commitment = ssz.deneb.KZGCommitment.defaultValue(); + const singedBlock = ssz.electra.SignedBeaconBlock.defaultValue(); + + singedBlock.message.body.blobKzgCommitments.push(commitment); + singedBlock.message.body.blobKzgCommitments.push(commitment); + singedBlock.message.body.blobKzgCommitments.push(commitment); + const dataColumnSidecars = computeDataColumnSidecars(config, singedBlock, {blobs: [blob, blob, blob]}); + for (let j = 0; j < dataColumnSidecars.length; j++) { + dataColumnSidecars[j].index = j; + } + + const blobKzgCommitmentsLen = 3; + const columnsSize = + ssz.electra.DataColumnSidecar.minSize + + blobKzgCommitmentsLen * + (ssz.electra.Cell.fixedSize + ssz.deneb.KZGCommitment.fixedSize + ssz.deneb.KZGProof.fixedSize); + + const numColumns = NUMBER_OF_COLUMNS; + const custodyColumns = new Uint8Array(numColumns); + + const writeData = { + blockRoot, + slot, + numColumns, + columnsSize, + custodyColumns, + dataColumnSidecars, + }; + + await dataColumnRepo.add(writeData); + const retrievedBinary = await dataColumnRepo.getBinary(blockRoot); + if (!retrievedBinary) throw Error("get by root returned null"); + + const retrieved = dataColumnSidecarsWrapperSsz.deserialize(retrievedBinary); + expect(dataColumnSidecarsWrapperSsz.equals(retrieved, writeData)).toBe(true); + + const retrievedColumnsSizeBytes = retrievedBinary.slice( + COLUMN_SIZE_IN_WRAPPER_INDEX, + CUSTODY_COLUMNS_IN_IN_WRAPPER_INDEX + ); + + const retrievedColumnsSize = ssz.UintNum64.deserialize(retrievedColumnsSizeBytes); + expect(retrievedColumnsSize === columnsSize).toBe(true); + const dataColumnSidecarsBytes = retrievedBinary.slice(DATA_COLUMN_SIDECARS_IN_WRAPPER_INDEX); + expect(dataColumnSidecarsBytes.length === columnsSize * numColumns).toBe(true); + + for (let j = 0; j < numColumns; j++) { + const dataColumnBytes = dataColumnSidecarsBytes.slice(j * columnsSize, (j + 1) * columnsSize); + const retrivedDataColumnSidecar = ssz.electra.DataColumnSidecar.deserialize(dataColumnBytes); + const index = retrivedDataColumnSidecar.index; + expect(j === index).toBe(true); + } + }); +}); diff --git a/packages/beacon-node/test/unit/network/gossip/topic.test.ts b/packages/beacon-node/test/unit/network/gossip/topic.test.ts index dbaa4002bfcc..430f45ee6807 100644 --- a/packages/beacon-node/test/unit/network/gossip/topic.test.ts +++ b/packages/beacon-node/test/unit/network/gossip/topic.test.ts @@ -21,6 +21,12 @@ describe("network / gossip / topic", function () { topicStr: "/eth2/46acb19a/blob_sidecar_1/ssz_snappy", }, ], + [GossipType.data_column_sidecar]: [ + { + topic: {type: GossipType.data_column_sidecar, index: 1, fork: ForkName.electra, encoding}, + topicStr: "/eth2/46acb19a/data_column_sidecar/ssz_snappy", + }, + ], [GossipType.beacon_aggregate_and_proof]: [ { topic: {type: GossipType.beacon_aggregate_and_proof, fork: ForkName.phase0, encoding}, diff --git a/packages/beacon-node/test/unit/util/dataColumn.test.ts b/packages/beacon-node/test/unit/util/dataColumn.test.ts new file mode 100644 index 000000000000..62a4ef6b8d5d --- /dev/null +++ b/packages/beacon-node/test/unit/util/dataColumn.test.ts @@ -0,0 +1,14 @@ +import {describe, it, expect} from "vitest"; +import {ssz} from "@lodestar/types"; + +import {getCustodyColumns} from "../../../src/util/dataColumns.js"; + +describe("custody columns", () => { + it("getCustodyColumnIndexes", async () => { + const nodeId = ssz.UintBn256.serialize( + BigInt("84065159290331321853352677657753050104170032838956724170714636178275273565505") + ); + const columnIndexs = getCustodyColumns(nodeId, 1); + expect(columnIndexs).toEqual([27, 59, 91, 123]); + }); +}); diff --git a/packages/beacon-node/test/utils/node/beacon.ts b/packages/beacon-node/test/utils/node/beacon.ts index 0163fa148102..f8fee8294907 100644 --- a/packages/beacon-node/test/utils/node/beacon.ts +++ b/packages/beacon-node/test/utils/node/beacon.ts @@ -1,3 +1,4 @@ +import crypto from "node:crypto"; import deepmerge from "deepmerge"; import tmp from "tmp"; import {PeerId} from "@libp2p/interface"; @@ -19,6 +20,7 @@ import {defaultOptions} from "../../../src/node/options.js"; import {BeaconDb} from "../../../src/db/index.js"; import {testLogger} from "../logger.js"; import {InteropStateOpts} from "../../../src/node/utils/interop/state.js"; +import {NodeId} from "../../../src/network/subnets/interface.js"; export async function getDevBeaconNode( opts: { @@ -27,15 +29,17 @@ export async function getDevBeaconNode( validatorCount?: number; logger?: LoggerNode; peerId?: PeerId; + nodeId?: NodeId; peerStoreDir?: string; anchorState?: BeaconStateAllForks; wsCheckpoint?: phase0.Checkpoint; } & InteropStateOpts ): Promise { const {params, validatorCount = 8, peerStoreDir} = opts; - let {options = {}, logger, peerId} = opts; + let {options = {}, logger, peerId, nodeId} = opts; if (!peerId) peerId = await createSecp256k1PeerId(); + if (!nodeId) nodeId = crypto.randomBytes(32); const tmpDir = tmp.dirSync({unsafeCleanup: true}); const config = createChainForkConfig({...minimalConfig, ...params}); logger = logger ?? testLogger(); @@ -94,6 +98,7 @@ export async function getDevBeaconNode( logger, processShutdownCallback: () => {}, peerId, + nodeId, peerStoreDir, anchorState, wsCheckpoint: opts.wsCheckpoint, diff --git a/packages/cli/src/cmds/beacon/handler.ts b/packages/cli/src/cmds/beacon/handler.ts index e24089194185..7dcec1aa2f73 100644 --- a/packages/cli/src/cmds/beacon/handler.ts +++ b/packages/cli/src/cmds/beacon/handler.ts @@ -35,7 +35,8 @@ const EIGHT_GB = 8 * 1024 * 1024 * 1024; * Runs a beacon node. */ export async function beaconHandler(args: BeaconArgs & GlobalArgs): Promise { - const {config, options, beaconPaths, network, version, commit, peerId, logger} = await beaconHandlerInit(args); + const {config, options, beaconPaths, network, version, commit, nodeId, peerId, logger} = + await beaconHandlerInit(args); const heapSizeLimit = getHeapStatistics().heap_size_limit; if (heapSizeLimit < EIGHT_GB) { @@ -89,6 +90,7 @@ export async function beaconHandler(args: BeaconArgs & GlobalArgs): Promise { +): Promise<{peerId: PeerId; enr: SignableENR; nodeId: Uint8Array}> { const {persistNetworkIdentity} = args; const newPeerIdAndENR = async (): Promise<{peerId: PeerId; enr: SignableENR}> => { @@ -181,14 +188,16 @@ export async function initPeerIdAndEnr( const enrFile = path.join(beaconDir, "enr"); const peerIdFile = path.join(beaconDir, "peer-id.json"); const {peerId, enr, newEnr} = await readPersistedPeerIdAndENR(peerIdFile, enrFile); - overwriteEnrWithCliArgs(enr, args, logger, {newEnr, bootnode}); + overwriteEnrWithCliArgs(config, enr, args, logger, {newEnr, bootnode}); // Re-persist peer-id and enr writeFile600Perm(peerIdFile, exportToJSON(peerId)); writeFile600Perm(enrFile, enr.encodeTxt()); - return {peerId, enr}; + const nodeId = fromHex(enr.nodeId); + return {peerId, enr, nodeId}; } else { const {peerId, enr} = await newPeerIdAndENR(); - overwriteEnrWithCliArgs(enr, args, logger, {newEnr: true, bootnode}); - return {peerId, enr}; + overwriteEnrWithCliArgs(config, enr, args, logger, {newEnr: true, bootnode}); + const nodeId = fromHex(enr.nodeId); + return {peerId, enr, nodeId}; } } diff --git a/packages/cli/src/cmds/bootnode/handler.ts b/packages/cli/src/cmds/bootnode/handler.ts index 77851b1fcb88..990bce624551 100644 --- a/packages/cli/src/cmds/bootnode/handler.ts +++ b/packages/cli/src/cmds/bootnode/handler.ts @@ -181,7 +181,7 @@ export async function bootnodeHandlerInit(args: BootnodeArgs & GlobalArgs) { ); const logger = initLogger(args, beaconPaths.dataDir, config, "bootnode.log"); - const {peerId, enr} = await initPeerIdAndEnr(args as unknown as BeaconArgs, bootnodeDir, logger, true); + const {peerId, enr} = await initPeerIdAndEnr(config, args as unknown as BeaconArgs, bootnodeDir, logger, true); return {discv5Args, metricsArgs, bootnodeDir, network, version, commit, peerId, enr, logger}; } diff --git a/packages/config/src/chainConfig/configs/mainnet.ts b/packages/config/src/chainConfig/configs/mainnet.ts index 0de1bee666ec..eed180b4a7ea 100644 --- a/packages/config/src/chainConfig/configs/mainnet.ts +++ b/packages/config/src/chainConfig/configs/mainnet.ts @@ -102,4 +102,8 @@ export const chainConfig: ChainConfig = { // Deneb // `2**12` (= 4096 epochs, ~18 days) MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096, + + // Electra + SAMPLES_PER_SLOT: 8, + CUSTODY_REQUIREMENT: 1, }; diff --git a/packages/config/src/chainConfig/configs/minimal.ts b/packages/config/src/chainConfig/configs/minimal.ts index c99a76d1ee40..978161ed01d0 100644 --- a/packages/config/src/chainConfig/configs/minimal.ts +++ b/packages/config/src/chainConfig/configs/minimal.ts @@ -100,4 +100,8 @@ export const chainConfig: ChainConfig = { // Deneb // `2**12` (= 4096 epochs, ~18 days) MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096, + + // Electra + SAMPLES_PER_SLOT: 8, + CUSTODY_REQUIREMENT: 1, }; diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index 234a08558be5..bce53e357095 100644 --- a/packages/config/src/chainConfig/types.ts +++ b/packages/config/src/chainConfig/types.ts @@ -72,6 +72,9 @@ export type ChainConfig = { // Networking MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: number; + + SAMPLES_PER_SLOT: number; + CUSTODY_REQUIREMENT: number; }; export const chainConfigTypes: SpecTypes = { @@ -134,6 +137,9 @@ export const chainConfigTypes: SpecTypes = { // Networking MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: "number", + + SAMPLES_PER_SLOT: "number", + CUSTODY_REQUIREMENT: "number", }; /** Allows values in a Spec file */ diff --git a/packages/params/src/forkName.ts b/packages/params/src/forkName.ts index d6505bf85dc4..20e2163db86e 100644 --- a/packages/params/src/forkName.ts +++ b/packages/params/src/forkName.ts @@ -47,3 +47,9 @@ export type ForkBlobs = Exclude; export function isForkBlobs(fork: ForkName): fork is ForkBlobs { return isForkWithdrawals(fork) && fork !== ForkName.capella; } + +export type ForkPrePeerDAS = ForkPreBlobs | ForkName.deneb; +export type ForkPeerDAS = Exclude; +export function isForkPeerDAS(fork: ForkName): fork is ForkPeerDAS { + return isForkBlobs(fork) && fork !== ForkName.deneb; +} diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index 6a95e3ca632e..d2f53859bbe9 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -93,6 +93,12 @@ export const { MAX_BLOB_COMMITMENTS_PER_BLOCK, MAX_BLOBS_PER_BLOCK, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, + + FIELD_ELEMENTS_PER_CELL, + FIELD_ELEMENTS_PER_EXT_BLOB, + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH, + MAX_REQUEST_DATA_COLUMN_SIDECARS, + DATA_COLUMN_SIDECAR_SUBNET_COUNT, } = activePreset; //////////// @@ -244,3 +250,12 @@ export const KZG_COMMITMENT_SUBTREE_INDEX0 = KZG_COMMITMENT_GINDEX0 - 2 ** KZG_C // ssz.deneb.BlobSidecars.elementType.fixedSize export const BLOBSIDECAR_FIXED_SIZE = ACTIVE_PRESET === PresetName.minimal ? 131672 : 131928; + +// 128 +export const NUMBER_OF_COLUMNS = (FIELD_ELEMENTS_PER_BLOB * 2) / FIELD_ELEMENTS_PER_CELL; +export const BYTES_PER_CELL = FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT; +export const CELLS_PER_BLOB = FIELD_ELEMENTS_PER_EXT_BLOB / FIELD_ELEMENTS_PER_CELL; + +// ssz.electra.BeaconBlockBody.getPathInfo(['blobKzgCommitments']).gindex +export const KZG_COMMITMENTS_GINDEX = 27; +export const KZG_COMMITMENTS_SUBTREE_INDEX = KZG_COMMITMENTS_GINDEX - 2 ** KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH; diff --git a/packages/params/src/presets/mainnet.ts b/packages/params/src/presets/mainnet.ts index 42a705a07f03..4be082d90ecc 100644 --- a/packages/params/src/presets/mainnet.ts +++ b/packages/params/src/presets/mainnet.ts @@ -118,4 +118,12 @@ export const mainnetPreset: BeaconPreset = { MAX_BLOB_COMMITMENTS_PER_BLOCK: 4096, MAX_BLOBS_PER_BLOCK: 6, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 17, + + // ELECTRA + /////////// + FIELD_ELEMENTS_PER_CELL: 64, + FIELD_ELEMENTS_PER_EXT_BLOB: 8192, + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4, + MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384, + DATA_COLUMN_SIDECAR_SUBNET_COUNT: 32, }; diff --git a/packages/params/src/presets/minimal.ts b/packages/params/src/presets/minimal.ts index b940841a0429..e3ccc5909ca3 100644 --- a/packages/params/src/presets/minimal.ts +++ b/packages/params/src/presets/minimal.ts @@ -119,4 +119,12 @@ export const minimalPreset: BeaconPreset = { MAX_BLOB_COMMITMENTS_PER_BLOCK: 16, MAX_BLOBS_PER_BLOCK: 6, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 9, + + // ELECTRA + /////////// + FIELD_ELEMENTS_PER_CELL: 64, + FIELD_ELEMENTS_PER_EXT_BLOB: 8192, + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4, + MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384, + DATA_COLUMN_SIDECAR_SUBNET_COUNT: 32, }; diff --git a/packages/params/src/types.ts b/packages/params/src/types.ts index 3c5ba6381131..ee4d4c8b85b2 100644 --- a/packages/params/src/types.ts +++ b/packages/params/src/types.ts @@ -82,6 +82,14 @@ export type BeaconPreset = { MAX_BLOB_COMMITMENTS_PER_BLOCK: number; MAX_BLOBS_PER_BLOCK: number; KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: number; + + // ELECTRA + /////////// + FIELD_ELEMENTS_PER_CELL: number; + FIELD_ELEMENTS_PER_EXT_BLOB: number; + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: number; + MAX_REQUEST_DATA_COLUMN_SIDECARS: number; + DATA_COLUMN_SIDECAR_SUBNET_COUNT: number; }; /** @@ -167,6 +175,14 @@ export const beaconPresetTypes: BeaconPresetTypes = { MAX_BLOB_COMMITMENTS_PER_BLOCK: "number", MAX_BLOBS_PER_BLOCK: "number", KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: "number", + + // ELECTRA + /////////// + FIELD_ELEMENTS_PER_CELL: "number", + FIELD_ELEMENTS_PER_EXT_BLOB: "number", + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: "number", + MAX_REQUEST_DATA_COLUMN_SIDECARS: "number", + DATA_COLUMN_SIDECAR_SUBNET_COUNT: "number", }; type BeaconPresetTypes = { diff --git a/packages/types/src/electra/sszTypes.ts b/packages/types/src/electra/sszTypes.ts index 30690a499845..ebd86dd482c7 100644 --- a/packages/types/src/electra/sszTypes.ts +++ b/packages/types/src/electra/sszTypes.ts @@ -1,8 +1,62 @@ -import {ContainerType} from "@chainsafe/ssz"; +import {ContainerType, ByteVectorType, ListCompositeType, VectorCompositeType, ListBasicType} from "@chainsafe/ssz"; +import { + BYTES_PER_FIELD_ELEMENT, + FIELD_ELEMENTS_PER_CELL, + MAX_BLOB_COMMITMENTS_PER_BLOCK, + NUMBER_OF_COLUMNS, + KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH, + MAX_REQUEST_DATA_COLUMN_SIDECARS, +} from "@lodestar/params"; + import {ssz as primitiveSsz} from "../primitive/index.js"; +import {ssz as phase0Ssz} from "../phase0/index.js"; import {ssz as denebSsz} from "../deneb/index.js"; -const {BLSSignature} = primitiveSsz; +const {BLSSignature, Root, ColumnIndex, Bytes32, Slot, UintNum64} = primitiveSsz; + +export const Cell = new ByteVectorType(BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_CELL); +export const DataColumn = new ListCompositeType(Cell, MAX_BLOB_COMMITMENTS_PER_BLOCK); +export const ExtendedMatrix = new ListCompositeType(Cell, MAX_BLOB_COMMITMENTS_PER_BLOCK * NUMBER_OF_COLUMNS); +export const KzgCommitmentsInclusionProof = new VectorCompositeType(Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH); + +export const DataColumnSidecar = new ContainerType( + { + index: ColumnIndex, + column: DataColumn, + kzgCommitments: denebSsz.BlobKzgCommitments, + kzgProofs: denebSsz.KZGProofs, + signedBlockHeader: phase0Ssz.SignedBeaconBlockHeader, + kzgCommitmentsInclusionProof: KzgCommitmentsInclusionProof, + }, + {typeName: "DataColumnSidecar", jsonCase: "eth2"} +); + +export const DataColumnSidecars = new ListCompositeType(DataColumnSidecar, NUMBER_OF_COLUMNS); + +// ReqResp types +// ============= + +export const DataColumnIdentifier = new ContainerType( + { + blockRoot: Root, + index: ColumnIndex, + }, + {typeName: "DataColumnIdentifier", jsonCase: "eth2"} +); + +export const DataColumnSidecarsByRootRequest = new ListCompositeType( + DataColumnIdentifier, + MAX_REQUEST_DATA_COLUMN_SIDECARS +); + +export const DataColumnSidecarsByRangeRequest = new ContainerType( + { + startSlot: Slot, + count: UintNum64, + columns: new ListBasicType(ColumnIndex, NUMBER_OF_COLUMNS), + }, + {typeName: "DataColumnSidecarsByRangeRequest", jsonCase: "eth2"} +); export const ExecutionPayload = new ContainerType( { diff --git a/packages/types/src/electra/types.ts b/packages/types/src/electra/types.ts index 198259eed1dd..a59bb4e673fa 100644 --- a/packages/types/src/electra/types.ts +++ b/packages/types/src/electra/types.ts @@ -1,7 +1,17 @@ import {ValueOf} from "@chainsafe/ssz"; import * as ssz from "./sszTypes.js"; -export type BlobSidecar = ValueOf; +export type Cell = ValueOf; +export type DataColumn = ValueOf; +export type ExtendedMatrix = ValueOf; +export type KzgCommitmentsInclusionProof = ValueOf; +export type DataColumnSidecar = ValueOf; +export type DataColumnSidecars = ValueOf; + +export type DataColumnIdentifier = ValueOf; +export type DataColumnSidecarsByRootRequest = ValueOf; +export type DataColumnSidecarsByRangeRequest = ValueOf; + export type ExecutionPayloadAndBlobsBundle = ValueOf; export type ExecutionPayload = ValueOf; diff --git a/packages/types/src/primitive/sszTypes.ts b/packages/types/src/primitive/sszTypes.ts index 068a32e2cc17..88193d2902fe 100644 --- a/packages/types/src/primitive/sszTypes.ts +++ b/packages/types/src/primitive/sszTypes.ts @@ -63,3 +63,4 @@ export const BLSSignature = Bytes96; export const Domain = Bytes32; export const ParticipationFlags = new UintNumberType(1, {setBitwiseOR: true}); export const ExecutionAddress = new ExecutionAddressType(); +export const ColumnIndex = UintNum64; diff --git a/packages/types/src/primitive/types.ts b/packages/types/src/primitive/types.ts index 53422cc9b995..90e7eadb178e 100644 --- a/packages/types/src/primitive/types.ts +++ b/packages/types/src/primitive/types.ts @@ -47,3 +47,4 @@ export type ExecutionAddress = Bytes20; export type RootHex = string; /** Non-spec type to signal time is represented in seconds */ export type TimeSeconds = number; +export type ColumnIndex = UintNum64; diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 0afede39b951..2e2066992898 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -222,5 +222,17 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record Date: Fri, 21 Jun 2024 17:09:44 +0200 Subject: [PATCH 16/17] fix: docker build issue for c-kzg wip: REPLACE THIS COMMIT commit yarn lock --- Dockerfile | 2 ++ package.json | 1 + packages/beacon-node/package.json | 2 +- packages/beacon-node/test/e2e/network/mdns.test.ts | 2 +- .../e2e/network/onWorker/dataSerialization.test.ts | 2 +- .../perf/chain/produceBlock/produceBlockBody.test.ts | 2 ++ .../test/perf/chain/verifyImportBlocks.test.ts | 2 ++ .../beacon-node/test/spec/presets/fork_choice.test.ts | 2 ++ .../unit/chain/seenCache/seenGossipBlockInput.test.ts | 6 +++++- .../test/unit/db/api/repositories/dataColumn.test.ts | 5 ++++- packages/beacon-node/test/utils/networkWithMockDb.ts | 2 ++ scripts/build_c_kzg.sh | 10 ++++++++++ yarn.lock | 6 +++--- 13 files changed, 36 insertions(+), 8 deletions(-) create mode 100755 scripts/build_c_kzg.sh diff --git a/Dockerfile b/Dockerfile index fdab1140af47..a2c49d6a3691 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,8 @@ RUN apk update && apk add --no-cache g++ make python3 py3-setuptools && rm -rf / COPY . . +RUN chmod +x ./scripts/build_c_kzg.sh + RUN yarn install --non-interactive --frozen-lockfile && \ yarn build && \ yarn install --non-interactive --frozen-lockfile --production diff --git a/package.json b/package.json index 02e285e0c86a..7127d3f7d00a 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "packages/*" ], "scripts": { + "postinstall": "./scripts/build_c_kzg.sh", "clean": "rm -rf ./packages/*/lib ./packages/*/*.tsbuildinfo", "clean:nm": "rm -rf ./packages/*/node_modules ./node_modules", "build": "lerna run build", diff --git a/packages/beacon-node/package.json b/packages/beacon-node/package.json index c6202f3178f9..34977a5bd2a9 100644 --- a/packages/beacon-node/package.json +++ b/packages/beacon-node/package.json @@ -133,7 +133,7 @@ "@lodestar/utils": "^1.19.0", "@lodestar/validator": "^1.19.0", "@multiformats/multiaddr": "^12.1.3", - "c-kzg": "matthewkeil/c-kzg-4844#67bf9367817f0fa5ebd390aeb8c3ae88bdbc170e", + "c-kzg": "matthewkeil/c-kzg-4844#3f4a7a44d6b8cdf1fac3dd777159d130d6e1cd03", "datastore-core": "^9.1.1", "datastore-level": "^10.1.1", "deepmerge": "^4.3.1", diff --git a/packages/beacon-node/test/e2e/network/mdns.test.ts b/packages/beacon-node/test/e2e/network/mdns.test.ts index d8e148e191fe..b278de4503e6 100644 --- a/packages/beacon-node/test/e2e/network/mdns.test.ts +++ b/packages/beacon-node/test/e2e/network/mdns.test.ts @@ -86,7 +86,7 @@ describe.skip("mdns", function () { const opts = await getOpts(peerId); - const modules: Omit = { + const modules: Omit = { config, chain, db, diff --git a/packages/beacon-node/test/e2e/network/onWorker/dataSerialization.test.ts b/packages/beacon-node/test/e2e/network/onWorker/dataSerialization.test.ts index 09c74f1f5dfe..d7204a8560f9 100644 --- a/packages/beacon-node/test/e2e/network/onWorker/dataSerialization.test.ts +++ b/packages/beacon-node/test/e2e/network/onWorker/dataSerialization.test.ts @@ -79,7 +79,7 @@ describe.skip("data serialization through worker boundary", function () { // Defining tests in this notation ensures that any event data is tested and probably safe to send const networkEventData = filterByUsedEvents(networkEventDirection, { - [NetworkEvent.peerConnected]: {peer, status: statusZero}, + [NetworkEvent.peerConnected]: {peer, status: statusZero, dataColumns: []}, [NetworkEvent.peerDisconnected]: {peer}, [NetworkEvent.reqRespRequest]: { request: {method: ReqRespMethod.Status, body: statusZero}, diff --git a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts index 7bf8c2f7252f..b126c049fb0d 100644 --- a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts +++ b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts @@ -1,3 +1,4 @@ +import crypto from "node:crypto"; import {fromHexString} from "@chainsafe/ssz"; import {itBench} from "@dapplion/benchmark"; import {config} from "@lodestar/config/default"; @@ -39,6 +40,7 @@ describe("produceBlockBody", () => { minSameMessageSignatureSetsToBatch: 32, }, { + nodeId: Buffer.alloc(32, crypto.randomBytes(32)), config: state.config, db, logger, diff --git a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts index a21c28adaec0..07ba099b9659 100644 --- a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts +++ b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts @@ -1,3 +1,4 @@ +import crypto from "node:crypto"; import {itBench, setBenchOpts} from "@dapplion/benchmark"; import {config} from "@lodestar/config/default"; import {SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY, SLOTS_PER_EPOCH} from "@lodestar/params"; @@ -95,6 +96,7 @@ describe.skip("verify+import blocks - range sync perf test", () => { minSameMessageSignatureSetsToBatch: 32, }, { + nodeId: Buffer.alloc(32, crypto.randomBytes(32)), config: state.config, db, logger, diff --git a/packages/beacon-node/test/spec/presets/fork_choice.test.ts b/packages/beacon-node/test/spec/presets/fork_choice.test.ts index 92862c6cb03b..c9404dbcd094 100644 --- a/packages/beacon-node/test/spec/presets/fork_choice.test.ts +++ b/packages/beacon-node/test/spec/presets/fork_choice.test.ts @@ -1,4 +1,5 @@ import path from "node:path"; +import crypto from "node:crypto"; import {expect} from "vitest"; import {toHexString} from "@chainsafe/ssz"; import {BeaconStateAllForks, isExecutionStateType, signedBlockToSignedHeader} from "@lodestar/state-transition"; @@ -101,6 +102,7 @@ const forkChoiceTest = proposerBoostReorg: true, }, { + nodeId: Buffer.alloc(32, crypto.randomBytes(32)), config: createBeaconConfig(config, state.genesisValidatorsRoot), db: getMockedBeaconDb(), logger, diff --git a/packages/beacon-node/test/unit/chain/seenCache/seenGossipBlockInput.test.ts b/packages/beacon-node/test/unit/chain/seenCache/seenGossipBlockInput.test.ts index 2a3275536f22..3d1aa494464c 100644 --- a/packages/beacon-node/test/unit/chain/seenCache/seenGossipBlockInput.test.ts +++ b/packages/beacon-node/test/unit/chain/seenCache/seenGossipBlockInput.test.ts @@ -15,7 +15,11 @@ describe("SeenGossipBlockInput", () => { }); const genesisValidatorsRoot = Buffer.alloc(32, 0xaa); const config = createBeaconConfig(chainConfig, genesisValidatorsRoot); - const seenGossipBlockInput = new SeenGossipBlockInput(); + const seenGossipBlockInput = new SeenGossipBlockInput({ + custodyColumns: [], + custodyColumnsIndex: [], + custodyColumnsLen: 0, + }); // array of numBlobs, events where events are array of // [block|blob11|blob2, pd | bp | null | error string reflecting the expected result] diff --git a/packages/beacon-node/test/unit/db/api/repositories/dataColumn.test.ts b/packages/beacon-node/test/unit/db/api/repositories/dataColumn.test.ts index 93ac6f4cde1e..2eca3931e2a3 100644 --- a/packages/beacon-node/test/unit/db/api/repositories/dataColumn.test.ts +++ b/packages/beacon-node/test/unit/db/api/repositories/dataColumn.test.ts @@ -53,7 +53,10 @@ describe("block archive repository", function () { singedBlock.message.body.blobKzgCommitments.push(commitment); singedBlock.message.body.blobKzgCommitments.push(commitment); singedBlock.message.body.blobKzgCommitments.push(commitment); - const dataColumnSidecars = computeDataColumnSidecars(config, singedBlock, {blobs: [blob, blob, blob]}); + const dataColumnSidecars = computeDataColumnSidecars(config, singedBlock, { + blobs: [blob, blob, blob], + kzgProofs: [commitment, commitment, commitment], + }); for (let j = 0; j < dataColumnSidecars.length; j++) { dataColumnSidecars[j].index = j; } diff --git a/packages/beacon-node/test/utils/networkWithMockDb.ts b/packages/beacon-node/test/utils/networkWithMockDb.ts index f8a4a6181d2f..71bd70d6259e 100644 --- a/packages/beacon-node/test/utils/networkWithMockDb.ts +++ b/packages/beacon-node/test/utils/networkWithMockDb.ts @@ -1,3 +1,4 @@ +import crypto from "node:crypto"; import {createSecp256k1PeerId} from "@libp2p/peer-id-factory"; import {ChainForkConfig, createBeaconConfig} from "@lodestar/config"; import {ssz} from "@lodestar/types"; @@ -56,6 +57,7 @@ export async function getNetworkForTest( minSameMessageSignatureSetsToBatch: 32, }, { + nodeId: Buffer.alloc(32, crypto.randomBytes(32)), config: beaconConfig, db, logger, diff --git a/scripts/build_c_kzg.sh b/scripts/build_c_kzg.sh new file mode 100755 index 000000000000..77d3799ab20e --- /dev/null +++ b/scripts/build_c_kzg.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +cd node_modules/c-kzg + +git clone https://github.com/supranational/blst.git + +cd bindings/node.js + +make build + diff --git a/yarn.lock b/yarn.lock index f128e8afbe83..6ef847a55c75 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4494,9 +4494,9 @@ byte-size@8.1.1: resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-8.1.1.tgz#3424608c62d59de5bfda05d31e0313c6174842ae" integrity sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg== -c-kzg@matthewkeil/c-kzg-4844#67bf9367817f0fa5ebd390aeb8c3ae88bdbc170e: - version "4.0.0-alpha.0" - resolved "https://codeload.github.com/matthewkeil/c-kzg-4844/tar.gz/67bf9367817f0fa5ebd390aeb8c3ae88bdbc170e" +c-kzg@matthewkeil/c-kzg-4844#3f4a7a44d6b8cdf1fac3dd777159d130d6e1cd03: + version "0.0.0" + resolved "https://codeload.github.com/matthewkeil/c-kzg-4844/tar.gz/3f4a7a44d6b8cdf1fac3dd777159d130d6e1cd03" cac@^6.7.14: version "6.7.14" From ae1a03e6bbeabb404c66cb3fb81cb7e5e1fcf927 Mon Sep 17 00:00:00 2001 From: harkamal Date: Wed, 26 Jun 2024 13:21:08 +0530 Subject: [PATCH 17/17] rebase fixes --- packages/beacon-node/src/chain/blocks/types.ts | 2 +- .../src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts | 2 +- packages/beacon-node/src/util/blobs.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/beacon-node/src/chain/blocks/types.ts b/packages/beacon-node/src/chain/blocks/types.ts index 9166bfbd59fb..c03a2169da28 100644 --- a/packages/beacon-node/src/chain/blocks/types.ts +++ b/packages/beacon-node/src/chain/blocks/types.ts @@ -48,7 +48,7 @@ export type DataColumnsCacheMap = Map< {dataColumnSidecar: electra.DataColumnSidecar; dataColumnBytes: Uint8Array | null} >; -type ForkBlobsInfo = {fork: ForkName.deneb | ForkName.electra}; +type ForkBlobsInfo = {fork: ForkName.deneb}; type BlobsData = {blobs: deneb.BlobSidecars; blobsBytes: (Uint8Array | null)[]; blobsSource: BlobsSource}; export type BlockInputDataBlobs = ForkBlobsInfo & BlobsData; diff --git a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts index 0479e6f2c3c1..392d9c43faea 100644 --- a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts +++ b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts @@ -163,7 +163,7 @@ export function matchBlockWithDataColumns( peerId: PeerIdStr, config: ChainForkConfig, custodyConfig: CustodyConfig, - allBlocks: WithBytes[], + allBlocks: WithBytes[], allDataColumnSidecars: electra.DataColumnSidecar[], endSlot: Slot, blockSource: BlockSource, diff --git a/packages/beacon-node/src/util/blobs.ts b/packages/beacon-node/src/util/blobs.ts index 5923ba8ab1a5..8d9439d76f81 100644 --- a/packages/beacon-node/src/util/blobs.ts +++ b/packages/beacon-node/src/util/blobs.ts @@ -1,7 +1,7 @@ import {digest as sha256Digest} from "@chainsafe/as-sha256"; import {Tree} from "@chainsafe/persistent-merkle-tree"; import {VERSIONED_HASH_VERSION_KZG, KZG_COMMITMENT_GINDEX0, KZG_COMMITMENTS_GINDEX, ForkName, ForkAll, NUMBER_OF_COLUMNS} from "@lodestar/params"; -import {deneb, ssz, BeaconBlockBody, SignedBeaconBlock, SSZTypesFor} from "@lodestar/types"; +import {deneb, ssz, BeaconBlockBody, SignedBeaconBlock, SSZTypesFor, electra} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {signedBlockToSignedHeader} from "@lodestar/state-transition"; import {ckzg} from "./kzg.js";