diff --git a/.github/workflows/test-sim.yml b/.github/workflows/test-sim.yml index a5cae2cb5440..6c8a90e733fd 100644 --- a/.github/workflows/test-sim.yml +++ b/.github/workflows/test-sim.yml @@ -48,8 +48,8 @@ jobs: run: yarn test:sim:endpoints working-directory: packages/cli - - name: Sim tests eip4844 - run: yarn test:sim:eip4844 + - name: Sim tests deneb + run: yarn test:sim:deneb working-directory: packages/cli - name: Sim tests backup eth provider diff --git a/lerna.json b/lerna.json index 2c110ab3c414..51bec2b5fde4 100644 --- a/lerna.json +++ b/lerna.json @@ -4,7 +4,7 @@ ], "npmClient": "yarn", "useWorkspaces": true, - "version": "1.4.3", + "version": "1.5.0", "stream": "true", "command": { "version": { diff --git a/packages/api/package.json b/packages/api/package.json index 16ecba1bf71f..8c9d40be2cce 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.4.3", + "version": "1.5.0", "type": "module", "exports": { ".": { @@ -70,10 +70,10 @@ "dependencies": { "@chainsafe/persistent-merkle-tree": "^0.4.2", "@chainsafe/ssz": "^0.9.2", - "@lodestar/config": "^1.4.3", - "@lodestar/params": "^1.4.3", - "@lodestar/types": "^1.4.3", - "@lodestar/utils": "^1.4.3", + "@lodestar/config": "^1.5.0", + "@lodestar/params": "^1.5.0", + "@lodestar/types": "^1.5.0", + "@lodestar/utils": "^1.5.0", "cross-fetch": "^3.1.4", "eventsource": "^2.0.2", "qs": "^6.10.1" diff --git a/packages/api/src/beacon/client/debug.ts b/packages/api/src/beacon/client/debug.ts index 94bf6af746eb..be9acc7e1a08 100644 --- a/packages/api/src/beacon/client/debug.ts +++ b/packages/api/src/beacon/client/debug.ts @@ -38,7 +38,6 @@ export function getClient(_config: IChainForkConfig, httpClient: IHttpClient): A timeoutMs: GET_STATE_TIMEOUT_MS, }); // Casting to any otherwise Typescript doesn't like the multi-type return - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any return { ok: true, response: new Uint8Array(res.body), @@ -58,7 +57,6 @@ export function getClient(_config: IChainForkConfig, httpClient: IHttpClient): A timeoutMs: GET_STATE_TIMEOUT_MS, }); // Casting to any otherwise Typescript doesn't like the multi-type return - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any return {ok: true, response: new Uint8Array(res.body), status: res.status} as ApiClientResponse<{ [HttpStatusCode.OK]: Uint8Array; }>; diff --git a/packages/api/src/beacon/client/events.ts b/packages/api/src/beacon/client/events.ts index 996899f98a1f..208cd44d7514 100644 --- a/packages/api/src/beacon/client/events.ts +++ b/packages/api/src/beacon/client/events.ts @@ -7,8 +7,8 @@ import {HttpStatusCode} from "../../utils/client/httpStatusCode.js"; /** * REST HTTP client for events routes */ -export function getClient(_config: IChainForkConfig, baseUrl: string): Api { - const eventSerdes = getEventSerdes(); +export function getClient(config: IChainForkConfig, baseUrl: string): Api { + const eventSerdes = getEventSerdes(config); return { eventstream: async (topics, signal, onEvent) => { diff --git a/packages/api/src/beacon/routes/beacon/block.ts b/packages/api/src/beacon/routes/beacon/block.ts index de071418c3f3..2a4ad38fc1a3 100644 --- a/packages/api/src/beacon/routes/beacon/block.ts +++ b/packages/api/src/beacon/routes/beacon/block.ts @@ -1,7 +1,7 @@ import {ContainerType} from "@chainsafe/ssz"; import {ForkName} from "@lodestar/params"; import {IChainForkConfig} from "@lodestar/config"; -import {phase0, allForks, Slot, Root, ssz, RootHex, eip4844} from "@lodestar/types"; +import {phase0, allForks, Slot, Root, ssz, RootHex, deneb} from "@lodestar/types"; import { RoutesData, @@ -196,7 +196,7 @@ export type Api = { blockId: BlockId ): Promise< ApiClientResponse<{ - [HttpStatusCode.OK]: {executionOptimistic: ExecutionOptimistic; data: eip4844.BlobsSidecar}; + [HttpStatusCode.OK]: {executionOptimistic: ExecutionOptimistic; data: deneb.BlobsSidecar}; }> >; }; @@ -294,6 +294,6 @@ export function getReturnTypes(): ReturnTypes { getBlockHeader: ContainerDataExecutionOptimistic(BeaconHeaderResType), getBlockHeaders: ContainerDataExecutionOptimistic(ArrayOf(BeaconHeaderResType)), getBlockRoot: ContainerDataExecutionOptimistic(RootContainer), - getBlobsSidecar: ContainerDataExecutionOptimistic(ssz.eip4844.BlobsSidecar), + getBlobsSidecar: ContainerDataExecutionOptimistic(ssz.deneb.BlobsSidecar), }; } diff --git a/packages/api/src/beacon/routes/beacon/index.ts b/packages/api/src/beacon/routes/beacon/index.ts index 0cddd2d2a894..f6e739b4fdc4 100644 --- a/packages/api/src/beacon/routes/beacon/index.ts +++ b/packages/api/src/beacon/routes/beacon/index.ts @@ -48,7 +48,7 @@ export type ReqTypes = { [K in keyof ReturnType]: ReturnType[K]["writeReq"]>; }; -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function getReqSerializers(config: IChainForkConfig) { return { getGenesis: reqEmpty, diff --git a/packages/api/src/beacon/routes/events.ts b/packages/api/src/beacon/routes/events.ts index 2dd1d6a34442..f553a3202551 100644 --- a/packages/api/src/beacon/routes/events.ts +++ b/packages/api/src/beacon/routes/events.ts @@ -1,6 +1,7 @@ -import {Epoch, phase0, capella, Slot, ssz, StringType, RootHex, altair, UintNum64} from "@lodestar/types"; -import {ContainerType, Type, VectorCompositeType} from "@chainsafe/ssz"; -import {FINALIZED_ROOT_DEPTH} from "@lodestar/params"; +import {Epoch, phase0, capella, Slot, ssz, StringType, RootHex, altair, UintNum64, allForks} from "@lodestar/types"; +import {ContainerType} from "@chainsafe/ssz"; +import {IChainForkConfig} from "@lodestar/config"; + import {RouteDef, TypeJson} from "../../utils/index.js"; import {HttpStatusCode} from "../../utils/client/httpStatusCode.js"; import {ApiClientResponse} from "../../interfaces.js"; @@ -86,9 +87,9 @@ export type EventData = { executionOptimistic: boolean; }; [EventType.contributionAndProof]: altair.SignedContributionAndProof; - [EventType.lightClientOptimisticUpdate]: altair.LightClientOptimisticUpdate; - [EventType.lightClientFinalityUpdate]: altair.LightClientFinalityUpdate; - [EventType.lightClientUpdate]: altair.LightClientUpdate; + [EventType.lightClientOptimisticUpdate]: allForks.LightClientOptimisticUpdate; + [EventType.lightClientFinalityUpdate]: allForks.LightClientFinalityUpdate; + [EventType.lightClientUpdate]: allForks.LightClientUpdate; }; export type BeaconEvent = {[K in EventType]: {type: K; message: EventData[K]}}[EventType]; @@ -123,8 +124,12 @@ export type ReqTypes = { // It doesn't make sense to define a getReqSerializers() here given the exotic argument of eventstream() // The request is very simple: (topics) => {query: {topics}}, and the test will ensure compatibility server - client -export function getTypeByEvent(): {[K in EventType]: Type} { +export function getTypeByEvent(config: IChainForkConfig): {[K in EventType]: TypeJson} { const stringType = new StringType(); + const getLightClientTypeFromHeader = (data: allForks.LightClientHeader): allForks.AllForksLightClientSSZTypes => { + return config.getLightClientForkTypes(data.beacon.slot); + }; + return { [EventType.head]: new ContainerType( { @@ -178,31 +183,45 @@ export function getTypeByEvent(): {[K in EventType]: Type} { [EventType.contributionAndProof]: ssz.altair.SignedContributionAndProof, - [EventType.lightClientOptimisticUpdate]: new ContainerType( - { - syncAggregate: ssz.altair.SyncAggregate, - attestedHeader: ssz.altair.LightClientHeader, - signatureSlot: ssz.Slot, - }, - {jsonCase: "eth2"} - ), - [EventType.lightClientFinalityUpdate]: new ContainerType( - { - attestedHeader: ssz.altair.LightClientHeader, - finalizedHeader: ssz.altair.LightClientHeader, - finalityBranch: new VectorCompositeType(ssz.Bytes32, FINALIZED_ROOT_DEPTH), - syncAggregate: ssz.altair.SyncAggregate, - signatureSlot: ssz.Slot, - }, - {jsonCase: "eth2"} - ), - [EventType.lightClientUpdate]: ssz.altair.LightClientUpdate, + [EventType.lightClientOptimisticUpdate]: { + toJson: (data) => + getLightClientTypeFromHeader(((data as unknown) as allForks.LightClientOptimisticUpdate).attestedHeader)[ + "LightClientOptimisticUpdate" + ].toJson(data), + fromJson: (data) => + getLightClientTypeFromHeader( + // eslint-disable-next-line @typescript-eslint/naming-convention + ((data as unknown) as {attested_header: allForks.LightClientHeader}).attested_header + )["LightClientOptimisticUpdate"].fromJson(data), + }, + [EventType.lightClientFinalityUpdate]: { + toJson: (data) => + getLightClientTypeFromHeader(((data as unknown) as allForks.LightClientFinalityUpdate).attestedHeader)[ + "LightClientFinalityUpdate" + ].toJson(data), + fromJson: (data) => + getLightClientTypeFromHeader( + // eslint-disable-next-line @typescript-eslint/naming-convention + ((data as unknown) as {attested_header: allForks.LightClientHeader}).attested_header + )["LightClientFinalityUpdate"].fromJson(data), + }, + [EventType.lightClientUpdate]: { + toJson: (data) => + getLightClientTypeFromHeader(((data as unknown) as allForks.LightClientUpdate).attestedHeader)[ + "LightClientUpdate" + ].toJson(data), + fromJson: (data) => + getLightClientTypeFromHeader( + // eslint-disable-next-line @typescript-eslint/naming-convention + ((data as unknown) as {attested_header: allForks.LightClientHeader}).attested_header + )["LightClientUpdate"].fromJson(data), + }, }; } -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type -export function getEventSerdes() { - const typeByEvent = getTypeByEvent(); +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function getEventSerdes(config: IChainForkConfig) { + const typeByEvent = getTypeByEvent(config); return { toJson: (event: BeaconEvent): unknown => { diff --git a/packages/api/src/beacon/routes/lightclient.ts b/packages/api/src/beacon/routes/lightclient.ts index df32efd08252..e3defa3dec1a 100644 --- a/packages/api/src/beacon/routes/lightclient.ts +++ b/packages/api/src/beacon/routes/lightclient.ts @@ -1,6 +1,5 @@ -import {altair, ssz, StringType, SyncPeriod} from "@lodestar/types"; -import {ForkName} from "@lodestar/params"; -import {ContainerType} from "@chainsafe/ssz"; +import {ssz, SyncPeriod, allForks} from "@lodestar/types"; +import {ForkName, isForkLightClient} from "@lodestar/params"; import { ArrayOf, ReturnTypes, @@ -17,13 +16,6 @@ import {ApiClientResponse} from "../../interfaces.js"; // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes -export type LightClientBootstrap = { - header: altair.LightClientHeader; - currentSyncCommittee: altair.SyncCommittee; - /** Single branch proof from state root to currentSyncCommittee */ - currentSyncCommitteeBranch: Uint8Array[]; -}; - export type Api = { /** * Returns an array of best updates given a `startPeriod` and `count` number of sync committee period to return. @@ -39,7 +31,7 @@ export type Api = { ApiClientResponse<{ [HttpStatusCode.OK]: { version: ForkName; - data: altair.LightClientUpdate; + data: allForks.LightClientUpdate; }[]; }> >; @@ -51,7 +43,7 @@ export type Api = { ApiClientResponse<{ [HttpStatusCode.OK]: { version: ForkName; - data: altair.LightClientOptimisticUpdate; + data: allForks.LightClientOptimisticUpdate; }; }> >; @@ -59,7 +51,7 @@ export type Api = { ApiClientResponse<{ [HttpStatusCode.OK]: { version: ForkName; - data: altair.LightClientFinalityUpdate; + data: allForks.LightClientFinalityUpdate; }; }> >; @@ -74,7 +66,7 @@ export type Api = { ApiClientResponse<{ [HttpStatusCode.OK]: { version: ForkName; - data: altair.LightClientBootstrap; + data: allForks.LightClientBootstrap; }; }> >; @@ -138,16 +130,31 @@ export function getReqSerializers(): ReqSerializers { } export function getReturnTypes(): ReturnTypes { + // Form a TypeJson convertor for getUpdates + const VersionedUpdate = WithVersion((fork: ForkName) => + isForkLightClient(fork) ? ssz.allForksLightClient[fork].LightClientUpdate : ssz.altair.LightClientUpdate + ); + const getUpdates = { + toJson: (updates: {version: ForkName; data: allForks.LightClientUpdate}[]) => + updates.map((data) => VersionedUpdate.toJson(data)), + fromJson: (updates: unknown[]) => updates.map((data) => VersionedUpdate.fromJson(data)), + }; + return { - getUpdates: ArrayOf( - new ContainerType({ - version: new StringType(), - data: ssz.altair.LightClientUpdate, - }) + getUpdates, + getOptimisticUpdate: WithVersion((fork: ForkName) => + isForkLightClient(fork) + ? ssz.allForksLightClient[fork].LightClientOptimisticUpdate + : ssz.altair.LightClientOptimisticUpdate + ), + getFinalityUpdate: WithVersion((fork: ForkName) => + isForkLightClient(fork) + ? ssz.allForksLightClient[fork].LightClientFinalityUpdate + : ssz.altair.LightClientFinalityUpdate + ), + getBootstrap: WithVersion((fork: ForkName) => + isForkLightClient(fork) ? ssz.allForksLightClient[fork].LightClientBootstrap : ssz.altair.LightClientBootstrap ), - getOptimisticUpdate: WithVersion(() => ssz.altair.LightClientOptimisticUpdate), - getFinalityUpdate: WithVersion(() => ssz.altair.LightClientFinalityUpdate), - getBootstrap: WithVersion(() => ssz.altair.LightClientBootstrap), getCommitteeRoot: ContainerData(ArrayOf(ssz.Root)), }; } diff --git a/packages/api/src/beacon/routes/lodestar.ts b/packages/api/src/beacon/routes/lodestar.ts index 3d08114edaa5..44771b432176 100644 --- a/packages/api/src/beacon/routes/lodestar.ts +++ b/packages/api/src/beacon/routes/lodestar.ts @@ -213,7 +213,6 @@ export function getReqSerializers(): ReqSerializers { }; } -/* eslint-disable @typescript-eslint/naming-convention */ export function getReturnTypes(): ReturnTypes { return { writeHeapdump: sameType(), diff --git a/packages/api/src/beacon/server/events.ts b/packages/api/src/beacon/server/events.ts index 4ee4b36b4133..1235ca980bdf 100644 --- a/packages/api/src/beacon/server/events.ts +++ b/packages/api/src/beacon/server/events.ts @@ -4,7 +4,7 @@ import {ServerRoutes} from "../../utils/server/index.js"; import {ServerApi} from "../../interfaces.js"; export function getRoutes(config: IChainForkConfig, api: ServerApi): ServerRoutes { - const eventSerdes = getEventSerdes(); + const eventSerdes = getEventSerdes(config); return { // Non-JSON route. Server Sent Events (SSE) diff --git a/packages/api/src/builder/routes.ts b/packages/api/src/builder/routes.ts index 02f48fe6dc77..146ee33911fd 100644 --- a/packages/api/src/builder/routes.ts +++ b/packages/api/src/builder/routes.ts @@ -101,7 +101,7 @@ export function getReturnTypes(): ReturnTypes { submitBlindedBlockV2: WithVersion((fork: ForkName) => isForkBlobs(fork) ? ssz.allForksBlobs[fork].SignedBeaconBlockAndBlobsSidecar - : ssz.eip4844.SignedBeaconBlockAndBlobsSidecar + : ssz.deneb.SignedBeaconBlockAndBlobsSidecar ), }; } diff --git a/packages/api/src/interfaces.ts b/packages/api/src/interfaces.ts index 54a48c286053..b9e3c3a92d59 100644 --- a/packages/api/src/interfaces.ts +++ b/packages/api/src/interfaces.ts @@ -1,7 +1,7 @@ import {HttpStatusCode, HttpSuccessCodes} from "./utils/client/httpStatusCode.js"; import {Resolves} from "./utils/types.js"; -/* eslint-disable @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-explicit-any */ export type APIClientHandler = (...args: any) => PromiseLike; export type APIServerHandler = (...args: any) => PromiseLike; diff --git a/packages/api/src/keymanager/routes.ts b/packages/api/src/keymanager/routes.ts index 6177fecc49cc..7f93a03ed799 100644 --- a/packages/api/src/keymanager/routes.ts +++ b/packages/api/src/keymanager/routes.ts @@ -349,7 +349,6 @@ export function getReqSerializers(): ReqSerializers { }; } -/* eslint-disable @typescript-eslint/naming-convention */ export function getReturnTypes(): ReturnTypes { return { listKeys: jsonType("snake"), diff --git a/packages/api/src/utils/client/client.ts b/packages/api/src/utils/client/client.ts index 3e6310887eb5..0a8cd95204b1 100644 --- a/packages/api/src/utils/client/client.ts +++ b/packages/api/src/utils/client/client.ts @@ -12,7 +12,7 @@ import {FetchOpts, HttpError, IHttpClient} from "./httpClient.js"; * Format FetchFn opts from Fn arguments given a route definition and request serializer. * For routes that return only JSOn use @see getGenericJsonClient */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function getFetchOptsSerializer any, ReqType extends ReqGeneric>( routeDef: RouteDef, reqSerializer: ReqSerializer, @@ -36,7 +36,7 @@ export function getFetchOptsSerializer any, ReqType /** * Generate `getFetchOptsSerializer()` functions for all routes in `Api` */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function getFetchOptsSerializers< Api extends Record, ReqTypes extends {[K in keyof Api]: ReqGeneric} diff --git a/packages/api/src/utils/server/genericJsonServer.ts b/packages/api/src/utils/server/genericJsonServer.ts index 1e39fd497c0f..5d79ebbadf7b 100644 --- a/packages/api/src/utils/server/genericJsonServer.ts +++ b/packages/api/src/utils/server/genericJsonServer.ts @@ -1,5 +1,4 @@ import {mapValues} from "@lodestar/utils"; -// eslint-disable-next-line import/no-extraneous-dependencies import {IChainForkConfig} from "@lodestar/config"; import {ReqGeneric, TypeJson, Resolves, RouteGroupDefinition} from "../types.js"; import {getFastifySchema} from "../schema.js"; @@ -9,7 +8,7 @@ import {ServerRoute} from "./types.js"; // See /packages/api/src/routes/index.ts for reasoning -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/naming-convention */ +/* eslint-disable @typescript-eslint/no-explicit-any */ export type ServerRoutes< Api extends Record, diff --git a/packages/api/src/utils/server/types.ts b/packages/api/src/utils/server/types.ts index ae602a8477ec..a25c606b8a1c 100644 --- a/packages/api/src/utils/server/types.ts +++ b/packages/api/src/utils/server/types.ts @@ -19,7 +19,7 @@ export type ServerRoute = { id: string; }; -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/naming-convention */ +/* eslint-disable @typescript-eslint/naming-convention */ /** Adaptor for Fastify v3.x.x route type which has a ton of arguments */ export type FastifyHandler = fastify.RouteHandlerMethod< diff --git a/packages/api/test/unit/beacon/oapiSpec.test.ts b/packages/api/test/unit/beacon/oapiSpec.test.ts index c95de6a33196..381e852a5add 100644 --- a/packages/api/test/unit/beacon/oapiSpec.test.ts +++ b/packages/api/test/unit/beacon/oapiSpec.test.ts @@ -110,7 +110,7 @@ describe("eventstream event data", () => { } }); - const eventSerdes = routes.events.getEventSerdes(); + const eventSerdes = routes.events.getEventSerdes(config); const knownTopics = new Set(Object.values(routes.events.eventTypes)); for (const [topic, {value}] of Object.entries(eventstreamExamples ?? {})) { diff --git a/packages/api/test/unit/beacon/testData/beacon.ts b/packages/api/test/unit/beacon/testData/beacon.ts index 6efd420986b5..41f4b1d84f08 100644 --- a/packages/api/test/unit/beacon/testData/beacon.ts +++ b/packages/api/test/unit/beacon/testData/beacon.ts @@ -58,7 +58,7 @@ export const testData: GenericServerTestCases = { }, getBlobsSidecar: { args: ["head"], - res: {executionOptimistic: true, data: ssz.eip4844.BlobsSidecar.defaultValue()}, + res: {executionOptimistic: true, data: ssz.deneb.BlobsSidecar.defaultValue()}, }, // pool diff --git a/packages/api/test/unit/beacon/testData/config.ts b/packages/api/test/unit/beacon/testData/config.ts index d88479b7de43..463d584880a3 100644 --- a/packages/api/test/unit/beacon/testData/config.ts +++ b/packages/api/test/unit/beacon/testData/config.ts @@ -5,8 +5,6 @@ import {activePreset, presetToJson} from "@lodestar/params"; import {Api} from "../../../../src/beacon/routes/config.js"; import {GenericServerTestCases} from "../../../utils/genericServerTest.js"; -/* eslint-disable @typescript-eslint/naming-convention */ - const configJson = chainConfigToJson(chainConfig); const presetJson = presetToJson(activePreset); const jsonSpec = {...configJson, ...presetJson}; diff --git a/packages/api/test/unit/builder/testData.ts b/packages/api/test/unit/builder/testData.ts index 38a12ae6d637..443522269fa2 100644 --- a/packages/api/test/unit/builder/testData.ts +++ b/packages/api/test/unit/builder/testData.ts @@ -23,11 +23,11 @@ export const testData: GenericServerTestCases = { res: {version: ForkName.bellatrix, data: ssz.bellatrix.SignedBuilderBid.defaultValue()}, }, submitBlindedBlock: { - args: [ssz.eip4844.SignedBlindedBeaconBlock.defaultValue()], + args: [ssz.deneb.SignedBlindedBeaconBlock.defaultValue()], res: {version: ForkName.bellatrix, data: ssz.bellatrix.ExecutionPayload.defaultValue()}, }, submitBlindedBlockV2: { - args: [ssz.eip4844.SignedBlindedBeaconBlock.defaultValue()], - res: {version: ForkName.eip4844, data: ssz.eip4844.SignedBeaconBlockAndBlobsSidecar.defaultValue()}, + args: [ssz.deneb.SignedBlindedBeaconBlock.defaultValue()], + res: {version: ForkName.deneb, data: ssz.deneb.SignedBeaconBlockAndBlobsSidecar.defaultValue()}, }, }; diff --git a/packages/api/test/utils/genericServerTest.ts b/packages/api/test/utils/genericServerTest.ts index aa629f05d693..43b5f8589ebd 100644 --- a/packages/api/test/utils/genericServerTest.ts +++ b/packages/api/test/utils/genericServerTest.ts @@ -49,7 +49,6 @@ export function runGenericServerTest< mockApi[routeId].resolves(testCases[routeId].res); // Do the call - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const res = await (client[routeId] as APIClientHandler)(...(testCase.args as any[])); // Use spy to assert argument serialization diff --git a/packages/beacon-node/package.json b/packages/beacon-node/package.json index 110f6285d369..47b01c7a7f29 100644 --- a/packages/beacon-node/package.json +++ b/packages/beacon-node/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.4.3", + "version": "1.5.0", "type": "module", "exports": { ".": { @@ -114,17 +114,17 @@ "@libp2p/peer-id-factory": "^2.0.1", "@libp2p/prometheus-metrics": "^1.1.2", "@libp2p/tcp": "^6.1.0", - "@lodestar/api": "^1.4.3", - "@lodestar/config": "^1.4.3", - "@lodestar/db": "^1.4.3", - "@lodestar/fork-choice": "^1.4.3", - "@lodestar/light-client": "^1.4.3", - "@lodestar/params": "^1.4.3", - "@lodestar/reqresp": "^1.4.3", - "@lodestar/state-transition": "^1.4.3", - "@lodestar/types": "^1.4.3", - "@lodestar/utils": "^1.4.3", - "@lodestar/validator": "^1.4.3", + "@lodestar/api": "^1.5.0", + "@lodestar/config": "^1.5.0", + "@lodestar/db": "^1.5.0", + "@lodestar/fork-choice": "^1.5.0", + "@lodestar/light-client": "^1.5.0", + "@lodestar/params": "^1.5.0", + "@lodestar/reqresp": "^1.5.0", + "@lodestar/state-transition": "^1.5.0", + "@lodestar/types": "^1.5.0", + "@lodestar/utils": "^1.5.0", + "@lodestar/validator": "^1.5.0", "@multiformats/multiaddr": "^11.0.0", "@types/datastore-level": "^3.0.0", "buffer-xor": "^2.0.2", 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 e349b399f242..1d7ab43f64eb 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -2,7 +2,7 @@ import {routes, ServerApi} from "@lodestar/api"; import {computeTimeAtSlot} from "@lodestar/state-transition"; import {ForkSeq, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params"; import {sleep} from "@lodestar/utils"; -import {eip4844, allForks} from "@lodestar/types"; +import {deneb, allForks} from "@lodestar/types"; import {fromHexString, toHexString} from "@chainsafe/ssz"; import {getBlockInput} from "../../../../chain/blocks/types.js"; import {promiseAllMaybeAsync} from "../../../../util/promises.js"; @@ -183,7 +183,7 @@ export function getBeaconBlockApi({ const executionBuilder = chain.executionBuilder; if (!executionBuilder) throw Error("exeutionBuilder required to publish SignedBlindedBeaconBlock"); let signedBlock: allForks.SignedBeaconBlock; - if (config.getForkSeq(signedBlindedBlock.message.slot) >= ForkSeq.eip4844) { + if (config.getForkSeq(signedBlindedBlock.message.slot) >= ForkSeq.deneb) { const {beaconBlock, blobsSidecar} = await executionBuilder.submitBlindedBlockV2(signedBlindedBlock); signedBlock = beaconBlock; // add this blobs to the map for access & broadcasting in publishBlock @@ -215,15 +215,15 @@ export function getBeaconBlockApi({ metrics?.registerBeaconBlock(OpSource.api, seenTimestampSec, signedBlock.message); - // TODO EIP-4844: Open question if broadcast to both block topic + block_and_blobs topic + // TODO Deneb: Open question if broadcast to both block topic + block_and_blobs topic const blockForImport = - config.getForkSeq(signedBlock.message.slot) >= ForkSeq.eip4844 - ? getBlockInput.postEIP4844( + config.getForkSeq(signedBlock.message.slot) >= ForkSeq.deneb + ? getBlockInput.postDeneb( config, signedBlock, - chain.getBlobsSidecar(signedBlock.message as eip4844.BeaconBlock) + chain.getBlobsSidecar(signedBlock.message as deneb.BeaconBlock) ) - : getBlockInput.preEIP4844(config, signedBlock); + : getBlockInput.preDeneb(config, signedBlock); await promiseAllMaybeAsync([ // Send the block, regardless of whether or not it is valid. The API @@ -252,7 +252,7 @@ export function getBeaconBlockApi({ blobsSidecar = { beaconBlockRoot: blockRoot, beaconBlockSlot: block.message.slot, - blobs: [] as eip4844.Blobs, + blobs: [] as deneb.Blobs, kzgAggregatedProof: ckzg.computeAggregateKzgProof([]), }; } diff --git a/packages/beacon-node/src/api/impl/beacon/pool/index.ts b/packages/beacon-node/src/api/impl/beacon/pool/index.ts index 20e8cf56543a..25bed05075de 100644 --- a/packages/beacon-node/src/api/impl/beacon/pool/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/pool/index.ts @@ -41,7 +41,7 @@ export function getBeaconPoolApi({ }, async getPoolBlsToExecutionChanges() { - return {data: chain.opPool.getAllBlsToExecutionChanges()}; + return {data: chain.opPool.getAllBlsToExecutionChanges().map(({data}) => data)}; }, async submitPoolAttestations(attestations) { @@ -102,13 +102,21 @@ export function getBeaconPoolApi({ await Promise.all( blsToExecutionChanges.map(async (blsToExecutionChange, i) => { try { - await validateBlsToExecutionChange(chain, blsToExecutionChange); - chain.opPool.insertBlsToExecutionChange(blsToExecutionChange); - await network.gossip.publishBlsToExecutionChange(blsToExecutionChange); + // Ignore even if the change exists and reprocess + await validateBlsToExecutionChange(chain, blsToExecutionChange, true); + const preCapella = !( + chain.clock.currentEpoch >= chain.config.CAPELLA_FORK_EPOCH && + // TODO: Remove this condition once testing is done + network.isSubscribedToGossipCoreTopics() + ); + chain.opPool.insertBlsToExecutionChange(blsToExecutionChange, preCapella); + if (!preCapella) { + await network.gossip.publishBlsToExecutionChange(blsToExecutionChange); + } } catch (e) { errors.push(e as Error); logger.error( - `Error on submitPoolSyncCommitteeSignatures [${i}]`, + `Error on submitPoolBlsToExecutionChange [${i}]`, {validatorIndex: blsToExecutionChange.message.validatorIndex}, e as Error ); diff --git a/packages/beacon-node/src/api/impl/config/constants.ts b/packages/beacon-node/src/api/impl/config/constants.ts index 603d66b11308..87ffce91b4d9 100644 --- a/packages/beacon-node/src/api/impl/config/constants.ts +++ b/packages/beacon-node/src/api/impl/config/constants.ts @@ -97,7 +97,7 @@ export const specConstants = { // ## Capella domain types DOMAIN_BLS_TO_EXECUTION_CHANGE, - // EIP4844 types + // Deneb types BLOB_TX_TYPE, VERSIONED_HASH_VERSION_KZG, }; diff --git a/packages/beacon-node/src/api/impl/events/index.ts b/packages/beacon-node/src/api/impl/events/index.ts index 3466fee2fccc..18f9ff160b3a 100644 --- a/packages/beacon-node/src/api/impl/events/index.ts +++ b/packages/beacon-node/src/api/impl/events/index.ts @@ -18,7 +18,7 @@ export function getEventsApi({chain}: Pick): Ser const handler = (data: any): void => { // TODO: What happens if this handler throws? Does it break the other chain.emitter listeners? - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment onEvent({type: topic, message: data}); }; diff --git a/packages/beacon-node/src/api/impl/lodestar/index.ts b/packages/beacon-node/src/api/impl/lodestar/index.ts index e22ac6bdf295..daf423672dbb 100644 --- a/packages/beacon-node/src/api/impl/lodestar/index.ts +++ b/packages/beacon-node/src/api/impl/lodestar/index.ts @@ -119,7 +119,6 @@ export function getLodestarApi({ }, async runGC() { - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (!global.gc) throw Error("You must expose GC running the Node.js process with 'node --expose_gc'"); global.gc(); }, diff --git a/packages/beacon-node/src/chain/archiver/archiveBlocks.ts b/packages/beacon-node/src/chain/archiver/archiveBlocks.ts index b60eb3622d64..fb2d37407e72 100644 --- a/packages/beacon-node/src/chain/archiver/archiveBlocks.ts +++ b/packages/beacon-node/src/chain/archiver/archiveBlocks.ts @@ -41,7 +41,7 @@ export async function archiveBlocks( const finalizedNonCanonicalBlocks = forkChoice.getAllNonAncestorBlocks(finalizedCheckpoint.rootHex); // NOTE: The finalized block will be exactly the first block of `epoch` or previous - const finalizedPostEIP4844 = finalizedCheckpoint.epoch >= config.EIP4844_FORK_EPOCH; + const finalizedPostDeneb = finalizedCheckpoint.epoch >= config.EIP4844_FORK_EPOCH; const finalizedCanonicalBlockRoots: BlockRootSlot[] = finalizedCanonicalBlocks.map((block) => ({ slot: block.slot, @@ -56,7 +56,7 @@ export async function archiveBlocks( size: finalizedCanonicalBlockRoots.length, }); - if (finalizedPostEIP4844) { + if (finalizedPostDeneb) { await migrateBlobsSidecarFromHotToColdDb(config, db, finalizedCanonicalBlockRoots); logger.verbose("Migrated blobsSidecar from hot DB to cold DB"); } @@ -72,7 +72,7 @@ export async function archiveBlocks( slots: finalizedNonCanonicalBlocks.map((summary) => summary.slot).join(","), }); - if (finalizedPostEIP4844) { + if (finalizedPostDeneb) { await db.blobsSidecar.batchDelete(nonCanonicalBlockRoots); logger.verbose("Deleted non canonical blobsSider from hot DB"); } @@ -80,7 +80,7 @@ export async function archiveBlocks( // Delete expired blobs // Keep only `[max(GENESIS_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS), current_epoch]` - if (finalizedPostEIP4844) { + if (finalizedPostDeneb) { const blobsSidecarMinEpoch = currentEpoch - config.MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS; if (blobsSidecarMinEpoch >= config.EIP4844_FORK_EPOCH) { const slotsToDelete = await db.blobsSidecarArchive.keys({lt: computeStartSlotAtEpoch(blobsSidecarMinEpoch)}); @@ -162,7 +162,7 @@ async function migrateBlobsSidecarFromHotToColdDb( // load Buffer instead of ssz deserialized to improve performance const canonicalBlobsSidecarEntries: IKeyValue[] = await Promise.all( canonicalBlocks - .filter((block) => config.getForkSeq(block.slot) >= ForkSeq.eip4844) + .filter((block) => config.getForkSeq(block.slot) >= ForkSeq.deneb) .map(async (block) => { const bytes = await db.blobsSidecar.getBinary(block.root); if (!bytes) { diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index b0005ba607d9..b24667041cd3 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -1,4 +1,4 @@ -import {altair, capella, ssz} from "@lodestar/types"; +import {capella, ssz, allForks} from "@lodestar/types"; import {MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params"; import {toHexString} from "@chainsafe/ssz"; import { @@ -66,7 +66,7 @@ export async function importBlock( root: blockRootHex, }); - if (blockInput.type === BlockInputType.postEIP4844) { + if (blockInput.type === BlockInputType.postDeneb) { const {blobs} = blockInput; // NOTE: Old blobs are pruned on archive await this.db.blobsSidecar.add(blobs); @@ -275,7 +275,7 @@ export async function importBlock( if (blockEpoch >= this.config.ALTAIR_FORK_EPOCH) { try { this.lightClientServer.onImportBlockHead( - block.message as altair.BeaconBlock, + block.message as allForks.AllForksLightClient["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 f290c002a703..33ce1fd24cb9 100644 --- a/packages/beacon-node/src/chain/blocks/index.ts +++ b/packages/beacon-node/src/chain/blocks/index.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */ import {allForks} from "@lodestar/types"; import {toHex} from "@lodestar/utils"; import {JobItemQueue} from "../../util/queue/index.js"; diff --git a/packages/beacon-node/src/chain/blocks/types.ts b/packages/beacon-node/src/chain/blocks/types.ts index ddb8c1c25409..e6c7dfa76249 100644 --- a/packages/beacon-node/src/chain/blocks/types.ts +++ b/packages/beacon-node/src/chain/blocks/types.ts @@ -1,56 +1,56 @@ import {CachedBeaconStateAllForks, computeEpochAtSlot} from "@lodestar/state-transition"; import {MaybeValidExecutionStatus} from "@lodestar/fork-choice"; -import {allForks, eip4844, Slot} from "@lodestar/types"; +import {allForks, deneb, Slot} from "@lodestar/types"; import {ForkSeq} from "@lodestar/params"; import {IChainForkConfig} from "@lodestar/config"; export enum BlockInputType { - preEIP4844 = "preEIP4844", - postEIP4844 = "postEIP4844", - postEIP4844OldBlobs = "postEIP4844OldBlobs", + preDeneb = "preDeneb", + postDeneb = "postDeneb", + postDenebOldBlobs = "postDenebOldBlobs", } export type BlockInput = - | {type: BlockInputType.preEIP4844; block: allForks.SignedBeaconBlock} - | {type: BlockInputType.postEIP4844; block: allForks.SignedBeaconBlock; blobs: eip4844.BlobsSidecar} - | {type: BlockInputType.postEIP4844OldBlobs; block: allForks.SignedBeaconBlock}; + | {type: BlockInputType.preDeneb; block: allForks.SignedBeaconBlock} + | {type: BlockInputType.postDeneb; block: allForks.SignedBeaconBlock; blobs: deneb.BlobsSidecar} + | {type: BlockInputType.postDenebOldBlobs; block: allForks.SignedBeaconBlock}; export function blockRequiresBlobs(config: IChainForkConfig, blockSlot: Slot, clockSlot: Slot): boolean { return ( - config.getForkSeq(blockSlot) >= ForkSeq.eip4844 && + config.getForkSeq(blockSlot) >= ForkSeq.deneb && // Only request blobs if they are recent enough computeEpochAtSlot(blockSlot) >= computeEpochAtSlot(clockSlot) - config.MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS ); } export const getBlockInput = { - preEIP4844(config: IChainForkConfig, block: allForks.SignedBeaconBlock): BlockInput { - if (config.getForkSeq(block.message.slot) >= ForkSeq.eip4844) { - throw Error(`Post EIP4844 block slot ${block.message.slot}`); + preDeneb(config: IChainForkConfig, block: allForks.SignedBeaconBlock): BlockInput { + if (config.getForkSeq(block.message.slot) >= ForkSeq.deneb) { + throw Error(`Post Deneb block slot ${block.message.slot}`); } return { - type: BlockInputType.preEIP4844, + type: BlockInputType.preDeneb, block, }; }, - postEIP4844(config: IChainForkConfig, block: allForks.SignedBeaconBlock, blobs: eip4844.BlobsSidecar): BlockInput { - if (config.getForkSeq(block.message.slot) < ForkSeq.eip4844) { - throw Error(`Pre EIP4844 block slot ${block.message.slot}`); + postDeneb(config: IChainForkConfig, block: allForks.SignedBeaconBlock, blobs: deneb.BlobsSidecar): BlockInput { + if (config.getForkSeq(block.message.slot) < ForkSeq.deneb) { + throw Error(`Pre Deneb block slot ${block.message.slot}`); } return { - type: BlockInputType.postEIP4844, + type: BlockInputType.postDeneb, block, blobs, }; }, - postEIP4844OldBlobs(config: IChainForkConfig, block: allForks.SignedBeaconBlock): BlockInput { - if (config.getForkSeq(block.message.slot) < ForkSeq.eip4844) { - throw Error(`Pre EIP4844 block slot ${block.message.slot}`); + postDenebOldBlobs(config: IChainForkConfig, block: allForks.SignedBeaconBlock): BlockInput { + if (config.getForkSeq(block.message.slot) < ForkSeq.deneb) { + throw Error(`Pre Deneb block slot ${block.message.slot}`); } return { - type: BlockInputType.postEIP4844OldBlobs, + type: BlockInputType.postDenebOldBlobs, block, }; }, diff --git a/packages/beacon-node/src/chain/blocks/utils/ownBanner.ts b/packages/beacon-node/src/chain/blocks/utils/ownBanner.ts index e2d49243f8a1..b14b778d53ef 100644 --- a/packages/beacon-node/src/chain/blocks/utils/ownBanner.ts +++ b/packages/beacon-node/src/chain/blocks/utils/ownBanner.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-useless-escape */ export const CAPELLA_OWL_BANNER = String.raw` /^----^\ diff --git a/packages/beacon-node/src/chain/blocks/utils/pandaMergeTransitionBanner.ts b/packages/beacon-node/src/chain/blocks/utils/pandaMergeTransitionBanner.ts index ea1b6133785a..53251cb9a035 100644 --- a/packages/beacon-node/src/chain/blocks/utils/pandaMergeTransitionBanner.ts +++ b/packages/beacon-node/src/chain/blocks/utils/pandaMergeTransitionBanner.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-useless-escape */ // Note: 1 extra new line for space // Note: String.raw preserve backslash character // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts index 981e2c17ce90..0c3e00face4e 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts @@ -4,7 +4,7 @@ import { ExecutionPayloadStatus, DataAvailableStatus, } from "@lodestar/state-transition"; -import {eip4844} from "@lodestar/types"; +import {deneb} from "@lodestar/types"; import {ErrorAborted, ILogger, sleep} from "@lodestar/utils"; import {IChainForkConfig} from "@lodestar/config"; import {IMetrics} from "../../metrics/index.js"; @@ -39,9 +39,9 @@ export async function verifyBlocksStateTransitionOnly( const {block} = blocks[i]; const preState = i === 0 ? preState0 : postStates[i - 1]; - // TODO EIP-4844: Is the best place here to call validateBlobsSidecar()? - // TODO EIP-4844: Gossip may already call validateBlobsSidecar, add some flag to de-dup from here - // TODO EIP-4844: For sync if this function is expensive, consider adding sleep(0) if metrics show it + // TODO Deneb: Is the best place here to call validateBlobsSidecar()? + // TODO Deneb: Gossip may already call validateBlobsSidecar, add some flag to de-dup from here + // TODO Deneb: For sync if this function is expensive, consider adding sleep(0) if metrics show it const dataAvailableStatus = maybeValidateBlobs(config, blocks[i], opts); // STFN - per_slot_processing() + per_block_processing() @@ -54,7 +54,7 @@ export async function verifyBlocksStateTransitionOnly( // NOTE: Assume valid for now while sending payload to execution engine in parallel // Latter verifyBlocksInEpoch() will make sure that payload is indeed valid executionPayloadStatus: ExecutionPayloadStatus.valid, - // TODO EIP-4844: Data is validated above for + // TODO Deneb: Data is validated above for dataAvailableStatus, // false because it's verified below with better error typing verifyStateRoot: false, @@ -109,28 +109,28 @@ function maybeValidateBlobs( blockInput: BlockInput, opts: ImportBlockOpts ): DataAvailableStatus { - // TODO EIP4844: Make switch verify it's exhaustive + // TODO Deneb: Make switch verify it's exhaustive switch (blockInput.type) { - case BlockInputType.postEIP4844: { + case BlockInputType.postDeneb: { if (opts.validBlobsSidecar) { return DataAvailableStatus.available; } const {block, blobs} = blockInput; const blockSlot = block.message.slot; - const {blobKzgCommitments} = (block as eip4844.SignedBeaconBlock).message.body; + const {blobKzgCommitments} = (block as deneb.SignedBeaconBlock).message.body; const beaconBlockRoot = config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(block.message); - // TODO EIP-4844: This function throws un-typed errors + // TODO Deneb: This function throws un-typed errors validateBlobsSidecar(blockSlot, beaconBlockRoot, blobKzgCommitments, blobs); return DataAvailableStatus.available; } - case BlockInputType.preEIP4844: - return DataAvailableStatus.preEIP4844; + case BlockInputType.preDeneb: + return DataAvailableStatus.preDeneb; // TODO: Ok to assume old data available? - case BlockInputType.postEIP4844OldBlobs: + case BlockInputType.postDenebOldBlobs: return DataAvailableStatus.available; } } diff --git a/packages/beacon-node/src/chain/bls/multithread/worker.ts b/packages/beacon-node/src/chain/bls/multithread/worker.ts index 8e3800edc5f7..ac33a356a23f 100644 --- a/packages/beacon-node/src/chain/bls/multithread/worker.ts +++ b/packages/beacon-node/src/chain/bls/multithread/worker.ts @@ -16,8 +16,6 @@ import {chunkifyMaximizeChunkSize} from "./utils.js"; */ const BATCHABLE_MIN_PER_CHUNK = 16; -/* eslint-disable no-console */ - // Cloned data from instatiation const workerData = worker.workerData as WorkerData; if (!workerData) throw Error("workerData must be defined"); diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index ab86b36b71c7..007fe981a299 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -12,7 +12,7 @@ import { PubkeyIndexMap, } from "@lodestar/state-transition"; import {IBeaconConfig} from "@lodestar/config"; -import {allForks, UintNum64, Root, phase0, Slot, RootHex, Epoch, ValidatorIndex, eip4844, Wei} from "@lodestar/types"; +import {allForks, UintNum64, Root, phase0, Slot, RootHex, Epoch, ValidatorIndex, deneb, Wei} from "@lodestar/types"; import {CheckpointWithHex, ExecutionStatus, IForkChoice, ProtoBlock} from "@lodestar/fork-choice"; import {ProcessShutdownCallback} from "@lodestar/validator"; import {ILogger, pruneSetToMax, toHex} from "@lodestar/utils"; @@ -118,9 +118,9 @@ export class BeaconChain implements IBeaconChain { readonly beaconProposerCache: BeaconProposerCache; readonly checkpointBalancesCache: CheckpointBalancesCache; - // TODO EIP-4844: Prune data structure every time period, for both old entries + // TODO DENEB: Prune data structure every time period, for both old entries /** Map keyed by executionPayload.blockHash of the block for those blobs */ - readonly producedBlobsSidecarCache = new Map(); + readonly producedBlobsSidecarCache = new Map(); readonly opts: IChainOptions; protected readonly blockProcessor: BlockProcessor; @@ -398,9 +398,9 @@ export class BeaconChain implements IBeaconChain { // blinded blobs will be fetched and added to this cache later before finally // publishing the blinded block's full version if (blobs.type === BlobsResultType.produced) { - // TODO EIP-4844: Prune data structure for max entries + // TODO DENEB: Prune data structure for max entries this.producedBlobsSidecarCache.set(blobs.blockHash, { - // TODO EIP-4844: Optimize, hashing the full block is not free. + // TODO DENEB: Optimize, hashing the full block is not free. beaconBlockRoot: this.config.getForkTypes(block.slot).BeaconBlock.hashTreeRoot(block), beaconBlockSlot: block.slot, blobs: blobs.blobs, @@ -425,7 +425,7 @@ export class BeaconChain implements IBeaconChain { * kzg_aggregated_proof=compute_proof_from_blobs(blobs), * ) */ - getBlobsSidecar(beaconBlock: eip4844.BeaconBlock): eip4844.BlobsSidecar { + getBlobsSidecar(beaconBlock: deneb.BeaconBlock): deneb.BlobsSidecar { const blockHash = toHex(beaconBlock.body.executionPayload.blockHash); const blobsSidecar = this.producedBlobsSidecarCache.get(blockHash); if (!blobsSidecar) { @@ -649,7 +649,7 @@ export class BeaconChain implements IBeaconChain { } // Prune old blobsSidecar for block production, those are only useful on their slot - if (this.config.getForkSeq(slot) >= ForkSeq.eip4844 && this.producedBlobsSidecarCache.size > 0) { + if (this.config.getForkSeq(slot) >= ForkSeq.deneb && this.producedBlobsSidecarCache.size > 0) { for (const [key, blobsSidecar] of this.producedBlobsSidecarCache) { if (slot > blobsSidecar.beaconBlockSlot + MAX_RETAINED_SLOTS_CACHED_BLOBS_SIDECAR) { this.producedBlobsSidecarCache.delete(key); diff --git a/packages/beacon-node/src/chain/interface.ts b/packages/beacon-node/src/chain/interface.ts index 8242bf677ee3..65f27f105b55 100644 --- a/packages/beacon-node/src/chain/interface.ts +++ b/packages/beacon-node/src/chain/interface.ts @@ -1,4 +1,4 @@ -import {allForks, UintNum64, Root, phase0, Slot, RootHex, Epoch, ValidatorIndex, eip4844, Wei} from "@lodestar/types"; +import {allForks, UintNum64, Root, phase0, Slot, RootHex, Epoch, ValidatorIndex, deneb, Wei} from "@lodestar/types"; import {CachedBeaconStateAllForks} from "@lodestar/state-transition"; import {IBeaconConfig} from "@lodestar/config"; import {CompositeTypeAny, TreeView, Type} from "@chainsafe/ssz"; @@ -88,7 +88,7 @@ export interface IBeaconChain { readonly beaconProposerCache: BeaconProposerCache; readonly checkpointBalancesCache: CheckpointBalancesCache; - readonly producedBlobsSidecarCache: Map; + readonly producedBlobsSidecarCache: Map; readonly opts: IChainOptions; /** Stop beacon chain processing */ @@ -111,7 +111,7 @@ export interface IBeaconChain { */ getCanonicalBlockAtSlot(slot: Slot): Promise; - getBlobsSidecar(beaconBlock: eip4844.BeaconBlock): eip4844.BlobsSidecar; + getBlobsSidecar(beaconBlock: deneb.BeaconBlock): deneb.BlobsSidecar; produceBlock(blockAttributes: BlockAttributes): Promise<{block: allForks.BeaconBlock; blockValue: Wei}>; produceBlindedBlock(blockAttributes: BlockAttributes): Promise<{block: allForks.BlindedBeaconBlock; blockValue: Wei}>; diff --git a/packages/beacon-node/src/chain/lightClient/index.ts b/packages/beacon-node/src/chain/lightClient/index.ts index 87c539f9e04c..01ef4a97efe3 100644 --- a/packages/beacon-node/src/chain/lightClient/index.ts +++ b/packages/beacon-node/src/chain/lightClient/index.ts @@ -1,16 +1,23 @@ -import {altair, phase0, Root, RootHex, Slot, ssz, SyncPeriod} from "@lodestar/types"; +import {altair, phase0, Root, RootHex, Slot, ssz, SyncPeriod, allForks} from "@lodestar/types"; import {IChainForkConfig} from "@lodestar/config"; import { CachedBeaconStateAltair, computeStartSlotAtEpoch, computeSyncPeriodAtEpoch, computeSyncPeriodAtSlot, + executionPayloadToPayloadHeader, } from "@lodestar/state-transition"; -import {isBetterUpdate, toLightClientUpdateSummary, LightClientUpdateSummary} from "@lodestar/light-client/spec"; +import { + isBetterUpdate, + toLightClientUpdateSummary, + LightClientUpdateSummary, + upgradeLightClientHeader, +} from "@lodestar/light-client/spec"; import {ILogger, MapDef, pruneSetToMax} from "@lodestar/utils"; import {routes} from "@lodestar/api"; import {BitArray, CompositeViewDU, toHexString} from "@chainsafe/ssz"; -import {MIN_SYNC_COMMITTEE_PARTICIPANTS, SYNC_COMMITTEE_SIZE} from "@lodestar/params"; +import {MIN_SYNC_COMMITTEE_PARTICIPANTS, SYNC_COMMITTEE_SIZE, ForkName, ForkSeq, ForkExecution} from "@lodestar/params"; + import {IBeaconDb} from "../../db/index.js"; import {IMetrics} from "../../metrics/index.js"; import {ChainEventEmitter} from "../emitter.js"; @@ -22,6 +29,7 @@ import { getSyncCommitteesWitness, getFinalizedRootProof, getCurrentSyncCommitteeBranch, + getBlockBodyExecutionHeaderProof, } from "./proofs.js"; export type LightClientServerOpts = { @@ -32,7 +40,7 @@ type DependantRootHex = RootHex; type BlockRooHex = RootHex; export type SyncAttestedData = { - attestedHeader: phase0.BeaconBlockHeader; + attestedHeader: allForks.LightClientHeader; /** Precomputed root to prevent re-hashing */ blockRoot: Uint8Array; } & ( @@ -170,11 +178,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: altair.LightClientOptimisticUpdate | null = null; + private checkpointHeaders = new Map(); + private latestHeadUpdate: allForks.LightClientOptimisticUpdate | null = null; private readonly zero: Pick; - private finalized: altair.LightClientFinalityUpdate | null = null; + private finalized: allForks.LightClientFinalityUpdate | null = null; constructor(private readonly opts: LightClientServerOpts, modules: LightClientServerModules) { const {config, db, metrics, emitter, logger} = modules; @@ -185,7 +193,8 @@ export class LightClientServer { this.logger = logger; this.zero = { - finalizedHeader: ssz.altair.LightClientHeader.defaultValue(), + // Assign the hightest fork's default value because it can always be typecasted down to correct fork + finalizedHeader: Object.values(ssz.allForksLightClient).slice(-1)[0].LightClientHeader.defaultValue(), finalityBranch: ssz.altair.LightClientUpdate.fields["finalityBranch"].defaultValue(), }; @@ -212,7 +221,11 @@ export class LightClientServer { * - Persist state witness * - Use block's syncAggregate */ - onImportBlockHead(block: altair.BeaconBlock, postState: CachedBeaconStateAltair, parentBlockSlot: Slot): void { + onImportBlockHead( + block: allForks.AllForksLightClient["BeaconBlock"], + postState: CachedBeaconStateAltair, + parentBlockSlot: Slot + ): void { // TEMP: To disable this functionality for fork_choice spec tests. // Since the tests have deep-reorgs attested data is not available often printing lots of error logs. // While this function is only called for head blocks, best to disable. @@ -244,7 +257,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( @@ -276,7 +289,7 @@ export class LightClientServer { } return { - header: {beacon: header}, + header, currentSyncCommittee, currentSyncCommitteeBranch: getCurrentSyncCommitteeBranch(syncCommitteeWitness), }; @@ -289,7 +302,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) { @@ -320,11 +333,11 @@ export class LightClientServer { * API ROUTE to poll LightclientHeaderUpdate. * Clients should use the SSE type `light_client_optimistic_update` if available */ - getOptimisticUpdate(): altair.LightClientOptimisticUpdate | null { + getOptimisticUpdate(): allForks.LightClientOptimisticUpdate | null { return this.latestHeadUpdate; } - getFinalityUpdate(): altair.LightClientFinalityUpdate | null { + getFinalityUpdate(): allForks.LightClientFinalityUpdate | null { return this.finalized; } @@ -340,21 +353,14 @@ export class LightClientServer { } private async persistPostBlockImportData( - block: altair.BeaconBlock, + block: allForks.AllForksLightClient["BeaconBlock"], postState: CachedBeaconStateAltair, parentBlockSlot: Slot ): Promise { const blockSlot = block.slot; + const header = blockToLightClientHeader(this.config.getForkName(blockSlot), block); - const header: phase0.BeaconBlockHeader = { - slot: blockSlot, - proposerIndex: block.proposerIndex, - parentRoot: block.parentRoot, - stateRoot: block.stateRoot, - bodyRoot: this.config.getForkTypes(blockSlot).BeaconBlockBody.hashTreeRoot(block.body), - }; - - const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(header); + const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(header.beacon); const blockRootHex = toHexString(blockRoot); const syncCommitteeWitness = getSyncCommitteesWitness(postState); @@ -459,15 +465,16 @@ export class LightClientServer { return; } - const attestedPeriod = computeSyncPeriodAtSlot(attestedData.attestedHeader.slot); + const {attestedHeader, isFinalized} = attestedData; + const attestedPeriod = computeSyncPeriodAtSlot(attestedHeader.beacon.slot); if (syncPeriod !== attestedPeriod) { this.logger.debug("attested data period different than signature period", {syncPeriod, attestedPeriod}); this.metrics?.lightclientServer.onSyncAggregate.inc({event: "ignore_attested_period_diff"}); return; } - const headerUpdate: altair.LightClientOptimisticUpdate = { - attestedHeader: {beacon: attestedData.attestedHeader}, + const headerUpdate: allForks.LightClientOptimisticUpdate = { + attestedHeader, syncAggregate, signatureSlot, }; @@ -489,24 +496,29 @@ export class LightClientServer { // Persist latest best update for getLatestHeadUpdate() // TODO: Once SyncAggregate are constructed from P2P too, count bits to decide "best" - if (!this.latestHeadUpdate || attestedData.attestedHeader.slot > this.latestHeadUpdate.attestedHeader.beacon.slot) { + if (!this.latestHeadUpdate || attestedHeader.beacon.slot > this.latestHeadUpdate.attestedHeader.beacon.slot) { this.latestHeadUpdate = headerUpdate; this.metrics?.lightclientServer.onSyncAggregate.inc({event: "update_latest_head_update"}); } - if (attestedData.isFinalized) { + if (isFinalized) { const finalizedCheckpointRoot = attestedData.finalizedCheckpoint.root as Uint8Array; - const finalizedHeader = await this.getFinalizedHeader(finalizedCheckpointRoot); + let finalizedHeader = await this.getFinalizedHeader(finalizedCheckpointRoot); if ( finalizedHeader && (!this.finalized || - finalizedHeader.slot > this.finalized.finalizedHeader.beacon.slot || + finalizedHeader.beacon.slot > this.finalized.finalizedHeader.beacon.slot || syncAggregateParticipation > sumBits(this.finalized.syncAggregate.syncCommitteeBits)) ) { + // Fork of LightClientFinalityUpdate is based off on attested header's fork + const attestedFork = this.config.getForkName(attestedHeader.beacon.slot); + if (this.config.getForkName(finalizedHeader.beacon.slot) !== attestedFork) { + finalizedHeader = upgradeLightClientHeader(this.config, attestedFork, finalizedHeader); + } this.finalized = { - attestedHeader: {beacon: attestedData.attestedHeader}, - finalizedHeader: {beacon: finalizedHeader}, + attestedHeader, + finalizedHeader, syncAggregate, finalityBranch: attestedData.finalityBranch, signatureSlot, @@ -524,7 +536,7 @@ export class LightClientServer { } catch (e) { this.logger.error( "Error updating best LightClientUpdate", - {syncPeriod, slot: attestedData.attestedHeader.slot, blockRoot: toHexString(attestedData.blockRoot)}, + {syncPeriod, slot: attestedHeader.beacon.slot, blockRoot: toHexString(attestedData.blockRoot)}, e as Error ); } @@ -541,13 +553,14 @@ export class LightClientServer { attestedData: SyncAttestedData ): Promise { const prevBestUpdate = await this.db.bestLightClientUpdate.get(syncPeriod); + const {attestedHeader} = attestedData; if (prevBestUpdate) { const prevBestUpdateSummary = toLightClientUpdateSummary(prevBestUpdate); const nextBestUpdate: LightClientUpdateSummary = { activeParticipants: sumBits(syncAggregate.syncCommitteeBits), - attestedHeaderSlot: attestedData.attestedHeader.slot, + attestedHeaderSlot: attestedHeader.beacon.slot, signatureSlot, // The actual finalizedHeader is fetched below. To prevent a DB read we approximate the actual slot. // If update is not finalized finalizedHeaderSlot does not matter (see is_better_update), so setting @@ -575,36 +588,42 @@ export class LightClientServer { throw Error("nextSyncCommittee not available"); } const nextSyncCommitteeBranch = getNextSyncCommitteeBranch(syncCommitteeWitness); - const finalizedHeader = attestedData.isFinalized + const finalizedHeaderAttested = attestedData.isFinalized ? await this.getFinalizedHeader(attestedData.finalizedCheckpoint.root as Uint8Array) : null; - let newUpdate: altair.LightClientUpdate; - let isFinalized; - if (attestedData.isFinalized && finalizedHeader && computeSyncPeriodAtSlot(finalizedHeader.slot) == syncPeriod) { + let isFinalized, finalityBranch, finalizedHeader; + + if ( + attestedData.isFinalized && + finalizedHeaderAttested && + computeSyncPeriodAtSlot(finalizedHeaderAttested.beacon.slot) == syncPeriod + ) { isFinalized = true; - newUpdate = { - attestedHeader: {beacon: attestedData.attestedHeader}, - nextSyncCommittee: nextSyncCommittee, - nextSyncCommitteeBranch, - finalizedHeader: {beacon: finalizedHeader}, - finalityBranch: attestedData.finalityBranch, - syncAggregate, - signatureSlot, - }; + finalityBranch = attestedData.finalityBranch; + finalizedHeader = finalizedHeaderAttested; } else { isFinalized = false; - newUpdate = { - attestedHeader: {beacon: attestedData.attestedHeader}, - nextSyncCommittee: nextSyncCommittee, - nextSyncCommitteeBranch, - finalizedHeader: this.zero.finalizedHeader, - finalityBranch: this.zero.finalityBranch, - syncAggregate, - signatureSlot, - }; + finalityBranch = this.zero.finalityBranch; + finalizedHeader = this.zero.finalizedHeader; + } + + // Fork of LightClientUpdate is based off on attested header's fork + const attestedFork = this.config.getForkName(attestedHeader.beacon.slot); + if (this.config.getForkName(finalizedHeader.beacon.slot) !== attestedFork) { + finalizedHeader = upgradeLightClientHeader(this.config, attestedFork, finalizedHeader); } + const newUpdate = { + attestedHeader, + nextSyncCommittee: nextSyncCommittee, + nextSyncCommitteeBranch, + finalizedHeader, + finalityBranch, + syncAggregate, + signatureSlot, + } as allForks.LightClientUpdate; + // attestedData and the block of syncAggregate may not be in same sync period // should not use attested data slot as sync period // see https://github.com/ChainSafe/lodestar/issues/3933 @@ -639,7 +658,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) { @@ -666,3 +685,30 @@ export class LightClientServer { export function sumBits(bits: BitArray): number { return bits.getTrueBitIndexes().length; } + +export function blockToLightClientHeader( + fork: ForkName, + block: allForks.AllForksLightClient["BeaconBlock"] +): allForks.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 + ), + }; + if (ForkSeq[fork] >= ForkSeq.capella) { + const blockBody = block.body as allForks.AllForksExecution["BeaconBlockBody"]; + const execution = executionPayloadToPayloadHeader(ForkSeq[fork], blockBody.executionPayload); + return { + beacon, + execution, + executionBranch: getBlockBodyExecutionHeaderProof(fork as ForkExecution, blockBody), + } as allForks.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 633dcba9c308..6f68e0eae7c4 100644 --- a/packages/beacon-node/src/chain/lightClient/proofs.ts +++ b/packages/beacon-node/src/chain/lightClient/proofs.ts @@ -1,6 +1,8 @@ import {BeaconStateAllForks} from "@lodestar/state-transition"; -import {FINALIZED_ROOT_GINDEX} from "@lodestar/params"; +import {FINALIZED_ROOT_GINDEX, BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX, ForkExecution} from "@lodestar/params"; +import {allForks, ssz} from "@lodestar/types"; import {Tree} from "@chainsafe/persistent-merkle-tree"; + import {SyncCommitteeWitness} from "./types.js"; export function getSyncCommitteesWitness(state: BeaconStateAllForks): SyncCommitteeWitness { @@ -42,3 +44,11 @@ export function getFinalizedRootProof(state: BeaconStateAllForks): Uint8Array[] state.commit(); return new Tree(state.node).getSingleProof(BigInt(FINALIZED_ROOT_GINDEX)); } + +export function getBlockBodyExecutionHeaderProof( + fork: ForkExecution, + body: allForks.AllForksExecution["BeaconBlockBody"] +): Uint8Array[] { + const bodyView = (ssz[fork].BeaconBlockBody as allForks.AllForksExecutionSSZTypes["BeaconBlockBody"]).toView(body); + return new Tree(bodyView.node).getSingleProof(BigInt(BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX)); +} diff --git a/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts b/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts index 9a3531dc76e3..2b7711261865 100644 --- a/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts +++ b/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts @@ -194,7 +194,6 @@ export class AggregatedAttestationPool { } } -// eslint-disable-next-line @typescript-eslint/naming-convention interface AttestationWithIndex { attestation: phase0.Attestation; trueBitsCount: number; diff --git a/packages/beacon-node/src/chain/opPools/opPool.ts b/packages/beacon-node/src/chain/opPools/opPool.ts index 565243c9b600..67e68fd8bee2 100644 --- a/packages/beacon-node/src/chain/opPools/opPool.ts +++ b/packages/beacon-node/src/chain/opPools/opPool.ts @@ -1,6 +1,5 @@ import { CachedBeaconStateAllForks, - CachedBeaconStateCapella, computeEpochAtSlot, computeStartSlotAtEpoch, getAttesterSlashableIndices, @@ -16,6 +15,7 @@ import { import {Epoch, phase0, capella, ssz, ValidatorIndex} from "@lodestar/types"; import {fromHexString, toHexString} from "@chainsafe/ssz"; import {IBeaconDb} from "../../db/index.js"; +import {SignedBLSToExecutionChangeVersioned} from "../../util/types.js"; import {isValidBlsToExecutionChangeForBlockInclusion} from "./utils.js"; type HexRoot = string; @@ -34,7 +34,7 @@ export class OpPool { /** Set of seen attester slashing indexes. No need to prune */ private readonly attesterSlashingIndexes = new Set(); /** Map of validator index -> SignedBLSToExecutionChange */ - private readonly blsToExecutionChanges = new Map(); + private readonly blsToExecutionChanges = new Map(); // Getters for metrics @@ -69,7 +69,7 @@ export class OpPool { this.insertVoluntaryExit(voluntaryExit); } for (const item of blsToExecutionChanges) { - this.blsToExecutionChanges.set(item.message.validatorIndex, item); + this.insertBlsToExecutionChange(item.data, item.preCapella); } } @@ -150,8 +150,11 @@ export class OpPool { } /** Must be validated beforehand */ - insertBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange): void { - this.blsToExecutionChanges.set(blsToExecutionChange.message.validatorIndex, blsToExecutionChange); + insertBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange, preCapella = false): void { + this.blsToExecutionChanges.set(blsToExecutionChange.message.validatorIndex, { + data: blsToExecutionChange, + preCapella, + }); } /** @@ -233,8 +236,8 @@ export class OpPool { const blsToExecutionChanges: capella.SignedBLSToExecutionChange[] = []; for (const blsToExecutionChange of this.blsToExecutionChanges.values()) { - if (isValidBlsToExecutionChangeForBlockInclusion(state as CachedBeaconStateCapella, blsToExecutionChange)) { - blsToExecutionChanges.push(blsToExecutionChange); + if (isValidBlsToExecutionChangeForBlockInclusion(state, blsToExecutionChange.data)) { + blsToExecutionChanges.push(blsToExecutionChange.data); if (blsToExecutionChanges.length >= MAX_BLS_TO_EXECUTION_CHANGES) { break; } @@ -260,7 +263,7 @@ export class OpPool { } /** For beacon pool API */ - getAllBlsToExecutionChanges(): capella.SignedBLSToExecutionChange[] { + getAllBlsToExecutionChanges(): SignedBLSToExecutionChangeVersioned[] { return Array.from(this.blsToExecutionChanges.values()); } @@ -348,7 +351,7 @@ export class OpPool { // TODO CAPELLA: We need the finalizedState to safely prune BlsToExecutionChanges. Finalized state may not be // available in the cache, so it can be null. Once there's a head only prunning strategy, change if (finalizedState !== null) { - const validator = finalizedState.validators.getReadonly(blsToExecutionChange.message.validatorIndex); + const validator = finalizedState.validators.getReadonly(blsToExecutionChange.data.message.validatorIndex); if (validator.withdrawalCredentials[0] !== BLS_WITHDRAWAL_PREFIX) { this.blsToExecutionChanges.delete(key); } diff --git a/packages/beacon-node/src/chain/opPools/utils.ts b/packages/beacon-node/src/chain/opPools/utils.ts index 103f7c8b579f..906f51593bde 100644 --- a/packages/beacon-node/src/chain/opPools/utils.ts +++ b/packages/beacon-node/src/chain/opPools/utils.ts @@ -1,7 +1,7 @@ import bls from "@chainsafe/bls"; import {CoordType, Signature} from "@chainsafe/bls/types"; import {BLS_WITHDRAWAL_PREFIX} from "@lodestar/params"; -import {CachedBeaconStateCapella} from "@lodestar/state-transition"; +import {CachedBeaconStateAllForks} from "@lodestar/state-transition"; import {Slot, capella} from "@lodestar/types"; /** @@ -38,7 +38,7 @@ export function signatureFromBytesNoCheck(signature: Uint8Array): Signature { * can become invalid for certain forks. */ export function isValidBlsToExecutionChangeForBlockInclusion( - state: CachedBeaconStateCapella, + state: CachedBeaconStateAllForks, signedBLSToExecutionChange: capella.SignedBLSToExecutionChange ): boolean { // For each condition from https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#new-process_bls_to_execution_change diff --git a/packages/beacon-node/src/chain/options.ts b/packages/beacon-node/src/chain/options.ts index 0f804dfd46c9..21358d5b1b28 100644 --- a/packages/beacon-node/src/chain/options.ts +++ b/packages/beacon-node/src/chain/options.ts @@ -4,7 +4,6 @@ import {ArchiverOpts} from "./archiver/index.js"; import {ForkChoiceOpts} from "./forkChoice/index.js"; import {LightClientServerOpts} from "./lightClient/index.js"; -// eslint-disable-next-line @typescript-eslint/ban-types export type IChainOptions = BlockProcessOpts & ForkChoiceOpts & ArchiverOpts & diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index 260a41ac4a8a..91f3cc57762e 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -11,7 +11,7 @@ import { BLSPubkey, BLSSignature, capella, - eip4844, + deneb, Wei, } from "@lodestar/types"; import { @@ -63,14 +63,14 @@ export type AssembledBlockType = T extends BlockType.Full : allForks.BlindedBeaconBlock; export enum BlobsResultType { - preEIP4844, + preDeneb, produced, blinded, } export type BlobsResult = - | {type: BlobsResultType.preEIP4844 | BlobsResultType.blinded} - | {type: BlobsResultType.produced; blobs: eip4844.Blobs; blockHash: RootHex}; + | {type: BlobsResultType.preDeneb | BlobsResultType.blinded} + | {type: BlobsResultType.produced; blobs: deneb.Blobs; blockHash: RootHex}; export async function produceBlockBody( this: BeaconChain, @@ -91,9 +91,9 @@ export async function produceBlockBody( proposerPubKey: BLSPubkey; } ): Promise<{body: AssembledBodyType; blobs: BlobsResult; blockValue: Wei}> { - // Type-safe for blobs variable. Translate 'null' value into 'preEIP4844' enum + // Type-safe for blobs variable. Translate 'null' value into 'preDeneb' enum // TODO: Not ideal, but better than just using null. - // TODO: Does not guarantee that preEIP4844 enum goes with a preEIP4844 block + // TODO: Does not guarantee that preDeneb enum goes with a preDeneb block let blobsResult: BlobsResult; let blockValue: Wei; @@ -173,15 +173,15 @@ export async function produceBlockBody( ); (blockBody as allForks.BlindedBeaconBlockBody).executionPayloadHeader = builderRes.header; blockValue = builderRes.blockValue; - if (ForkSeq[fork] >= ForkSeq.eip4844) { + if (ForkSeq[fork] >= ForkSeq.deneb) { const {blobKzgCommitments} = builderRes; if (blobKzgCommitments === undefined) { throw Error(`Invalid builder getHeader response for fork=${fork}, missing blobKzgCommitments`); } - (blockBody as eip4844.BlindedBeaconBlockBody).blobKzgCommitments = blobKzgCommitments; + (blockBody as deneb.BlindedBeaconBlockBody).blobKzgCommitments = blobKzgCommitments; blobsResult = {type: BlobsResultType.blinded}; } else { - blobsResult = {type: BlobsResultType.preEIP4844}; + blobsResult = {type: BlobsResultType.preDeneb}; } } @@ -205,7 +205,7 @@ export async function produceBlockBody( (blockBody as allForks.ExecutionBlockBody).executionPayload = ssz.allForksExecution[ fork ].ExecutionPayload.defaultValue(); - blobsResult = {type: BlobsResultType.preEIP4844}; + blobsResult = {type: BlobsResultType.preDeneb}; blockValue = BigInt(0); } else { const {prepType, payloadId} = prepareRes; @@ -229,11 +229,11 @@ export async function produceBlockBody( this.metrics?.blockPayload.emptyPayloads.inc({prepType}); } - if (ForkSeq[fork] >= ForkSeq.eip4844) { + if (ForkSeq[fork] >= ForkSeq.deneb) { // SPEC: https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/validator.md#blob-kzg-commitments // After retrieving the execution payload from the execution engine as specified in Bellatrix, use the // payload_id to retrieve blobs and blob_kzg_commitments via get_blobs_and_kzg_commitments(payload_id) - // TODO EIP-4844: getBlobsBundle and getPayload must be either coupled or called in parallel to save time. + // TODO Deneb: getBlobsBundle and getPayload must be either coupled or called in parallel to save time. const blobsBundle = await this.executionEngine.getBlobsBundle(payloadId); // Sanity check consistency between getPayload() and getBlobsBundle() @@ -247,10 +247,10 @@ export async function produceBlockBody( validateBlobsAndKzgCommitments(executionPayload, blobsBundle); } - (blockBody as eip4844.BeaconBlockBody).blobKzgCommitments = blobsBundle.kzgs; + (blockBody as deneb.BeaconBlockBody).blobKzgCommitments = blobsBundle.kzgs; blobsResult = {type: BlobsResultType.produced, blobs: blobsBundle.blobs, blockHash}; } else { - blobsResult = {type: BlobsResultType.preEIP4844}; + blobsResult = {type: BlobsResultType.preDeneb}; } } } catch (e) { @@ -267,7 +267,7 @@ export async function produceBlockBody( (blockBody as allForks.ExecutionBlockBody).executionPayload = ssz.allForksExecution[ fork ].ExecutionPayload.defaultValue(); - blobsResult = {type: BlobsResultType.preEIP4844}; + blobsResult = {type: BlobsResultType.preDeneb}; blockValue = BigInt(0); } else { // since merge transition is complete, we need a valid payload even if with an @@ -277,7 +277,7 @@ export async function produceBlockBody( } } } else { - blobsResult = {type: BlobsResultType.preEIP4844}; + blobsResult = {type: BlobsResultType.preDeneb}; blockValue = BigInt(0); } @@ -387,14 +387,11 @@ async function prepareExecutionPayloadHeader( ): Promise<{ header: allForks.ExecutionPayloadHeader; blockValue: Wei; - blobKzgCommitments?: eip4844.BlobKzgCommitments; + blobKzgCommitments?: deneb.BlobKzgCommitments; }> { if (!chain.executionBuilder) { throw Error("executionBuilder required"); } - if (ForkSeq[fork] >= ForkSeq.capella) { - throw Error("executionBuilder capella api not implemented"); - } const parentHashRes = await getExecutionPayloadParentHash(chain, state); diff --git a/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts b/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts index 51590555f3e6..ea1a9b37aa01 100644 --- a/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts +++ b/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts @@ -1,5 +1,5 @@ import {verifyKzgCommitmentsAgainstTransactions} from "@lodestar/state-transition"; -import {allForks, eip4844} from "@lodestar/types"; +import {allForks, deneb} from "@lodestar/types"; import {toHex} from "@lodestar/utils"; import {BlobsBundle} from "../../execution/index.js"; import {byteArrayEquals} from "../../util/bytes.js"; @@ -18,7 +18,7 @@ export function validateBlobsAndKzgCommitments(payload: allForks.ExecutionPayloa } for (let i = 0; i < blobsBundle.blobs.length; i++) { - const kzg = ckzg.blobToKzgCommitment(blobsBundle.blobs[i]) as eip4844.KZGCommitment; + const kzg = ckzg.blobToKzgCommitment(blobsBundle.blobs[i]) as deneb.KZGCommitment; if (!byteArrayEquals(kzg, blobsBundle.kzgs[i])) { throw Error(`Wrong KZG[${i}] ${toHex(blobsBundle.kzgs[i])} expected ${toHex(kzg)}`); } diff --git a/packages/beacon-node/src/chain/reprocess.ts b/packages/beacon-node/src/chain/reprocess.ts index 32a82d6e1a79..51b01fbf54b3 100644 --- a/packages/beacon-node/src/chain/reprocess.ts +++ b/packages/beacon-node/src/chain/reprocess.ts @@ -24,7 +24,6 @@ enum ReprocessStatus { type AwaitingAttestationPromise = { resolve: (foundBlock: boolean) => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any promise: Promise; // there are multiple subnet/aggregated attestations waiting for same promise awaitingAttestationsCount: number; diff --git a/packages/beacon-node/src/chain/validation/blobsSidecar.ts b/packages/beacon-node/src/chain/validation/blobsSidecar.ts index fb98f6495cea..bb46c384e508 100644 --- a/packages/beacon-node/src/chain/validation/blobsSidecar.ts +++ b/packages/beacon-node/src/chain/validation/blobsSidecar.ts @@ -1,6 +1,6 @@ import bls from "@chainsafe/bls"; import {CoordType} from "@chainsafe/bls/types"; -import {eip4844, Root, ssz} from "@lodestar/types"; +import {deneb, Root, ssz} from "@lodestar/types"; import {bytesToBigInt} from "@lodestar/utils"; import {BYTES_PER_FIELD_ELEMENT, FIELD_ELEMENTS_PER_BLOB} from "@lodestar/params"; import {verifyKzgCommitmentsAgainstTransactions} from "@lodestar/state-transition"; @@ -12,8 +12,8 @@ import {ckzg} from "../../util/kzg.js"; const BLS_MODULUS = BigInt("52435875175126190479447740508185965837690552500527637822603658699938581184513"); export function validateGossipBlobsSidecar( - signedBlock: eip4844.SignedBeaconBlock, - blobsSidecar: eip4844.BlobsSidecar + signedBlock: deneb.SignedBeaconBlock, + blobsSidecar: deneb.BlobsSidecar ): void { const block = signedBlock.message; @@ -72,8 +72,8 @@ export function validateGossipBlobsSidecar( export function validateBlobsSidecar( slot: number, beaconBlockRoot: Root, - expectedKzgCommitments: eip4844.KZGCommitment[], - blobsSidecar: eip4844.BlobsSidecar + expectedKzgCommitments: deneb.KZGCommitment[], + blobsSidecar: deneb.BlobsSidecar ): void { // assert slot == blobs_sidecar.beacon_block_slot if (slot != blobsSidecar.beaconBlockSlot) { @@ -106,12 +106,12 @@ export function validateBlobsSidecar( try { isProofValid = ckzg.verifyAggregateKzgProof(blobs, expectedKzgCommitments, kzgAggregatedProof); } catch (e) { - // TODO EIP-4844: TEMP Nov17: May always throw error -- we need to fix Geth's KZG to match C-KZG and the trusted setup used here + // TODO DENEB: TEMP Nov17: May always throw error -- we need to fix Geth's KZG to match C-KZG and the trusted setup used here (e as Error).message = `Error on verifyAggregateKzgProof: ${(e as Error).message}`; throw e; } - // TODO EIP-4844: TEMP Nov17: May always throw error -- we need to fix Geth's KZG to match C-KZG and the trusted setup used here + // TODO DENEB: TEMP Nov17: May always throw error -- we need to fix Geth's KZG to match C-KZG and the trusted setup used here if (!isProofValid) { throw Error("Invalid AggregateKzgProof"); } @@ -137,7 +137,7 @@ function blsKeyValidate(g1Point: Uint8Array): boolean { * ``` * Check that each FIELD_ELEMENT as a uint256 < BLS_MODULUS */ -function blobIsValidRange(blob: eip4844.Blob): boolean { +function blobIsValidRange(blob: deneb.Blob): boolean { for (let i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) { const fieldElement = blob.subarray(i * BYTES_PER_FIELD_ELEMENT, (i + 1) * BYTES_PER_FIELD_ELEMENT); const fieldElementBN = bytesToBigInt(fieldElement, "be"); diff --git a/packages/beacon-node/src/chain/validation/blsToExecutionChange.ts b/packages/beacon-node/src/chain/validation/blsToExecutionChange.ts index 54934af18833..c2c183aa35a3 100644 --- a/packages/beacon-node/src/chain/validation/blsToExecutionChange.ts +++ b/packages/beacon-node/src/chain/validation/blsToExecutionChange.ts @@ -9,11 +9,12 @@ import {BlsToExecutionChangeError, BlsToExecutionChangeErrorCode, GossipAction} export async function validateBlsToExecutionChange( chain: IBeaconChain, - blsToExecutionChange: capella.SignedBLSToExecutionChange + blsToExecutionChange: capella.SignedBLSToExecutionChange, + ignoreExists = false ): Promise { // [IGNORE] The blsToExecutionChange is the first valid blsToExecutionChange received for the validator with index // signedBLSToExecutionChange.message.validatorIndex. - if (chain.opPool.hasSeenBlsToExecutionChange(blsToExecutionChange.message.validatorIndex)) { + if (!ignoreExists && chain.opPool.hasSeenBlsToExecutionChange(blsToExecutionChange.message.validatorIndex)) { throw new BlsToExecutionChangeError(GossipAction.IGNORE, { code: BlsToExecutionChangeErrorCode.ALREADY_EXISTS, }); diff --git a/packages/beacon-node/src/chain/validation/lightClientFinalityUpdate.ts b/packages/beacon-node/src/chain/validation/lightClientFinalityUpdate.ts index edeef0a07149..afa274ee46d9 100644 --- a/packages/beacon-node/src/chain/validation/lightClientFinalityUpdate.ts +++ b/packages/beacon-node/src/chain/validation/lightClientFinalityUpdate.ts @@ -1,5 +1,5 @@ import {IChainForkConfig} from "@lodestar/config"; -import {altair, ssz} from "@lodestar/types"; +import {allForks} 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: IChainForkConfig, chain: IBeaconChain, - gossipedFinalityUpdate: altair.LightClientFinalityUpdate + gossipedFinalityUpdate: allForks.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; @@ -31,10 +31,10 @@ export function validateLightClientFinalityUpdate( } // [IGNORE] The received finality_update matches the locally computed one exactly - if ( - localFinalityUpdate === null || - !ssz.altair.LightClientFinalityUpdate.equals(gossipedFinalityUpdate, localFinalityUpdate) - ) { + const sszType = config.getLightClientForkTypes(gossipedFinalityUpdate.attestedHeader.beacon.slot)[ + "LightClientFinalityUpdate" + ]; + if (localFinalityUpdate === null || !sszType.equals(gossipedFinalityUpdate, localFinalityUpdate)) { throw new LightClientError(GossipAction.IGNORE, { code: LightClientErrorCode.FINALITY_UPDATE_NOT_MATCHING_LOCAL, }); diff --git a/packages/beacon-node/src/chain/validation/lightClientOptimisticUpdate.ts b/packages/beacon-node/src/chain/validation/lightClientOptimisticUpdate.ts index 0d07d80fee47..123a1630f589 100644 --- a/packages/beacon-node/src/chain/validation/lightClientOptimisticUpdate.ts +++ b/packages/beacon-node/src/chain/validation/lightClientOptimisticUpdate.ts @@ -1,4 +1,4 @@ -import {altair, ssz} from "@lodestar/types"; +import {allForks} from "@lodestar/types"; import {IChainForkConfig} from "@lodestar/config"; import {computeTimeAtSlot} from "@lodestar/state-transition"; import {IBeaconChain} from "../interface.js"; @@ -10,7 +10,7 @@ import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../../constants/index.js"; export function validateLightClientOptimisticUpdate( config: IChainForkConfig, chain: IBeaconChain, - gossipedOptimisticUpdate: altair.LightClientOptimisticUpdate + gossipedOptimisticUpdate: allForks.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; @@ -32,10 +32,10 @@ export function validateLightClientOptimisticUpdate( } // [IGNORE] The received optimistic_update matches the locally computed one exactly - if ( - localOptimisticUpdate === null || - !ssz.altair.LightClientOptimisticUpdate.equals(gossipedOptimisticUpdate, localOptimisticUpdate) - ) { + const sszType = config.getLightClientForkTypes(gossipedOptimisticUpdate.attestedHeader.beacon.slot)[ + "LightClientOptimisticUpdate" + ]; + if (localOptimisticUpdate === null || !sszType.equals(gossipedOptimisticUpdate, localOptimisticUpdate)) { throw new LightClientError(GossipAction.IGNORE, { code: LightClientErrorCode.OPTIMISTIC_UPDATE_NOT_MATCHING_LOCAL, }); @@ -56,7 +56,7 @@ export function validateLightClientOptimisticUpdate( export function updateReceivedTooEarly( config: IChainForkConfig, 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/constants/network.ts b/packages/beacon-node/src/constants/network.ts index 0f3cff4e8fc4..9c59426e8cee 100644 --- a/packages/beacon-node/src/constants/network.ts +++ b/packages/beacon-node/src/constants/network.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ - /** * For more info on some of these constants: * https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/p2p-interface.md#configuration @@ -28,7 +26,6 @@ export enum GoodByeReasonCode { BANNED = 251, } -// eslint-disable-next-line @typescript-eslint/naming-convention export const GOODBYE_KNOWN_CODES: Record = { 0: "Unknown", diff --git a/packages/beacon-node/src/db/repositories/blobsSidecar.ts b/packages/beacon-node/src/db/repositories/blobsSidecar.ts index 12388713033a..13a7cd357a66 100644 --- a/packages/beacon-node/src/db/repositories/blobsSidecar.ts +++ b/packages/beacon-node/src/db/repositories/blobsSidecar.ts @@ -1,21 +1,21 @@ import {IChainForkConfig} from "@lodestar/config"; import {Bucket, Db, Repository} from "@lodestar/db"; -import {eip4844, ssz} from "@lodestar/types"; +import {deneb, ssz} from "@lodestar/types"; /** * BlobsSidecar by block root (= hash_tree_root(SignedBeaconBlockAndBlobsSidecar.beacon_block.message)) * * Used to store unfinalized BlobsSidecar */ -export class BlobsSidecarRepository extends Repository { +export class BlobsSidecarRepository extends Repository { constructor(config: IChainForkConfig, db: Db) { - super(config, db, Bucket.allForks_blobsSidecar, ssz.eip4844.BlobsSidecar); + super(config, db, Bucket.allForks_blobsSidecar, ssz.deneb.BlobsSidecar); } /** * Id is hashTreeRoot of unsigned BeaconBlock */ - getId(value: eip4844.BlobsSidecar): Uint8Array { + getId(value: deneb.BlobsSidecar): Uint8Array { return value.beaconBlockRoot; } } diff --git a/packages/beacon-node/src/db/repositories/blobsSidecarArchive.ts b/packages/beacon-node/src/db/repositories/blobsSidecarArchive.ts index 23d92b9b148c..58c84411992d 100644 --- a/packages/beacon-node/src/db/repositories/blobsSidecarArchive.ts +++ b/packages/beacon-node/src/db/repositories/blobsSidecarArchive.ts @@ -1,6 +1,6 @@ import {IChainForkConfig} from "@lodestar/config"; import {Db, Repository, IKeyValue, IFilterOptions, Bucket} from "@lodestar/db"; -import {Slot, Root, ssz, eip4844} from "@lodestar/types"; +import {Slot, Root, ssz, deneb} from "@lodestar/types"; import {bytesToInt} from "@lodestar/utils"; export interface IBlockFilterOptions extends IFilterOptions { @@ -16,14 +16,14 @@ export type BlockArchiveBatchPutBinaryItem = IKeyValue & { /** * Stores finalized blocks. Block slot is identifier. */ -export class BlobsSidecarArchiveRepository extends Repository { +export class BlobsSidecarArchiveRepository extends Repository { constructor(config: IChainForkConfig, db: Db) { - super(config, db, Bucket.allForks_blobsSidecarArchive, ssz.eip4844.BlobsSidecar); + super(config, db, Bucket.allForks_blobsSidecarArchive, ssz.deneb.BlobsSidecar); } // Handle key as slot - getId(value: eip4844.BlobsSidecar): Slot { + getId(value: deneb.BlobsSidecar): Slot { return value.beaconBlockSlot; } diff --git a/packages/beacon-node/src/db/repositories/blsToExecutionChange.ts b/packages/beacon-node/src/db/repositories/blsToExecutionChange.ts index 3f41dfa9fc1f..a62b606613c9 100644 --- a/packages/beacon-node/src/db/repositories/blsToExecutionChange.ts +++ b/packages/beacon-node/src/db/repositories/blsToExecutionChange.ts @@ -1,13 +1,14 @@ -import {ValidatorIndex, capella, ssz} from "@lodestar/types"; +import {ValidatorIndex} from "@lodestar/types"; import {IChainForkConfig} from "@lodestar/config"; import {Db, Bucket, Repository} from "@lodestar/db"; +import {SignedBLSToExecutionChangeVersioned, signedBLSToExecutionChangeVersionedType} from "../../util/types.js"; -export class BLSToExecutionChangeRepository extends Repository { +export class BLSToExecutionChangeRepository extends Repository { constructor(config: IChainForkConfig, db: Db) { - super(config, db, Bucket.capella_blsToExecutionChange, ssz.capella.SignedBLSToExecutionChange); + super(config, db, Bucket.capella_blsToExecutionChange, signedBLSToExecutionChangeVersionedType); } - getId(value: capella.SignedBLSToExecutionChange): ValidatorIndex { - return value.message.validatorIndex; + getId(value: SignedBLSToExecutionChangeVersioned): ValidatorIndex { + return value.data.message.validatorIndex; } } diff --git a/packages/beacon-node/src/db/repositories/lightclientBestUpdate.ts b/packages/beacon-node/src/db/repositories/lightclientBestUpdate.ts index b90e77dc1b44..2595cfa7f89e 100644 --- a/packages/beacon-node/src/db/repositories/lightclientBestUpdate.ts +++ b/packages/beacon-node/src/db/repositories/lightclientBestUpdate.ts @@ -1,14 +1,39 @@ import {IChainForkConfig} from "@lodestar/config"; import {Bucket, IDatabaseController, Repository} from "@lodestar/db"; -import {altair, ssz, SyncPeriod} from "@lodestar/types"; +import {ssz, SyncPeriod, allForks} from "@lodestar/types"; + +const SLOT_BYTE_COUNT = 8; /** * Best PartialLightClientUpdate in each SyncPeriod * * Used to prepare light client updates */ -export class BestLightClientUpdateRepository extends Repository { +export class BestLightClientUpdateRepository extends Repository { constructor(config: IChainForkConfig, db: IDatabaseController) { + // Pick some type but won't be used super(config, db, Bucket.lightClient_bestLightClientUpdate, ssz.altair.LightClientUpdate); } + + // Overrides for multi-fork + encodeValue(value: allForks.LightClientUpdate): Uint8Array { + // Not easy to have a fixed slot position for all forks in attested header, so lets + // prefix by attestedHeader's slot bytes + const slotBytes = ssz.Slot.serialize(value.attestedHeader.beacon.slot) as Uint8Array; + const valueBytes = this.config + .getLightClientForkTypes(value.attestedHeader.beacon.slot) + .LightClientUpdate.serialize(value) as Uint8Array; + + const prefixedData = new Uint8Array(SLOT_BYTE_COUNT + valueBytes.length); + prefixedData.set(slotBytes, 0); + prefixedData.set(valueBytes, SLOT_BYTE_COUNT); + + return prefixedData; + } + + decodeValue(data: Uint8Array): allForks.LightClientUpdate { + // First slot is written + const slot = ssz.Slot.deserialize(data.subarray(0, SLOT_BYTE_COUNT)); + return this.config.getLightClientForkTypes(slot).LightClientUpdate.deserialize(data.subarray(SLOT_BYTE_COUNT)); + } } diff --git a/packages/beacon-node/src/db/repositories/lightclientCheckpointHeader.ts b/packages/beacon-node/src/db/repositories/lightclientCheckpointHeader.ts index 907e964e67c3..e80ec9d1fe1a 100644 --- a/packages/beacon-node/src/db/repositories/lightclientCheckpointHeader.ts +++ b/packages/beacon-node/src/db/repositories/lightclientCheckpointHeader.ts @@ -1,6 +1,8 @@ import {IChainForkConfig} from "@lodestar/config"; import {Bucket, IDatabaseController, Repository} from "@lodestar/db"; -import {phase0, ssz} from "@lodestar/types"; +import {ssz, allForks} from "@lodestar/types"; + +import {getLightClientHeaderTypeFromBytes} from "../../util/multifork.js"; /** * Block headers by block root. Until finality includes all headers seen by this node. After finality, @@ -8,8 +10,18 @@ import {phase0, ssz} from "@lodestar/types"; * * Used to prepare light client updates */ -export class CheckpointHeaderRepository extends Repository { +export class CheckpointHeaderRepository extends Repository { constructor(config: IChainForkConfig, db: IDatabaseController) { - super(config, db, Bucket.lightClient_checkpointHeader, ssz.phase0.BeaconBlockHeader); + // Pick some type but won't be used + super(config, db, Bucket.lightClient_checkpointHeader, ssz.altair.LightClientHeader); + } + + // Overrides for multi-fork + encodeValue(value: allForks.LightClientHeader): Uint8Array { + return this.config.getLightClientForkTypes(value.beacon.slot).LightClientHeader.serialize(value) as Uint8Array; + } + + decodeValue(data: Uint8Array): allForks.LightClientHeader { + return getLightClientHeaderTypeFromBytes(this.config, data).deserialize(data); } } diff --git a/packages/beacon-node/src/db/repositories/stateArchive.ts b/packages/beacon-node/src/db/repositories/stateArchive.ts index 82ccfe21c8bf..9c81ba98bc9b 100644 --- a/packages/beacon-node/src/db/repositories/stateArchive.ts +++ b/packages/beacon-node/src/db/repositories/stateArchive.ts @@ -6,8 +6,6 @@ import {Db, Bucket, Repository} from "@lodestar/db"; import {getStateTypeFromBytes} from "../../util/multifork.js"; import {getRootIndexKey, storeRootIndex} from "./stateArchiveIndex.js"; -/* eslint-disable @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call */ - export class StateArchiveRepository extends Repository { constructor(config: IChainForkConfig, db: Db) { // Pick some type but won't be used. Casted to any because no type can match `BeaconStateAllForks` diff --git a/packages/beacon-node/src/eth1/provider/jwt.ts b/packages/beacon-node/src/eth1/provider/jwt.ts index e38a47127bbc..bc844755634f 100644 --- a/packages/beacon-node/src/eth1/provider/jwt.ts +++ b/packages/beacon-node/src/eth1/provider/jwt.ts @@ -1,9 +1,7 @@ import type {TAlgorithm} from "jwt-simple"; // TODO: fix jwt-simple types -// eslint-disable-next-line import/default import jwt from "jwt-simple"; -// eslint-disable-next-line import/no-named-as-default-member const {encode, decode} = jwt; /** jwt token has iat which is issued at unix timestamp, and an optional exp for expiry */ diff --git a/packages/beacon-node/src/eth1/provider/utils.ts b/packages/beacon-node/src/eth1/provider/utils.ts index b2794ca9e403..0217a8fddfbd 100644 --- a/packages/beacon-node/src/eth1/provider/utils.ts +++ b/packages/beacon-node/src/eth1/provider/utils.ts @@ -3,8 +3,6 @@ import {bytesToBigInt, bigIntToBytes} from "@lodestar/utils"; import {fromHexString, toHexString} from "@chainsafe/ssz"; import {ErrorParseJson} from "./jsonRpcHttpClient.js"; -/* eslint-disable @typescript-eslint/naming-convention */ - /** QUANTITY as defined in ethereum execution layer JSON RPC https://eth.wiki/json-rpc/API */ export type QUANTITY = string; /** DATA as defined in ethereum execution layer JSON RPC https://eth.wiki/json-rpc/API */ diff --git a/packages/beacon-node/src/execution/builder/http.ts b/packages/beacon-node/src/execution/builder/http.ts index 174888b275a8..eb8f9336bc24 100644 --- a/packages/beacon-node/src/execution/builder/http.ts +++ b/packages/beacon-node/src/execution/builder/http.ts @@ -1,4 +1,4 @@ -import {allForks, bellatrix, Slot, Root, BLSPubkey, ssz, eip4844, Wei} from "@lodestar/types"; +import {allForks, bellatrix, Slot, Root, BLSPubkey, ssz, deneb, Wei} from "@lodestar/types"; import {IChainForkConfig} from "@lodestar/config"; import {getClient, Api as BuilderApi} from "@lodestar/api/builder"; import {byteArrayEquals, toHexString} from "@chainsafe/ssz"; @@ -61,12 +61,12 @@ export class ExecutionBuilderHttp implements IExecutionBuilder { ): Promise<{ header: allForks.ExecutionPayloadHeader; blockValue: Wei; - blobKzgCommitments?: eip4844.BlobKzgCommitments; + blobKzgCommitments?: deneb.BlobKzgCommitments; }> { const res = await this.api.getHeader(slot, parentHash, proposerPubKey); ApiError.assert(res, "execution.builder.getheader"); const {header, value: blockValue} = res.response.data.message; - const {blobKzgCommitments} = res.response.data.message as {blobKzgCommitments?: eip4844.BlobKzgCommitments}; + const {blobKzgCommitments} = res.response.data.message as {blobKzgCommitments?: deneb.BlobKzgCommitments}; return {header, blockValue, blobKzgCommitments}; } @@ -124,7 +124,7 @@ export class ExecutionBuilderHttp implements IExecutionBuilder { throw Error(`blobsSidecar incorrect blockHash expected=${blockHash}, actual=${blobsBlockHash}`); } // Sanity-check that the KZG commitments match the versioned hashes in the transactions - const {blobKzgCommitments: kzgs} = beaconBlock.message.body as eip4844.BeaconBlockBody; + const {blobKzgCommitments: kzgs} = beaconBlock.message.body as deneb.BeaconBlockBody; if (kzgs === undefined) { throw Error("Missing blobKzgCommitments on beaconBlock's body"); } diff --git a/packages/beacon-node/src/execution/builder/interface.ts b/packages/beacon-node/src/execution/builder/interface.ts index 19f03b7f6367..42724afc6d36 100644 --- a/packages/beacon-node/src/execution/builder/interface.ts +++ b/packages/beacon-node/src/execution/builder/interface.ts @@ -1,4 +1,4 @@ -import {allForks, bellatrix, Root, Slot, BLSPubkey, eip4844, Wei} from "@lodestar/types"; +import {allForks, bellatrix, Root, Slot, BLSPubkey, deneb, Wei} from "@lodestar/types"; export interface IExecutionBuilder { /** @@ -18,7 +18,7 @@ export interface IExecutionBuilder { ): Promise<{ header: allForks.ExecutionPayloadHeader; blockValue: Wei; - blobKzgCommitments?: eip4844.BlobKzgCommitments; + blobKzgCommitments?: deneb.BlobKzgCommitments; }>; submitBlindedBlock(signedBlock: allForks.SignedBlindedBeaconBlock): Promise; submitBlindedBlockV2( diff --git a/packages/beacon-node/src/execution/engine/http.ts b/packages/beacon-node/src/execution/engine/http.ts index 3171c1cd69ec..45360269be2b 100644 --- a/packages/beacon-node/src/execution/engine/http.ts +++ b/packages/beacon-node/src/execution/engine/http.ts @@ -132,7 +132,7 @@ export class ExecutionEngineHttp implements IExecutionEngine { */ async notifyNewPayload(fork: ForkName, executionPayload: allForks.ExecutionPayload): Promise { const method = - ForkSeq[fork] >= ForkSeq.eip4844 + ForkSeq[fork] >= ForkSeq.deneb ? "engine_newPayloadV3" : ForkSeq[fork] >= ForkSeq.capella ? "engine_newPayloadV2" @@ -289,7 +289,7 @@ export class ExecutionEngineHttp implements IExecutionEngine { payloadId: PayloadId ): Promise<{executionPayload: allForks.ExecutionPayload; blockValue: Wei}> { const method = - ForkSeq[fork] >= ForkSeq.eip4844 + ForkSeq[fork] >= ForkSeq.deneb ? "engine_getPayloadV3" : ForkSeq[fork] >= ForkSeq.capella ? "engine_getPayloadV2" diff --git a/packages/beacon-node/src/execution/engine/interface.ts b/packages/beacon-node/src/execution/engine/interface.ts index 6a996d49bb3f..59daa2a4cc08 100644 --- a/packages/beacon-node/src/execution/engine/interface.ts +++ b/packages/beacon-node/src/execution/engine/interface.ts @@ -1,5 +1,5 @@ import {ForkName} from "@lodestar/params"; -import {KZGCommitment, Blob} from "@lodestar/types/eip4844"; +import {KZGCommitment, Blob} from "@lodestar/types/deneb"; import {RootHex, allForks, capella, Wei} from "@lodestar/types"; import {DATA, QUANTITY} from "../../eth1/provider/utils.js"; diff --git a/packages/beacon-node/src/execution/engine/mock.ts b/packages/beacon-node/src/execution/engine/mock.ts index feb4e5b53147..1de8e352f54f 100644 --- a/packages/beacon-node/src/execution/engine/mock.ts +++ b/packages/beacon-node/src/execution/engine/mock.ts @@ -4,7 +4,7 @@ import { OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET, OPAQUE_TX_MESSAGE_OFFSET, } from "@lodestar/state-transition"; -import {bellatrix, eip4844, RootHex, ssz} from "@lodestar/types"; +import {bellatrix, deneb, RootHex, ssz} from "@lodestar/types"; import {fromHex, toHex} from "@lodestar/utils"; import { BYTES_PER_FIELD_ELEMENT, @@ -37,7 +37,7 @@ export type ExecutionEngineMockOpts = { genesisBlockHash: string; onlyPredefinedResponses?: boolean; capellaForkTimestamp?: number; - eip4844ForkTimestamp?: number; + denebForkTimestamp?: number; }; type ExecutionBlock = { @@ -292,13 +292,13 @@ export class ExecutionEngineMockBackend implements IJsonRpcBackend { executionPayload.transactions.push(tx); } - const kzgs: eip4844.KZGCommitment[] = []; - const blobs: eip4844.Blob[] = []; + const kzgs: deneb.KZGCommitment[] = []; + const blobs: deneb.Blob[] = []; - // if post eip4844, add between 0 and 2 blob transactions - if (ForkSeq[fork] >= ForkSeq.eip4844) { - const eip4844TxCount = Math.round(2 * Math.random()); - for (let i = 0; i < eip4844TxCount; i++) { + // if post deneb, add between 0 and 2 blob transactions + if (ForkSeq[fork] >= ForkSeq.deneb) { + const denebTxCount = Math.round(2 * Math.random()); + for (let i = 0; i < denebTxCount; i++) { const blob = generateRandomBlob(); const kzg = ckzg.blobToKzgCommitment(blob); executionPayload.transactions.push(transactionForKzgCommitment(kzg)); @@ -391,13 +391,13 @@ export class ExecutionEngineMockBackend implements IJsonRpcBackend { } private timestampToFork(timestamp: number): ForkExecution { - if (timestamp > (this.opts.eip4844ForkTimestamp ?? Infinity)) return ForkName.eip4844; + if (timestamp > (this.opts.denebForkTimestamp ?? Infinity)) return ForkName.deneb; if (timestamp > (this.opts.capellaForkTimestamp ?? Infinity)) return ForkName.capella; return ForkName.bellatrix; } } -function transactionForKzgCommitment(kzgCommitment: eip4844.KZGCommitment): bellatrix.Transaction { +function transactionForKzgCommitment(kzgCommitment: deneb.KZGCommitment): bellatrix.Transaction { // Some random value that after the offset's position const blobVersionedHashesOffset = OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET + 64; @@ -423,7 +423,7 @@ function transactionForKzgCommitment(kzgCommitment: eip4844.KZGCommitment): bell /** * Generate random blob of sequential integers such that each element is < BLS_MODULUS */ -function generateRandomBlob(): eip4844.Blob { +function generateRandomBlob(): deneb.Blob { const blob = new Uint8Array(FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT); const dv = new DataView(blob.buffer, blob.byteOffset, blob.byteLength); for (let i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) { diff --git a/packages/beacon-node/src/execution/engine/types.ts b/packages/beacon-node/src/execution/engine/types.ts index 629a10e87e58..8c08682db5f6 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, eip4844, Wei} from "@lodestar/types"; +import {allForks, capella, deneb, Wei} from "@lodestar/types"; import { BYTES_PER_LOGS_BLOOM, FIELD_ELEMENTS_PER_BLOB, @@ -112,7 +112,7 @@ export type ExecutionPayloadRpc = { blockHash: DATA; // 32 bytes transactions: DATA[]; withdrawals?: WithdrawalRpc[]; // Capella hardfork - excessDataGas?: QUANTITY; // EIP-4844 + excessDataGas?: QUANTITY; // DENEB }; export type WithdrawalRpc = { @@ -162,9 +162,9 @@ export function serializeExecutionPayload(fork: ForkName, data: allForks.Executi payload.withdrawals = withdrawals.map(serializeWithdrawal); } - // EIP-4844 adds excessDataGas to the ExecutionPayload - if ((data as eip4844.ExecutionPayload).excessDataGas !== undefined) { - payload.excessDataGas = numToQuantity((data as eip4844.ExecutionPayload).excessDataGas); + // DENEB adds excessDataGas to the ExecutionPayload + if ((data as deneb.ExecutionPayload).excessDataGas !== undefined) { + payload.excessDataGas = numToQuantity((data as deneb.ExecutionPayload).excessDataGas); } return payload; @@ -217,14 +217,14 @@ export function parseExecutionPayload( (executionPayload as capella.ExecutionPayload).withdrawals = withdrawals.map((w) => deserializeWithdrawal(w)); } - // EIP-4844 adds excessDataGas to the ExecutionPayload - if (ForkSeq[fork] >= ForkSeq.eip4844) { + // DENEB adds excessDataGas to the ExecutionPayload + if (ForkSeq[fork] >= ForkSeq.deneb) { if (data.excessDataGas == null) { throw Error( - `excessDataGas missing for ${fork} >= eip4844 executionPayload number=${executionPayload.blockNumber} hash=${data.blockHash}` + `excessDataGas missing for ${fork} >= deneb executionPayload number=${executionPayload.blockNumber} hash=${data.blockHash}` ); } - (executionPayload as eip4844.ExecutionPayload).excessDataGas = quantityToBigint(data.excessDataGas); + (executionPayload as deneb.ExecutionPayload).excessDataGas = quantityToBigint(data.excessDataGas); } return {executionPayload, blockValue}; diff --git a/packages/beacon-node/src/metrics/metrics/beacon.ts b/packages/beacon-node/src/metrics/metrics/beacon.ts index 36df109ebf73..220d18a42df6 100644 --- a/packages/beacon-node/src/metrics/metrics/beacon.ts +++ b/packages/beacon-node/src/metrics/metrics/beacon.ts @@ -7,7 +7,7 @@ export type IBeaconMetrics = ReturnType; * https://github.com/ethereum/beacon-metrics/ and * https://hackmd.io/D5FmoeFZScim_squBFl8oA */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function createBeaconMetrics(register: RegistryMetricCreator) { return { // From https://github.com/ethereum/beacon-metrics/blob/master/metrics.md diff --git a/packages/beacon-node/src/metrics/metrics/lodestar.ts b/packages/beacon-node/src/metrics/metrics/lodestar.ts index eba20646cc34..82fcb5fdd0dd 100644 --- a/packages/beacon-node/src/metrics/metrics/lodestar.ts +++ b/packages/beacon-node/src/metrics/metrics/lodestar.ts @@ -7,7 +7,7 @@ export type ILodestarMetrics = ReturnType; /** * Extra Lodestar custom metrics */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function createLodestarMetrics( register: RegistryMetricCreator, metadata?: LodestarMetadata, diff --git a/packages/beacon-node/src/network/gossip/gossipsub.ts b/packages/beacon-node/src/network/gossip/gossipsub.ts index bc9306864cf7..0cde08a57f81 100644 --- a/packages/beacon-node/src/network/gossip/gossipsub.ts +++ b/packages/beacon-node/src/network/gossip/gossipsub.ts @@ -1,11 +1,10 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import {GossipSub, GossipsubEvents} from "@chainsafe/libp2p-gossipsub"; import {SignaturePolicy, TopicStr} from "@chainsafe/libp2p-gossipsub/types"; import {PeerScore, PeerScoreParams} from "@chainsafe/libp2p-gossipsub/score"; import {MetricsRegister, TopicLabel, TopicStrToLabel} from "@chainsafe/libp2p-gossipsub/metrics"; import {IBeaconConfig} from "@lodestar/config"; import {ATTESTATION_SUBNET_COUNT, ForkName, SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params"; -import {allForks, altair, phase0, capella, eip4844} from "@lodestar/types"; +import {allForks, altair, phase0, capella, deneb} from "@lodestar/types"; import {ILogger, Map2d, Map2dArr} from "@lodestar/utils"; import {computeStartSlotAtEpoch} from "@lodestar/state-transition"; @@ -200,7 +199,7 @@ export class Eth2Gossipsub extends GossipSub { await this.publishObject({type: GossipType.beacon_block, fork}, signedBlock); } - async publishSignedBeaconBlockAndBlobsSidecar(item: eip4844.SignedBeaconBlockAndBlobsSidecar): Promise { + async publishSignedBeaconBlockAndBlobsSidecar(item: deneb.SignedBeaconBlockAndBlobsSidecar): Promise { const fork = this.config.getForkName(item.beaconBlock.message.slot); await this.publishObject( {type: GossipType.beacon_block_and_blobs_sidecar, fork}, @@ -266,7 +265,7 @@ export class Eth2Gossipsub extends GossipSub { ); } - async publishLightClientFinalityUpdate(lightClientFinalityUpdate: altair.LightClientFinalityUpdate): Promise { + async publishLightClientFinalityUpdate(lightClientFinalityUpdate: allForks.LightClientFinalityUpdate): Promise { const fork = this.config.getForkName(lightClientFinalityUpdate.signatureSlot); await this.publishObject( {type: GossipType.light_client_finality_update, fork}, @@ -275,7 +274,7 @@ export class Eth2Gossipsub extends GossipSub { } async publishLightClientOptimisticUpdate( - lightClientOptimisitcUpdate: altair.LightClientOptimisticUpdate + lightClientOptimisitcUpdate: allForks.LightClientOptimisticUpdate ): Promise { const fork = this.config.getForkName(lightClientOptimisitcUpdate.signatureSlot); await this.publishObject( diff --git a/packages/beacon-node/src/network/gossip/handlers/index.ts b/packages/beacon-node/src/network/gossip/handlers/index.ts index c5dba75f4b96..79dde50e7d92 100644 --- a/packages/beacon-node/src/network/gossip/handlers/index.ts +++ b/packages/beacon-node/src/network/gossip/handlers/index.ts @@ -165,25 +165,25 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH return { [GossipType.beacon_block]: async (signedBlock, topic, peerIdStr, seenTimestampSec) => { - // TODO EIP-4844: Can blocks be received by this topic? - if (config.getForkSeq(signedBlock.message.slot) >= ForkSeq.eip4844) { - throw new GossipActionError(GossipAction.REJECT, {code: "POST_EIP4844_BLOCK"}); + // TODO Deneb: Can blocks be received by this topic? + if (config.getForkSeq(signedBlock.message.slot) >= ForkSeq.deneb) { + throw new GossipActionError(GossipAction.REJECT, {code: "POST_DENEB_BLOCK"}); } - const blockInput = getBlockInput.preEIP4844(config, signedBlock); + const blockInput = getBlockInput.preDeneb(config, signedBlock); await validateBeaconBlock(blockInput, topic.fork, peerIdStr, seenTimestampSec); handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec); }, [GossipType.beacon_block_and_blobs_sidecar]: async (blockAndBlocks, topic, peerIdStr, seenTimestampSec) => { const {beaconBlock, blobsSidecar} = blockAndBlocks; - // TODO EIP-4844: Should throw for pre fork blocks? - if (config.getForkSeq(beaconBlock.message.slot) < ForkSeq.eip4844) { - throw new GossipActionError(GossipAction.REJECT, {code: "PRE_EIP4844_BLOCK"}); + // TODO Deneb: Should throw for pre fork blocks? + if (config.getForkSeq(beaconBlock.message.slot) < ForkSeq.deneb) { + throw new GossipActionError(GossipAction.REJECT, {code: "PRE_DENEB_BLOCK"}); } // Validate block + blob. Then forward, then handle both - const blockInput = getBlockInput.postEIP4844(config, beaconBlock, blobsSidecar); + const blockInput = getBlockInput.postDeneb(config, beaconBlock, blobsSidecar); await validateBeaconBlock(blockInput, topic.fork, peerIdStr, seenTimestampSec); validateGossipBlobsSidecar(beaconBlock, blobsSidecar); handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec); diff --git a/packages/beacon-node/src/network/gossip/interface.ts b/packages/beacon-node/src/network/gossip/interface.ts index 9ebe784165ae..25972ac06146 100644 --- a/packages/beacon-node/src/network/gossip/interface.ts +++ b/packages/beacon-node/src/network/gossip/interface.ts @@ -4,7 +4,7 @@ import {Message, TopicValidatorResult} from "@libp2p/interface-pubsub"; import StrictEventEmitter from "strict-event-emitter-types"; import {PeerIdStr} from "@chainsafe/libp2p-gossipsub/types"; import {ForkName} from "@lodestar/params"; -import {allForks, altair, capella, eip4844, phase0} from "@lodestar/types"; +import {allForks, altair, capella, deneb, phase0} from "@lodestar/types"; import {IBeaconConfig} from "@lodestar/config"; import {ILogger} from "@lodestar/utils"; import {IBeaconChain} from "../../chain/index.js"; @@ -67,7 +67,7 @@ export type GossipTopic = GossipTopicMap[keyof GossipTopicMap]; export type GossipTypeMap = { [GossipType.beacon_block]: allForks.SignedBeaconBlock; - [GossipType.beacon_block_and_blobs_sidecar]: eip4844.SignedBeaconBlockAndBlobsSidecar; + [GossipType.beacon_block_and_blobs_sidecar]: deneb.SignedBeaconBlockAndBlobsSidecar; [GossipType.beacon_aggregate_and_proof]: phase0.SignedAggregateAndProof; [GossipType.beacon_attestation]: phase0.Attestation; [GossipType.voluntary_exit]: phase0.SignedVoluntaryExit; @@ -75,15 +75,15 @@ 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]: altair.LightClientFinalityUpdate; - [GossipType.light_client_optimistic_update]: altair.LightClientOptimisticUpdate; + [GossipType.light_client_finality_update]: allForks.LightClientFinalityUpdate; + [GossipType.light_client_optimistic_update]: allForks.LightClientOptimisticUpdate; [GossipType.bls_to_execution_change]: capella.SignedBLSToExecutionChange; }; export type GossipFnByType = { [GossipType.beacon_block]: (signedBlock: allForks.SignedBeaconBlock) => Promise | void; [GossipType.beacon_block_and_blobs_sidecar]: ( - signedBeaconBlockAndBlobsSidecar: eip4844.SignedBeaconBlockAndBlobsSidecar + signedBeaconBlockAndBlobsSidecar: deneb.SignedBeaconBlockAndBlobsSidecar ) => Promise | void; [GossipType.beacon_aggregate_and_proof]: (aggregateAndProof: phase0.SignedAggregateAndProof) => Promise | void; [GossipType.beacon_attestation]: (attestation: phase0.Attestation) => Promise | void; @@ -95,10 +95,10 @@ export type GossipFnByType = { ) => Promise | void; [GossipType.sync_committee]: (syncCommittee: altair.SyncCommitteeMessage) => Promise | void; [GossipType.light_client_finality_update]: ( - lightClientFinalityUpdate: altair.LightClientFinalityUpdate + lightClientFinalityUpdate: allForks.LightClientFinalityUpdate ) => Promise | void; [GossipType.light_client_optimistic_update]: ( - lightClientOptimisticUpdate: altair.LightClientOptimisticUpdate + lightClientOptimisticUpdate: allForks.LightClientOptimisticUpdate ) => Promise | void; [GossipType.bls_to_execution_change]: ( blsToExecutionChange: capella.SignedBLSToExecutionChange diff --git a/packages/beacon-node/src/network/gossip/topic.ts b/packages/beacon-node/src/network/gossip/topic.ts index 8cbf9093e0e2..9c3ab74ac784 100644 --- a/packages/beacon-node/src/network/gossip/topic.ts +++ b/packages/beacon-node/src/network/gossip/topic.ts @@ -1,6 +1,12 @@ import {ssz} from "@lodestar/types"; import {IForkDigestContext} from "@lodestar/config"; -import {ATTESTATION_SUBNET_COUNT, ForkName, ForkSeq, SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params"; +import { + ATTESTATION_SUBNET_COUNT, + ForkName, + ForkSeq, + SYNC_COMMITTEE_SUBNET_COUNT, + isForkLightClient, +} from "@lodestar/params"; import {GossipEncoding, GossipTopic, GossipType, GossipTopicTypeMap} from "./interface.js"; import {DEFAULT_ENCODING} from "./constants.js"; @@ -69,14 +75,14 @@ function stringifyGossipTopicType(topic: GossipTopic): string { } } -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function getGossipSSZType(topic: GossipTopic) { switch (topic.type) { case GossipType.beacon_block: // beacon_block is updated in altair to support the updated SignedBeaconBlock type return ssz[topic.fork].SignedBeaconBlock; case GossipType.beacon_block_and_blobs_sidecar: - return ssz.eip4844.SignedBeaconBlockAndBlobsSidecar; + return ssz.deneb.SignedBeaconBlockAndBlobsSidecar; case GossipType.beacon_aggregate_and_proof: return ssz.phase0.SignedAggregateAndProof; case GossipType.beacon_attestation: @@ -92,9 +98,13 @@ export function getGossipSSZType(topic: GossipTopic) { case GossipType.sync_committee: return ssz.altair.SyncCommitteeMessage; case GossipType.light_client_optimistic_update: - return ssz.altair.LightClientOptimisticUpdate; + return isForkLightClient(topic.fork) + ? ssz.allForksLightClient[topic.fork].LightClientOptimisticUpdate + : ssz.altair.LightClientOptimisticUpdate; case GossipType.light_client_finality_update: - return ssz.altair.LightClientFinalityUpdate; + return isForkLightClient(topic.fork) + ? ssz.allForksLightClient[topic.fork].LightClientFinalityUpdate + : ssz.altair.LightClientFinalityUpdate; case GossipType.bls_to_execution_change: return ssz.capella.SignedBLSToExecutionChange; } @@ -170,8 +180,8 @@ export function getCoreTopicsAtFork( {type: GossipType.attester_slashing}, ]; - // After EIP4844 only track beacon_block_and_blobs_sidecar topic - if (ForkSeq[fork] < ForkSeq.eip4844) { + // After Deneb only track beacon_block_and_blobs_sidecar topic + if (ForkSeq[fork] < ForkSeq.deneb) { topics.push({type: GossipType.beacon_block}); } else { topics.push({type: GossipType.beacon_block_and_blobs_sidecar}); diff --git a/packages/beacon-node/src/network/gossip/validation/queue.ts b/packages/beacon-node/src/network/gossip/validation/queue.ts index 84030d101fc5..4a0c7eba4839 100644 --- a/packages/beacon-node/src/network/gossip/validation/queue.ts +++ b/packages/beacon-node/src/network/gossip/validation/queue.ts @@ -11,7 +11,7 @@ const gossipQueueOpts: { } = { // validation gossip block asap [GossipType.beacon_block]: {maxLength: 1024, type: QueueType.FIFO, noYieldIfOneItem: true}, - // TODO EIP-4844: What's a good queue max given that now blocks are much bigger? + // TODO DENEB: What's a good queue max given that now blocks are much bigger? [GossipType.beacon_block_and_blobs_sidecar]: {maxLength: 32, type: QueueType.FIFO, noYieldIfOneItem: true}, // lighthoue has aggregate_queue 4096 and unknown_block_aggregate_queue 1024, we use single queue [GossipType.beacon_aggregate_and_proof]: {maxLength: 5120, type: QueueType.LIFO, maxConcurrency: 16}, diff --git a/packages/beacon-node/src/network/network.ts b/packages/beacon-node/src/network/network.ts index 6c90c6d4dbd8..daea6a9b1af5 100644 --- a/packages/beacon-node/src/network/network.ts +++ b/packages/beacon-node/src/network/network.ts @@ -6,11 +6,12 @@ import {ILogger, sleep} from "@lodestar/utils"; import {ATTESTATION_SUBNET_COUNT, ForkName, ForkSeq, SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params"; import {SignableENR} from "@chainsafe/discv5"; import {computeEpochAtSlot, computeTimeAtSlot} from "@lodestar/state-transition"; -import {altair, eip4844, Epoch, phase0} from "@lodestar/types"; +import {deneb, Epoch, phase0, allForks} from "@lodestar/types"; import {routes} from "@lodestar/api"; import {IMetrics} from "../metrics/index.js"; import {ChainEvent, IBeaconChain, IBeaconClock} from "../chain/index.js"; import {BlockInput, BlockInputType, getBlockInput} from "../chain/blocks/types.js"; +import {isValidBlsToExecutionChangeForBlockInclusion} from "../chain/opPools/utils.js"; import {INetworkOptions} from "./options.js"; import {INetwork, Libp2p} from "./interface.js"; import {ReqRespBeaconNode, ReqRespHandlers, doBeaconBlocksMaybeBlobsByRange} from "./reqresp/index.js"; @@ -32,6 +33,9 @@ import {PeersData} from "./peers/peersData.js"; import {getConnectionsMap, isPublishToZeroPeersError} from "./util.js"; import {Discv5Worker} from "./discv5/index.js"; +// How many changes to batch cleanup +const CACHED_BLS_BATCH_CLEANUP_LIMIT = 10; + interface INetworkModules { config: IBeaconConfig; libp2p: Libp2p; @@ -63,6 +67,7 @@ export class Network implements INetwork { private readonly signal: AbortSignal; private subscribedForks = new Set(); + private regossipBlsChangesPromise: Promise | null = null; constructor(private readonly opts: INetworkOptions, modules: INetworkModules) { const {config, libp2p, logger, metrics, chain, reqRespHandlers, gossipHandlers, signal} = modules; @@ -212,16 +217,16 @@ export class Network implements INetwork { publishBeaconBlockMaybeBlobs(blockInput: BlockInput): Promise { switch (blockInput.type) { - case BlockInputType.preEIP4844: + case BlockInputType.preDeneb: return this.gossip.publishBeaconBlock(blockInput.block); - case BlockInputType.postEIP4844: + case BlockInputType.postDeneb: return this.gossip.publishSignedBeaconBlockAndBlobsSidecar({ - beaconBlock: blockInput.block as eip4844.SignedBeaconBlock, + beaconBlock: blockInput.block as deneb.SignedBeaconBlock, blobsSidecar: blockInput.blobs, }); - case BlockInputType.postEIP4844OldBlobs: + case BlockInputType.postDenebOldBlobs: throw Error(`Attempting to broadcast old BlockInput slot ${blockInput.block.message.slot}`); } } @@ -234,22 +239,22 @@ export class Network implements INetwork { } async beaconBlocksMaybeBlobsByRoot(peerId: PeerId, request: phase0.BeaconBlocksByRootRequest): Promise { - // Assume all requests are post EIP-4844 - if (this.config.getForkSeq(this.chain.forkChoice.getFinalizedBlock().slot) >= ForkSeq.eip4844) { + // Assume all requests are post Deneb + if (this.config.getForkSeq(this.chain.forkChoice.getFinalizedBlock().slot) >= ForkSeq.deneb) { const blocksAndBlobs = await this.reqResp.beaconBlockAndBlobsSidecarByRoot(peerId, request); return blocksAndBlobs.map(({beaconBlock, blobsSidecar}) => - getBlockInput.postEIP4844(this.config, beaconBlock, blobsSidecar) + getBlockInput.postDeneb(this.config, beaconBlock, blobsSidecar) ); } - // Assume all request are pre EIP-4844 - else if (this.config.getForkSeq(this.clock.currentSlot) < ForkSeq.eip4844) { + // Assume all request are pre Deneb + else if (this.config.getForkSeq(this.clock.currentSlot) < ForkSeq.deneb) { const blocks = await this.reqResp.beaconBlocksByRoot(peerId, request); - return blocks.map((block) => getBlockInput.preEIP4844(this.config, block)); + return blocks.map((block) => getBlockInput.preDeneb(this.config, block)); } - // NOTE: Consider blocks may be post or pre EIP-4844 - // TODO EIP-4844: Request either blocks, or blocks+blobs + // NOTE: Consider blocks may be post or pre Deneb + // TODO Deneb: Request either blocks, or blocks+blobs else { const results = await Promise.all( request.map( @@ -261,7 +266,7 @@ export class Network implements INetwork { if (resultBlockBlobs.status === "fulfilled" && resultBlockBlobs.value.length === 1) { const {beaconBlock, blobsSidecar} = resultBlockBlobs.value[0]; - return getBlockInput.postEIP4844(this.config, beaconBlock, blobsSidecar); + return getBlockInput.postDeneb(this.config, beaconBlock, blobsSidecar); } if (resultBlocks.status === "rejected") { @@ -275,20 +280,20 @@ export class Network implements INetwork { const block = resultBlocks.value[0]; - if (this.config.getForkSeq(block.message.slot) >= ForkSeq.eip4844) { + if (this.config.getForkSeq(block.message.slot) >= ForkSeq.deneb) { // beaconBlockAndBlobsSidecarByRoot should have succeeded if (resultBlockBlobs.status === "rejected") { // Recycle existing error for beaconBlockAndBlobsSidecarByRoot if any return Promise.reject(resultBlockBlobs.reason); } else { throw Error( - `Received post EIP-4844 ${beaconBlockRoot} over beaconBlocksByRoot not beaconBlockAndBlobsSidecarByRoot` + `Received post Deneb ${beaconBlockRoot} over beaconBlocksByRoot not beaconBlockAndBlobsSidecarByRoot` ); } } - // Block is pre EIP-4844 - return getBlockInput.preEIP4844(this.config, block); + // Block is pre Deneb + return getBlockInput.preDeneb(this.config, block); } ) ); @@ -411,6 +416,20 @@ export class Network implements INetwork { } } } + + // If we are subscribed and post capella fork epoch, try gossiping the cached bls changes + if ( + this.isSubscribedToGossipCoreTopics() && + epoch >= this.config.CAPELLA_FORK_EPOCH && + !this.regossipBlsChangesPromise + ) { + this.regossipBlsChangesPromise = this.regossipCachedBlsChanges() + // If the processing fails for e.g. because of lack of peers set the promise + // to be null again to be retried + .catch((_e) => { + this.regossipBlsChangesPromise = null; + }); + } } catch (e) { this.logger.error("Error on BeaconGossipHandler.onEpoch", {epoch}, e as Error); } @@ -449,8 +468,8 @@ export class Network implements INetwork { {type: GossipType.attester_slashing}, ]; - // After EIP4844 only track beacon_block_and_blobs_sidecar topic - if (ForkSeq[fork] < ForkSeq.eip4844) { + // After Deneb only track beacon_block_and_blobs_sidecar topic + if (ForkSeq[fork] < ForkSeq.deneb) { topics.push({type: GossipType.beacon_block}); } else { topics.push({type: GossipType.beacon_block_and_blobs_sidecar}); @@ -482,7 +501,63 @@ export class Network implements INetwork { return topics; } - private onLightClientFinalityUpdate = async (finalityUpdate: altair.LightClientFinalityUpdate): Promise => { + private async regossipCachedBlsChanges(): Promise { + let gossipedIndexes = []; + let includedIndexes = []; + let totalProcessed = 0; + + this.logger.debug("Re-gossiping unsubmitted cached bls changes"); + try { + const headState = this.chain.getHeadState(); + for (const poolData of this.chain.opPool.getAllBlsToExecutionChanges()) { + const {data: value, preCapella} = poolData; + if (preCapella) { + if (isValidBlsToExecutionChangeForBlockInclusion(headState, value)) { + await this.gossip.publishBlsToExecutionChange(value); + gossipedIndexes.push(value.message.validatorIndex); + } else { + // No need to gossip if its already been in the headState + // TODO: Should use final state? + includedIndexes.push(value.message.validatorIndex); + } + + this.chain.opPool.insertBlsToExecutionChange(value, false); + totalProcessed += 1; + + // Cleanup in small batches + if (totalProcessed % CACHED_BLS_BATCH_CLEANUP_LIMIT === 0) { + this.logger.debug("Gossiped cached blsChanges", { + gossipedIndexes: `${gossipedIndexes}`, + includedIndexes: `${includedIndexes}`, + totalProcessed, + }); + gossipedIndexes = []; + includedIndexes = []; + } + } + } + + // Log any remaining changes + if (totalProcessed % CACHED_BLS_BATCH_CLEANUP_LIMIT !== 0) { + this.logger.debug("Gossiped cached blsChanges", { + gossipedIndexes: `${gossipedIndexes}`, + includedIndexes: `${includedIndexes}`, + totalProcessed, + }); + } + } catch (e) { + this.logger.error("Failed to completely gossip unsubmitted cached bls changes", {totalProcessed}, e as Error); + // Throw error so that the promise can be set null to be retied + throw e; + } + if (totalProcessed > 0) { + this.logger.info("Regossiped unsubmitted blsChanges", {totalProcessed}); + } else { + this.logger.debug("No unsubmitted blsChanges to gossip", {totalProcessed}); + } + } + + private onLightClientFinalityUpdate = async (finalityUpdate: allForks.LightClientFinalityUpdate): Promise => { if (this.hasAttachedSyncCommitteeMember()) { try { // messages SHOULD be broadcast after one-third of slot has transpired @@ -500,7 +575,7 @@ export class Network implements INetwork { }; private onLightClientOptimisticUpdate = async ( - optimisticUpdate: altair.LightClientOptimisticUpdate + optimisticUpdate: allForks.LightClientOptimisticUpdate ): Promise => { if (this.hasAttachedSyncCommitteeMember()) { try { diff --git a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts index 09db51b1e861..5c932b4ccc88 100644 --- a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts +++ b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts @@ -14,7 +14,7 @@ import { } from "@lodestar/reqresp"; import {ReqRespOpts} from "@lodestar/reqresp/lib/ReqResp.js"; import * as reqRespProtocols from "@lodestar/reqresp/protocols"; -import {allForks, altair, eip4844, phase0, Root} from "@lodestar/types"; +import {allForks, altair, deneb, phase0, Root} from "@lodestar/types"; import {ILogger} from "@lodestar/utils"; import {IMetrics} from "../../metrics/metrics.js"; import {INetworkEventBus, NetworkEvent} from "../events.js"; @@ -201,9 +201,9 @@ export class ReqRespBeaconNode extends ReqResp implements IReqRespBeaconNode { ); } - async lightClientBootstrap(peerId: PeerId, request: Root): Promise { + async lightClientBootstrap(peerId: PeerId, request: Root): Promise { return collectExactOne( - this.sendRequest( + this.sendRequest( peerId, ReqRespMethod.LightClientBootstrap, [Version.V1], @@ -212,9 +212,9 @@ export class ReqRespBeaconNode extends ReqResp implements IReqRespBeaconNode { ); } - async lightClientOptimisticUpdate(peerId: PeerId): Promise { + async lightClientOptimisticUpdate(peerId: PeerId): Promise { return collectExactOne( - this.sendRequest( + this.sendRequest( peerId, ReqRespMethod.LightClientOptimisticUpdate, [Version.V1], @@ -223,9 +223,9 @@ export class ReqRespBeaconNode extends ReqResp implements IReqRespBeaconNode { ); } - async lightClientFinalityUpdate(peerId: PeerId): Promise { + async lightClientFinalityUpdate(peerId: PeerId): Promise { return collectExactOne( - this.sendRequest( + this.sendRequest( peerId, ReqRespMethod.LightClientFinalityUpdate, [Version.V1], @@ -237,9 +237,9 @@ export class ReqRespBeaconNode extends ReqResp implements IReqRespBeaconNode { async lightClientUpdatesByRange( peerId: PeerId, request: altair.LightClientUpdatesByRange - ): Promise { + ): Promise { return collectMaxResponse( - this.sendRequest( + this.sendRequest( peerId, ReqRespMethod.LightClientUpdatesByRange, [Version.V1], @@ -251,10 +251,10 @@ export class ReqRespBeaconNode extends ReqResp implements IReqRespBeaconNode { async blobsSidecarsByRange( peerId: PeerId, - request: eip4844.BlobsSidecarsByRangeRequest - ): Promise { + request: deneb.BlobsSidecarsByRangeRequest + ): Promise { return collectMaxResponse( - this.sendRequest( + this.sendRequest( peerId, ReqRespMethod.BlobsSidecarsByRange, [Version.V1], @@ -266,10 +266,10 @@ export class ReqRespBeaconNode extends ReqResp implements IReqRespBeaconNode { async beaconBlockAndBlobsSidecarByRoot( peerId: PeerId, - request: eip4844.BeaconBlockAndBlobsSidecarByRootRequest - ): Promise { + request: deneb.BeaconBlockAndBlobsSidecarByRootRequest + ): Promise { return collectMaxResponse( - this.sendRequest( + this.sendRequest( peerId, ReqRespMethod.BeaconBlockAndBlobsSidecarByRoot, [Version.V1], @@ -316,7 +316,7 @@ export class ReqRespBeaconNode extends ReqResp implements IReqRespBeaconNode { ); } - if (ForkSeq[fork] >= ForkSeq.eip4844) { + if (ForkSeq[fork] >= ForkSeq.deneb) { protocols.push( reqRespProtocols.BeaconBlockAndBlobsSidecarByRoot( modules, diff --git a/packages/beacon-node/src/network/reqresp/doBeaconBlocksMaybeBlobsByRange.ts b/packages/beacon-node/src/network/reqresp/doBeaconBlocksMaybeBlobsByRange.ts index aeb0fb89fa5f..ea3fe659a998 100644 --- a/packages/beacon-node/src/network/reqresp/doBeaconBlocksMaybeBlobsByRange.ts +++ b/packages/beacon-node/src/network/reqresp/doBeaconBlocksMaybeBlobsByRange.ts @@ -1,6 +1,6 @@ import {PeerId} from "@libp2p/interface-peer-id"; import {IBeaconConfig} from "@lodestar/config"; -import {eip4844, Epoch, phase0} from "@lodestar/types"; +import {deneb, Epoch, phase0} from "@lodestar/types"; import {ForkSeq} from "@lodestar/params"; import {computeEpochAtSlot} from "@lodestar/state-transition"; @@ -15,16 +15,16 @@ export async function doBeaconBlocksMaybeBlobsByRange( request: phase0.BeaconBlocksByRangeRequest, currentEpoch: Epoch ): Promise { - // TODO EIP-4844: Assumes all blocks in the same epoch - // TODO EIP-4844: Ensure all blocks are in the same epoch - if (config.getForkSeq(request.startSlot) < ForkSeq.eip4844) { + // TODO Deneb: Assumes all blocks in the same epoch + // TODO Deneb: Ensure all blocks are in the same epoch + if (config.getForkSeq(request.startSlot) < ForkSeq.deneb) { const blocks = await reqResp.beaconBlocksByRange(peerId, request); - return blocks.map((block) => getBlockInput.preEIP4844(config, block)); + return blocks.map((block) => getBlockInput.preDeneb(config, block)); } // Only request blobs if they are recent enough else if (computeEpochAtSlot(request.startSlot) >= currentEpoch - config.MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS) { - // TODO EIP-4844: Do two requests at once for blocks and blobs + // TODO Deneb: Do two requests at once for blocks and blobs const blocks = await reqResp.beaconBlocksByRange(peerId, request); const blobsSidecars = await reqResp.blobsSidecarsByRange(peerId, request); @@ -42,7 +42,7 @@ export async function doBeaconBlocksMaybeBlobsByRange( // Assuming that the blocks and blobs will come in same sorted order for (let i = 0; i < blocks.length; i++) { const block = blocks[i]; - let blobsSidecar: eip4844.BlobsSidecar; + let blobsSidecar: deneb.BlobsSidecar; if (blobsSidecars[blobSideCarIndex]?.beaconBlockSlot === block.message.slot) { blobsSidecar = blobsSidecars[blobSideCarIndex]; @@ -50,7 +50,7 @@ export async function doBeaconBlocksMaybeBlobsByRange( blobSideCarIndex++; } else { // Quick inspect if the blobsSidecar was expected - const blobKzgCommitmentsLen = (block.message.body as eip4844.BeaconBlockBody).blobKzgCommitments.length; + const blobKzgCommitmentsLen = (block.message.body as deneb.BeaconBlockBody).blobKzgCommitments.length; if (blobKzgCommitmentsLen !== 0) { throw Error( `Missing blobsSidecar for blockSlot=${block.message.slot} with blobKzgCommitmentsLen=${blobKzgCommitmentsLen}` @@ -63,7 +63,7 @@ export async function doBeaconBlocksMaybeBlobsByRange( kzgAggregatedProof: emptyKzgAggregatedProof, }; } - blockInputs.push(getBlockInput.postEIP4844(config, block, blobsSidecar)); + blockInputs.push(getBlockInput.postDeneb(config, block, blobsSidecar)); } // If there are still unconsumed blobs this means that the response was inconsistent @@ -80,9 +80,9 @@ export async function doBeaconBlocksMaybeBlobsByRange( return blockInputs; } - // Post EIP-4844 but old blobs + // Post Deneb but old blobs else { const blocks = await reqResp.beaconBlocksByRange(peerId, request); - return blocks.map((block) => getBlockInput.postEIP4844OldBlobs(config, block)); + return blocks.map((block) => getBlockInput.postDenebOldBlobs(config, block)); } } diff --git a/packages/beacon-node/src/network/reqresp/handlers/beaconBlockAndBlobsSidecarByRoot.ts b/packages/beacon-node/src/network/reqresp/handlers/beaconBlockAndBlobsSidecarByRoot.ts index 34b45366f2f0..13147a47fde4 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/beaconBlockAndBlobsSidecarByRoot.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/beaconBlockAndBlobsSidecarByRoot.ts @@ -1,15 +1,15 @@ import {ContextBytesType, EncodedPayload, EncodedPayloadType} from "@lodestar/reqresp"; -import {eip4844} from "@lodestar/types"; +import {deneb} from "@lodestar/types"; import {toHex} from "@lodestar/utils"; import {IBeaconChain} from "../../../chain/index.js"; import {IBeaconDb} from "../../../db/index.js"; import {getSlotFromBytes} from "../../../util/multifork.js"; export async function* onBeaconBlockAndBlobsSidecarByRoot( - requestBody: eip4844.BeaconBlockAndBlobsSidecarByRootRequest, + requestBody: deneb.BeaconBlockAndBlobsSidecarByRootRequest, chain: IBeaconChain, db: IBeaconDb -): AsyncIterable> { +): AsyncIterable> { const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot; for (const blockRoot of requestBody) { diff --git a/packages/beacon-node/src/network/reqresp/handlers/beaconBlocksByRange.ts b/packages/beacon-node/src/network/reqresp/handlers/beaconBlocksByRange.ts index afc223ea8275..d7d6312c1e31 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/beaconBlocksByRange.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/beaconBlocksByRange.ts @@ -1,6 +1,6 @@ import {GENESIS_SLOT, MAX_REQUEST_BLOCKS} from "@lodestar/params"; import {ContextBytesType, EncodedPayloadBytes, EncodedPayloadType, ResponseError, RespStatus} from "@lodestar/reqresp"; -import {eip4844, phase0} from "@lodestar/types"; +import {deneb, phase0} from "@lodestar/types"; import {fromHex} from "@lodestar/utils"; import {IBeaconChain} from "../../../chain/index.js"; import {IBeaconDb} from "../../../db/index.js"; @@ -19,7 +19,7 @@ export function onBeaconBlocksByRange( } export async function* onBlocksOrBlobsSidecarsByRange( - request: eip4844.BlobsSidecarsByRangeRequest, + request: deneb.BlobsSidecarsByRangeRequest, chain: IBeaconChain, db: { finalized: Pick; @@ -37,7 +37,7 @@ export async function* onBlocksOrBlobsSidecarsByRange( const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot; // Finalized range of blobs - // TODO EIP-4844: Should the finalized block be included here or below? + // TODO DENEB: Should the finalized block be included here or below? if (startSlot <= finalizedSlot) { // Chain of blobs won't change @@ -57,7 +57,7 @@ export async function* onBlocksOrBlobsSidecarsByRange( if (endSlot > finalizedSlot) { const headRoot = chain.forkChoice.getHeadRoot(); - // TODO EIP-4844: forkChoice should mantain an array of canonical blocks, and change only on reorg + // 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 @@ -96,8 +96,8 @@ export async function* onBlocksOrBlobsSidecarsByRange( } export function validateBeaconBlocksByRangeRequest( - request: eip4844.BlobsSidecarsByRangeRequest -): eip4844.BlobsSidecarsByRangeRequest { + request: deneb.BlobsSidecarsByRangeRequest +): deneb.BlobsSidecarsByRangeRequest { const {startSlot} = request; let {count} = request; diff --git a/packages/beacon-node/src/network/reqresp/handlers/blobsSidecarsByRange.ts b/packages/beacon-node/src/network/reqresp/handlers/blobsSidecarsByRange.ts index e54fe62bd8d3..dbf389ba2134 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/blobsSidecarsByRange.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/blobsSidecarsByRange.ts @@ -1,13 +1,13 @@ -import {eip4844} from "@lodestar/types"; +import {deneb} from "@lodestar/types"; import {EncodedPayloadBytes} from "@lodestar/reqresp"; import {IBeaconChain} from "../../../chain/index.js"; import {IBeaconDb} from "../../../db/index.js"; import {onBlocksOrBlobsSidecarsByRange} from "./beaconBlocksByRange.js"; -// TODO EIP-4844: Unit test +// TODO DENEB: Unit test export function onBlobsSidecarsByRange( - request: eip4844.BlobsSidecarsByRangeRequest, + request: deneb.BlobsSidecarsByRangeRequest, chain: IBeaconChain, db: IBeaconDb ): AsyncIterable { diff --git a/packages/beacon-node/src/network/reqresp/handlers/lightClientBootstrap.ts b/packages/beacon-node/src/network/reqresp/handlers/lightClientBootstrap.ts index 5ad51debea92..e49024b5a873 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/lightClientBootstrap.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/lightClientBootstrap.ts @@ -6,13 +6,13 @@ import { LightClientServerError, LightClientServerErrorCode, } from "@lodestar/reqresp"; -import {altair, Root} from "@lodestar/types"; +import {Root, allForks} from "@lodestar/types"; import {IBeaconChain} from "../../../chain/index.js"; export async function* onLightClientBootstrap( requestBody: Root, chain: IBeaconChain -): AsyncIterable> { +): AsyncIterable> { try { yield { type: EncodedPayloadType.ssz, diff --git a/packages/beacon-node/src/network/reqresp/handlers/lightClientFinalityUpdate.ts b/packages/beacon-node/src/network/reqresp/handlers/lightClientFinalityUpdate.ts index 77bfab2cd16c..085f00c56fd6 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/lightClientFinalityUpdate.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/lightClientFinalityUpdate.ts @@ -1,10 +1,10 @@ import {EncodedPayload, ResponseError, RespStatus, EncodedPayloadType} from "@lodestar/reqresp"; -import {altair} from "@lodestar/types"; +import {allForks} from "@lodestar/types"; import {IBeaconChain} from "../../../chain/index.js"; export async function* onLightClientFinalityUpdate( chain: IBeaconChain -): AsyncIterable> { +): AsyncIterable> { const finalityUpdate = chain.lightClientServer.getFinalityUpdate(); if (finalityUpdate === null) { throw new ResponseError(RespStatus.RESOURCE_UNAVAILABLE, "No latest finality update available"); diff --git a/packages/beacon-node/src/network/reqresp/handlers/lightClientOptimisticUpdate.ts b/packages/beacon-node/src/network/reqresp/handlers/lightClientOptimisticUpdate.ts index 8bfae7d0f61f..b23be775810e 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/lightClientOptimisticUpdate.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/lightClientOptimisticUpdate.ts @@ -1,10 +1,10 @@ import {EncodedPayload, EncodedPayloadType, ResponseError, RespStatus} from "@lodestar/reqresp"; -import {altair} from "@lodestar/types"; +import {allForks} from "@lodestar/types"; import {IBeaconChain} from "../../../chain/index.js"; export async function* onLightClientOptimisticUpdate( chain: IBeaconChain -): AsyncIterable> { +): AsyncIterable> { const optimisticUpdate = chain.lightClientServer.getOptimisticUpdate(); if (optimisticUpdate === null) { throw new ResponseError(RespStatus.RESOURCE_UNAVAILABLE, "No latest optimistic update available"); diff --git a/packages/beacon-node/src/network/reqresp/handlers/lightClientUpdatesByRange.ts b/packages/beacon-node/src/network/reqresp/handlers/lightClientUpdatesByRange.ts index d5ab43984a19..3e7150399874 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/lightClientUpdatesByRange.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/lightClientUpdatesByRange.ts @@ -1,4 +1,4 @@ -import {altair} from "@lodestar/types"; +import {altair, allForks} from "@lodestar/types"; import {MAX_REQUEST_LIGHT_CLIENT_UPDATES} from "@lodestar/params"; import { EncodedPayload, @@ -13,7 +13,7 @@ import {IBeaconChain} from "../../../chain/index.js"; export async function* onLightClientUpdatesByRange( requestBody: altair.LightClientUpdatesByRange, chain: IBeaconChain -): AsyncIterable> { +): AsyncIterable> { const count = Math.min(MAX_REQUEST_LIGHT_CLIENT_UPDATES, requestBody.count); for (let period = requestBody.startPeriod; period < requestBody.startPeriod + count; period++) { try { diff --git a/packages/beacon-node/src/network/reqresp/interface.ts b/packages/beacon-node/src/network/reqresp/interface.ts index 554b58c9f6bd..efcbc2dac94a 100644 --- a/packages/beacon-node/src/network/reqresp/interface.ts +++ b/packages/beacon-node/src/network/reqresp/interface.ts @@ -1,5 +1,5 @@ import {PeerId} from "@libp2p/interface-peer-id"; -import {allForks, altair, eip4844, phase0} from "@lodestar/types"; +import {allForks, altair, deneb, phase0} from "@lodestar/types"; export interface IReqRespBeaconNode { start(): void; @@ -13,18 +13,18 @@ export interface IReqRespBeaconNode { request: phase0.BeaconBlocksByRangeRequest ): Promise; beaconBlocksByRoot(peerId: PeerId, request: phase0.BeaconBlocksByRootRequest): Promise; - blobsSidecarsByRange(peerId: PeerId, request: eip4844.BlobsSidecarsByRangeRequest): Promise; + blobsSidecarsByRange(peerId: PeerId, request: deneb.BlobsSidecarsByRangeRequest): Promise; beaconBlockAndBlobsSidecarByRoot( peerId: PeerId, - request: eip4844.BeaconBlockAndBlobsSidecarByRootRequest - ): Promise; - lightClientBootstrap(peerId: PeerId, request: Uint8Array): Promise; - lightClientOptimisticUpdate(peerId: PeerId): Promise; - lightClientFinalityUpdate(peerId: PeerId): Promise; + request: deneb.BeaconBlockAndBlobsSidecarByRootRequest + ): Promise; + lightClientBootstrap(peerId: PeerId, request: Uint8Array): Promise; + lightClientOptimisticUpdate(peerId: PeerId): Promise; + lightClientFinalityUpdate(peerId: PeerId): Promise; lightClientUpdatesByRange( peerId: PeerId, request: altair.LightClientUpdatesByRange - ): Promise; + ): Promise; } /** diff --git a/packages/beacon-node/src/network/util.ts b/packages/beacon-node/src/network/util.ts index 2ef90d4e5d93..8df8cc7e2ab9 100644 --- a/packages/beacon-node/src/network/util.ts +++ b/packages/beacon-node/src/network/util.ts @@ -9,8 +9,6 @@ import type {DefaultDialer} from "libp2p/connection-manager/dialer/index.js"; import type {SignableENR} from "@chainsafe/discv5"; import type {Libp2p} from "./interface.js"; -/* eslint-disable @typescript-eslint/no-explicit-any */ - // peers /** diff --git a/packages/beacon-node/src/node/nodejs.ts b/packages/beacon-node/src/node/nodejs.ts index 93c7cd4b544c..e4c6706e3da8 100644 --- a/packages/beacon-node/src/node/nodejs.ts +++ b/packages/beacon-node/src/node/nodejs.ts @@ -140,9 +140,9 @@ export class BeaconNode { setMaxListeners(Infinity, controller.signal); const signal = controller.signal; - // TODO EIP-4844, where is the best place to do this? + // TODO DENEB, where is the best place to do this? if (config.EIP4844_FORK_EPOCH < Infinity) { - // TODO EIP-4844: "c-kzg" is not installed by default, so if the library is not installed this will throw + // TODO DENEB: "c-kzg" is not installed by default, so if the library is not installed this will throw // See "Not able to build lodestar from source" https://github.com/ChainSafe/lodestar/issues/4886 await initCKZG(); loadEthereumTrustedSetup(); diff --git a/packages/beacon-node/src/sync/unknownBlock.ts b/packages/beacon-node/src/sync/unknownBlock.ts index a6d032506058..fc7dd6570594 100644 --- a/packages/beacon-node/src/sync/unknownBlock.ts +++ b/packages/beacon-node/src/sync/unknownBlock.ts @@ -260,7 +260,7 @@ export class UnknownBlockSync { for (let i = 0; i < MAX_ATTEMPTS_PER_BLOCK; i++) { const peer = shuffledPeers[i % shuffledPeers.length]; try { - // TODO EIP-4844: Use + // TODO DENEB: Use const [blockInput] = await this.network.beaconBlocksMaybeBlobsByRoot(peer, [blockRoot]); // Peer does not have the block, try with next peer diff --git a/packages/beacon-node/src/util/kzg.ts b/packages/beacon-node/src/util/kzg.ts index c13e5026eea5..f3c0d96fd7eb 100644 --- a/packages/beacon-node/src/util/kzg.ts +++ b/packages/beacon-node/src/util/kzg.ts @@ -32,7 +32,6 @@ export let ckzg: { // Global variable __dirname no longer available in ES6 modules. // Solutions: https://stackoverflow.com/questions/46745014/alternative-for-dirname-in-node-js-when-using-es6-modules -// eslint-disable-next-line @typescript-eslint/naming-convention const __dirname = path.dirname(fileURLToPath(import.meta.url)); export const TRUSTED_SETUP_BIN_FILEPATH = path.join(__dirname, "../../trusted_setup.bin"); const TRUSTED_SETUP_JSON_FILEPATH = path.join(__dirname, "../../trusted_setup.json"); @@ -49,7 +48,6 @@ export async function initCKZG(): Promise { /* eslint-disable import/no-extraneous-dependencies, @typescript-eslint/ban-ts-comment */ // @ts-ignore ckzg = (await import("c-kzg")) as typeof ckzg; - /* eslint-enable import/no-extraneous-dependencies, @typescript-eslint/ban-ts-comment */ } /** diff --git a/packages/beacon-node/src/util/multifork.ts b/packages/beacon-node/src/util/multifork.ts index b131660382be..938c52607678 100644 --- a/packages/beacon-node/src/util/multifork.ts +++ b/packages/beacon-node/src/util/multifork.ts @@ -53,3 +53,26 @@ export function getStateTypeFromBytes( const slot = bytesToInt(bytes.subarray(SLOT_BYTES_POSITION_IN_STATE, SLOT_BYTES_POSITION_IN_STATE + SLOT_BYTE_COUNT)); return config.getForkTypes(slot).BeaconState; } + +/** + * First field in update is beacon, first field in beacon is slot + * + * header = { + * beacon: { + * slot + * ... + * } + * ... + * } + * ... + */ +const SLOT_BYTES_POSITION_IN_LIGHTCLIENTHEADER = 0; +export function getLightClientHeaderTypeFromBytes( + config: IChainForkConfig, + bytes: Buffer | Uint8Array +): allForks.AllForksLightClientSSZTypes["LightClientHeader"] { + const slot = bytesToInt( + bytes.subarray(SLOT_BYTES_POSITION_IN_LIGHTCLIENTHEADER, SLOT_BYTES_POSITION_IN_LIGHTCLIENTHEADER + SLOT_BYTE_COUNT) + ); + return config.getLightClientForkTypes(slot).LightClientHeader; +} diff --git a/packages/beacon-node/src/util/types.ts b/packages/beacon-node/src/util/types.ts new file mode 100644 index 000000000000..545a706c7511 --- /dev/null +++ b/packages/beacon-node/src/util/types.ts @@ -0,0 +1,14 @@ +import {ContainerType, ValueOf} from "@chainsafe/ssz"; +import {ssz} from "@lodestar/types"; + +// Misc SSZ types used only in the beacon-node package, no need to upstream to types + +export const signedBLSToExecutionChangeVersionedType = new ContainerType( + { + // Assumes less than 256 forks, sounds reasonable in our lifetime + preCapella: ssz.Boolean, + data: ssz.capella.SignedBLSToExecutionChange, + }, + {jsonCase: "eth2", typeName: "SignedBLSToExecutionChangeVersionedType"} +); +export type SignedBLSToExecutionChangeVersioned = ValueOf; diff --git a/packages/beacon-node/test/e2e/api/impl/config.test.ts b/packages/beacon-node/test/e2e/api/impl/config.test.ts index 9913cf400b5a..a60be56977fb 100644 --- a/packages/beacon-node/test/e2e/api/impl/config.test.ts +++ b/packages/beacon-node/test/e2e/api/impl/config.test.ts @@ -9,8 +9,8 @@ const CONSTANT_NAMES_SKIP_LIST = new Set([ // This constant can also be derived from existing constants so it's not critical. // PARTICIPATION_FLAG_WEIGHTS = [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT, TIMELY_HEAD_WEIGHT] "PARTICIPATION_FLAG_WEIGHTS", - // TODO EIP-4844: This constant was added then removed on a spec re-write. - // When developing eip-4844 branch the tracked version still doesn't have released the removal + // TODO DENEB: This constant was added then removed on a spec re-write. + // When developing DENEB branch the tracked version still doesn't have released the removal "DOMAIN_BLOBS_SIDECAR", ]); diff --git a/packages/beacon-node/test/e2e/api/impl/getBlobsSidecar.test.ts b/packages/beacon-node/test/e2e/api/impl/getBlobsSidecar.test.ts index 06200ef4f407..d95c776b5b30 100644 --- a/packages/beacon-node/test/e2e/api/impl/getBlobsSidecar.test.ts +++ b/packages/beacon-node/test/e2e/api/impl/getBlobsSidecar.test.ts @@ -14,7 +14,7 @@ describe("getBlobsSideCar", function () { it("getBlobsSideCar", async () => { const block = config.getForkTypes(GENESIS_SLOT).SignedBeaconBlock.defaultValue(); - const blobsSidecar = ssz.eip4844.BlobsSidecar.defaultValue(); + const blobsSidecar = ssz.deneb.BlobsSidecar.defaultValue(); block.message.slot = GENESIS_SLOT; server.dbStub.blockArchive.get.resolves(block); diff --git a/packages/beacon-node/test/e2e/doppelganger/doppelganger.test.ts b/packages/beacon-node/test/e2e/doppelganger/doppelganger.test.ts index 6db434d48370..0dbbab838ce2 100644 --- a/packages/beacon-node/test/e2e/doppelganger/doppelganger.test.ts +++ b/packages/beacon-node/test/e2e/doppelganger/doppelganger.test.ts @@ -15,7 +15,6 @@ import {getDevBeaconNode} from "../../utils/node/beacon.js"; import {waitForEvent} from "../../utils/events/resolver.js"; import {BeaconNode} from "../../../src/node/index.js"; -/* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */ // TODO: Reconsider this tests latter. // Doppelganger testing can be split in two items: // 1. Can a running beacon node detect liveness of the validator? diff --git a/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts b/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts index f90b2c1080ad..46ff2d323767 100644 --- a/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts +++ b/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import "mocha"; import {promisify} from "node:util"; import {expect} from "chai"; @@ -79,7 +78,6 @@ describe.skip("eth1 / Eth1Provider", function () { }); // Resolves when Eth1ForBlockProduction has fetched both blocks and deposits - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const {eth1Datas, deposits} = await (async function resolveWithEth1DataAndDeposits() { // eslint-disable-next-line no-constant-condition while (true) { diff --git a/packages/beacon-node/test/e2e/network/eip4844.test.ts b/packages/beacon-node/test/e2e/network/deneb.test.ts similarity index 52% rename from packages/beacon-node/test/e2e/network/eip4844.test.ts rename to packages/beacon-node/test/e2e/network/deneb.test.ts index 242e2de835d3..af99e916c3de 100644 --- a/packages/beacon-node/test/e2e/network/eip4844.test.ts +++ b/packages/beacon-node/test/e2e/network/deneb.test.ts @@ -1,29 +1,29 @@ import {expect} from "chai"; -import {eip4844, ssz} from "@lodestar/types"; +import {deneb, ssz} from "@lodestar/types"; import {toHex} from "@lodestar/utils"; import {signedBeaconBlockAndBlobsSidecarFromBytes} from "../../../src/network/reqresp/handlers/beaconBlockAndBlobsSidecarByRoot.js"; describe("signedBeaconBlockAndBlobsSidecarFromBytes", () => { it("signedBeaconBlockAndBlobsSidecarFromBytes", () => { - const beaconBlock = ssz.eip4844.SignedBeaconBlock.defaultValue(); - const blobsSidecar = ssz.eip4844.BlobsSidecar.defaultValue(); + const beaconBlock = ssz.deneb.SignedBeaconBlock.defaultValue(); + const blobsSidecar = ssz.deneb.BlobsSidecar.defaultValue(); const signedBeaconBlockAndBlobsSidecarBytes = signedBeaconBlockAndBlobsSidecarFromBytes( - ssz.eip4844.SignedBeaconBlock.serialize(beaconBlock), - ssz.eip4844.BlobsSidecar.serialize(blobsSidecar) + ssz.deneb.SignedBeaconBlock.serialize(beaconBlock), + ssz.deneb.BlobsSidecar.serialize(blobsSidecar) ); - const signedBeaconBlockAndBlobsSidecar: eip4844.SignedBeaconBlockAndBlobsSidecar = { + const signedBeaconBlockAndBlobsSidecar: deneb.SignedBeaconBlockAndBlobsSidecar = { beaconBlock, blobsSidecar, }; expect(toHex(signedBeaconBlockAndBlobsSidecarBytes)).equals( - toHex(ssz.eip4844.SignedBeaconBlockAndBlobsSidecar.serialize(signedBeaconBlockAndBlobsSidecar)), + toHex(ssz.deneb.SignedBeaconBlockAndBlobsSidecar.serialize(signedBeaconBlockAndBlobsSidecar)), "Wrong signedBeaconBlockAndBlobsSidecarBytes" ); // Ensure deserialize does not throw - ssz.eip4844.SignedBeaconBlockAndBlobsSidecar.deserialize(signedBeaconBlockAndBlobsSidecarBytes); + ssz.deneb.SignedBeaconBlockAndBlobsSidecar.deserialize(signedBeaconBlockAndBlobsSidecarBytes); }); }); diff --git a/packages/beacon-node/test/e2e/network/gossipsub.test.ts b/packages/beacon-node/test/e2e/network/gossipsub.test.ts index e7f1ec2450d6..f9f21a1d1513 100644 --- a/packages/beacon-node/test/e2e/network/gossipsub.test.ts +++ b/packages/beacon-node/test/e2e/network/gossipsub.test.ts @@ -1,7 +1,7 @@ import sinon from "sinon"; import {expect} from "chai"; import {createIBeaconConfig, createIChainForkConfig, defaultChainConfig} from "@lodestar/config"; -import {capella, altair, phase0, ssz} from "@lodestar/types"; +import {capella, phase0, ssz, allForks} from "@lodestar/types"; import {sleep} from "@lodestar/utils"; import {computeStartSlotAtEpoch} from "@lodestar/state-transition"; @@ -225,8 +225,8 @@ describe("gossipsub", function () { }); it("Publish and receive a LightClientOptimisticUpdate", async function () { - let onLightClientOptimisticUpdate: (ou: altair.LightClientOptimisticUpdate) => void; - const onLightClientOptimisticUpdatePromise = new Promise( + let onLightClientOptimisticUpdate: (ou: allForks.LightClientOptimisticUpdate) => void; + const onLightClientOptimisticUpdatePromise = new Promise( (resolve) => (onLightClientOptimisticUpdate = resolve) ); @@ -252,7 +252,7 @@ describe("gossipsub", function () { } } - const lightClientOptimisticUpdate = ssz.altair.LightClientOptimisticUpdate.defaultValue(); + const lightClientOptimisticUpdate = ssz.capella.LightClientOptimisticUpdate.defaultValue(); lightClientOptimisticUpdate.signatureSlot = START_SLOT; await netA.gossip.publishLightClientOptimisticUpdate(lightClientOptimisticUpdate); @@ -261,8 +261,8 @@ describe("gossipsub", function () { }); it("Publish and receive a LightClientFinalityUpdate", async function () { - let onLightClientFinalityUpdate: (fu: altair.LightClientFinalityUpdate) => void; - const onLightClientFinalityUpdatePromise = new Promise( + let onLightClientFinalityUpdate: (fu: allForks.LightClientFinalityUpdate) => void; + const onLightClientFinalityUpdatePromise = new Promise( (resolve) => (onLightClientFinalityUpdate = resolve) ); @@ -288,7 +288,7 @@ describe("gossipsub", function () { } } - const lightClientFinalityUpdate = ssz.altair.LightClientFinalityUpdate.defaultValue(); + const lightClientFinalityUpdate = ssz.capella.LightClientFinalityUpdate.defaultValue(); lightClientFinalityUpdate.signatureSlot = START_SLOT; await netA.gossip.publishLightClientFinalityUpdate(lightClientFinalityUpdate); diff --git a/packages/beacon-node/test/e2e/network/peers/peerManager.test.ts b/packages/beacon-node/test/e2e/network/peers/peerManager.test.ts index 8003ce723320..08d2f4600630 100644 --- a/packages/beacon-node/test/e2e/network/peers/peerManager.test.ts +++ b/packages/beacon-node/test/e2e/network/peers/peerManager.test.ts @@ -162,7 +162,6 @@ describe("network / peers / PeerManager", function () { const {chain, libp2p, networkEventBus} = await mockModules(); // Simualate a peer connection, get() should return truthy - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call getConnectionsMap(libp2p.connectionManager).set(peerId1.toString(), [libp2pConnectionOutboud]); // Subscribe to `peerConnected` event, which must fire after checking peer relevance @@ -179,7 +178,6 @@ describe("network / peers / PeerManager", function () { const {chain, libp2p, reqResp, peerManager, networkEventBus} = await mockModules(); // Simualate a peer connection, get() should return truthy - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call getConnectionsMap(libp2p.connectionManager).set(peerId1.toString(), [libp2pConnectionOutboud]); // Subscribe to `peerConnected` event, which must fire after checking peer relevance @@ -193,7 +191,6 @@ describe("network / peers / PeerManager", function () { reqResp.metadata.resolves(remoteMetadata); // Simualate a peer connection, get() should return truthy - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call getConnectionsMap(libp2p.connectionManager).set(peerId1.toString(), [libp2pConnectionOutboud]); (libp2p.connectionManager as DefaultConnectionManager).dispatchEvent( new CustomEvent("peer:connect", {detail: libp2pConnectionOutboud}) diff --git a/packages/beacon-node/test/e2e/network/reqresp.test.ts b/packages/beacon-node/test/e2e/network/reqresp.test.ts index 3c052201ab93..ad56be57a77d 100644 --- a/packages/beacon-node/test/e2e/network/reqresp.test.ts +++ b/packages/beacon-node/test/e2e/network/reqresp.test.ts @@ -83,7 +83,6 @@ describe("network / ReqResp", function () { const peerIdB = await createSecp256k1PeerId(); const [libp2pA, libp2pB] = await Promise.all([createNode(multiaddr), createNode(multiaddr, peerIdB)]); - // eslint-disable-next-line const notImplemented = async function* (): AsyncIterable { throw Error("not implemented"); }; diff --git a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts index 39f75d75c296..a70e43b2e344 100644 --- a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts +++ b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts @@ -106,7 +106,7 @@ describe.skip("verify+import blocks - range sync perf test", () => { return chain; }, fn: async (chain) => { - const blocksImport = blocks.value.map((block) => getBlockInput.preEIP4844(chain.config, block)); + const blocksImport = blocks.value.map((block) => getBlockInput.preDeneb(chain.config, block)); await chain.processChainSegment(blocksImport, { // Only skip importing attestations for finalized sync. For head sync attestation are valuable. diff --git a/packages/beacon-node/test/sim/mergemock.test.ts b/packages/beacon-node/test/sim/mergemock.test.ts index 7ab57882fe51..ffe544d00655 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, TimestampFormatCode} from "@lodestar/utils"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; import {IChainConfig} from "@lodestar/config"; import {Epoch} from "@lodestar/types"; -import {ValidatorProposerConfig} from "@lodestar/validator"; +import {ValidatorProposerConfig, BuilderSelection} from "@lodestar/validator"; import {ChainEvent} from "../../src/chain/index.js"; import {testLogger, TestLoggerOpts} from "../utils/logger.js"; @@ -24,7 +24,7 @@ import {shell} from "./shell.js"; // ETH_PORT=8661 ENGINE_PORT=8551 yarn mocha test/sim/mergemock.test.ts // ``` -/* eslint-disable no-console, @typescript-eslint/naming-convention, quotes */ +/* eslint-disable no-console, @typescript-eslint/naming-convention */ const jwtSecretHex = "0xdc6457099f127cf0bac78de8b297df04951281909db4f58b43def7c7151e765d"; @@ -163,6 +163,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { builder: { enabled: true, gasLimit: 30000000, + selection: BuilderSelection.BuilderAlways, }, }, } as ValidatorProposerConfig; diff --git a/packages/beacon-node/test/sim/withdrawal-interop.test.ts b/packages/beacon-node/test/sim/withdrawal-interop.test.ts index 4cd98a36c50d..eb4d828c4241 100644 --- a/packages/beacon-node/test/sim/withdrawal-interop.test.ts +++ b/packages/beacon-node/test/sim/withdrawal-interop.test.ts @@ -29,7 +29,7 @@ import {shell} from "./shell.js"; // EL_BINARY_DIR=g11tech/geth:withdrawals EL_SCRIPT_DIR=gethdocker yarn mocha test/sim/withdrawal-interop.test.ts // ``` -/* eslint-disable no-console, @typescript-eslint/naming-convention, quotes */ +/* eslint-disable no-console, @typescript-eslint/naming-convention */ const jwtSecretHex = "0xdc6457099f127cf0bac78de8b297df04951281909db4f58b43def7c7151e765d"; const retryAttempts = defaultExecutionEngineHttpOpts.retryAttempts; diff --git a/packages/beacon-node/test/spec/bls/index.test.ts b/packages/beacon-node/test/spec/bls/index.test.ts index 9791af96c4cd..35b44eec8f9d 100644 --- a/packages/beacon-node/test/spec/bls/index.test.ts +++ b/packages/beacon-node/test/spec/bls/index.test.ts @@ -12,8 +12,6 @@ const skippedTestNames = [ "deserialization_succeeds_infinity_with_true_b_flag.yaml", ]; -/* eslint-disable @typescript-eslint/naming-convention */ - /** * File path structure: * ``` diff --git a/packages/beacon-node/test/spec/general/ssz_generic.ts b/packages/beacon-node/test/spec/general/ssz_generic.ts index b8894567af3e..64263353dc79 100644 --- a/packages/beacon-node/test/spec/general/ssz_generic.ts +++ b/packages/beacon-node/test/spec/general/ssz_generic.ts @@ -6,8 +6,6 @@ import {parseSszGenericInvalidTestcase, parseSszGenericValidTestcase} from "../u import {runValidSszTest} from "../utils/runValidSszTest.js"; import {getTestType} from "./ssz_generic_types.js"; -/* eslint-disable @typescript-eslint/naming-convention */ - // Mapping of sszGeneric() fn arguments to the path in spec tests // // / config / fork / test runner / test handler / test suite / test case diff --git a/packages/beacon-node/test/spec/presets/finality.ts b/packages/beacon-node/test/spec/presets/finality.ts index fedc1165cd65..d488ec115d19 100644 --- a/packages/beacon-node/test/spec/presets/finality.ts +++ b/packages/beacon-node/test/spec/presets/finality.ts @@ -23,7 +23,7 @@ export const finality: TestRunnerFn = (fo const signedBlock = testcase[`blocks_${i}`] as bellatrix.SignedBeaconBlock; state = stateTransition(state, signedBlock, { - // TODO EIP-4844: Should assume valid and available for this test? + // TODO DENEB: Should assume valid and available for this test? executionPayloadStatus: ExecutionPayloadStatus.valid, dataAvailableStatus: DataAvailableStatus.available, verifyStateRoot: false, diff --git a/packages/beacon-node/test/spec/presets/fork.ts b/packages/beacon-node/test/spec/presets/fork.ts index 30e722b0c97f..d04521ed263c 100644 --- a/packages/beacon-node/test/spec/presets/fork.ts +++ b/packages/beacon-node/test/spec/presets/fork.ts @@ -30,8 +30,8 @@ export const fork: TestRunnerFn = (forkNext) return slotFns.upgradeStateToBellatrix(preState as CachedBeaconStateAltair); case ForkName.capella: return slotFns.upgradeStateToCapella(preState as CachedBeaconStateBellatrix); - case ForkName.eip4844: - return slotFns.upgradeStateTo4844(preState as CachedBeaconStateCapella); + case ForkName.deneb: + return slotFns.upgradeStateToDeneb(preState as CachedBeaconStateCapella); } }, options: { diff --git a/packages/beacon-node/test/spec/presets/fork_choice.ts b/packages/beacon-node/test/spec/presets/fork_choice.ts index 87785cda3ba1..dd86d6f3e658 100644 --- a/packages/beacon-node/test/spec/presets/fork_choice.ts +++ b/packages/beacon-node/test/spec/presets/fork_choice.ts @@ -26,8 +26,6 @@ import {assertCorrectProgressiveBalances} from "../config.js"; /* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/naming-convention */ - const ANCHOR_STATE_FILE_NAME = "anchor_state"; const ANCHOR_BLOCK_FILE_NAME = "anchor_block"; const BLOCK_FILE_NAME = "^(block)_([0-9a-zA-Z]+)$"; @@ -150,9 +148,9 @@ export const forkChoiceTest = (opts: {onlyPredefinedResponses: boolean}): TestRu }); const blockImport = - config.getForkSeq(slot) < ForkSeq.eip4844 - ? getBlockInput.preEIP4844(config, signedBlock) - : getBlockInput.postEIP4844OldBlobs(config, signedBlock); + config.getForkSeq(slot) < ForkSeq.deneb + ? getBlockInput.preDeneb(config, signedBlock) + : getBlockInput.postDenebOldBlobs(config, signedBlock); try { await chain.processBlock(blockImport, { diff --git a/packages/beacon-node/test/spec/presets/genesis.ts b/packages/beacon-node/test/spec/presets/genesis.ts index 110a0f7bfa15..2768241d90fa 100644 --- a/packages/beacon-node/test/spec/presets/genesis.ts +++ b/packages/beacon-node/test/spec/presets/genesis.ts @@ -1,5 +1,5 @@ import {expect} from "chai"; -import {phase0, Root, ssz, TimeSeconds, allForks, eip4844} from "@lodestar/types"; +import {phase0, Root, ssz, TimeSeconds, allForks, deneb} from "@lodestar/types"; import {InputType} from "@lodestar/spec-test-util"; import { BeaconStateAllForks, @@ -54,7 +54,7 @@ const genesisInitialization: TestRunnerFn>> = { + deneb: { + LightClientHeader: ssz.altair.LightClientHeader, + LightClientUpdate: ssz.altair.LightClientUpdate, + LightClientOptimisticUpdate: ssz.altair.LightClientOptimisticUpdate, + LightClientFinalityUpdate: ssz.altair.LightClientFinalityUpdate, + LightClientBootstrap: ssz.altair.LightClientBootstrap, + }, + capella: { + LightClientHeader: ssz.altair.LightClientHeader, + LightClientUpdate: ssz.altair.LightClientUpdate, + LightClientOptimisticUpdate: ssz.altair.LightClientOptimisticUpdate, + LightClientFinalityUpdate: ssz.altair.LightClientFinalityUpdate, + LightClientBootstrap: ssz.altair.LightClientBootstrap, + }, + bellatrix: { + LightClientHeader: ssz.altair.LightClientHeader, + LightClientUpdate: ssz.altair.LightClientUpdate, + LightClientOptimisticUpdate: ssz.altair.LightClientOptimisticUpdate, + LightClientFinalityUpdate: ssz.altair.LightClientFinalityUpdate, + LightClientBootstrap: ssz.altair.LightClientBootstrap, + }, +}; + specTestIterator( path.join(ethereumConsensusSpecsTests.outputDir, "tests", ACTIVE_PRESET), { @@ -64,7 +94,7 @@ specTestIterator( shuffling: {type: RunnerType.default, fn: shuffling}, ssz_static: { type: RunnerType.custom, - fn: sszStatic(), + fn: sszStatic(undefined, overrideSSZTypes), }, sync: {type: RunnerType.default, fn: forkChoiceTest({onlyPredefinedResponses: true})}, transition: { diff --git a/packages/beacon-node/test/spec/presets/operations.ts b/packages/beacon-node/test/spec/presets/operations.ts index bc7d172deefd..6f4d6cbf9b69 100644 --- a/packages/beacon-node/test/spec/presets/operations.ts +++ b/packages/beacon-node/test/spec/presets/operations.ts @@ -79,8 +79,8 @@ const operationFns: Record> = executionPayloadStatus: testCase.execution.execution_valid ? ExecutionPayloadStatus.valid : ExecutionPayloadStatus.invalid, - // TODO EIP-4844: Make this value dynamic on fork EIP4844 - dataAvailableStatus: DataAvailableStatus.preEIP4844, + // TODO Deneb: Make this value dynamic on fork Deneb + dataAvailableStatus: DataAvailableStatus.preDeneb, } ); }, diff --git a/packages/beacon-node/test/spec/presets/sanity.ts b/packages/beacon-node/test/spec/presets/sanity.ts index 2e374b6a4804..eb10f3e8de85 100644 --- a/packages/beacon-node/test/spec/presets/sanity.ts +++ b/packages/beacon-node/test/spec/presets/sanity.ts @@ -6,7 +6,7 @@ import { processSlots, stateTransition, } from "@lodestar/state-transition"; -import {allForks, eip4844, ssz} from "@lodestar/types"; +import {allForks, deneb, ssz} from "@lodestar/types"; import {ForkName} from "@lodestar/params"; import {bnToNum} from "@lodestar/utils"; import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; @@ -62,9 +62,9 @@ export const sanityBlocks: TestRunnerFn>; // Mapping of sszGeneric() fn arguments to the path in spec tests @@ -35,7 +26,7 @@ type Types = Record>; // tests / mainnet / altair / ssz_static / Validator / ssz_random / case_0/roots.yaml // -export const sszStatic = (skippedTypes?: string[]) => ( +export const sszStatic = (skippedTypes?: string[], overrideSSZTypes?: Record) => ( fork: ForkName, typeName: string, testSuite: string, @@ -48,6 +39,7 @@ export const sszStatic = (skippedTypes?: string[]) => ( /* eslint-disable @typescript-eslint/strict-boolean-expressions */ const sszType = + (((overrideSSZTypes ?? {})[fork] ?? {}) as Types)[typeName] || (ssz[fork] as Types)[typeName] || (ssz.capella as Types)[typeName] || (ssz.bellatrix as Types)[typeName] || diff --git a/packages/beacon-node/test/spec/presets/transition.ts b/packages/beacon-node/test/spec/presets/transition.ts index 733c94bd86db..3dcaf0efbbe7 100644 --- a/packages/beacon-node/test/spec/presets/transition.ts +++ b/packages/beacon-node/test/spec/presets/transition.ts @@ -52,7 +52,7 @@ export const transition = (skipTestNames?: string[]): TestRunnerFn() { let resolve: ((value: T) => void) | null = null; const promise = new Promise((_resolve) => { 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 1d0b40ba2ae1..4c0d9bca8176 100644 --- a/packages/beacon-node/test/unit/chain/blocks/verifyBlocksSanityChecks.test.ts +++ b/packages/beacon-node/test/unit/chain/blocks/verifyBlocksSanityChecks.test.ts @@ -127,7 +127,7 @@ function verifyBlocksSanityChecks( ): {relevantBlocks: allForks.SignedBeaconBlock[]; parentSlots: Slot[]; parentBlock: ProtoBlock | null} { const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksImportSanityChecks( modules, - blocks.map((block) => getBlockInput.preEIP4844(config, block)), + blocks.map((block) => getBlockInput.preDeneb(config, block)), opts ); return { diff --git a/packages/beacon-node/test/unit/chain/validation/aggregateAndProof.test.ts b/packages/beacon-node/test/unit/chain/validation/aggregateAndProof.test.ts index 632fe45ddd92..d0449477b1d4 100644 --- a/packages/beacon-node/test/unit/chain/validation/aggregateAndProof.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/aggregateAndProof.test.ts @@ -6,7 +6,6 @@ import {IBeaconChain} from "../../../../src/chain/index.js"; import {AttestationErrorCode} from "../../../../src/chain/errors/index.js"; import {validateGossipAggregateAndProof} from "../../../../src/chain/validation/index.js"; import {expectRejectedWithLodestarError} from "../../../utils/errors.js"; -// eslint-disable-next-line import/no-relative-packages import {generateTestCachedBeaconStateOnlyValidators} from "../../../../../state-transition/test/perf/util.js"; import {memoOnce} from "../../../utils/cache.js"; import { diff --git a/packages/beacon-node/test/unit/chain/validation/attestation.test.ts b/packages/beacon-node/test/unit/chain/validation/attestation.test.ts index d9845e522fcd..646df2f3db9c 100644 --- a/packages/beacon-node/test/unit/chain/validation/attestation.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/attestation.test.ts @@ -6,7 +6,6 @@ import {IBeaconChain} from "../../../../src/chain/index.js"; import {AttestationErrorCode} from "../../../../src/chain/errors/index.js"; import {validateGossipAttestation} from "../../../../src/chain/validation/index.js"; import {expectRejectedWithLodestarError} from "../../../utils/errors.js"; -// eslint-disable-next-line import/no-relative-packages import {generateTestCachedBeaconStateOnlyValidators} from "../../../../../state-transition/test/perf/util.js"; import {memoOnce} from "../../../utils/cache.js"; import {getAttestationValidData, AttestationValidDataOpts} from "../../../utils/validationData/attestation.js"; diff --git a/packages/beacon-node/test/unit/chain/validation/lightClientFinalityUpdate.test.ts b/packages/beacon-node/test/unit/chain/validation/lightClientFinalityUpdate.test.ts index 26e6eb004039..5aea7bad9e33 100644 --- a/packages/beacon-node/test/unit/chain/validation/lightClientFinalityUpdate.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/lightClientFinalityUpdate.test.ts @@ -1,7 +1,6 @@ import {expect} from "chai"; import sinon from "sinon"; -import {createIBeaconConfig} from "@lodestar/config"; -import {config} from "@lodestar/config/default"; +import {createIBeaconConfig, createIChainForkConfig, defaultChainConfig} from "@lodestar/config"; import {altair, ssz} from "@lodestar/types"; import {computeTimeAtSlot} from "@lodestar/state-transition"; @@ -14,6 +13,14 @@ import {IBeaconChain} from "../../../../src/chain/index.js"; describe("Light Client Finality Update validation", function () { let fakeClock: sinon.SinonFakeTimers; const afterEachCallbacks: (() => Promise | void)[] = []; + const config = createIChainForkConfig({ + ...defaultChainConfig, + /* eslint-disable @typescript-eslint/naming-convention */ + ALTAIR_FORK_EPOCH: 1, + BELLATRIX_FORK_EPOCH: 3, + CAPELLA_FORK_EPOCH: Infinity, + }); + beforeEach(() => { fakeClock = sinon.useFakeTimers(); }); @@ -93,15 +100,15 @@ describe("Light Client Finality Update validation", function () { it("should return invalid - finality update not matching local", async () => { const lightClientFinalityUpdate: altair.LightClientFinalityUpdate = ssz.altair.LightClientFinalityUpdate.defaultValue(); - lightClientFinalityUpdate.finalizedHeader.beacon.slot = 2; - lightClientFinalityUpdate.signatureSlot = lightClientFinalityUpdate.finalizedHeader.beacon.slot + 1; + lightClientFinalityUpdate.finalizedHeader.beacon.slot = 42; + lightClientFinalityUpdate.attestedHeader.beacon.slot = lightClientFinalityUpdate.finalizedHeader.beacon.slot + 1; const chain = mockChain(); // make lightclientserver return another update with different value from gossiped chain.lightClientServer.getFinalityUpdate = () => { const defaultValue = ssz.altair.LightClientFinalityUpdate.defaultValue(); - defaultValue.finalizedHeader.beacon.slot = 1; + defaultValue.finalizedHeader.beacon.slot = 41; return defaultValue; }; @@ -120,8 +127,8 @@ describe("Light Client Finality Update validation", function () { it("should return invalid - not matching local when no local finality update yet", async () => { const lightClientFinalityUpdate: altair.LightClientFinalityUpdate = ssz.altair.LightClientFinalityUpdate.defaultValue(); - lightClientFinalityUpdate.finalizedHeader.beacon.slot = 2; - lightClientFinalityUpdate.signatureSlot = lightClientFinalityUpdate.finalizedHeader.beacon.slot + 1; + lightClientFinalityUpdate.finalizedHeader.beacon.slot = 42; + lightClientFinalityUpdate.attestedHeader.beacon.slot = lightClientFinalityUpdate.finalizedHeader.beacon.slot + 1; const chain = mockChain(); @@ -149,6 +156,7 @@ describe("Light Client Finality Update validation", function () { // satisfy: // No other finality_update with a lower or equal finalized_header.beacon.slot was already forwarded on the network lightClientFinalityUpdate.finalizedHeader.beacon.slot = 2; + lightClientFinalityUpdate.signatureSlot = lightClientFinalityUpdate.finalizedHeader.beacon.slot + 1; chain.lightClientServer.getFinalityUpdate = () => { const defaultValue = ssz.altair.LightClientFinalityUpdate.defaultValue(); diff --git a/packages/beacon-node/test/unit/chain/validation/lightClientOptimisticUpdate.test.ts b/packages/beacon-node/test/unit/chain/validation/lightClientOptimisticUpdate.test.ts index a5d2bfb1d7cc..71c941af1013 100644 --- a/packages/beacon-node/test/unit/chain/validation/lightClientOptimisticUpdate.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/lightClientOptimisticUpdate.test.ts @@ -1,7 +1,6 @@ import {expect} from "chai"; import sinon from "sinon"; -import {createIBeaconConfig} from "@lodestar/config"; -import {config} from "@lodestar/config/default"; +import {createIBeaconConfig, createIChainForkConfig, defaultChainConfig} from "@lodestar/config"; import {altair, ssz} from "@lodestar/types"; import {computeTimeAtSlot} from "@lodestar/state-transition"; @@ -14,6 +13,15 @@ import {IBeaconChain} from "../../../../src/chain/index.js"; describe("Light Client Optimistic Update validation", function () { let fakeClock: sinon.SinonFakeTimers; const afterEachCallbacks: (() => Promise | void)[] = []; + // eslint-disable-next-line @typescript-eslint/naming-convention + const config = createIChainForkConfig({ + ...defaultChainConfig, + /* eslint-disable @typescript-eslint/naming-convention */ + ALTAIR_FORK_EPOCH: 1, + BELLATRIX_FORK_EPOCH: 3, + CAPELLA_FORK_EPOCH: Infinity, + }); + beforeEach(() => { fakeClock = sinon.useFakeTimers(); }); @@ -93,7 +101,7 @@ describe("Light Client Optimistic Update validation", function () { it("should return invalid - optimistic update not matching local", async () => { const lightclientOptimisticUpdate: altair.LightClientOptimisticUpdate = ssz.altair.LightClientOptimisticUpdate.defaultValue(); - lightclientOptimisticUpdate.attestedHeader.beacon.slot = 2; + lightclientOptimisticUpdate.attestedHeader.beacon.slot = 42; const chain = mockChain(); @@ -118,7 +126,7 @@ describe("Light Client Optimistic Update validation", function () { it("should return invalid - not matching local when no local optimistic update yet", async () => { const lightclientOptimisticUpdate: altair.LightClientOptimisticUpdate = ssz.altair.LightClientOptimisticUpdate.defaultValue(); - lightclientOptimisticUpdate.attestedHeader.beacon.slot = 2; + lightclientOptimisticUpdate.attestedHeader.beacon.slot = 42; const chain = mockChain(); @@ -145,12 +153,6 @@ describe("Light Client Optimistic Update validation", function () { // No other optimistic_update with a lower or equal attested_header.beacon.slot was already forwarded on the network lightclientOptimisticUpdate.attestedHeader.beacon.slot = 2; - chain.lightClientServer.getOptimisticUpdate = () => { - const defaultValue = ssz.altair.LightClientOptimisticUpdate.defaultValue(); - defaultValue.attestedHeader.beacon.slot = 1; - return defaultValue; - }; - // satisfy: // [IGNORE] The optimistic_update is received after the block at signature_slot was given enough time to propagate // through the network -- i.e. validate that one-third of optimistic_update.signature_slot has transpired diff --git a/packages/beacon-node/test/unit/db/api/repository.test.ts b/packages/beacon-node/test/unit/db/api/repository.test.ts index 6747a7b46325..99bdce9e7f95 100644 --- a/packages/beacon-node/test/unit/db/api/repository.test.ts +++ b/packages/beacon-node/test/unit/db/api/repository.test.ts @@ -7,7 +7,6 @@ import {Bytes32, ssz} from "@lodestar/types"; import {config} from "@lodestar/config/default"; import {Db, LevelDbController, Repository, Bucket} from "@lodestar/db"; -// eslint-disable-next-line @typescript-eslint/naming-convention interface TestType { bool: boolean; bytes: Bytes32; diff --git a/packages/beacon-node/test/unit/network/doBeaconBlocksMaybeBlobsByRange.test.ts b/packages/beacon-node/test/unit/network/doBeaconBlocksMaybeBlobsByRange.test.ts index 180bc019108e..18fbb8d5e9c5 100644 --- a/packages/beacon-node/test/unit/network/doBeaconBlocksMaybeBlobsByRange.test.ts +++ b/packages/beacon-node/test/unit/network/doBeaconBlocksMaybeBlobsByRange.test.ts @@ -1,7 +1,7 @@ import sinon, {SinonStubbedInstance} from "sinon"; import {expect} from "chai"; import {peerIdFromString} from "@libp2p/peer-id"; -import {ssz, eip4844} from "@lodestar/types"; +import {ssz, deneb} from "@lodestar/types"; import {createIBeaconConfig, createIChainForkConfig, defaultChainConfig} from "@lodestar/config"; import {doBeaconBlocksMaybeBlobsByRange, ReqRespBeaconNode} from "../../../src/network/reqresp/index.js"; @@ -32,18 +32,18 @@ describe("doBeaconBlocksMaybeBlobsByRange", () => { const config = createIBeaconConfig(chainConfig, genesisValidatorsRoot); const rangeRequest = ssz.phase0.BeaconBlocksByRangeRequest.defaultValue(); - const block1 = ssz.eip4844.SignedBeaconBlock.defaultValue(); + const block1 = ssz.deneb.SignedBeaconBlock.defaultValue(); block1.message.slot = 1; - const block2 = ssz.eip4844.SignedBeaconBlock.defaultValue(); + const block2 = ssz.deneb.SignedBeaconBlock.defaultValue(); block2.message.slot = 2; - const blobsSidecar1 = ssz.eip4844.BlobsSidecar.defaultValue(); + const blobsSidecar1 = ssz.deneb.BlobsSidecar.defaultValue(); blobsSidecar1.beaconBlockSlot = 1; - const blobsSidecar2 = ssz.eip4844.BlobsSidecar.defaultValue(); + const blobsSidecar2 = ssz.deneb.BlobsSidecar.defaultValue(); blobsSidecar2.beaconBlockSlot = 2; // Array of testcases which are array of matched blocks with/without (if empty) sidecars - const testCases: [string, [eip4844.SignedBeaconBlock, eip4844.BlobsSidecar | undefined][]][] = [ + const testCases: [string, [deneb.SignedBeaconBlock, deneb.BlobsSidecar | undefined][]][] = [ ["one block with sidecar", [[block1, blobsSidecar1]]], [ "two blocks with sidecar", @@ -56,9 +56,9 @@ describe("doBeaconBlocksMaybeBlobsByRange", () => { ]; testCases.map(([testName, blocksWithBlobs]) => { it(testName, async () => { - const blocks = blocksWithBlobs.map(([block, _blobs]) => block as eip4844.SignedBeaconBlock); + const blocks = blocksWithBlobs.map(([block, _blobs]) => block as deneb.SignedBeaconBlock); const blobsSidecars = blocksWithBlobs - .map(([_block, blobs]) => blobs as eip4844.BlobsSidecar) + .map(([_block, blobs]) => blobs as deneb.BlobsSidecar) .filter((blobs) => blobs !== undefined); const emptyKzgAggregatedProof = ckzg.computeAggregateKzgProof([]); const expectedResponse = blocksWithBlobs.map(([block, blobsSidecar]) => { @@ -66,13 +66,13 @@ describe("doBeaconBlocksMaybeBlobsByRange", () => { blobsSidecar !== undefined ? blobsSidecar : { - beaconBlockRoot: ssz.eip4844.BeaconBlock.hashTreeRoot(block.message), + beaconBlockRoot: ssz.deneb.BeaconBlock.hashTreeRoot(block.message), beaconBlockSlot: block.message.slot, blobs: [], kzgAggregatedProof: emptyKzgAggregatedProof, }; return { - type: BlockInputType.postEIP4844, + type: BlockInputType.postDeneb, block, blobs, }; diff --git a/packages/beacon-node/test/unit/network/fork.test.ts b/packages/beacon-node/test/unit/network/fork.test.ts index c631969507c0..b4c5ed35cee8 100644 --- a/packages/beacon-node/test/unit/network/fork.test.ts +++ b/packages/beacon-node/test/unit/network/fork.test.ts @@ -8,13 +8,13 @@ function getForkConfig({ altair, bellatrix, capella, - eip4844, + deneb, }: { phase0: number; altair: number; bellatrix: number; capella: number; - eip4844: number; + deneb: number; }): IBeaconConfig { const forks: Record = { phase0: { @@ -49,10 +49,10 @@ function getForkConfig({ prevVersion: Buffer.from([0, 0, 0, 2]), prevForkName: ForkName.bellatrix, }, - eip4844: { - name: ForkName.eip4844, - seq: ForkSeq.eip4844, - epoch: eip4844, + deneb: { + name: ForkName.deneb, + seq: ForkSeq.deneb, + epoch: deneb, version: Buffer.from([0, 0, 0, 4]), prevVersion: Buffer.from([0, 0, 0, 3]), prevForkName: ForkName.capella, @@ -132,11 +132,11 @@ const testScenarios = [ for (const testScenario of testScenarios) { const {phase0, altair, bellatrix, capella, testCases} = testScenario; - // TODO EIP-4844: Is it necessary to test? - const eip4844 = Infinity; + // TODO DENEB: Is it necessary to test? + const deneb = Infinity; describe(`network / fork: phase0: ${phase0}, altair: ${altair}, bellatrix: ${bellatrix} capella: ${capella}`, () => { - const forkConfig = getForkConfig({phase0, altair, bellatrix, capella, eip4844}); + const forkConfig = getForkConfig({phase0, altair, bellatrix, capella, deneb}); const forks = forkConfig.forks; for (const testCase of testCases) { const {epoch, currentFork, nextFork, activeForks} = testCase; 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 387238341891..7eb037a8fe43 100644 --- a/packages/beacon-node/test/unit/network/gossip/topic.test.ts +++ b/packages/beacon-node/test/unit/network/gossip/topic.test.ts @@ -17,7 +17,7 @@ describe("network / gossip / topic", function () { ], [GossipType.beacon_block_and_blobs_sidecar]: [ { - topic: {type: GossipType.beacon_block_and_blobs_sidecar, fork: ForkName.eip4844, encoding}, + topic: {type: GossipType.beacon_block_and_blobs_sidecar, fork: ForkName.deneb, encoding}, topicStr: "/eth2/46acb19a/beacon_block_and_blobs_sidecar/ssz_snappy", }, ], diff --git a/packages/beacon-node/test/unit/sync/range/batch.test.ts b/packages/beacon-node/test/unit/sync/range/batch.test.ts index 54b7614a24fc..1cc55b4f6f31 100644 --- a/packages/beacon-node/test/unit/sync/range/batch.test.ts +++ b/packages/beacon-node/test/unit/sync/range/batch.test.ts @@ -12,7 +12,7 @@ describe("sync / range / batch", async () => { // Common mock data const startEpoch = 0; const peer = await createSecp256k1PeerId(); - const blocksDownloaded = [getBlockInput.preEIP4844(config, ssz.phase0.SignedBeaconBlock.defaultValue())]; + const blocksDownloaded = [getBlockInput.preDeneb(config, ssz.phase0.SignedBeaconBlock.defaultValue())]; it("Should return correct blockByRangeRequest", () => { const batch = new Batch(startEpoch, config); diff --git a/packages/beacon-node/test/unit/sync/range/chain.test.ts b/packages/beacon-node/test/unit/sync/range/chain.test.ts index 03b675f9a7d5..cefdbbb00be9 100644 --- a/packages/beacon-node/test/unit/sync/range/chain.test.ts +++ b/packages/beacon-node/test/unit/sync/range/chain.test.ts @@ -83,7 +83,7 @@ describe("sync / range / chain", () => { const shouldReject = badBlocks?.has(i); if (shouldReject) badBlocks?.delete(i); blocks.push( - getBlockInput.preEIP4844(config, { + getBlockInput.preDeneb(config, { message: generateEmptyBlock(i), signature: shouldReject ? REJECT_BLOCK : ACCEPT_BLOCK, }) @@ -124,7 +124,7 @@ describe("sync / range / chain", () => { const blocks: BlockInput[] = []; for (let i = request.startSlot; i < request.startSlot + request.count; i += request.step) { blocks.push( - getBlockInput.preEIP4844(config, { + getBlockInput.preDeneb(config, { message: generateEmptyBlock(i), signature: ACCEPT_BLOCK, }) diff --git a/packages/beacon-node/test/unit/sync/unknownBlock.test.ts b/packages/beacon-node/test/unit/sync/unknownBlock.test.ts index cb405fd505ce..77eed4c2cdd9 100644 --- a/packages/beacon-node/test/unit/sync/unknownBlock.test.ts +++ b/packages/beacon-node/test/unit/sync/unknownBlock.test.ts @@ -55,7 +55,7 @@ describe("sync / UnknownBlockSync", () => { Array.from(roots) .map((root) => blocksByRoot.get(toHexString(root))) .filter(notNullish) - .map((block) => getBlockInput.preEIP4844(config, block)), + .map((block) => getBlockInput.preDeneb(config, block)), reportPeer: (peerId, action, actionName) => reportPeerResolveFn([peerId, action, actionName]), }; @@ -77,7 +77,7 @@ describe("sync / UnknownBlockSync", () => { }; new UnknownBlockSync(config, network as INetwork, chain as IBeaconChain, logger, null); - network.events?.emit(NetworkEvent.unknownBlockParent, getBlockInput.preEIP4844(config, blockC), peerIdStr); + network.events?.emit(NetworkEvent.unknownBlockParent, getBlockInput.preDeneb(config, blockC), peerIdStr); if (reportPeer) { const err = await reportPeerPromise; diff --git a/packages/beacon-node/test/unit/util/kzg.test.ts b/packages/beacon-node/test/unit/util/kzg.test.ts index b8d0402bf461..6bb8c17d41c5 100644 --- a/packages/beacon-node/test/unit/util/kzg.test.ts +++ b/packages/beacon-node/test/unit/util/kzg.test.ts @@ -1,5 +1,5 @@ import {expect} from "chai"; -import {bellatrix, eip4844, ssz} from "@lodestar/types"; +import {bellatrix, deneb, ssz} from "@lodestar/types"; import {BLOB_TX_TYPE, BYTES_PER_FIELD_ELEMENT} from "@lodestar/params"; import { kzgCommitmentToVersionedHash, @@ -31,14 +31,14 @@ describe("C-KZG", () => { const blobs = [generateRandomBlob(), generateRandomBlob()]; const kzgCommitments = blobs.map((blob) => ckzg.blobToKzgCommitment(blob)); - const signedBeaconBlock = ssz.eip4844.SignedBeaconBlock.defaultValue(); + const signedBeaconBlock = ssz.deneb.SignedBeaconBlock.defaultValue(); for (const kzgCommitment of kzgCommitments) { signedBeaconBlock.message.body.executionPayload.transactions.push(transactionForKzgCommitment(kzgCommitment)); signedBeaconBlock.message.body.blobKzgCommitments.push(kzgCommitment); } - const beaconBlockRoot = ssz.eip4844.BeaconBlock.hashTreeRoot(signedBeaconBlock.message); + const beaconBlockRoot = ssz.deneb.BeaconBlock.hashTreeRoot(signedBeaconBlock.message); - const blobsSidecar: eip4844.BlobsSidecar = { + const blobsSidecar: deneb.BlobsSidecar = { beaconBlockRoot, beaconBlockSlot: 0, blobs, @@ -53,7 +53,7 @@ describe("C-KZG", () => { }); }); -function transactionForKzgCommitment(kzgCommitment: eip4844.KZGCommitment): bellatrix.Transaction { +function transactionForKzgCommitment(kzgCommitment: deneb.KZGCommitment): bellatrix.Transaction { // Some random value that after the offset's position const blobVersionedHashesOffset = OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET + 64; @@ -79,7 +79,7 @@ function transactionForKzgCommitment(kzgCommitment: eip4844.KZGCommitment): bell /** * Generate random blob of sequential integers such that each element is < BLS_MODULUS */ -function generateRandomBlob(): eip4844.Blob { +function generateRandomBlob(): deneb.Blob { const blob = new Uint8Array(FIELD_ELEMENTS_PER_BLOB_MAINNET * BYTES_PER_FIELD_ELEMENT); const dv = new DataView(blob.buffer, blob.byteOffset, blob.byteLength); for (let i = 0; i < FIELD_ELEMENTS_PER_BLOB_MAINNET; i++) { diff --git a/packages/beacon-node/test/utils/cliTools/kzgTrustedSetupFromJson.ts b/packages/beacon-node/test/utils/cliTools/kzgTrustedSetupFromJson.ts index c91c95bacabc..731f44df59c8 100644 --- a/packages/beacon-node/test/utils/cliTools/kzgTrustedSetupFromJson.ts +++ b/packages/beacon-node/test/utils/cliTools/kzgTrustedSetupFromJson.ts @@ -3,7 +3,7 @@ import {TrustedSetupJSON, trustedSetupJsonToBin, TRUSTED_SETUP_BIN_FILEPATH} fro // CLI TOOL: Use to transform a JSON trusted setup into .ssz // -// Note: Closer to EIP-4844 this tool may never be useful again, +// Note: Closer to DENEB this tool may never be useful again, // see https://github.com/ethereum/c-kzg-4844/issues/3 const INPUT_FILE = process.argv[2]; diff --git a/packages/beacon-node/test/utils/config.ts b/packages/beacon-node/test/utils/config.ts index a1d381462380..18d6b0e14b90 100644 --- a/packages/beacon-node/test/utils/config.ts +++ b/packages/beacon-node/test/utils/config.ts @@ -24,7 +24,7 @@ export function getConfig(fork: ForkName, forkEpoch = 0): IChainForkConfig { BELLATRIX_FORK_EPOCH: 0, CAPELLA_FORK_EPOCH: forkEpoch, }); - case ForkName.eip4844: + case ForkName.deneb: return createIChainForkConfig({ ALTAIR_FORK_EPOCH: 0, BELLATRIX_FORK_EPOCH: 0, diff --git a/packages/beacon-node/test/utils/logger.ts b/packages/beacon-node/test/utils/logger.ts index a3f83d957e5c..c9afdc6425e5 100644 --- a/packages/beacon-node/test/utils/logger.ts +++ b/packages/beacon-node/test/utils/logger.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */ import winston from "winston"; import {createWinstonLogger, ILogger, LogLevel, TimestampFormat} from "@lodestar/utils"; export {LogLevel}; diff --git a/packages/beacon-node/test/utils/mocks/chain/chain.ts b/packages/beacon-node/test/utils/mocks/chain/chain.ts index fb7843195dcb..9784f978af0d 100644 --- a/packages/beacon-node/test/utils/mocks/chain/chain.ts +++ b/packages/beacon-node/test/utils/mocks/chain/chain.ts @@ -1,7 +1,7 @@ import sinon from "sinon"; import {CompositeTypeAny, toHexString, TreeView} from "@chainsafe/ssz"; -import {phase0, allForks, UintNum64, Root, Slot, ssz, Uint16, UintBn64, RootHex, eip4844, Wei} from "@lodestar/types"; +import {phase0, allForks, UintNum64, Root, Slot, ssz, Uint16, UintBn64, RootHex, deneb, Wei} from "@lodestar/types"; import {IBeaconConfig} from "@lodestar/config"; import {BeaconStateAllForks, CachedBeaconStateAllForks} from "@lodestar/state-transition"; import {CheckpointWithHex, IForkChoice, ProtoBlock, ExecutionStatus, AncestorStatus} from "@lodestar/fork-choice"; @@ -105,7 +105,7 @@ export class MockBeaconChain implements IBeaconChain { private readonly state: CachedBeaconStateAllForks; private abortController: AbortController; - readonly producedBlobsSidecarCache = new Map(); + readonly producedBlobsSidecarCache = new Map(); constructor({genesisTime, chainId, networkId, state, config}: IMockChainParams) { this.logger = testLogger(); @@ -142,7 +142,7 @@ export class MockBeaconChain implements IBeaconChain { {}, { config: this.config, - db: db, + db, metrics: null, emitter: this.emitter, logger: this.logger, diff --git a/packages/beacon-node/test/utils/network.ts b/packages/beacon-node/test/utils/network.ts index ea9b128668f0..2b83229105b0 100644 --- a/packages/beacon-node/test/utils/network.ts +++ b/packages/beacon-node/test/utils/network.ts @@ -38,14 +38,12 @@ export async function disconnect(network: INetworkDebug, peer: PeerId): Promise< export function onPeerConnect(network: Network): Promise { return new Promise((resolve) => - // eslint-disable-next-line @typescript-eslint/no-unsafe-return network["libp2p"].connectionManager.addEventListener(Libp2pEvent.peerConnect, () => resolve()) ); } export function onPeerDisconnect(network: Network): Promise { return new Promise((resolve) => - // eslint-disable-next-line @typescript-eslint/no-unsafe-return network["libp2p"].connectionManager.addEventListener(Libp2pEvent.peerDisconnect, () => resolve()) ); } diff --git a/packages/beacon-node/test/utils/node/beacon.ts b/packages/beacon-node/test/utils/node/beacon.ts index 047be78fa1bb..bb5a61597a3c 100644 --- a/packages/beacon-node/test/utils/node/beacon.ts +++ b/packages/beacon-node/test/utils/node/beacon.ts @@ -92,8 +92,8 @@ export async function getDevBeaconNode( block.message.stateRoot = state.hashTreeRoot(); await db.blockArchive.add(block); - if (config.getForkSeq(GENESIS_SLOT) >= ForkSeq.eip4844) { - const blobsSidecar = ssz.eip4844.BlobsSidecar.defaultValue(); + if (config.getForkSeq(GENESIS_SLOT) >= ForkSeq.deneb) { + const blobsSidecar = ssz.deneb.BlobsSidecar.defaultValue(); blobsSidecar.beaconBlockRoot = config.getForkTypes(GENESIS_SLOT).BeaconBlock.hashTreeRoot(block.message); await db.blobsSidecar.add(blobsSidecar); } diff --git a/packages/beacon-node/test/utils/stub/beaconDb.ts b/packages/beacon-node/test/utils/stub/beaconDb.ts index 3e775030f4d4..cb7e2e8cea01 100644 --- a/packages/beacon-node/test/utils/stub/beaconDb.ts +++ b/packages/beacon-node/test/utils/stub/beaconDb.ts @@ -39,7 +39,6 @@ export class StubbedBeaconDb extends BeaconDb { depositDataRoot: SinonStubbedInstance & DepositDataRootRepository; eth1Data: SinonStubbedInstance & Eth1DataRepository; - // eslint-disable-next-line @typescript-eslint/no-unused-vars constructor(config = minimalConfig) { // eslint-disable-next-line super({config, controller: {} as any}); diff --git a/packages/cli/package.json b/packages/cli/package.json index 46da522c84b9..2de95efa17b9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@chainsafe/lodestar", - "version": "1.4.3", + "version": "1.5.0", "description": "Command line interface for lodestar", "author": "ChainSafe Systems", "license": "LGPL-3.0", @@ -36,7 +36,7 @@ "test:e2e": "mocha --timeout 30000 'test/e2e/**/*.test.ts'", "test:sim:multifork": "LODESTAR_PRESET=minimal ts-node --esm test/sim/multi_fork.test.ts", "test:sim:endpoints": "LODESTAR_PRESET=minimal ts-node --esm test/sim/endpoints.test.ts", - "test:sim:eip4844": "LODESTAR_PRESET=minimal ts-node --esm test/sim/eip4844.test.ts", + "test:sim:deneb": "LODESTAR_PRESET=minimal ts-node --esm test/sim/deneb.test.ts", "test:sim:backup_eth_provider": "LODESTAR_PRESET=minimal ts-node --esm test/sim/backup_eth_provider.test.ts", "test": "yarn test:unit && yarn test:e2e", "coverage": "codecov -F lodestar", @@ -61,16 +61,16 @@ "@chainsafe/discv5": "^3.0.0", "@chainsafe/ssz": "^0.9.2", "@libp2p/peer-id-factory": "^2.0.1", - "@lodestar/api": "^1.4.3", - "@lodestar/beacon-node": "^1.4.3", - "@lodestar/config": "^1.4.3", - "@lodestar/db": "^1.4.3", - "@lodestar/light-client": "^1.4.3", - "@lodestar/params": "^1.4.3", - "@lodestar/state-transition": "^1.4.3", - "@lodestar/types": "^1.4.3", - "@lodestar/utils": "^1.4.3", - "@lodestar/validator": "^1.4.3", + "@lodestar/api": "^1.5.0", + "@lodestar/beacon-node": "^1.5.0", + "@lodestar/config": "^1.5.0", + "@lodestar/db": "^1.5.0", + "@lodestar/light-client": "^1.5.0", + "@lodestar/params": "^1.5.0", + "@lodestar/state-transition": "^1.5.0", + "@lodestar/types": "^1.5.0", + "@lodestar/utils": "^1.5.0", + "@lodestar/validator": "^1.5.0", "@multiformats/multiaddr": "^11.0.0", "@types/lockfile": "^1.0.1", "bip39": "^3.0.2", diff --git a/packages/cli/src/cmds/dev/files.ts b/packages/cli/src/cmds/dev/files.ts index 01e69fcd77a6..33edf112d6c6 100644 --- a/packages/cli/src/cmds/dev/files.ts +++ b/packages/cli/src/cmds/dev/files.ts @@ -7,7 +7,7 @@ import {interopSecretKey} from "@lodestar/state-transition"; import {Keystore} from "@chainsafe/bls-keystore"; import {PersistedKeysBackend} from "../validator/keymanager/persistedKeys.js"; -/* eslint-disable @typescript-eslint/naming-convention, no-console */ +/* eslint-disable no-console */ export async function writeTestnetFiles( config: IChainForkConfig, diff --git a/packages/cli/src/cmds/validator/handler.ts b/packages/cli/src/cmds/validator/handler.ts index d59fdafec901..1949d5e4265e 100644 --- a/packages/cli/src/cmds/validator/handler.ts +++ b/packages/cli/src/cmds/validator/handler.ts @@ -1,7 +1,13 @@ import path from "node:path"; import {setMaxListeners} from "node:events"; import {LevelDbController} from "@lodestar/db"; -import {ProcessShutdownCallback, SlashingProtection, Validator, ValidatorProposerConfig} from "@lodestar/validator"; +import { + ProcessShutdownCallback, + SlashingProtection, + Validator, + ValidatorProposerConfig, + BuilderSelection, +} from "@lodestar/validator"; import {getMetrics, MetricsRegister} from "@lodestar/validator"; import {RegistryMetricCreator, collectNodeJSMetrics, HttpMetricsServer} from "@lodestar/beacon-node"; import {getBeaconConfigFromArgs} from "../../config/index.js"; @@ -179,7 +185,11 @@ function getProposerConfigFromArgs( graffiti: args.graffiti || getDefaultGraffiti(), strictFeeRecipientCheck: args.strictFeeRecipientCheck, feeRecipient: args.suggestedFeeRecipient ? parseFeeRecipient(args.suggestedFeeRecipient) : undefined, - builder: {enabled: args.builder, gasLimit: args.defaultGasLimit}, + builder: { + enabled: args.builder, + gasLimit: args.defaultGasLimit, + selection: parseBuilderSelection(args["builder.selection"]), + }, }; let valProposerConfig: ValidatorProposerConfig; @@ -204,3 +214,17 @@ function getProposerConfigFromArgs( } return valProposerConfig; } + +function parseBuilderSelection(builderSelection?: string): BuilderSelection | undefined { + if (builderSelection) { + switch (builderSelection) { + case "maxprofit": + break; + case "builderalways": + break; + default: + throw Error("Invalid input for builder selection, check help."); + } + } + return builderSelection as BuilderSelection; +} diff --git a/packages/cli/src/cmds/validator/options.ts b/packages/cli/src/cmds/validator/options.ts index 46ecdd6054bf..e743c2cb545f 100644 --- a/packages/cli/src/cmds/validator/options.ts +++ b/packages/cli/src/cmds/validator/options.ts @@ -34,7 +34,9 @@ export type IValidatorCliArgs = AccountValidatorArgs & strictFeeRecipientCheck?: boolean; doppelgangerProtectionEnabled?: boolean; defaultGasLimit?: number; + builder?: boolean; + "builder.selection"?: string; importKeystores?: string[]; importKeystoresPassword?: string; @@ -204,6 +206,13 @@ export const validatorOptions: ICliCommandOptions = { group: "builder", }, + "builder.selection": { + type: "string", + description: "Default builder block selection strategy: maxprofit or builderalways", + defaultDescription: `${defaultOptions.builderSelection}`, + group: "builder", + }, + importKeystores: { alias: ["keystore"], // Backwards compatibility with old `validator import` cmdx description: "Path(s) to a directory or single filepath to validator keystores, i.e. Launchpad validators", diff --git a/packages/cli/src/cmds/validator/signers/importExternalKeystores.ts b/packages/cli/src/cmds/validator/signers/importExternalKeystores.ts index 561b3e33e117..648b4dc180eb 100644 --- a/packages/cli/src/cmds/validator/signers/importExternalKeystores.ts +++ b/packages/cli/src/cmds/validator/signers/importExternalKeystores.ts @@ -60,7 +60,6 @@ export function isVotingKeystore(filename: string): boolean { // The format exported by the `staking-deposit-cli` library. // // Reference to function that generates keystores: - // eslint-disable-next-line max-len // https://github.com/ethereum/staking-deposit-cli/blob/7cebff15eac299b3b1b090c896dd3410c8463450/eth2deposit/credentials.py#L58-L62 // // Since we include the key derivation path of `m/12381/3600/x/0/0` this should only ever match diff --git a/packages/cli/src/cmds/validator/slashingProtection/export.ts b/packages/cli/src/cmds/validator/slashingProtection/export.ts index 8474a1ce45a6..2bef20299102 100644 --- a/packages/cli/src/cmds/validator/slashingProtection/export.ts +++ b/packages/cli/src/cmds/validator/slashingProtection/export.ts @@ -9,8 +9,6 @@ import {getValidatorPaths} from "../paths.js"; import {getGenesisValidatorsRoot, getSlashingProtection} from "./utils.js"; import {ISlashingProtectionArgs} from "./options.js"; -/* eslint-disable no-console */ - interface IExportArgs { file: string; } diff --git a/packages/cli/src/cmds/validator/slashingProtection/import.ts b/packages/cli/src/cmds/validator/slashingProtection/import.ts index f0281dcdd9fe..32230fb204e3 100644 --- a/packages/cli/src/cmds/validator/slashingProtection/import.ts +++ b/packages/cli/src/cmds/validator/slashingProtection/import.ts @@ -10,8 +10,6 @@ import {getValidatorPaths} from "../paths.js"; import {getGenesisValidatorsRoot, getSlashingProtection} from "./utils.js"; import {ISlashingProtectionArgs} from "./options.js"; -/* eslint-disable no-console */ - interface IImportArgs { file: string; } diff --git a/packages/cli/src/networks/dev.ts b/packages/cli/src/networks/dev.ts index 4a4981b85baa..77d55bc6a32d 100644 --- a/packages/cli/src/networks/dev.ts +++ b/packages/cli/src/networks/dev.ts @@ -19,8 +19,6 @@ switch (ACTIVE_PRESET) { export {chainConfig}; -/* eslint-disable max-len */ - export const depositContractDeployBlock = 0; export const genesisFileUrl = null; export const bootnodesFileUrl = null; diff --git a/packages/cli/src/networks/gnosis.ts b/packages/cli/src/networks/gnosis.ts index bf80d06de7e7..58ecbbe983a3 100644 --- a/packages/cli/src/networks/gnosis.ts +++ b/packages/cli/src/networks/gnosis.ts @@ -1,7 +1,5 @@ export {gnosisChainConfig as chainConfig} from "@lodestar/config/networks"; -/* eslint-disable max-len */ - // eth1.providerUrls suggestion: https://rpc.gnosischain.com export const depositContractDeployBlock = 19469077; export const genesisFileUrl = "https://github.com/raw/gnosischain/configs/main/mainnet/genesis.ssz"; diff --git a/packages/cli/src/networks/goerli.ts b/packages/cli/src/networks/goerli.ts index e6001d07785c..0447bdb24302 100644 --- a/packages/cli/src/networks/goerli.ts +++ b/packages/cli/src/networks/goerli.ts @@ -1,7 +1,5 @@ export {goerliChainConfig as chainConfig} from "@lodestar/config/networks"; -/* eslint-disable max-len */ - export const depositContractDeployBlock = 4367322; export const genesisFileUrl = "https://github.com/raw/eth2-clients/eth2-testnets/master/shared/prater/genesis.ssz"; diff --git a/packages/cli/src/networks/mainnet.ts b/packages/cli/src/networks/mainnet.ts index 8cafbdc68d8a..c65fabc47efd 100644 --- a/packages/cli/src/networks/mainnet.ts +++ b/packages/cli/src/networks/mainnet.ts @@ -1,7 +1,5 @@ export {mainnetChainConfig as chainConfig} from "@lodestar/config/networks"; -/* eslint-disable max-len */ - export const depositContractDeployBlock = 11052984; export const genesisFileUrl = "https://github.com/raw/eth2-clients/eth2-testnets/master/shared/mainnet/genesis.ssz"; diff --git a/packages/cli/src/networks/ropsten.ts b/packages/cli/src/networks/ropsten.ts index 7d44f0ed1768..378277c6b0bd 100644 --- a/packages/cli/src/networks/ropsten.ts +++ b/packages/cli/src/networks/ropsten.ts @@ -1,7 +1,5 @@ export {ropstenChainConfig as chainConfig} from "@lodestar/config/networks"; -/* eslint-disable max-len */ - export const depositContractDeployBlock = 12269949; export const genesisFileUrl = "https://github.com/raw/eth-clients/merge-testnets/main/ropsten-beacon-chain/genesis.ssz"; diff --git a/packages/cli/src/networks/sepolia.ts b/packages/cli/src/networks/sepolia.ts index 73c955aee581..878cf40cad54 100644 --- a/packages/cli/src/networks/sepolia.ts +++ b/packages/cli/src/networks/sepolia.ts @@ -1,7 +1,5 @@ export {sepoliaChainConfig as chainConfig} from "@lodestar/config/networks"; -/* eslint-disable max-len */ - export const depositContractDeployBlock = 1273020; export const genesisFileUrl = "https://github.com/raw/eth-clients/merge-testnets/main/sepolia/genesis.ssz"; export const bootnodesFileUrl = diff --git a/packages/cli/src/util/proposerConfig.ts b/packages/cli/src/util/proposerConfig.ts index 3da7307e05f0..29fcb25a48ea 100644 --- a/packages/cli/src/util/proposerConfig.ts +++ b/packages/cli/src/util/proposerConfig.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import path from "node:path"; -import {ValidatorProposerConfig} from "@lodestar/validator"; +import {ValidatorProposerConfig, BuilderSelection} from "@lodestar/validator"; import {parseFeeRecipient} from "./feeRecipient.js"; import {readFile} from "./file.js"; @@ -18,6 +18,7 @@ type ProposerConfigFileSection = { // for js-yaml enabled?: string; gas_limit?: number; + selection?: BuilderSelection; }; }; @@ -55,7 +56,7 @@ function parseProposerConfigSection( overrideConfig?: ProposerConfig ): ProposerConfig { const {graffiti, strict_fee_recipient_check, fee_recipient, builder} = proposerFileSection; - const {enabled, gas_limit} = builder || {}; + const {enabled, gas_limit, selection: builderSelection} = builder || {}; if (graffiti !== undefined && typeof graffiti !== "string") { throw Error("graffiti is not 'string"); @@ -90,6 +91,7 @@ function parseProposerConfigSection( builder: { enabled: overrideConfig?.builder?.enabled ?? (enabled ? stringtoBool(enabled) : undefined), gasLimit: overrideConfig?.builder?.gasLimit ?? (gas_limit !== undefined ? Number(gas_limit) : undefined), + selection: overrideConfig?.builder?.selection ?? builderSelection, }, }; } diff --git a/packages/cli/test/e2e/importFromFsDirect.test.ts b/packages/cli/test/e2e/importFromFsDirect.test.ts index 1a22b67b57a6..a4735f7766f3 100644 --- a/packages/cli/test/e2e/importFromFsDirect.test.ts +++ b/packages/cli/test/e2e/importFromFsDirect.test.ts @@ -8,8 +8,6 @@ import {cachedPubkeysHex, cachedSeckeysHex} from "../utils/cachedKeys.js"; import {expectKeys, getKeymanagerTestRunner} from "../utils/keymanagerTestRunners.js"; import {getKeystoresStr} from "../utils/keystores.js"; -/* eslint-disable no-console */ - describeCliTest("import from fs same cmd as validate", function ({spawnCli}) { const dataDir = path.join(testFilesDir, "import-and-validate-test"); const importFromDir = path.join(dataDir, "eth2.0_deposit_out"); diff --git a/packages/cli/test/e2e/importFromFsPreStep.test.ts b/packages/cli/test/e2e/importFromFsPreStep.test.ts index 46f1d42f42bf..3964372b4cdd 100644 --- a/packages/cli/test/e2e/importFromFsPreStep.test.ts +++ b/packages/cli/test/e2e/importFromFsPreStep.test.ts @@ -9,8 +9,6 @@ import {cachedPubkeysHex, cachedSeckeysHex} from "../utils/cachedKeys.js"; import {expectKeys, getKeymanagerTestRunner} from "../utils/keymanagerTestRunners.js"; import {getKeystoresStr} from "../utils/keystores.js"; -/* eslint-disable no-console */ - describeCliTest("import from fs then validate", function ({spawnCli}) { const dataDir = path.join(testFilesDir, "import-then-validate-test"); const importFromDir = path.join(dataDir, "eth2.0_deposit_out"); diff --git a/packages/cli/test/sim/eip4844.test.ts b/packages/cli/test/sim/deneb.test.ts similarity index 100% rename from packages/cli/test/sim/eip4844.test.ts rename to packages/cli/test/sim/deneb.test.ts diff --git a/packages/cli/test/unit/validator/parseProposerConfig.test.ts b/packages/cli/test/unit/validator/parseProposerConfig.test.ts index e90c8fa38c9b..ba4a7610f521 100644 --- a/packages/cli/test/unit/validator/parseProposerConfig.test.ts +++ b/packages/cli/test/unit/validator/parseProposerConfig.test.ts @@ -2,6 +2,8 @@ import path from "node:path"; import {fileURLToPath} from "node:url"; import {expect} from "chai"; +import {BuilderSelection} from "@lodestar/validator"; + import {parseProposerConfig} from "../../../src/util/index.js"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -15,6 +17,7 @@ const testValue = { builder: { enabled: true, gasLimit: 30000000, + selection: undefined, }, }, "0xa4855c83d868f772a579133d9f23818008417b743e8447e235d8eb78b1d8f8a9f63f98c551beb7de254400f89592314d": { @@ -24,6 +27,7 @@ const testValue = { builder: { enabled: true, gasLimit: 35000000, + selection: BuilderSelection.MaxProfit, }, }, }, @@ -34,6 +38,7 @@ const testValue = { builder: { enabled: true, gasLimit: 30000000, + selection: BuilderSelection.BuilderAlways, }, }, }; diff --git a/packages/cli/test/unit/validator/proposerConfigs/validData.yaml b/packages/cli/test/unit/validator/proposerConfigs/validData.yaml index 377d9e7630e7..2d954e85d7b3 100644 --- a/packages/cli/test/unit/validator/proposerConfigs/validData.yaml +++ b/packages/cli/test/unit/validator/proposerConfigs/validData.yaml @@ -11,6 +11,7 @@ proposer_config: builder: enabled: "true" gas_limit: "35000000" + selection: "maxprofit" default_config: graffiti: 'default graffiti' strict_fee_recipient_check: "true" @@ -18,3 +19,4 @@ default_config: builder: enabled: true gas_limit: "30000000" + selection: "builderalways" diff --git a/packages/cli/test/utils/simulation/Eth1ProviderWithAdmin.ts b/packages/cli/test/utils/simulation/Eth1ProviderWithAdmin.ts index 89a51e7b56c1..01f7a7f11377 100644 --- a/packages/cli/test/utils/simulation/Eth1ProviderWithAdmin.ts +++ b/packages/cli/test/utils/simulation/Eth1ProviderWithAdmin.ts @@ -35,7 +35,6 @@ export class Eth1ProviderWithAdmin extends Eth1Provider { nodeInfo: async (): Promise => { const method = "admin_nodeInfo"; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return await this.getRpc().fetch( // false = include only transaction roots, not full objects {method, params: []} @@ -45,7 +44,6 @@ export class Eth1ProviderWithAdmin extends Eth1Provider { addPeer: async (enode: string): Promise => { const method = "admin_addPeer"; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return await this.getRpc().fetch( // false = include only transaction roots, not full objects {method, params: [enode]} diff --git a/packages/cli/test/utils/simulation/el_clients/mock.ts b/packages/cli/test/utils/simulation/el_clients/mock.ts index e2af569d4a98..3eacfd6559c1 100644 --- a/packages/cli/test/utils/simulation/el_clients/mock.ts +++ b/packages/cli/test/utils/simulation/el_clients/mock.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import {ELClient, ELClientGenerator, ELGeneratorClientOptions, Runner, RunnerType} from "../interfaces.js"; export const generateMockNode: ELClientGenerator = ( diff --git a/packages/cli/test/utils/simulation/utils/child_process.ts b/packages/cli/test/utils/simulation/utils/child_process.ts index e95a3d9fb814..502a49863797 100644 --- a/packages/cli/test/utils/simulation/utils/child_process.ts +++ b/packages/cli/test/utils/simulation/utils/child_process.ts @@ -53,13 +53,11 @@ export const startChildProcess = async (jobOptions: JobOptions): Promise logHealthChecksAfterMs) { - // eslint-disable-next-line no-console console.log(`Health check unsuccessful '${jobOptions.id}' after ${timeSinceHealthCheckStart} ms`); } } }) .catch((e) => { - // eslint-disable-next-line no-console console.error("error on health check, health functions must never throw", e); }); }, childProcessHealthCheckInterval); diff --git a/packages/config/package.json b/packages/config/package.json index f5ee0889e502..595500236cd8 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@lodestar/config", - "version": "1.4.3", + "version": "1.5.0", "description": "Chain configuration required for lodestar", "author": "ChainSafe Systems", "license": "Apache-2.0", @@ -66,7 +66,7 @@ ], "dependencies": { "@chainsafe/ssz": "^0.9.2", - "@lodestar/params": "^1.4.3", - "@lodestar/types": "^1.4.3" + "@lodestar/params": "^1.5.0", + "@lodestar/types": "^1.5.0" } } diff --git a/packages/config/src/chainConfig/networks/gnosis.ts b/packages/config/src/chainConfig/networks/gnosis.ts index 06e4e23d7546..d11daf0bd5b3 100644 --- a/packages/config/src/chainConfig/networks/gnosis.ts +++ b/packages/config/src/chainConfig/networks/gnosis.ts @@ -4,8 +4,6 @@ import {PresetName} from "@lodestar/params"; import {IChainConfig} from "../types.js"; import {chainConfig as mainnet} from "../presets/mainnet.js"; -/* eslint-disable max-len */ - export const gnosisChainConfig: IChainConfig = { ...mainnet, diff --git a/packages/config/src/chainConfig/networks/goerli.ts b/packages/config/src/chainConfig/networks/goerli.ts index 8796632f3075..48366dead16f 100644 --- a/packages/config/src/chainConfig/networks/goerli.ts +++ b/packages/config/src/chainConfig/networks/goerli.ts @@ -3,8 +3,6 @@ import {fromHexString as b} from "@chainsafe/ssz"; import {IChainConfig} from "../types.js"; import {chainConfig as mainnet} from "../presets/mainnet.js"; -/* eslint-disable max-len */ - export const goerliChainConfig: IChainConfig = { ...mainnet, diff --git a/packages/config/src/chainConfig/networks/mainnet.ts b/packages/config/src/chainConfig/networks/mainnet.ts index 633161328009..4c7e2059df3c 100644 --- a/packages/config/src/chainConfig/networks/mainnet.ts +++ b/packages/config/src/chainConfig/networks/mainnet.ts @@ -3,8 +3,6 @@ import {fromHexString as b} from "@chainsafe/ssz"; import {IChainConfig} from "../types.js"; import {chainConfig as mainnet} from "../presets/mainnet.js"; -/* eslint-disable max-len */ - export const mainnetChainConfig: IChainConfig = { ...mainnet, diff --git a/packages/config/src/chainConfig/networks/ropsten.ts b/packages/config/src/chainConfig/networks/ropsten.ts index c4d604f5cf56..003c66c36104 100644 --- a/packages/config/src/chainConfig/networks/ropsten.ts +++ b/packages/config/src/chainConfig/networks/ropsten.ts @@ -3,8 +3,6 @@ import {fromHexString as b} from "@chainsafe/ssz"; import {IChainConfig} from "../types.js"; import {chainConfig as mainnet} from "../presets/mainnet.js"; -/* eslint-disable max-len */ - // Ropsten beacon chain config: // https://github.com/eth-clients/merge-testnets/blob/main/ropsten-beacon-chain/config.yaml diff --git a/packages/config/src/chainConfig/networks/sepolia.ts b/packages/config/src/chainConfig/networks/sepolia.ts index c7c17d5b1eaa..844035366233 100644 --- a/packages/config/src/chainConfig/networks/sepolia.ts +++ b/packages/config/src/chainConfig/networks/sepolia.ts @@ -3,8 +3,6 @@ import {fromHexString as b} from "@chainsafe/ssz"; import {IChainConfig} from "../types.js"; import {chainConfig as mainnet} from "../presets/mainnet.js"; -/* eslint-disable max-len */ - // Ropsten beacon chain config: // https://github.com/eth-clients/merge-testnets/blob/main/sepolia-beacon-chain/config.yaml @@ -29,6 +27,9 @@ export const sepoliaChainConfig: IChainConfig = { BELLATRIX_FORK_VERSION: b("0x90000071"), BELLATRIX_FORK_EPOCH: 100, TERMINAL_TOTAL_DIFFICULTY: BigInt("17000000000000000"), + // Capella + CAPELLA_FORK_VERSION: b("0x90000072"), + CAPELLA_FORK_EPOCH: 56832, // Deposit contract // --------------------------------------------------------------- diff --git a/packages/config/src/chainConfig/presets/mainnet.ts b/packages/config/src/chainConfig/presets/mainnet.ts index 900450e236b8..5881db80b6ce 100644 --- a/packages/config/src/chainConfig/presets/mainnet.ts +++ b/packages/config/src/chainConfig/presets/mainnet.ts @@ -41,7 +41,7 @@ export const chainConfig: IChainConfig = { CAPELLA_FORK_VERSION: b("0x03000000"), CAPELLA_FORK_EPOCH: Infinity, - // EIP-4844 + // DENEB EIP4844_FORK_VERSION: b("0x04000000"), EIP4844_FORK_EPOCH: Infinity, diff --git a/packages/config/src/chainConfig/presets/minimal.ts b/packages/config/src/chainConfig/presets/minimal.ts index f63dad4e00d7..e4281b20e900 100644 --- a/packages/config/src/chainConfig/presets/minimal.ts +++ b/packages/config/src/chainConfig/presets/minimal.ts @@ -39,7 +39,7 @@ export const chainConfig: IChainConfig = { // Capella CAPELLA_FORK_VERSION: b("0x03000001"), CAPELLA_FORK_EPOCH: Infinity, - // EIP-4844 + // Deneb EIP4844_FORK_VERSION: b("0x04000001"), EIP4844_FORK_EPOCH: Infinity, diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index 84430e18abe6..b17e0fa01556 100644 --- a/packages/config/src/chainConfig/types.ts +++ b/packages/config/src/chainConfig/types.ts @@ -37,7 +37,7 @@ export type IChainConfig = { // Capella CAPELLA_FORK_VERSION: Uint8Array; CAPELLA_FORK_EPOCH: number; - // EIP-4844 + // DENEB EIP4844_FORK_VERSION: Uint8Array; EIP4844_FORK_EPOCH: number; @@ -63,7 +63,7 @@ export type IChainConfig = { DEPOSIT_NETWORK_ID: number; DEPOSIT_CONTRACT_ADDRESS: Uint8Array; - // EIP-4844 + // DENEB // https://github.com/ethereum/consensus-specs/blob/11a037fd9227e29ee809c9397b09f8cc3383a8c0/specs/eip4844/p2p-interface.md#configuration MAX_REQUEST_BLOBS_SIDECARS: number; /** The minimum epoch range over which a node must serve blobs sidecars */ @@ -95,7 +95,7 @@ export const chainConfigTypes: SpecTypes = { // Capella CAPELLA_FORK_VERSION: "bytes", CAPELLA_FORK_EPOCH: "number", - // EIP-4844 + // DENEB EIP4844_FORK_VERSION: "bytes", EIP4844_FORK_EPOCH: "number", diff --git a/packages/config/src/forkConfig/index.ts b/packages/config/src/forkConfig/index.ts index a75e45728fdf..38d72d1c1d02 100644 --- a/packages/config/src/forkConfig/index.ts +++ b/packages/config/src/forkConfig/index.ts @@ -1,4 +1,12 @@ -import {GENESIS_EPOCH, ForkName, SLOTS_PER_EPOCH, ForkSeq, isForkExecution, isForkBlobs} from "@lodestar/params"; +import { + GENESIS_EPOCH, + ForkName, + SLOTS_PER_EPOCH, + ForkSeq, + isForkLightClient, + isForkExecution, + isForkBlobs, +} from "@lodestar/params"; import {Slot, allForks, Version, ssz} from "@lodestar/types"; import {IChainConfig} from "../chainConfig/index.js"; import {IForkConfig, IForkInfo} from "./types.js"; @@ -39,9 +47,9 @@ export function createIForkConfig(config: IChainConfig): IForkConfig { prevVersion: config.BELLATRIX_FORK_VERSION, prevForkName: ForkName.bellatrix, }; - const eip4844: IForkInfo = { - name: ForkName.eip4844, - seq: ForkSeq.eip4844, + const deneb: IForkInfo = { + name: ForkName.deneb, + seq: ForkSeq.deneb, epoch: config.EIP4844_FORK_EPOCH, version: config.EIP4844_FORK_VERSION, prevVersion: config.CAPELLA_FORK_VERSION, @@ -50,7 +58,7 @@ export function createIForkConfig(config: IChainConfig): IForkConfig { /** Forks in order order of occurence, `phase0` first */ // Note: Downstream code relies on proper ordering. - const forks = {phase0, altair, bellatrix, capella, eip4844}; + const forks = {phase0, altair, bellatrix, capella, deneb}; // Prevents allocating an array on every getForkInfo() call const forksAscendingEpochOrder = Object.values(forks); @@ -96,6 +104,13 @@ export function createIForkConfig(config: IChainConfig): IForkConfig { } return ssz.allForksBlinded[forkName] as allForks.AllForksBlindedSSZTypes; }, + getLightClientForkTypes(slot: Slot): allForks.AllForksLightClientSSZTypes { + 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; + }, getBlobsForkTypes(slot: Slot): allForks.AllForksBlobsSSZTypes { const forkName = this.getForkName(slot); if (!isForkBlobs(forkName)) { diff --git a/packages/config/src/forkConfig/types.ts b/packages/config/src/forkConfig/types.ts index 4be1ada8befb..5619ee0b3cc3 100644 --- a/packages/config/src/forkConfig/types.ts +++ b/packages/config/src/forkConfig/types.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import {ForkName, ForkSeq} from "@lodestar/params"; import {allForks, Epoch, Slot, Version} from "@lodestar/types"; @@ -31,10 +30,12 @@ export interface IForkConfig { getForkVersion(slot: Slot): Version; /** Get SSZ types by hard-fork */ getForkTypes(slot: Slot): allForks.AllForksSSZTypes; - /** Get execution SSZ tyoes by hard-fork*/ + /** Get lightclient SSZ types by hard-fork*/ + getLightClientForkTypes(slot: Slot): allForks.AllForksLightClientSSZTypes; + /** Get execution SSZ types by hard-fork*/ getExecutionForkTypes(slot: Slot): allForks.AllForksExecutionSSZTypes; /** Get blinded SSZ types by hard-fork */ getBlindedForkTypes(slot: Slot): allForks.AllForksBlindedSSZTypes; - /** Get blobs SSZ tyoes by hard-fork*/ + /** Get blobs SSZ types by hard-fork*/ getBlobsForkTypes(slot: Slot): allForks.AllForksBlobsSSZTypes; } diff --git a/packages/db/package.json b/packages/db/package.json index 31dbb064a080..32e8aeeb86f6 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@lodestar/db", - "version": "1.4.3", + "version": "1.5.0", "description": "DB modules of Lodestar", "author": "ChainSafe Systems", "homepage": "https://github.com/ChainSafe/lodestar#readme", @@ -39,8 +39,8 @@ }, "dependencies": { "@chainsafe/ssz": "^0.9.2", - "@lodestar/config": "^1.4.3", - "@lodestar/utils": "^1.4.3", + "@lodestar/config": "^1.5.0", + "@lodestar/utils": "^1.5.0", "@types/levelup": "^4.3.3", "it-all": "^2.0.0", "level": "^8.0.0" diff --git a/packages/db/src/controller/level.ts b/packages/db/src/controller/level.ts index 714b3d607c0f..5b980071fd50 100644 --- a/packages/db/src/controller/level.ts +++ b/packages/db/src/controller/level.ts @@ -1,5 +1,4 @@ import {Level} from "level"; -// eslint-disable-next-line import/no-extraneous-dependencies import type {ClassicLevel} from "classic-level"; import {DbReqOpts, IDatabaseController, IDatabaseOptions, IFilterOptions, IKeyValue} from "./interface.js"; import {ILevelDbControllerMetrics} from "./metrics.js"; diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts index 72ba515bbab6..bb42cb4a4786 100644 --- a/packages/db/src/schema.ts +++ b/packages/db/src/schema.ts @@ -47,8 +47,8 @@ export enum Bucket { index_stateArchiveRootIndex = 26, // State Root -> slot - allForks_blobsSidecar = 27, // EIP-4844 BeaconBlockRoot -> BlobsSidecar - allForks_blobsSidecarArchive = 28, // EIP-4844 BeaconBlockSlot -> BlobsSidecar + allForks_blobsSidecar = 27, // DENEB BeaconBlockRoot -> BlobsSidecar + allForks_blobsSidecarArchive = 28, // DENEB BeaconBlockSlot -> BlobsSidecar // Lightclient server // altair_bestUpdatePerCommitteePeriod = 30, // DEPRECATED on v0.32.0 @@ -65,7 +65,8 @@ export enum Bucket { // TODO: May be redundant to block stores lightClient_checkpointHeader = 53, // BlockRoot -> phase0.BeaconBlockHeader // 54 was for bestPartialLightClientUpdate, allocate a fresh one - lightClient_bestLightClientUpdate = 55, // SyncPeriod -> LightClientUpdate + // lightClient_bestLightClientUpdate = 55, // SyncPeriod -> LightClientUpdate // DEPRECATED on v1.5.0 + lightClient_bestLightClientUpdate = 56, // SyncPeriod -> [Slot, LightClientUpdate] validator_metaData = 41, diff --git a/packages/flare/package.json b/packages/flare/package.json index 3d0d51f897ac..09e1bc191683 100644 --- a/packages/flare/package.json +++ b/packages/flare/package.json @@ -1,6 +1,6 @@ { "name": "@lodestar/flare", - "version": "1.4.3", + "version": "1.5.0", "description": "Beacon chain debugging tool", "author": "ChainSafe Systems", "license": "Apache-2.0", @@ -59,10 +59,10 @@ "blockchain" ], "dependencies": { - "@lodestar/api": "^1.4.3", - "@lodestar/config": "^1.4.3", - "@lodestar/state-transition": "^1.4.3", - "@lodestar/types": "^1.4.3", + "@lodestar/api": "^1.5.0", + "@lodestar/config": "^1.5.0", + "@lodestar/state-transition": "^1.5.0", + "@lodestar/types": "^1.5.0", "source-map-support": "^0.5.19", "yargs": "^16.1.0" }, diff --git a/packages/fork-choice/package.json b/packages/fork-choice/package.json index 82f8f7670d3e..4aead6e4f6e9 100644 --- a/packages/fork-choice/package.json +++ b/packages/fork-choice/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.4.3", + "version": "1.5.0", "type": "module", "exports": "./lib/index.js", "types": "./lib/index.d.ts", @@ -40,11 +40,11 @@ }, "dependencies": { "@chainsafe/ssz": "^0.9.2", - "@lodestar/config": "^1.4.3", - "@lodestar/params": "^1.4.3", - "@lodestar/state-transition": "^1.4.3", - "@lodestar/types": "^1.4.3", - "@lodestar/utils": "^1.4.3" + "@lodestar/config": "^1.5.0", + "@lodestar/params": "^1.5.0", + "@lodestar/state-transition": "^1.5.0", + "@lodestar/types": "^1.5.0", + "@lodestar/utils": "^1.5.0" }, "keywords": [ "ethereum", diff --git a/packages/fork-choice/src/forkChoice/forkChoice.ts b/packages/fork-choice/src/forkChoice/forkChoice.ts index 9cdb41d1a4ce..1e18c99400d1 100644 --- a/packages/fork-choice/src/forkChoice/forkChoice.ts +++ b/packages/fork-choice/src/forkChoice/forkChoice.ts @@ -46,8 +46,6 @@ import { } from "./interface.js"; import {IForkChoiceStore, CheckpointWithHex, toCheckpointWithHex, JustifiedBalances} from "./store.js"; -/* eslint-disable max-len */ - export type ForkChoiceOpts = { proposerBoostEnabled?: boolean; computeUnrealized?: boolean; diff --git a/packages/fork-choice/src/forkChoice/store.ts b/packages/fork-choice/src/forkChoice/store.ts index 322f45982b11..16666c352360 100644 --- a/packages/fork-choice/src/forkChoice/store.ts +++ b/packages/fork-choice/src/forkChoice/store.ts @@ -46,8 +46,6 @@ export interface IForkChoiceStore { equivocatingIndices: Set; } -/* eslint-disable @typescript-eslint/naming-convention, @typescript-eslint/member-ordering */ - /** * IForkChoiceStore implementer which emits forkChoice events on updated justified and finalized checkpoints. */ diff --git a/packages/light-client/package.json b/packages/light-client/package.json index b074b760b120..f62c02da5901 100644 --- a/packages/light-client/package.json +++ b/packages/light-client/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.4.3", + "version": "1.5.0", "type": "module", "exports": { ".": { @@ -66,11 +66,11 @@ "@chainsafe/bls": "7.1.1", "@chainsafe/persistent-merkle-tree": "^0.4.2", "@chainsafe/ssz": "^0.9.2", - "@lodestar/api": "^1.4.3", - "@lodestar/config": "^1.4.3", - "@lodestar/params": "^1.4.3", - "@lodestar/types": "^1.4.3", - "@lodestar/utils": "^1.4.3", + "@lodestar/api": "^1.5.0", + "@lodestar/config": "^1.5.0", + "@lodestar/params": "^1.5.0", + "@lodestar/types": "^1.5.0", + "@lodestar/utils": "^1.5.0", "cross-fetch": "^3.1.4", "mitt": "^3.0.0", "strict-event-emitter-types": "^2.0.0" diff --git a/packages/light-client/src/events.ts b/packages/light-client/src/events.ts index 42d701a3e3ea..3bc500f461d1 100644 --- a/packages/light-client/src/events.ts +++ b/packages/light-client/src/events.ts @@ -1,4 +1,4 @@ -import {altair} from "@lodestar/types"; +import {allForks} from "@lodestar/types"; export enum LightclientEvent { lightClientOptimisticUpdate = "light_client_optimistic_update", @@ -6,8 +6,8 @@ export enum LightclientEvent { } export type LightclientEmitterEvents = { - [LightclientEvent.lightClientOptimisticUpdate]: (newHeader: altair.LightClientHeader) => void; - [LightclientEvent.lightClientFinalityUpdate]: (newHeader: altair.LightClientHeader) => void; + [LightclientEvent.lightClientOptimisticUpdate]: (newHeader: allForks.LightClientHeader) => void; + [LightclientEvent.lightClientFinalityUpdate]: (newHeader: allForks.LightClientHeader) => void; }; export type LightclientEmitter = MittEmitter; diff --git a/packages/light-client/src/index.ts b/packages/light-client/src/index.ts index e64ab517242d..295864c5bab2 100644 --- a/packages/light-client/src/index.ts +++ b/packages/light-client/src/index.ts @@ -1,7 +1,7 @@ import mitt from "mitt"; import {init as initBls} from "@chainsafe/bls/switchable"; import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD} from "@lodestar/params"; -import {altair, phase0, RootHex, Slot, SyncPeriod} from "@lodestar/types"; +import {phase0, RootHex, Slot, SyncPeriod, allForks} from "@lodestar/types"; import {createIBeaconConfig, IBeaconConfig, IChainForkConfig} from "@lodestar/config"; import {isErrorAborted, sleep} from "@lodestar/utils"; import {fromHexString, toHexString} from "@chainsafe/ssz"; @@ -33,7 +33,7 @@ export type LightclientInitArgs = { opts?: LightclientOpts; genesisData: GenesisData; transport: LightClientTransport; - bootstrap: altair.LightClientBootstrap; + bootstrap: allForks.LightClientBootstrap; }; /** Provides some protection against a server client sending header updates too far away in the future */ @@ -172,7 +172,7 @@ export class Lightclient { this.status = {code: RunStatusCode.stopped}; } - getHead(): altair.LightClientHeader { + getHead(): allForks.LightClientHeader { return this.lightclientSpec.store.optimisticHeader; } @@ -286,7 +286,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: altair.LightClientOptimisticUpdate): void { + private processOptimisticUpdate(optimisticUpdate: allForks.LightClientOptimisticUpdate): void { this.lightclientSpec.onOptimisticUpdate(this.currentSlotWithTolerance(), optimisticUpdate); } @@ -294,11 +294,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: altair.LightClientFinalityUpdate): void { + private processFinalizedUpdate(finalizedUpdate: allForks.LightClientFinalityUpdate): void { this.lightclientSpec.onFinalityUpdate(this.currentSlotWithTolerance(), finalizedUpdate); } - private processSyncCommitteeUpdate(update: altair.LightClientUpdate): void { + private processSyncCommitteeUpdate(update: allForks.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 ea933665256f..9123101f5bfc 100644 --- a/packages/light-client/src/spec/index.ts +++ b/packages/light-client/src/spec/index.ts @@ -1,12 +1,13 @@ import {IBeaconConfig} from "@lodestar/config"; import {UPDATE_TIMEOUT} from "@lodestar/params"; -import {altair, Slot} from "@lodestar/types"; +import {Slot, allForks} from "@lodestar/types"; import {computeSyncPeriodAtSlot} from "../utils/index.js"; import {getSyncCommitteeAtPeriod, processLightClientUpdate, ProcessUpdateOpts} from "./processLightClientUpdate.js"; import {ILightClientStore, LightClientStore, LightClientStoreEvents} from "./store.js"; import {ZERO_FINALITY_BRANCH, ZERO_HEADER, ZERO_NEXT_SYNC_COMMITTEE_BRANCH, ZERO_SYNC_COMMITTEE} from "./utils.js"; export {isBetterUpdate, toLightClientUpdateSummary, LightClientUpdateSummary} from "./isBetterUpdate.js"; +export {upgradeLightClientHeader} from "./utils.js"; export class LightclientSpec { readonly store: ILightClientStore; @@ -14,16 +15,16 @@ export class LightclientSpec { constructor( config: IBeaconConfig, private readonly opts: ProcessUpdateOpts & LightClientStoreEvents, - bootstrap: altair.LightClientBootstrap + bootstrap: allForks.LightClientBootstrap ) { this.store = new LightClientStore(config, bootstrap, opts); } - onUpdate(currentSlot: Slot, update: altair.LightClientUpdate): void { + onUpdate(currentSlot: Slot, update: allForks.LightClientUpdate): void { processLightClientUpdate(this.store, currentSlot, this.opts, update); } - onFinalityUpdate(currentSlot: Slot, finalityUpdate: altair.LightClientFinalityUpdate): void { + onFinalityUpdate(currentSlot: Slot, finalityUpdate: allForks.LightClientFinalityUpdate): void { this.onUpdate(currentSlot, { attestedHeader: finalityUpdate.attestedHeader, nextSyncCommittee: ZERO_SYNC_COMMITTEE, @@ -35,7 +36,7 @@ export class LightclientSpec { }); } - onOptimisticUpdate(currentSlot: Slot, optimisticUpdate: altair.LightClientOptimisticUpdate): void { + onOptimisticUpdate(currentSlot: Slot, optimisticUpdate: allForks.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 a0f537327328..e149386cf97f 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 {altair, Slot} from "@lodestar/types"; +import {Slot, allForks} 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: altair.LightClientUpdate): LightClientUpdateSummary { +export function toLightClientUpdateSummary(update: allForks.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 5ca212385a06..9ac0beb023ec 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 {altair, Slot, SyncPeriod} from "@lodestar/types"; +import {Slot, SyncPeriod, allForks} from "@lodestar/types"; import {pruneSetToMax} from "@lodestar/utils"; import {computeSyncPeriodAtSlot, deserializeSyncCommittee, sumBits} from "../utils/index.js"; import {isBetterUpdate, LightClientUpdateSummary, toLightClientUpdateSummary} from "./isBetterUpdate.js"; @@ -16,7 +16,7 @@ export function processLightClientUpdate( store: ILightClientStore, currentSlot: Slot, opts: ProcessUpdateOpts, - update: altair.LightClientUpdate + update: allForks.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 36a35debd9fd..6cd88a485d87 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 {IBeaconConfig} from "@lodestar/config"; -import {altair, SyncPeriod} from "@lodestar/types"; +import {SyncPeriod, allForks} 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: altair.LightClientHeader; + finalizedHeader: allForks.LightClientHeader; // Most recent available reasonably-safe header - optimisticHeader: altair.LightClientHeader; + optimisticHeader: allForks.LightClientHeader; } export interface LightClientStoreEvents { - onSetFinalizedHeader?: (header: altair.LightClientHeader) => void; - onSetOptimisticHeader?: (header: altair.LightClientHeader) => void; + onSetFinalizedHeader?: (header: allForks.LightClientHeader) => void; + onSetOptimisticHeader?: (header: allForks.LightClientHeader) => void; } export class LightClientStore implements ILightClientStore { readonly syncCommittees = new Map(); readonly bestValidUpdates = new Map(); - private _finalizedHeader: altair.LightClientHeader; - private _optimisticHeader: altair.LightClientHeader; + private _finalizedHeader: allForks.LightClientHeader; + private _optimisticHeader: allForks.LightClientHeader; private readonly maxActiveParticipants = new Map(); constructor( readonly config: IBeaconConfig, - bootstrap: altair.LightClientBootstrap, + bootstrap: allForks.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(): altair.LightClientHeader { + get finalizedHeader(): allForks.LightClientHeader { return this._finalizedHeader; } - set finalizedHeader(value: altair.LightClientHeader) { + set finalizedHeader(value: allForks.LightClientHeader) { this._finalizedHeader = value; this.events.onSetFinalizedHeader?.(value); } - get optimisticHeader(): altair.LightClientHeader { + get optimisticHeader(): allForks.LightClientHeader { return this._optimisticHeader; } - set optimisticHeader(value: altair.LightClientHeader) { + set optimisticHeader(value: allForks.LightClientHeader) { this._optimisticHeader = value; this.events.onSetOptimisticHeader?.(value); } @@ -95,7 +95,7 @@ export type SyncCommitteeFast = { }; export type LightClientUpdateWithSummary = { - update: altair.LightClientUpdate; + update: allForks.LightClientUpdate; summary: LightClientUpdateSummary; }; diff --git a/packages/light-client/src/spec/utils.ts b/packages/light-client/src/spec/utils.ts index b9ec4a7ee7cf..04c17b7edc0a 100644 --- a/packages/light-client/src/spec/utils.ts +++ b/packages/light-client/src/spec/utils.ts @@ -1,6 +1,7 @@ import {BitArray, byteArrayEquals} from "@chainsafe/ssz"; -import {FINALIZED_ROOT_DEPTH, NEXT_SYNC_COMMITTEE_DEPTH} from "@lodestar/params"; -import {altair, phase0, ssz} from "@lodestar/types"; +import {FINALIZED_ROOT_DEPTH, NEXT_SYNC_COMMITTEE_DEPTH, ForkSeq, ForkName} from "@lodestar/params"; +import {altair, phase0, ssz, allForks, capella, deneb} from "@lodestar/types"; +import {IChainForkConfig} from "@lodestar/config"; export const GENESIS_SLOT = 0; export const ZERO_HASH = new Uint8Array(32); @@ -20,7 +21,7 @@ export function getSafetyThreshold(maxActiveParticipants: number): number { return Math.floor(maxActiveParticipants / SAFETY_THRESHOLD_FACTOR); } -export function isSyncCommitteeUpdate(update: altair.LightClientUpdate): boolean { +export function isSyncCommitteeUpdate(update: allForks.LightClientUpdate): boolean { return ( // Fast return for when constructing full LightClientUpdate from partial updates update.nextSyncCommitteeBranch !== ZERO_NEXT_SYNC_COMMITTEE_BRANCH && @@ -28,7 +29,7 @@ export function isSyncCommitteeUpdate(update: altair.LightClientUpdate): boolean ); } -export function isFinalityUpdate(update: altair.LightClientUpdate): boolean { +export function isFinalityUpdate(update: allForks.LightClientUpdate): boolean { return ( // Fast return for when constructing full LightClientUpdate from partial updates update.finalityBranch !== ZERO_FINALITY_BRANCH && @@ -45,3 +46,39 @@ export function isZeroedSyncCommittee(syncCommittee: altair.SyncCommittee): bool // Fast return for when constructing full LightClientUpdate from partial updates return syncCommittee === ZERO_SYNC_COMMITTEE || byteArrayEquals(syncCommittee.pubkeys[0], ZERO_PUBKEY); } + +export function upgradeLightClientHeader( + config: IChainForkConfig, + targetFork: ForkName, + header: altair.LightClientHeader +): allForks.LightClientHeader { + const upgradedHeader = header; + + const headerFork = config.getForkName(header.beacon.slot); + switch (headerFork) { + case ForkName.phase0: + throw Error(`Invalid target fork=${headerFork} for LightClientHeader`); + + case ForkName.altair: + case ForkName.bellatrix: + // Break if no further upgradation is required else fall through + if (ForkSeq[targetFork] <= ForkSeq.bellatrix) break; + // eslint-disable-next-line no-fallthrough + + case ForkName.capella: + (upgradedHeader as capella.LightClientHeader).execution = ssz.capella.LightClientHeader.fields.execution.defaultValue(); + (upgradedHeader as capella.LightClientHeader).executionBranch = ssz.capella.LightClientHeader.fields.executionBranch.defaultValue(); + + // Break if no further upgradation is required else fall through + if (ForkSeq[targetFork] <= ForkSeq.capella) break; + // eslint-disable-next-line no-fallthrough + + case ForkName.deneb: + (upgradedHeader as deneb.LightClientHeader).execution.excessDataGas = ssz.deneb.LightClientHeader.fields.execution.fields.excessDataGas.defaultValue(); + + // Break if no further upgradation is required else fall through + if (ForkSeq[targetFork] <= ForkSeq.deneb) break; + // eslint-disable-next-line no-fallthrough + } + return upgradedHeader; +} diff --git a/packages/light-client/src/spec/validateLightClientBootstrap.ts b/packages/light-client/src/spec/validateLightClientBootstrap.ts index af25b0414596..e1334d82800c 100644 --- a/packages/light-client/src/spec/validateLightClientBootstrap.ts +++ b/packages/light-client/src/spec/validateLightClientBootstrap.ts @@ -1,12 +1,12 @@ import {byteArrayEquals} from "@chainsafe/ssz"; -import {altair, Root, ssz} from "@lodestar/types"; +import {Root, ssz, allForks} from "@lodestar/types"; import {toHex} from "@lodestar/utils"; import {isValidMerkleBranch} from "../utils/verifyMerkleBranch.js"; const CURRENT_SYNC_COMMITTEE_INDEX = 22; const CURRENT_SYNC_COMMITTEE_DEPTH = 5; -export function validateLightClientBootstrap(trustedBlockRoot: Root, bootstrap: altair.LightClientBootstrap): void { +export function validateLightClientBootstrap(trustedBlockRoot: Root, bootstrap: allForks.LightClientBootstrap): void { const headerRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(bootstrap.header.beacon); if (!byteArrayEquals(headerRoot, trustedBlockRoot)) { throw Error(`bootstrap header root ${toHex(headerRoot)} != trusted root ${toHex(trustedBlockRoot)}`); diff --git a/packages/light-client/src/spec/validateLightClientUpdate.ts b/packages/light-client/src/spec/validateLightClientUpdate.ts index cb45a9ca5550..1599d988fb38 100644 --- a/packages/light-client/src/spec/validateLightClientUpdate.ts +++ b/packages/light-client/src/spec/validateLightClientUpdate.ts @@ -1,4 +1,4 @@ -import {altair, Root, ssz} from "@lodestar/types"; +import {Root, ssz, allForks} from "@lodestar/types"; import bls from "@chainsafe/bls/switchable"; import type {PublicKey, Signature} from "@chainsafe/bls/types"; import { @@ -18,7 +18,7 @@ import {ILightClientStore} from "./store.js"; export function validateLightClientUpdate( store: ILightClientStore, - update: altair.LightClientUpdate, + update: allForks.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 cc3038f57e3f..dcd3210df70c 100644 --- a/packages/light-client/src/transport/interface.ts +++ b/packages/light-client/src/transport/interface.ts @@ -1,4 +1,4 @@ -import {altair, SyncPeriod} from "@lodestar/types"; +import {allForks, SyncPeriod} from "@lodestar/types"; import {ForkName} from "@lodestar/params"; export interface LightClientTransport { @@ -8,24 +8,24 @@ export interface LightClientTransport { ): Promise< { version: ForkName; - data: altair.LightClientUpdate; + data: allForks.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: altair.LightClientOptimisticUpdate}>; - getFinalityUpdate(): Promise<{version: ForkName; data: altair.LightClientFinalityUpdate}>; + getOptimisticUpdate(): Promise<{version: ForkName; data: allForks.LightClientOptimisticUpdate}>; + getFinalityUpdate(): Promise<{version: ForkName; data: allForks.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: altair.LightClientBootstrap}>; + getBootstrap(blockRoot: string): Promise<{version: ForkName; data: allForks.LightClientBootstrap}>; // registers handler for LightClientOptimisticUpdate. This can come either via sse or p2p - onOptimisticUpdate(handler: (optimisticUpdate: altair.LightClientOptimisticUpdate) => void): void; + onOptimisticUpdate(handler: (optimisticUpdate: allForks.LightClientOptimisticUpdate) => void): void; // registers handler for LightClientFinalityUpdate. This can come either via sse or p2p - onFinalityUpdate(handler: (finalityUpdate: altair.LightClientFinalityUpdate) => void): void; + onFinalityUpdate(handler: (finalityUpdate: allForks.LightClientFinalityUpdate) => void): void; } diff --git a/packages/light-client/src/transport/rest.ts b/packages/light-client/src/transport/rest.ts index da65def2766c..2ac3cbc79ac0 100644 --- a/packages/light-client/src/transport/rest.ts +++ b/packages/light-client/src/transport/rest.ts @@ -1,13 +1,13 @@ import EventEmitter from "events"; import StrictEventEmitter from "strict-event-emitter-types"; -import {allForks, altair, SyncPeriod} from "@lodestar/types"; +import {allForks, SyncPeriod} from "@lodestar/types"; import {Api, ApiError, routes} from "@lodestar/api"; import {ForkName} from "@lodestar/params"; import {LightClientTransport} from "./interface.js"; export type LightClientRestEvents = { - [routes.events.EventType.lightClientFinalityUpdate]: altair.LightClientFinalityUpdate; - [routes.events.EventType.lightClientOptimisticUpdate]: altair.LightClientOptimisticUpdate; + [routes.events.EventType.lightClientFinalityUpdate]: allForks.LightClientFinalityUpdate; + [routes.events.EventType.lightClientOptimisticUpdate]: allForks.LightClientOptimisticUpdate; }; type RestEvents = StrictEventEmitter; @@ -27,7 +27,7 @@ export class LightClientRestTransport extends (EventEmitter as {new (): RestEven ): Promise< { version: ForkName; - data: altair.LightClientUpdate; + data: allForks.LightClientUpdate; }[] > { const res = await this.api.lightclient.getUpdates(startPeriod, count); @@ -35,19 +35,19 @@ export class LightClientRestTransport extends (EventEmitter as {new (): RestEven return res.response; } - async getOptimisticUpdate(): Promise<{version: ForkName; data: altair.LightClientOptimisticUpdate}> { + async getOptimisticUpdate(): Promise<{version: ForkName; data: allForks.LightClientOptimisticUpdate}> { const res = await this.api.lightclient.getOptimisticUpdate(); ApiError.assert(res); return res.response; } - async getFinalityUpdate(): Promise<{version: ForkName; data: altair.LightClientFinalityUpdate}> { + async getFinalityUpdate(): Promise<{version: ForkName; data: allForks.LightClientFinalityUpdate}> { const res = await this.api.lightclient.getFinalityUpdate(); ApiError.assert(res); return res.response; } - async getBootstrap(blockRoot: string): Promise<{version: ForkName; data: altair.LightClientBootstrap}> { + async getBootstrap(blockRoot: string): Promise<{version: ForkName; data: allForks.LightClientBootstrap}> { const res = await this.api.lightclient.getBootstrap(blockRoot); ApiError.assert(res); return res.response; @@ -59,12 +59,12 @@ export class LightClientRestTransport extends (EventEmitter as {new (): RestEven return res.response; } - onOptimisticUpdate(handler: (optimisticUpdate: altair.LightClientOptimisticUpdate) => void): void { + onOptimisticUpdate(handler: (optimisticUpdate: allForks.LightClientOptimisticUpdate) => void): void { this.subscribeEventstream(); this.eventEmitter.on(routes.events.EventType.lightClientOptimisticUpdate, handler); } - onFinalityUpdate(handler: (finalityUpdate: altair.LightClientFinalityUpdate) => void): void { + onFinalityUpdate(handler: (finalityUpdate: allForks.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 21a4a5a926bb..6e6126f8a481 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 {altair, SyncPeriod} from "@lodestar/types"; +import {SyncPeriod, allForks} from "@lodestar/types"; export type LightClientStoreFast = { snapshot: LightClientSnapshotFast; - bestUpdates: Map; + bestUpdates: Map; }; export type LightClientSnapshotFast = { /** Beacon block header */ - header: altair.LightClientHeader; + header: allForks.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 d42ab5e6c8ae..7b4c67392f5a 100644 --- a/packages/light-client/src/validation.ts +++ b/packages/light-client/src/validation.ts @@ -1,4 +1,4 @@ -import {altair, Root, Slot, ssz} from "@lodestar/types"; +import {altair, Root, Slot, ssz, allForks} from "@lodestar/types"; import bls from "@chainsafe/bls/switchable"; import type {PublicKey, Signature} from "@chainsafe/bls/types"; import { @@ -24,7 +24,7 @@ import {computeSyncPeriodAtSlot} from "./utils/clock.js"; export function assertValidLightClientUpdate( config: IBeaconConfig, syncCommittee: SyncCommitteeFast, - update: altair.LightClientUpdate + update: allForks.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: altair.LightClientFinalityUpdate): void { +export function assertValidFinalityProof(update: allForks.LightClientFinalityUpdate): void { if ( !isValidMerkleBranch( ssz.phase0.BeaconBlockHeader.hashTreeRoot(update.finalizedHeader.beacon), @@ -94,7 +94,7 @@ export function assertValidFinalityProof(update: altair.LightClientFinalityUpdat * * Where `hashTreeRoot(state) == update.header.stateRoot` */ -export function assertValidSyncCommitteeProof(update: altair.LightClientUpdate): void { +export function assertValidSyncCommitteeProof(update: allForks.LightClientUpdate): void { if ( !isValidMerkleBranch( ssz.altair.SyncCommittee.hashTreeRoot(update.nextSyncCommittee), diff --git a/packages/light-client/test/mocks/LightclientServerApiMock.ts b/packages/light-client/test/mocks/LightclientServerApiMock.ts index 2d2d4da12b84..de8a3825cfa7 100644 --- a/packages/light-client/test/mocks/LightclientServerApiMock.ts +++ b/packages/light-client/test/mocks/LightclientServerApiMock.ts @@ -50,7 +50,6 @@ export class LightclientServerApiMock implements ServerApi { if (!this.finalized) throw Error("No finalized head update"); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment return {version: ForkName.bellatrix, data: this.finalized}; } diff --git a/packages/light-client/test/unit/sync.node.test.ts b/packages/light-client/test/unit/sync.node.test.ts index c53281cba405..f36ee887bc9b 100644 --- a/packages/light-client/test/unit/sync.node.test.ts +++ b/packages/light-client/test/unit/sync.node.test.ts @@ -82,7 +82,6 @@ describe("sync", () => { // So the first call to getLatestHeadUpdate() doesn't error, store the latest snapshot as latest header update lightclientServerApi.latestHeadUpdate = committeeUpdateToLatestHeadUpdate(lastInMap(lightclientServerApi.updates)); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment lightclientServerApi.finalized = committeeUpdateToLatestFinalizedHeadUpdate( lastInMap(lightclientServerApi.updates), targetSlot diff --git a/packages/light-client/test/utils/utils.ts b/packages/light-client/test/utils/utils.ts index 8fb8a87dace6..8638e6f86180 100644 --- a/packages/light-client/test/utils/utils.ts +++ b/packages/light-client/test/utils/utils.ts @@ -2,7 +2,6 @@ import bls from "@chainsafe/bls/switchable"; import {PointFormat, PublicKey, SecretKey} from "@chainsafe/bls/types"; import {hash, Tree} from "@chainsafe/persistent-merkle-tree"; import {BitArray, fromHexString} from "@chainsafe/ssz"; -import {routes} from "@lodestar/api"; import {IBeaconConfig} from "@lodestar/config"; import { DOMAIN_SYNC_COMMITTEE, @@ -12,7 +11,7 @@ import { SLOTS_PER_EPOCH, SYNC_COMMITTEE_SIZE, } from "@lodestar/params"; -import {altair, phase0, Slot, ssz, SyncPeriod} from "@lodestar/types"; +import {altair, phase0, Slot, ssz, SyncPeriod, allForks} from "@lodestar/types"; import {SyncCommitteeFast} from "../../src/types.js"; import {computeSigningRoot} from "../../src/utils/domain.js"; import {getLcLoggerConsole} from "../../src/utils/logger.js"; @@ -20,8 +19,6 @@ import {getLcLoggerConsole} from "../../src/utils/logger.js"; const CURRENT_SYNC_COMMITTEE_INDEX = 22; const CURRENT_SYNC_COMMITTEE_DEPTH = 5; -/* eslint-disable @typescript-eslint/naming-convention */ - /** * To enable debug logs run with * ``` @@ -161,7 +158,7 @@ export function computeLightclientUpdate(config: IBeaconConfig, period: SyncPeri export function computeLightClientSnapshot( period: SyncPeriod ): { - snapshot: routes.lightclient.LightClientBootstrap; + snapshot: allForks.LightClientBootstrap; checkpointRoot: Uint8Array; } { const currentSyncCommittee = getInteropSyncCommittee(period).syncCommittee; diff --git a/packages/params/package.json b/packages/params/package.json index eb4361599b1c..2eeb37c4bffc 100644 --- a/packages/params/package.json +++ b/packages/params/package.json @@ -1,6 +1,6 @@ { "name": "@lodestar/params", - "version": "1.4.3", + "version": "1.5.0", "description": "Chain parameters required for lodestar", "author": "ChainSafe Systems", "license": "Apache-2.0", diff --git a/packages/params/src/forkName.ts b/packages/params/src/forkName.ts index da6c9f70a1a3..23fe84ea6473 100644 --- a/packages/params/src/forkName.ts +++ b/packages/params/src/forkName.ts @@ -6,7 +6,7 @@ export enum ForkName { altair = "altair", bellatrix = "bellatrix", capella = "capella", - eip4844 = "eip4844", + deneb = "deneb", } /** @@ -17,15 +17,25 @@ export enum ForkSeq { altair = 1, bellatrix = 2, capella = 3, - eip4844 = 4, + deneb = 4, } -export type ForkExecution = Exclude; +export type ForkLightClient = Exclude; +export function isForkLightClient(fork: ForkName): fork is ForkLightClient { + return fork !== ForkName.phase0; +} + +export type ForkExecution = Exclude; export function isForkExecution(fork: ForkName): fork is ForkExecution { - return fork !== ForkName.phase0 && fork !== ForkName.altair; + return isForkLightClient(fork) && fork !== ForkName.altair; +} + +export type ForkWithdrawals = Exclude; +export function isForkWithdrawals(fork: ForkName): fork is ForkWithdrawals { + return isForkExecution(fork) && fork !== ForkName.capella; } export type ForkBlobs = Exclude; export function isForkBlobs(fork: ForkName): fork is ForkBlobs { - return isForkExecution(fork) && fork !== ForkName.bellatrix && fork !== ForkName.capella; + return isForkWithdrawals(fork) && fork !== ForkName.capella; } diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index 4862d73ce280..8809a6b80ada 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -6,7 +6,15 @@ import {presetStatus} from "./presetStatus.js"; import {userSelectedPreset, userOverrides} from "./setPreset.js"; export {BeaconPreset} from "./interface.js"; -export {ForkName, ForkSeq, ForkExecution, ForkBlobs, isForkExecution, isForkBlobs} from "./forkName.js"; +export { + ForkName, + ForkSeq, + ForkExecution, + ForkBlobs, + isForkExecution, + isForkBlobs, + isForkLightClient, +} from "./forkName.js"; export {presetToJson} from "./json.js"; export {PresetName}; @@ -186,6 +194,21 @@ export const FINALIZED_ROOT_GINDEX = 105; */ export const FINALIZED_ROOT_DEPTH = 6; export const FINALIZED_ROOT_INDEX = 41; + +/** + * ```ts + * types.ssz.capella.BeaconBlockBody.getPathInfo(['executionPayload']).gindex + * ``` + */ +export const BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX = 25; +/** + * ```ts + * Math.floor(Math.log2(EXECUTION_PAYLOAD_GINDEX)) + * ``` + */ +export const BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH = 4; +export const BLOCK_BODY_EXECUTION_PAYLOAD_INDEX = 9; + /** * ```ts * config.types.altair.BeaconState.getPathGindex(["nextSyncCommittee"]) diff --git a/packages/params/src/presets/mainnet.ts b/packages/params/src/presets/mainnet.ts index 3fc4cdd3690b..6c09ae2b5f50 100644 --- a/packages/params/src/presets/mainnet.ts +++ b/packages/params/src/presets/mainnet.ts @@ -114,7 +114,7 @@ export const mainnetPreset: BeaconPreset = { MAX_WITHDRAWALS_PER_PAYLOAD: 16, MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: 16384, - // EIP-4844 + // DENEB /////////// // https://github.com/ethereum/consensus-specs/blob/dev/presets/mainnet/eip4844.yaml FIELD_ELEMENTS_PER_BLOB: 4096, diff --git a/packages/params/src/presets/minimal.ts b/packages/params/src/presets/minimal.ts index b9de2107bc88..5e4aca671137 100644 --- a/packages/params/src/presets/minimal.ts +++ b/packages/params/src/presets/minimal.ts @@ -121,7 +121,7 @@ export const minimalPreset: BeaconPreset = { MAX_WITHDRAWALS_PER_PAYLOAD: 4, MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: 16, - // EIP-4844 + // DENEB /////////// // https://github.com/ethereum/consensus-specs/blob/dev/presets/minimal/eip4844.yaml FIELD_ELEMENTS_PER_BLOB: 4, diff --git a/packages/params/test/e2e/ensure-config-is-synced.test.ts b/packages/params/test/e2e/ensure-config-is-synced.test.ts index e27dd13c7d2b..916283984423 100644 --- a/packages/params/test/e2e/ensure-config-is-synced.test.ts +++ b/packages/params/test/e2e/ensure-config-is-synced.test.ts @@ -37,7 +37,10 @@ async function downloadRemoteConfig(preset: "mainnet" | "minimal", commit: strin const downloadedParams = await Promise.all( Object.values(ForkName).map((forkName) => axios({ - url: `https://github.com/raw/ethereum/consensus-specs/${commit}/presets/${preset}/${forkName}.yaml`, + url: `https://github.com/raw/ethereum/consensus-specs/${commit}/presets/${preset}/${ + // TODO eip4844: clean this up when specs are released with deneb + forkName === "deneb" ? "eip4844" : forkName + }.yaml`, timeout: 30 * 1000, }).then((response) => loadConfigYaml(response.data)) ) diff --git a/packages/reqresp/package.json b/packages/reqresp/package.json index 85ad9e944f75..47285775cc94 100644 --- a/packages/reqresp/package.json +++ b/packages/reqresp/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.4.3", + "version": "1.5.0", "type": "module", "exports": { ".": { @@ -59,10 +59,10 @@ "@chainsafe/snappy-stream": "^5.1.2", "@libp2p/interface-connection": "^3.0.2", "@libp2p/interface-peer-id": "^2.0.1", - "@lodestar/config": "^1.4.3", - "@lodestar/params": "^1.4.3", - "@lodestar/types": "^1.4.3", - "@lodestar/utils": "^1.4.3", + "@lodestar/config": "^1.5.0", + "@lodestar/params": "^1.5.0", + "@lodestar/types": "^1.5.0", + "@lodestar/utils": "^1.5.0", "it-all": "^2.0.0", "snappyjs": "^0.7.0", "stream-to-it": "^0.2.0", diff --git a/packages/reqresp/src/encodingStrategies/sszSnappy/encode.ts b/packages/reqresp/src/encodingStrategies/sszSnappy/encode.ts index a8842e5e7979..9334b1ce3ed1 100644 --- a/packages/reqresp/src/encodingStrategies/sszSnappy/encode.ts +++ b/packages/reqresp/src/encodingStrategies/sszSnappy/encode.ts @@ -54,7 +54,6 @@ function serializeSszBody(chunk: EncodedPayload, type: TypeSerializer): case EncodedPayloadType.ssz: { try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any const bytes = type.serialize(chunk.data); return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.length); } catch (e) { diff --git a/packages/reqresp/src/metrics.ts b/packages/reqresp/src/metrics.ts index fc99ef1298f0..6ce90e11322f 100644 --- a/packages/reqresp/src/metrics.ts +++ b/packages/reqresp/src/metrics.ts @@ -60,7 +60,7 @@ export type LodestarGitData = { /** * A collection of metrics used throughout the Gossipsub behaviour. */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function getMetrics(register: MetricsRegister) { // Using function style instead of class to prevent having to re-declare all MetricsPrometheus types. diff --git a/packages/reqresp/src/protocols/BeaconBlockAndBlobsSidecarByRoot.ts b/packages/reqresp/src/protocols/BeaconBlockAndBlobsSidecarByRoot.ts index 57219069eae3..03b8c566bfd5 100644 --- a/packages/reqresp/src/protocols/BeaconBlockAndBlobsSidecarByRoot.ts +++ b/packages/reqresp/src/protocols/BeaconBlockAndBlobsSidecarByRoot.ts @@ -1,20 +1,20 @@ -import {eip4844, ssz} from "@lodestar/types"; +import {deneb, ssz} from "@lodestar/types"; import {toHex} from "@lodestar/utils"; import {ContextBytesType, Encoding, ProtocolDefinitionGenerator} from "../types.js"; // eslint-disable-next-line @typescript-eslint/naming-convention export const BeaconBlockAndBlobsSidecarByRoot: ProtocolDefinitionGenerator< - eip4844.BeaconBlockAndBlobsSidecarByRootRequest, - eip4844.SignedBeaconBlockAndBlobsSidecar + deneb.BeaconBlockAndBlobsSidecarByRootRequest, + deneb.SignedBeaconBlockAndBlobsSidecar > = (modules, handler) => { return { method: "beacon_block_and_blobs_sidecar_by_root", version: 1, encoding: Encoding.SSZ_SNAPPY, handler, - requestType: () => ssz.eip4844.BeaconBlockAndBlobsSidecarByRootRequest, + requestType: () => ssz.deneb.BeaconBlockAndBlobsSidecarByRootRequest, // TODO: Make it fork compliant - responseType: () => ssz.eip4844.SignedBeaconBlockAndBlobsSidecar, + responseType: () => ssz.deneb.SignedBeaconBlockAndBlobsSidecar, renderRequestBody: (req) => req.map((root) => toHex(root)).join(","), contextBytes: { type: ContextBytesType.ForkDigest, @@ -22,7 +22,7 @@ export const BeaconBlockAndBlobsSidecarByRoot: ProtocolDefinitionGenerator< forkFromResponse: ({beaconBlock}) => modules.config.getForkName(beaconBlock.message.slot), }, inboundRateLimits: { - // TODO EIP-4844: For now same value as BeaconBlocksByRoot https://github.com/sigp/lighthouse/blob/bf533c8e42cc73c35730e285c21df8add0195369/beacon_node/lighthouse_network/src/rpc/mod.rs#L118-L130 + // TODO DENEB: For now same value as BeaconBlocksByRoot https://github.com/sigp/lighthouse/blob/bf533c8e42cc73c35730e285c21df8add0195369/beacon_node/lighthouse_network/src/rpc/mod.rs#L118-L130 byPeer: {quota: 128, quotaTimeMs: 10_000}, getRequestCount: (req) => req.length, }, diff --git a/packages/reqresp/src/protocols/BeaconBlocksByRange.ts b/packages/reqresp/src/protocols/BeaconBlocksByRange.ts index 8c0a08a9e772..82ccb285084a 100644 --- a/packages/reqresp/src/protocols/BeaconBlocksByRange.ts +++ b/packages/reqresp/src/protocols/BeaconBlocksByRange.ts @@ -18,7 +18,6 @@ const BeaconBlocksByRangeCommon: Pick< }, }; -// eslint-disable-next-line @typescript-eslint/naming-convention export const BeaconBlocksByRange: ProtocolDefinitionGenerator< phase0.BeaconBlocksByRangeRequest, allForks.SignedBeaconBlock @@ -32,7 +31,6 @@ export const BeaconBlocksByRange: ProtocolDefinitionGenerator< }; }; -// eslint-disable-next-line @typescript-eslint/naming-convention export const BeaconBlocksByRangeV2: ProtocolDefinitionGenerator< phase0.BeaconBlocksByRangeRequest, allForks.SignedBeaconBlock diff --git a/packages/reqresp/src/protocols/BlobsSidecarsByRange.ts b/packages/reqresp/src/protocols/BlobsSidecarsByRange.ts index a2e8ca5dbc55..36233885fe2d 100644 --- a/packages/reqresp/src/protocols/BlobsSidecarsByRange.ts +++ b/packages/reqresp/src/protocols/BlobsSidecarsByRange.ts @@ -1,20 +1,20 @@ import {MAX_REQUEST_BLOCKS} from "@lodestar/params"; -import {eip4844, ssz} from "@lodestar/types"; +import {deneb, ssz} from "@lodestar/types"; import {ContextBytesType, Encoding, ProtocolDefinitionGenerator} from "../types.js"; // eslint-disable-next-line @typescript-eslint/naming-convention export const BlobsSidecarsByRange: ProtocolDefinitionGenerator< - eip4844.BlobsSidecarsByRangeRequest, - eip4844.BlobsSidecar + deneb.BlobsSidecarsByRangeRequest, + deneb.BlobsSidecar > = (modules, handler) => { return { method: "blobs_sidecars_by_range", version: 1, encoding: Encoding.SSZ_SNAPPY, handler, - requestType: () => ssz.eip4844.BlobsSidecarsByRangeRequest, + requestType: () => ssz.deneb.BlobsSidecarsByRangeRequest, // TODO: Make it fork compliant - responseType: () => ssz.eip4844.BlobsSidecar, + responseType: () => ssz.deneb.BlobsSidecar, renderRequestBody: (req) => `${req.startSlot},${req.count}`, contextBytes: { type: ContextBytesType.ForkDigest, @@ -22,7 +22,7 @@ export const BlobsSidecarsByRange: ProtocolDefinitionGenerator< forkFromResponse: (blobsSidecar) => modules.config.getForkName(blobsSidecar.beaconBlockSlot), }, inboundRateLimits: { - // TODO EIP-4844: For now same value as BeaconBlocksByRange https://github.com/sigp/lighthouse/blob/bf533c8e42cc73c35730e285c21df8add0195369/beacon_node/lighthouse_network/src/rpc/mod.rs#L118-L130 + // TODO DENEB: For now same value as BeaconBlocksByRange https://github.com/sigp/lighthouse/blob/bf533c8e42cc73c35730e285c21df8add0195369/beacon_node/lighthouse_network/src/rpc/mod.rs#L118-L130 byPeer: {quota: MAX_REQUEST_BLOCKS, quotaTimeMs: 10_000}, getRequestCount: (req) => req.count, }, diff --git a/packages/reqresp/src/protocols/LightClientBootstrap.ts b/packages/reqresp/src/protocols/LightClientBootstrap.ts index 07d60db63d80..b4ae6fac4096 100644 --- a/packages/reqresp/src/protocols/LightClientBootstrap.ts +++ b/packages/reqresp/src/protocols/LightClientBootstrap.ts @@ -1,10 +1,11 @@ -import {altair, Root, ssz} from "@lodestar/types"; +import {Root, ssz, allForks} from "@lodestar/types"; +import {isForkLightClient} from "@lodestar/params"; import {toHex} from "@lodestar/utils"; import {Encoding, ProtocolDefinitionGenerator} from "../types.js"; import {getContextBytesLightclient} from "./utils.js"; // eslint-disable-next-line @typescript-eslint/naming-convention -export const LightClientBootstrap: ProtocolDefinitionGenerator = ( +export const LightClientBootstrap: ProtocolDefinitionGenerator = ( modules, handler ) => { @@ -14,7 +15,10 @@ export const LightClientBootstrap: ProtocolDefinitionGenerator ssz.Root, - responseType: () => ssz.altair.LightClientBootstrap, + responseType: (forkName) => + isForkLightClient(forkName) + ? ssz.allForksLightClient[forkName].LightClientBootstrap + : ssz.altair.LightClientBootstrap, renderRequestBody: (req) => toHex(req), contextBytes: getContextBytesLightclient( (bootstrap) => modules.config.getForkName(bootstrap.header.beacon.slot), diff --git a/packages/reqresp/src/protocols/LightClientFinalityUpdate.ts b/packages/reqresp/src/protocols/LightClientFinalityUpdate.ts index e96c6c0d0fef..c3dcb5ce26c2 100644 --- a/packages/reqresp/src/protocols/LightClientFinalityUpdate.ts +++ b/packages/reqresp/src/protocols/LightClientFinalityUpdate.ts @@ -1,9 +1,10 @@ -import {altair, ssz} from "@lodestar/types"; +import {ssz, allForks} from "@lodestar/types"; +import {isForkLightClient} from "@lodestar/params"; import {Encoding, ProtocolDefinitionGenerator} from "../types.js"; import {getContextBytesLightclient} from "./utils.js"; // eslint-disable-next-line @typescript-eslint/naming-convention -export const LightClientFinalityUpdate: ProtocolDefinitionGenerator = ( +export const LightClientFinalityUpdate: ProtocolDefinitionGenerator = ( modules, handler ) => { @@ -13,7 +14,10 @@ export const LightClientFinalityUpdate: ProtocolDefinitionGenerator null, - responseType: () => ssz.altair.LightClientFinalityUpdate, + responseType: (forkName) => + isForkLightClient(forkName) + ? ssz.allForksLightClient[forkName].LightClientFinalityUpdate + : ssz.altair.LightClientFinalityUpdate, contextBytes: getContextBytesLightclient((update) => modules.config.getForkName(update.signatureSlot), modules), inboundRateLimits: { // Finality updates should not be requested more than once per epoch. diff --git a/packages/reqresp/src/protocols/LightClientOptimisticUpdate.ts b/packages/reqresp/src/protocols/LightClientOptimisticUpdate.ts index 19912a7708f4..ba97a92ece3e 100644 --- a/packages/reqresp/src/protocols/LightClientOptimisticUpdate.ts +++ b/packages/reqresp/src/protocols/LightClientOptimisticUpdate.ts @@ -1,9 +1,10 @@ -import {altair, ssz} from "@lodestar/types"; +import {ssz, allForks} from "@lodestar/types"; +import {isForkLightClient} from "@lodestar/params"; import {Encoding, ProtocolDefinitionGenerator} from "../types.js"; import {getContextBytesLightclient} from "./utils.js"; // eslint-disable-next-line @typescript-eslint/naming-convention -export const LightClientOptimisticUpdate: ProtocolDefinitionGenerator = ( +export const LightClientOptimisticUpdate: ProtocolDefinitionGenerator = ( modules, handler ) => { @@ -13,7 +14,10 @@ export const LightClientOptimisticUpdate: ProtocolDefinitionGenerator null, - responseType: () => ssz.altair.LightClientOptimisticUpdate, + responseType: (forkName) => + isForkLightClient(forkName) + ? ssz.allForksLightClient[forkName].LightClientOptimisticUpdate + : ssz.altair.LightClientOptimisticUpdate, contextBytes: getContextBytesLightclient((update) => modules.config.getForkName(update.signatureSlot), modules), inboundRateLimits: { // Optimistic updates should not be requested more than once per slot. diff --git a/packages/reqresp/src/protocols/LightClientUpdatesByRange.ts b/packages/reqresp/src/protocols/LightClientUpdatesByRange.ts index 74350e70a2cc..1f6594a6a7c3 100644 --- a/packages/reqresp/src/protocols/LightClientUpdatesByRange.ts +++ b/packages/reqresp/src/protocols/LightClientUpdatesByRange.ts @@ -1,12 +1,12 @@ -import {MAX_REQUEST_LIGHT_CLIENT_UPDATES} from "@lodestar/params"; -import {altair, ssz} from "@lodestar/types"; +import {MAX_REQUEST_LIGHT_CLIENT_UPDATES, isForkLightClient} from "@lodestar/params"; +import {altair, ssz, allForks} from "@lodestar/types"; import {Encoding, ProtocolDefinitionGenerator} from "../types.js"; import {getContextBytesLightclient} from "./utils.js"; // eslint-disable-next-line @typescript-eslint/naming-convention export const LightClientUpdatesByRange: ProtocolDefinitionGenerator< altair.LightClientUpdatesByRange, - altair.LightClientUpdate + allForks.LightClientUpdate > = (modules, handler) => { return { method: "light_client_updates_by_range", @@ -14,7 +14,8 @@ export const LightClientUpdatesByRange: ProtocolDefinitionGenerator< encoding: Encoding.SSZ_SNAPPY, handler, requestType: () => ssz.altair.LightClientUpdatesByRange, - responseType: () => ssz.altair.LightClientUpdate, + responseType: (forkName) => + isForkLightClient(forkName) ? ssz.allForksLightClient[forkName].LightClientUpdate : ssz.altair.LightClientUpdate, renderRequestBody: (req) => `${req.startPeriod},${req.count}`, contextBytes: getContextBytesLightclient((update) => modules.config.getForkName(update.signatureSlot), modules), inboundRateLimits: { diff --git a/packages/reqresp/src/protocols/index.ts b/packages/reqresp/src/protocols/index.ts index 787a4368f3a5..0531341c5271 100644 --- a/packages/reqresp/src/protocols/index.ts +++ b/packages/reqresp/src/protocols/index.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ export {BeaconBlockAndBlobsSidecarByRoot} from "./BeaconBlockAndBlobsSidecarByRoot.js"; export {BeaconBlocksByRoot, BeaconBlocksByRootV2} from "./BeaconBlocksByRoot.js"; export {BeaconBlocksByRange, BeaconBlocksByRangeV2} from "./BeaconBlocksByRange.js"; diff --git a/packages/reqresp/src/types.ts b/packages/reqresp/src/types.ts index 75ae32e80f72..ae5fd2e98096 100644 --- a/packages/reqresp/src/types.ts +++ b/packages/reqresp/src/types.ts @@ -47,9 +47,7 @@ export interface InboundRateLimitQuota { // `protocolPrefix` is added runtime so not part of definition export interface ProtocolDefinition extends Omit { handler: ReqRespHandler; - // eslint-disable-next-line @typescript-eslint/no-explicit-any requestType: (fork: ForkName) => TypeSerializer | null; - // eslint-disable-next-line @typescript-eslint/no-explicit-any responseType: (fork: ForkName) => TypeSerializer; ignoreResponse?: boolean; renderRequestBody?: (request: Req) => string; diff --git a/packages/spec-test-util/package.json b/packages/spec-test-util/package.json index 8e192717b4df..2c083f26aa91 100644 --- a/packages/spec-test-util/package.json +++ b/packages/spec-test-util/package.json @@ -1,6 +1,6 @@ { "name": "@lodestar/spec-test-util", - "version": "1.4.3", + "version": "1.5.0", "description": "Spec test suite generator from yaml test files", "author": "ChainSafe Systems", "license": "Apache-2.0", @@ -46,7 +46,7 @@ "blockchain" ], "dependencies": { - "@lodestar/utils": "^1.4.3", + "@lodestar/utils": "^1.5.0", "async-retry": "^1.3.3", "axios": "^0.21.0", "chai": "^4.3.6", diff --git a/packages/spec-test-util/src/sszGeneric.ts b/packages/spec-test-util/src/sszGeneric.ts index 5e484f08da44..d62e4df42793 100644 --- a/packages/spec-test-util/src/sszGeneric.ts +++ b/packages/spec-test-util/src/sszGeneric.ts @@ -3,10 +3,7 @@ import fs from "node:fs"; import {uncompress} from "snappyjs"; import {loadYaml} from "@lodestar/utils"; -/* eslint-disable - @typescript-eslint/explicit-module-boundary-types, - @typescript-eslint/explicit-function-return-type -*/ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ export type ValidTestCaseData = { root: string; diff --git a/packages/spec-test-util/test/e2e/single/index.test.ts b/packages/spec-test-util/test/e2e/single/index.test.ts index b3f5a240aceb..c3cebb6ef493 100644 --- a/packages/spec-test-util/test/e2e/single/index.test.ts +++ b/packages/spec-test-util/test/e2e/single/index.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import {unlinkSync, writeFileSync} from "node:fs"; import path, {join} from "node:path"; import {fileURLToPath} from "node:url"; diff --git a/packages/state-transition/package.json b/packages/state-transition/package.json index 07c5f180de61..b5ab3ce6c816 100644 --- a/packages/state-transition/package.json +++ b/packages/state-transition/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.4.3", + "version": "1.5.0", "type": "module", "exports": { ".": { @@ -63,10 +63,10 @@ "@chainsafe/persistent-merkle-tree": "^0.4.2", "@chainsafe/persistent-ts": "^0.19.1", "@chainsafe/ssz": "^0.9.2", - "@lodestar/config": "^1.4.3", - "@lodestar/params": "^1.4.3", - "@lodestar/types": "^1.4.3", - "@lodestar/utils": "^1.4.3", + "@lodestar/config": "^1.5.0", + "@lodestar/params": "^1.5.0", + "@lodestar/types": "^1.5.0", + "@lodestar/utils": "^1.5.0", "bigint-buffer": "^1.1.5", "buffer-xor": "^2.0.2" }, diff --git a/packages/state-transition/src/block/externalData.ts b/packages/state-transition/src/block/externalData.ts index f20add5bd4cc..7e183f8ddb44 100644 --- a/packages/state-transition/src/block/externalData.ts +++ b/packages/state-transition/src/block/externalData.ts @@ -14,7 +14,7 @@ export enum ExecutionPayloadStatus { } export enum DataAvailableStatus { - preEIP4844 = "preEIP4844", + preDeneb = "preDeneb", notAvailable = "notAvailable", available = "available", } diff --git a/packages/state-transition/src/block/index.ts b/packages/state-transition/src/block/index.ts index 72c73140a523..1085fe6eab43 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, eip4844} from "@lodestar/types"; +import {allForks, altair, capella, deneb} from "@lodestar/types"; import {getFullOrBlindedPayload, isExecutionEnabled} from "../util/execution.js"; import {CachedBeaconStateAllForks, CachedBeaconStateCapella, CachedBeaconStateBellatrix} from "../types.js"; import {processExecutionPayload} from "./processExecutionPayload.js"; @@ -43,7 +43,7 @@ export function processBlock( // on the randao_mix computed with the reveal of the previous block. if (fork >= ForkSeq.bellatrix && isExecutionEnabled(state as CachedBeaconStateBellatrix, block)) { const fullOrBlindedPayload = getFullOrBlindedPayload(block); - // TODO EIP-4844: Allow to disable withdrawals for interop testing + // TODO Deneb: Allow to disable withdrawals for interop testing // https://github.com/ethereum/consensus-specs/blob/b62c9e877990242d63aa17a2a59a49bc649a2f2e/specs/eip4844/beacon-chain.md#disabling-withdrawals if (fork >= ForkSeq.capella) { processWithdrawals( @@ -61,16 +61,16 @@ export function processBlock( processSyncAggregate(state, block as altair.BeaconBlock, verifySignatures); } - if (fork >= ForkSeq.eip4844) { - processBlobKzgCommitments(block.body as eip4844.BeaconBlockBody); + if (fork >= ForkSeq.deneb) { + processBlobKzgCommitments(block.body as deneb.BeaconBlockBody); - // New in EIP-4844, note: Can sync optimistically without this condition, see note on `is_data_available` + // New in Deneb, note: Can sync optimistically without this condition, see note on `is_data_available` // NOTE: Ommitted and should be verified beforehand // assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments) switch (externalData.dataAvailableStatus) { - case DataAvailableStatus.preEIP4844: - throw Error("dataAvailableStatus preEIP4844"); + case DataAvailableStatus.preDeneb: + throw Error("dataAvailableStatus preDeneb"); case DataAvailableStatus.notAvailable: throw Error("dataAvailableStatus notAvailable"); case DataAvailableStatus.available: diff --git a/packages/state-transition/src/block/processBlobKzgCommitments.ts b/packages/state-transition/src/block/processBlobKzgCommitments.ts index 195ad056c26c..9ad74a0c74aa 100644 --- a/packages/state-transition/src/block/processBlobKzgCommitments.ts +++ b/packages/state-transition/src/block/processBlobKzgCommitments.ts @@ -1,4 +1,4 @@ -import {eip4844} from "@lodestar/types"; +import {deneb} from "@lodestar/types"; import {verifyKzgCommitmentsAgainstTransactions} from "../util/index.js"; /** @@ -10,7 +10,7 @@ import {verifyKzgCommitmentsAgainstTransactions} from "../util/index.js"; * body.blob_kzg_commitments * ) */ -export function processBlobKzgCommitments(body: eip4844.BeaconBlockBody): void { +export function processBlobKzgCommitments(body: deneb.BeaconBlockBody): void { if (!verifyKzgCommitmentsAgainstTransactions(body.executionPayload.transactions, body.blobKzgCommitments)) { throw Error("Invalid KZG commitments against transactions"); } diff --git a/packages/state-transition/src/block/processExecutionPayload.ts b/packages/state-transition/src/block/processExecutionPayload.ts index d8d83d991475..4d2ccb88516f 100644 --- a/packages/state-transition/src/block/processExecutionPayload.ts +++ b/packages/state-transition/src/block/processExecutionPayload.ts @@ -1,4 +1,4 @@ -import {ssz, allForks, capella, eip4844} from "@lodestar/types"; +import {ssz, allForks, capella, deneb} from "@lodestar/types"; import {toHexString, byteArrayEquals} from "@chainsafe/ssz"; import {ForkSeq} from "@lodestar/params"; import {CachedBeaconStateBellatrix, CachedBeaconStateCapella} from "../types.js"; @@ -61,10 +61,20 @@ export function processExecutionPayload( } } - // For blinded or full payload -> return common header - const transactionsRoot = isExecutionPayload(payload) - ? ssz.bellatrix.Transactions.hashTreeRoot(payload.transactions) - : payload.transactionsRoot; + 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 + state.latestExecutionPayloadHeader = state.config + .getExecutionForkTypes(state.slot) + .ExecutionPayloadHeader.toViewDU(payloadHeader) as typeof state.latestExecutionPayloadHeader; +} + +export function executionPayloadToPayloadHeader( + fork: ForkSeq, + payload: allForks.ExecutionPayload +): allForks.ExecutionPayloadHeader { + const transactionsRoot = ssz.bellatrix.Transactions.hashTreeRoot(payload.transactions); const bellatrixPayloadFields: allForks.ExecutionPayloadHeader = { parentHash: payload.parentHash, @@ -89,16 +99,12 @@ export function processExecutionPayload( ); } - if (fork >= ForkSeq.eip4844) { + if (fork >= ForkSeq.deneb) { // https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/beacon-chain.md#process_execution_payload - (bellatrixPayloadFields as eip4844.ExecutionPayloadHeader).excessDataGas = (payload as - | eip4844.ExecutionPayloadHeader - | eip4844.ExecutionPayload).excessDataGas; + (bellatrixPayloadFields as deneb.ExecutionPayloadHeader).excessDataGas = (payload as + | deneb.ExecutionPayloadHeader + | deneb.ExecutionPayload).excessDataGas; } - // TODO EIP-4844: 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 - state.latestExecutionPayloadHeader = state.config - .getExecutionForkTypes(state.slot) - .ExecutionPayloadHeader.toViewDU(bellatrixPayloadFields) as typeof state.latestExecutionPayloadHeader; + return bellatrixPayloadFields; } diff --git a/packages/state-transition/src/cache/epochContext.ts b/packages/state-transition/src/cache/epochContext.ts index b8ec6255bc8c..5da77a27edc7 100644 --- a/packages/state-transition/src/cache/epochContext.ts +++ b/packages/state-transition/src/cache/epochContext.ts @@ -645,8 +645,6 @@ export class EpochContext { for (let j = 0, committeeLength = slotCommittees[i].length; j < committeeLength; j++) { const validatorIndex = slotCommittees[i][j]; if (requestedValidatorIndicesSet.has(validatorIndex)) { - // no-non-null-assertion: We know that if index is in set there must exist an entry in the map - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion duties.set(validatorIndex, { validatorIndex, committeeLength, diff --git a/packages/state-transition/src/cache/epochProcess.ts b/packages/state-transition/src/cache/epochProcess.ts index 0cb5b6446999..4ca9bcae75fe 100644 --- a/packages/state-transition/src/cache/epochProcess.ts +++ b/packages/state-transition/src/cache/epochProcess.ts @@ -19,8 +19,6 @@ import {CachedBeaconStateAllForks, CachedBeaconStateAltair, CachedBeaconStatePha import {computeBaseRewardPerIncrement} from "../util/altair.js"; import {processPendingAttestations} from "../epoch/processPendingAttestations.js"; -/* eslint-disable @typescript-eslint/naming-convention */ - export type EpochProcessOpts = { /** * Assert progressive balances the same to EpochProcess diff --git a/packages/state-transition/src/cache/stateCache.ts b/packages/state-transition/src/cache/stateCache.ts index 83c834c1d6aa..0dd537c262ba 100644 --- a/packages/state-transition/src/cache/stateCache.ts +++ b/packages/state-transition/src/cache/stateCache.ts @@ -7,7 +7,7 @@ import { BeaconStateAltair, BeaconStateBellatrix, BeaconStateCapella, - BeaconStateEip4844, + BeaconStateDeneb, } from "./types.js"; export type BeaconStateCache = { @@ -117,7 +117,7 @@ export type CachedBeaconStatePhase0 = CachedBeaconState; export type CachedBeaconStateAltair = CachedBeaconState; export type CachedBeaconStateBellatrix = CachedBeaconState; export type CachedBeaconStateCapella = CachedBeaconState; -export type CachedBeaconStateEip4844 = CachedBeaconState; +export type CachedBeaconStateDeneb = CachedBeaconState; export type CachedBeaconStateAllForks = CachedBeaconState; export type CachedBeaconStateExecutions = CachedBeaconState; diff --git a/packages/state-transition/src/cache/types.ts b/packages/state-transition/src/cache/types.ts index 3d206fb16788..7d63139e3eb6 100644 --- a/packages/state-transition/src/cache/types.ts +++ b/packages/state-transition/src/cache/types.ts @@ -5,7 +5,7 @@ export type BeaconStatePhase0 = CompositeViewDU; export type BeaconStateAltair = CompositeViewDU; export type BeaconStateBellatrix = CompositeViewDU; export type BeaconStateCapella = CompositeViewDU; -export type BeaconStateEip4844 = CompositeViewDU; +export type BeaconStateDeneb = CompositeViewDU; // Union at the TreeViewDU level // - Works well as function argument and as generic type for allForks functions @@ -17,6 +17,6 @@ export type BeaconStateAllForks = | BeaconStateAltair | BeaconStateBellatrix | BeaconStateCapella - | BeaconStateEip4844; + | BeaconStateDeneb; -export type BeaconStateExecutions = BeaconStateBellatrix | BeaconStateCapella | BeaconStateEip4844; +export type BeaconStateExecutions = BeaconStateBellatrix | BeaconStateCapella | BeaconStateDeneb; diff --git a/packages/state-transition/src/index.ts b/packages/state-transition/src/index.ts index 506bf425316c..586e4e3d509e 100644 --- a/packages/state-transition/src/index.ts +++ b/packages/state-transition/src/index.ts @@ -9,7 +9,7 @@ export { CachedBeaconStateAltair, CachedBeaconStateBellatrix, CachedBeaconStateCapella, - CachedBeaconStateEip4844, + CachedBeaconStateDeneb, CachedBeaconStateAllForks, CachedBeaconStateExecutions, // Non-cached states @@ -17,7 +17,7 @@ export { BeaconStateAltair, BeaconStateBellatrix, BeaconStateCapella, - BeaconStateEip4844, + BeaconStateDeneb, BeaconStateAllForks, BeaconStateExecutions, } from "./types.js"; @@ -47,3 +47,4 @@ export {ExecutionPayloadStatus, DataAvailableStatus, BlockExternalData} from "./ export {becomesNewEth1Data} from "./block/processEth1Data.js"; // Withdrawals for new blocks export {getExpectedWithdrawals} from "./block/processWithdrawals.js"; +export {executionPayloadToPayloadHeader} from "./block/processExecutionPayload.js"; diff --git a/packages/state-transition/src/slot/index.ts b/packages/state-transition/src/slot/index.ts index 73f1017865d6..13c85b200622 100644 --- a/packages/state-transition/src/slot/index.ts +++ b/packages/state-transition/src/slot/index.ts @@ -6,7 +6,7 @@ import {ZERO_HASH} from "../constants/index.js"; export {upgradeStateToAltair} from "./upgradeStateToAltair.js"; export {upgradeStateToBellatrix} from "./upgradeStateToBellatrix.js"; export {upgradeStateToCapella} from "./upgradeStateToCapella.js"; -export {upgradeStateTo4844} from "./upgradeStateTo4844.js"; +export {upgradeStateToDeneb} from "./upgradeStateToDeneb.js"; /** * Dial state to next slot. Common for all forks diff --git a/packages/state-transition/src/slot/upgradeStateTo4844.ts b/packages/state-transition/src/slot/upgradeStateTo4844.ts deleted file mode 100644 index 2e3f7bdfb48e..000000000000 --- a/packages/state-transition/src/slot/upgradeStateTo4844.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {ssz} from "@lodestar/types"; -import {CachedBeaconStateEip4844} from "../types.js"; -import {getCachedBeaconState} from "../cache/stateCache.js"; -import {CachedBeaconStateCapella} from "../types.js"; - -/** - * Upgrade a state from Capella to 4844. - */ -export function upgradeStateTo4844(stateCapella: CachedBeaconStateCapella): CachedBeaconStateEip4844 { - const {config} = stateCapella; - - const stateCapellaNode = ssz.capella.BeaconState.commitViewDU(stateCapella); - const state4844View = ssz.eip4844.BeaconState.getViewDU(stateCapellaNode); - - const state4844 = getCachedBeaconState(state4844View, stateCapella); - - state4844.fork = ssz.phase0.Fork.toViewDU({ - previousVersion: stateCapella.fork.currentVersion, - currentVersion: config.EIP4844_FORK_VERSION, - epoch: stateCapella.epochCtx.epoch, - }); - - // The field order of eip4844 latestExecutionPayloadHeader is not the same to capella - // all fields after excessDataGas need to explicitly set - state4844.latestExecutionPayloadHeader.excessDataGas = ssz.UintBn256.defaultValue(); - state4844.latestExecutionPayloadHeader.blockHash = stateCapella.latestExecutionPayloadHeader.blockHash; - state4844.latestExecutionPayloadHeader.transactionsRoot = stateCapella.latestExecutionPayloadHeader.transactionsRoot; - state4844.latestExecutionPayloadHeader.withdrawalsRoot = stateCapella.latestExecutionPayloadHeader.withdrawalsRoot; - - state4844.commit(); - - return state4844; -} diff --git a/packages/state-transition/src/slot/upgradeStateToDeneb.ts b/packages/state-transition/src/slot/upgradeStateToDeneb.ts new file mode 100644 index 000000000000..1f5e2201f69f --- /dev/null +++ b/packages/state-transition/src/slot/upgradeStateToDeneb.ts @@ -0,0 +1,33 @@ +import {ssz} from "@lodestar/types"; +import {CachedBeaconStateDeneb} from "../types.js"; +import {getCachedBeaconState} from "../cache/stateCache.js"; +import {CachedBeaconStateCapella} from "../types.js"; + +/** + * Upgrade a state from Capella to Deneb. + */ +export function upgradeStateToDeneb(stateCapella: CachedBeaconStateCapella): CachedBeaconStateDeneb { + const {config} = stateCapella; + + const stateCapellaNode = ssz.capella.BeaconState.commitViewDU(stateCapella); + const stateDenebView = ssz.deneb.BeaconState.getViewDU(stateCapellaNode); + + const stateDeneb = getCachedBeaconState(stateDenebView, stateCapella); + + stateDeneb.fork = ssz.phase0.Fork.toViewDU({ + previousVersion: stateCapella.fork.currentVersion, + currentVersion: config.EIP4844_FORK_VERSION, + epoch: stateCapella.epochCtx.epoch, + }); + + // The field order of deneb latestExecutionPayloadHeader is not the same to capella + // all fields after excessDataGas need to explicitly set + stateDeneb.latestExecutionPayloadHeader.excessDataGas = ssz.UintBn256.defaultValue(); + stateDeneb.latestExecutionPayloadHeader.blockHash = stateCapella.latestExecutionPayloadHeader.blockHash; + stateDeneb.latestExecutionPayloadHeader.transactionsRoot = stateCapella.latestExecutionPayloadHeader.transactionsRoot; + stateDeneb.latestExecutionPayloadHeader.withdrawalsRoot = stateCapella.latestExecutionPayloadHeader.withdrawalsRoot; + + stateDeneb.commit(); + + return stateDeneb; +} diff --git a/packages/state-transition/src/stateTransition.ts b/packages/state-transition/src/stateTransition.ts index a1f4d91308c0..6f49415b1c54 100644 --- a/packages/state-transition/src/stateTransition.ts +++ b/packages/state-transition/src/stateTransition.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/namespace */ import {allForks, Slot, ssz} from "@lodestar/types"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; import {toHexString} from "@chainsafe/ssz"; @@ -18,7 +17,7 @@ import { upgradeStateToAltair, upgradeStateToBellatrix, upgradeStateToCapella, - upgradeStateTo4844, + upgradeStateToDeneb, } from "./slot/index.js"; import {processBlock} from "./block/index.js"; import {processEpoch} from "./epoch/index.js"; @@ -27,7 +26,7 @@ import {ProcessBlockOpts} from "./block/types.js"; // Multifork capable state transition -// NOTE EIP-4844: Mandatory BlockExternalData to decide if block is available or not +// NOTE DENEB: Mandatory BlockExternalData to decide if block is available or not export type StateTransitionOpts = BlockExternalData & EpochProcessOpts & ProcessBlockOpts & { @@ -43,7 +42,7 @@ export function stateTransition( state: CachedBeaconStateAllForks, signedBlock: allForks.FullOrBlindedSignedBeaconBlock, options: StateTransitionOpts = { - // TODO EIP-4844: Review what default values make sense + // TODO DENEB: Review what default values make sense executionPayloadStatus: ExecutionPayloadStatus.valid, dataAvailableStatus: DataAvailableStatus.available, }, @@ -178,7 +177,7 @@ function processSlotsWithTransientCache( postState = upgradeStateToCapella(postState as CachedBeaconStateBellatrix) as CachedBeaconStateAllForks; } if (stateSlot === config.EIP4844_FORK_EPOCH) { - postState = upgradeStateTo4844(postState as CachedBeaconStateCapella) as CachedBeaconStateAllForks; + postState = upgradeStateToDeneb(postState as CachedBeaconStateCapella) as CachedBeaconStateAllForks; } } else { postState.slot++; diff --git a/packages/state-transition/src/types.ts b/packages/state-transition/src/types.ts index e879a88e8b5b..b554b8f18c59 100644 --- a/packages/state-transition/src/types.ts +++ b/packages/state-transition/src/types.ts @@ -8,7 +8,7 @@ export { CachedBeaconStateAltair, CachedBeaconStateBellatrix, CachedBeaconStateCapella, - CachedBeaconStateEip4844, + CachedBeaconStateDeneb, } from "./cache/stateCache.js"; export { @@ -18,5 +18,5 @@ export { BeaconStateAltair, BeaconStateBellatrix, BeaconStateCapella, - BeaconStateEip4844, + BeaconStateDeneb, } from "./cache/types.js"; diff --git a/packages/state-transition/src/util/blobs.ts b/packages/state-transition/src/util/blobs.ts index 0ec5234a04e7..e9f0acd558e2 100644 --- a/packages/state-transition/src/util/blobs.ts +++ b/packages/state-transition/src/util/blobs.ts @@ -1,10 +1,10 @@ import SHA256 from "@chainsafe/as-sha256"; import {byteArrayEquals} from "@chainsafe/ssz"; import {BLOB_TX_TYPE, VERSIONED_HASH_VERSION_KZG} from "@lodestar/params"; -import {bellatrix, eip4844} from "@lodestar/types"; +import {bellatrix, deneb} from "@lodestar/types"; import {toHex} from "@lodestar/utils"; -// TODO EIP-4844: Move to params +// TODO DENEB: Move to params const BYTES_PER_HASH = 32; /** @@ -28,12 +28,12 @@ type VersionHash = Uint8Array; */ export function verifyKzgCommitmentsAgainstTransactions( transactions: bellatrix.Transaction[], - blobKzgCommitments: eip4844.KZGCommitment[] + blobKzgCommitments: deneb.KZGCommitment[] ): boolean { const allVersionedHashes: VersionHash[] = []; for (const tx of transactions) { if (tx[0] === BLOB_TX_TYPE) { - // TODO EIP-4844: Optimize array manipulation + // TODO DENEB: Optimize array manipulation allVersionedHashes.push(...txPeekBlobVersionedHashes(tx)); } } @@ -51,7 +51,7 @@ export function verifyKzgCommitmentsAgainstTransactions( } } - // TODO EIP-4844: Use proper API, either throw error or return boolean + // TODO DENEB: Use proper API, either throw error or return boolean return true; } @@ -86,7 +86,7 @@ function txPeekBlobVersionedHashes(opaqueTx: bellatrix.Transaction): VersionHash return versionedHashes; } -export function kzgCommitmentToVersionedHash(kzgCommitment: eip4844.KZGCommitment): VersionHash { +export function kzgCommitmentToVersionedHash(kzgCommitment: deneb.KZGCommitment): VersionHash { const hash = SHA256.digest(kzgCommitment); // Equivalent to `VERSIONED_HASH_VERSION_KZG + hash(kzg_commitment)[1:]` hash[0] = VERSIONED_HASH_VERSION_KZG; diff --git a/packages/state-transition/src/util/genesis.ts b/packages/state-transition/src/util/genesis.ts index 1070ef6effee..872c98d93eec 100644 --- a/packages/state-transition/src/util/genesis.ts +++ b/packages/state-transition/src/util/genesis.ts @@ -217,7 +217,7 @@ export function initializeBeaconStateFromEth1( executionPayloadHeader?: CompositeViewDU< | typeof ssz.bellatrix.ExecutionPayloadHeader | typeof ssz.capella.ExecutionPayloadHeader - | typeof ssz.eip4844.ExecutionPayloadHeader + | typeof ssz.deneb.ExecutionPayloadHeader > ): CachedBeaconStateAllForks { const stateView = getGenesisBeaconState( @@ -280,12 +280,12 @@ export function initializeBeaconStateFromEth1( } if (GENESIS_SLOT >= config.EIP4844_FORK_EPOCH) { - const stateEip4844 = state as CompositeViewDU; - stateEip4844.fork.previousVersion = config.EIP4844_FORK_VERSION; - stateEip4844.fork.currentVersion = config.EIP4844_FORK_VERSION; - stateEip4844.latestExecutionPayloadHeader = - (executionPayloadHeader as CompositeViewDU) ?? - ssz.eip4844.ExecutionPayloadHeader.defaultViewDU(); + const stateDeneb = state as CompositeViewDU; + stateDeneb.fork.previousVersion = config.EIP4844_FORK_VERSION; + stateDeneb.fork.currentVersion = config.EIP4844_FORK_VERSION; + stateDeneb.latestExecutionPayloadHeader = + (executionPayloadHeader as CompositeViewDU) ?? + ssz.deneb.ExecutionPayloadHeader.defaultViewDU(); } state.commit(); diff --git a/packages/state-transition/test/perf/shuffle/shuffle.test.ts b/packages/state-transition/test/perf/shuffle/shuffle.test.ts index 4bd9574e06a1..fd52e319aa20 100644 --- a/packages/state-transition/test/perf/shuffle/shuffle.test.ts +++ b/packages/state-transition/test/perf/shuffle/shuffle.test.ts @@ -7,7 +7,6 @@ import {unshuffleList} from "../../../src/index.js"; // 4000000 1.5617 s 4.9690 s (x3) describe("shuffle list", () => { - // eslint-disable-next-line @typescript-eslint/naming-convention const seed = new Uint8Array([42, 32]); for (const listSize of [ diff --git a/packages/state-transition/test/perf/util.ts b/packages/state-transition/test/perf/util.ts index 6bd29f8b0d93..3b9e695e312c 100644 --- a/packages/state-transition/test/perf/util.ts +++ b/packages/state-transition/test/perf/util.ts @@ -62,7 +62,7 @@ const secretKeyByModIndex = new Map(); const epoch = 23638; export const perfStateEpoch = epoch; -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function getPubkeys(vc = numValidators) { const pubkeysMod = interopPubkeysCached(keypairsMod); const pubkeysModObj = pubkeysMod.map((pk) => bls.PublicKey.fromBytes(pk, CoordType.jacobian)); diff --git a/packages/state-transition/test/unit/util/blobs.test.ts b/packages/state-transition/test/unit/util/blobs.test.ts index 9ce23fd7f85a..64e33b92929b 100644 --- a/packages/state-transition/test/unit/util/blobs.test.ts +++ b/packages/state-transition/test/unit/util/blobs.test.ts @@ -46,7 +46,7 @@ describe("blobs", () => { // value: uint256 // 32 bytes // data: ByteList[MAX_CALLDATA_SIZE] // 4 bytes offset // access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE] // 4 bytes offset - // max_fee_per_data_gas: uint256 # new in PR 5707, a.k.a. fee market change of EIP-4844 // 32 bytes offset + // max_fee_per_data_gas: uint256 # new in PR 5707, a.k.a. fee market change of Deneb // 32 bytes offset // blob_versioned_hashes: List[VersionedHash, MAX_VERSIONED_HASHES_LIST_SIZE] // 4 bytes offset // field_offset = 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 + 32 = 188 diff --git a/packages/types/package.json b/packages/types/package.json index b5028dec61fc..ba504dee9bd6 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.4.3", + "version": "1.5.0", "type": "module", "exports": { ".": { @@ -29,8 +29,8 @@ "./bellatrix": { "import": "./lib/bellatrix/index.js" }, - "./eip4844": { - "import": "./lib/eip4844/index.js" + "./deneb": { + "import": "./lib/deneb/index.js" }, "./phase0": { "import": "./lib/phase0/index.js" @@ -69,7 +69,7 @@ "types": "lib/index.d.ts", "dependencies": { "@chainsafe/ssz": "^0.9.2", - "@lodestar/params": "^1.4.3" + "@lodestar/params": "^1.5.0" }, "keywords": [ "ethereum", diff --git a/packages/types/src/allForks/sszTypes.ts b/packages/types/src/allForks/sszTypes.ts index bff6e24369a0..d55e45269e0e 100644 --- a/packages/types/src/allForks/sszTypes.ts +++ b/packages/types/src/allForks/sszTypes.ts @@ -2,7 +2,7 @@ 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 eip4844} from "../eip4844/index.js"; +import {ssz as deneb} from "../deneb/index.js"; /** * Index the ssz types that differ by fork @@ -37,11 +37,11 @@ export const allForks = { BeaconState: capella.BeaconState, Metadata: altair.Metadata, }, - eip4844: { - BeaconBlockBody: eip4844.BeaconBlockBody, - BeaconBlock: eip4844.BeaconBlock, - SignedBeaconBlock: eip4844.SignedBeaconBlock, - BeaconState: eip4844.BeaconState, + deneb: { + BeaconBlockBody: deneb.BeaconBlockBody, + BeaconBlock: deneb.BeaconBlock, + SignedBeaconBlock: deneb.SignedBeaconBlock, + BeaconState: deneb.BeaconState, Metadata: altair.Metadata, }, }; @@ -72,15 +72,15 @@ export const allForksExecution = { BuilderBid: capella.BuilderBid, SignedBuilderBid: capella.SignedBuilderBid, }, - eip4844: { - BeaconBlockBody: eip4844.BeaconBlockBody, - BeaconBlock: eip4844.BeaconBlock, - SignedBeaconBlock: eip4844.SignedBeaconBlock, - BeaconState: eip4844.BeaconState, - ExecutionPayload: eip4844.ExecutionPayload, - ExecutionPayloadHeader: eip4844.ExecutionPayloadHeader, - BuilderBid: eip4844.BuilderBid, - SignedBuilderBid: eip4844.SignedBuilderBid, + 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, }, }; @@ -99,15 +99,58 @@ export const allForksBlinded = { BeaconBlock: capella.BlindedBeaconBlock, SignedBeaconBlock: capella.SignedBlindedBeaconBlock, }, - eip4844: { - BeaconBlockBody: eip4844.BlindedBeaconBlockBody, - BeaconBlock: eip4844.BlindedBeaconBlock, - SignedBeaconBlock: eip4844.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 = { - eip4844: { - SignedBeaconBlockAndBlobsSidecar: eip4844.SignedBeaconBlockAndBlobsSidecar, + deneb: { + SignedBeaconBlockAndBlobsSidecar: deneb.SignedBeaconBlockAndBlobsSidecar, }, }; diff --git a/packages/types/src/allForks/types.ts b/packages/types/src/allForks/types.ts index c4b0b97cf511..d03bb1e20d3d 100644 --- a/packages/types/src/allForks/types.ts +++ b/packages/types/src/allForks/types.ts @@ -3,13 +3,13 @@ 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 eip4844} from "../eip4844/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 eip4844Ssz} from "../eip4844/index.js"; +import {ssz as denebSsz} from "../deneb/index.js"; // Re-export union types for types that are _known_ to differ @@ -18,47 +18,47 @@ export type BeaconBlockBody = | altair.BeaconBlockBody | bellatrix.BeaconBlockBody | capella.BeaconBlockBody - | eip4844.BeaconBlockBody; + | deneb.BeaconBlockBody; export type BeaconBlock = | phase0.BeaconBlock | altair.BeaconBlock | bellatrix.BeaconBlock | capella.BeaconBlock - | eip4844.BeaconBlock; + | deneb.BeaconBlock; export type SignedBeaconBlock = | phase0.SignedBeaconBlock | altair.SignedBeaconBlock | bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock - | eip4844.SignedBeaconBlock; + | deneb.SignedBeaconBlock; export type BeaconState = | phase0.BeaconState | altair.BeaconState | bellatrix.BeaconState | capella.BeaconState - | eip4844.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 | eip4844.BeaconBlockBody; +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 | eip4844.ExecutionPayload; +export type ExecutionPayload = bellatrix.ExecutionPayload | capella.ExecutionPayload | deneb.ExecutionPayload; export type ExecutionPayloadHeader = | bellatrix.ExecutionPayloadHeader | capella.ExecutionPayloadHeader - | eip4844.ExecutionPayloadHeader; + | deneb.ExecutionPayloadHeader; // Blinded types that will change across forks export type BlindedBeaconBlockBody = | bellatrix.BlindedBeaconBlockBody | capella.BlindedBeaconBlockBody - | eip4844.BlindedBeaconBlockBody; -export type BlindedBeaconBlock = bellatrix.BlindedBeaconBlock | capella.BlindedBeaconBlock | eip4844.BlindedBeaconBlock; + | deneb.BlindedBeaconBlockBody; +export type BlindedBeaconBlock = bellatrix.BlindedBeaconBlock | capella.BlindedBeaconBlock | deneb.BlindedBeaconBlock; export type SignedBlindedBeaconBlock = | bellatrix.SignedBlindedBeaconBlock | capella.SignedBlindedBeaconBlock - | eip4844.SignedBlindedBeaconBlock; + | deneb.SignedBlindedBeaconBlock; // Full or blinded types export type FullOrBlindedExecutionPayload = @@ -68,10 +68,26 @@ export type FullOrBlindedBeaconBlockBody = BeaconBlockBody | BlindedBeaconBlockB export type FullOrBlindedBeaconBlock = BeaconBlock | BlindedBeaconBlock; export type FullOrBlindedSignedBeaconBlock = SignedBeaconBlock | SignedBlindedBeaconBlock; -export type BuilderBid = bellatrix.BuilderBid | capella.BuilderBid | eip4844.BuilderBid; -export type SignedBuilderBid = bellatrix.SignedBuilderBid | capella.SignedBuilderBid | eip4844.SignedBuilderBid; +export type BuilderBid = bellatrix.BuilderBid | capella.BuilderBid | deneb.BuilderBid; +export type SignedBuilderBid = bellatrix.SignedBuilderBid | capella.SignedBuilderBid | deneb.SignedBuilderBid; -export type SignedBeaconBlockAndBlobsSidecar = eip4844.SignedBeaconBlockAndBlobsSidecar; +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 SignedBeaconBlockAndBlobsSidecar = deneb.SignedBeaconBlockAndBlobsSidecar; /** * Types known to change between forks */ @@ -83,6 +99,7 @@ export type AllForksTypes = { Metadata: Metadata; ExecutionPayload: ExecutionPayload; ExecutionPayloadHeader: ExecutionPayloadHeader; + LightClientHeader: LightClientHeader; BuilderBid: BuilderBid; SignedBuilderBid: SignedBuilderBid; SignedBeaconBlockAndBlobsSidecar: SignedBeaconBlockAndBlobsSidecar; @@ -94,6 +111,21 @@ export type AllForksBlindedTypes = { 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: @@ -129,58 +161,58 @@ export type AllForksSSZTypes = { | typeof altairSsz.BeaconBlockBody | typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody - | typeof eip4844Ssz.BeaconBlockBody + | typeof denebSsz.BeaconBlockBody >; BeaconBlock: AllForksTypeOf< | typeof phase0Ssz.BeaconBlock | typeof altairSsz.BeaconBlock | typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock - | typeof eip4844Ssz.BeaconBlock + | typeof denebSsz.BeaconBlock >; SignedBeaconBlock: AllForksTypeOf< | typeof phase0Ssz.SignedBeaconBlock | typeof altairSsz.SignedBeaconBlock | typeof bellatrixSsz.SignedBeaconBlock | typeof capellaSsz.SignedBeaconBlock - | typeof eip4844Ssz.SignedBeaconBlock + | typeof denebSsz.SignedBeaconBlock >; BeaconState: AllForksTypeOf< | typeof phase0Ssz.BeaconState | typeof altairSsz.BeaconState | typeof bellatrixSsz.BeaconState | typeof capellaSsz.BeaconState - | typeof eip4844Ssz.BeaconState + | typeof denebSsz.BeaconState >; Metadata: AllForksTypeOf; }; export type AllForksExecutionSSZTypes = { BeaconBlockBody: AllForksTypeOf< - typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody | typeof eip4844Ssz.BeaconBlockBody + typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody | typeof denebSsz.BeaconBlockBody >; BeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof eip4844Ssz.BeaconBlock + typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof denebSsz.BeaconBlock >; SignedBeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.SignedBeaconBlock | typeof capellaSsz.SignedBeaconBlock | typeof eip4844Ssz.SignedBeaconBlock + typeof bellatrixSsz.SignedBeaconBlock | typeof capellaSsz.SignedBeaconBlock | typeof denebSsz.SignedBeaconBlock >; BeaconState: AllForksTypeOf< - typeof bellatrixSsz.BeaconState | typeof capellaSsz.BeaconState | typeof eip4844Ssz.BeaconState + typeof bellatrixSsz.BeaconState | typeof capellaSsz.BeaconState | typeof denebSsz.BeaconState >; ExecutionPayload: AllForksTypeOf< - typeof bellatrixSsz.ExecutionPayload | typeof capellaSsz.ExecutionPayload | typeof eip4844Ssz.ExecutionPayload + typeof bellatrixSsz.ExecutionPayload | typeof capellaSsz.ExecutionPayload | typeof denebSsz.ExecutionPayload >; ExecutionPayloadHeader: AllForksTypeOf< | typeof bellatrixSsz.ExecutionPayloadHeader | typeof capellaSsz.ExecutionPayloadHeader - | typeof eip4844Ssz.ExecutionPayloadHeader + | typeof denebSsz.ExecutionPayloadHeader >; BuilderBid: AllForksTypeOf< - typeof bellatrixSsz.BuilderBid | typeof capellaSsz.BuilderBid | typeof eip4844Ssz.BuilderBid + typeof bellatrixSsz.BuilderBid | typeof capellaSsz.BuilderBid | typeof denebSsz.BuilderBid >; SignedBuilderBid: AllForksTypeOf< - typeof bellatrixSsz.SignedBuilderBid | typeof capellaSsz.SignedBuilderBid | typeof eip4844Ssz.SignedBuilderBid + typeof bellatrixSsz.SignedBuilderBid | typeof capellaSsz.SignedBuilderBid | typeof denebSsz.SignedBuilderBid >; }; @@ -188,18 +220,57 @@ export type AllForksBlindedSSZTypes = { BeaconBlockBody: AllForksTypeOf< | typeof bellatrixSsz.BlindedBeaconBlockBody | typeof capellaSsz.BlindedBeaconBlock - | typeof eip4844Ssz.BlindedBeaconBlock + | typeof denebSsz.BlindedBeaconBlock >; BeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.BlindedBeaconBlock | typeof capellaSsz.BlindedBeaconBlock | typeof eip4844Ssz.BlindedBeaconBlock + typeof bellatrixSsz.BlindedBeaconBlock | typeof capellaSsz.BlindedBeaconBlock | typeof denebSsz.BlindedBeaconBlock >; SignedBeaconBlock: AllForksTypeOf< | typeof bellatrixSsz.SignedBlindedBeaconBlock | typeof capellaSsz.SignedBlindedBeaconBlock - | typeof eip4844Ssz.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 = { - SignedBeaconBlockAndBlobsSidecar: AllForksTypeOf; + SignedBeaconBlockAndBlobsSidecar: AllForksTypeOf; }; diff --git a/packages/types/src/altair/types.ts b/packages/types/src/altair/types.ts index a805144e1def..f891bcca376f 100644 --- a/packages/types/src/altair/types.ts +++ b/packages/types/src/altair/types.ts @@ -14,9 +14,11 @@ export type BeaconBlockBody = ValueOf; export type BeaconBlock = ValueOf; export type SignedBeaconBlock = ValueOf; export type BeaconState = 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; export type LightClientUpdatesByRange = ValueOf; diff --git a/packages/types/src/capella/sszTypes.ts b/packages/types/src/capella/sszTypes.ts index 3ad637455531..858de7d635bd 100644 --- a/packages/types/src/capella/sszTypes.ts +++ b/packages/types/src/capella/sszTypes.ts @@ -1,5 +1,12 @@ -import {ContainerType, ListCompositeType} from "@chainsafe/ssz"; -import {HISTORICAL_ROOTS_LIMIT, MAX_WITHDRAWALS_PER_PAYLOAD, MAX_BLS_TO_EXECUTION_CHANGES} from "@lodestar/params"; +import {ContainerType, ListCompositeType, VectorCompositeType} from "@chainsafe/ssz"; +import { + HISTORICAL_ROOTS_LIMIT, + MAX_WITHDRAWALS_PER_PAYLOAD, + MAX_BLS_TO_EXECUTION_CHANGES, + BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH as EXECUTION_PAYLOAD_DEPTH, + EPOCHS_PER_SYNC_COMMITTEE_PERIOD, + SLOTS_PER_EPOCH, +} from "@lodestar/params"; import {ssz as primitiveSsz} from "../primitive/index.js"; import {ssz as phase0Ssz} from "../phase0/index.js"; import {ssz as altairSsz} from "../altair/index.js"; @@ -16,6 +23,7 @@ const { ExecutionAddress, Gwei, UintBn256, + Bytes32, } = primitiveSsz; export const Withdrawal = new ContainerType( @@ -193,3 +201,62 @@ export const SignedBlindedBeaconBlock = new ContainerType( }, {typeName: "SignedBlindedBeaconBlock", jsonCase: "eth2"} ); + +export const LightClientHeader = new ContainerType( + { + beacon: phase0Ssz.BeaconBlockHeader, + execution: ExecutionPayloadHeader, + executionBranch: new VectorCompositeType(Bytes32, EXECUTION_PAYLOAD_DEPTH), + }, + {typeName: "LightClientHeader", jsonCase: "eth2"} +); + +export const LightClientBootstrap = new ContainerType( + { + header: LightClientHeader, + currentSyncCommittee: altairSsz.SyncCommittee, + currentSyncCommitteeBranch: altairSsz.LightClientBootstrap.fields.currentSyncCommitteeBranch, + }, + {typeName: "LightClientBootstrap", jsonCase: "eth2"} +); + +export const LightClientUpdate = new ContainerType( + { + attestedHeader: LightClientHeader, + nextSyncCommittee: altairSsz.SyncCommittee, + nextSyncCommitteeBranch: altairSsz.LightClientUpdate.fields.nextSyncCommitteeBranch, + finalizedHeader: LightClientHeader, + finalityBranch: altairSsz.LightClientUpdate.fields.finalityBranch, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, + }, + {typeName: "LightClientUpdate", jsonCase: "eth2"} +); + +export const LightClientFinalityUpdate = new ContainerType( + { + attestedHeader: LightClientHeader, + finalizedHeader: LightClientHeader, + finalityBranch: altairSsz.LightClientFinalityUpdate.fields.finalityBranch, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, + }, + {typeName: "LightClientFinalityUpdate", jsonCase: "eth2"} +); + +export const LightClientOptimisticUpdate = new ContainerType( + { + attestedHeader: LightClientHeader, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, + }, + {typeName: "LightClientOptimisticUpdate", jsonCase: "eth2"} +); + +export const LightClientStore = new ContainerType( + { + snapshot: LightClientBootstrap, + validUpdates: new ListCompositeType(LightClientUpdate, EPOCHS_PER_SYNC_COMMITTEE_PERIOD * SLOTS_PER_EPOCH), + }, + {typeName: "LightClientStore", jsonCase: "eth2"} +); diff --git a/packages/types/src/capella/types.ts b/packages/types/src/capella/types.ts index 2239a6d3537b..1dfb375fb358 100644 --- a/packages/types/src/capella/types.ts +++ b/packages/types/src/capella/types.ts @@ -22,3 +22,10 @@ export type FullOrBlindedExecutionPayload = ExecutionPayload | ExecutionPayloadH export type BuilderBid = ValueOf; export type SignedBuilderBid = 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/eip4844/index.ts b/packages/types/src/deneb/index.ts similarity index 100% rename from packages/types/src/eip4844/index.ts rename to packages/types/src/deneb/index.ts diff --git a/packages/types/src/eip4844/sszTypes.ts b/packages/types/src/deneb/sszTypes.ts similarity index 73% rename from packages/types/src/eip4844/sszTypes.ts rename to packages/types/src/deneb/sszTypes.ts index 8b535f166aa9..221fd0e5dc7d 100644 --- a/packages/types/src/eip4844/sszTypes.ts +++ b/packages/types/src/deneb/sszTypes.ts @@ -1,10 +1,13 @@ -import {ContainerType, ListCompositeType, ByteVectorType} from "@chainsafe/ssz"; +import {ContainerType, ListCompositeType, ByteVectorType, VectorCompositeType} from "@chainsafe/ssz"; import { HISTORICAL_ROOTS_LIMIT, FIELD_ELEMENTS_PER_BLOB, MAX_BLOBS_PER_BLOCK, MAX_REQUEST_BLOCKS, BYTES_PER_FIELD_ELEMENT, + BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH as EXECUTION_PAYLOAD_DEPTH, + EPOCHS_PER_SYNC_COMMITTEE_PERIOD, + SLOTS_PER_EPOCH, } from "@lodestar/params"; import {ssz as primitiveSsz} from "../primitive/index.js"; import {ssz as phase0Ssz} from "../phase0/index.js"; @@ -84,7 +87,7 @@ export const BeaconBlockAndBlobsSidecarByRootRequest = new ListCompositeType(Roo export const ExecutionPayload = new ContainerType( { ...bellatrixSsz.CommonExecutionPayloadType.fields, - excessDataGas: UintBn256, // New in EIP-4844 + excessDataGas: UintBn256, // New in DENEB // Extra payload fields blockHash: Root, transactions: bellatrixSsz.Transactions, @@ -96,7 +99,7 @@ export const ExecutionPayload = new ContainerType( export const ExecutionPayloadHeader = new ContainerType( { ...bellatrixSsz.CommonExecutionPayloadType.fields, - excessDataGas: UintBn256, // New in EIP-4844 + excessDataGas: UintBn256, // New in DENEB blockHash: Root, transactionsRoot: Root, withdrawalsRoot: Root, @@ -108,9 +111,9 @@ export const ExecutionPayloadHeader = new ContainerType( export const BeaconBlockBody = new ContainerType( { ...altairSsz.BeaconBlockBody.fields, - executionPayload: ExecutionPayload, // Modified in EIP-4844 + executionPayload: ExecutionPayload, // Modified in DENEB blsToExecutionChanges: capellaSsz.BeaconBlockBody.fields.blsToExecutionChanges, - blobKzgCommitments: BlobKzgCommitments, // New in EIP-4844 + blobKzgCommitments: BlobKzgCommitments, // New in DENEB }, {typeName: "BeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} ); @@ -118,14 +121,14 @@ export const BeaconBlockBody = new ContainerType( export const BeaconBlock = new ContainerType( { ...capellaSsz.BeaconBlock.fields, - body: BeaconBlockBody, // Modified in EIP-4844 + body: BeaconBlockBody, // Modified in DENEB }, {typeName: "BeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} ); export const SignedBeaconBlock = new ContainerType( { - message: BeaconBlock, // Modified in EIP-4844 + message: BeaconBlock, // Modified in DENEB signature: BLSSignature, }, {typeName: "SignedBeaconBlock", jsonCase: "eth2"} @@ -152,8 +155,8 @@ export const SignedBeaconBlockAndBlobsSidecar = new ContainerType( export const BlindedBeaconBlockBody = new ContainerType( { ...BeaconBlockBody.fields, - executionPayloadHeader: ExecutionPayloadHeader, // Modified in EIP-4844 - blobKzgCommitments: BlobKzgCommitments, // New in EIP-4844 + executionPayloadHeader: ExecutionPayloadHeader, // Modified in DENEB + blobKzgCommitments: BlobKzgCommitments, // New in DENEB }, {typeName: "BlindedBeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} ); @@ -161,14 +164,14 @@ export const BlindedBeaconBlockBody = new ContainerType( export const BlindedBeaconBlock = new ContainerType( { ...capellaSsz.BlindedBeaconBlock.fields, - body: BlindedBeaconBlockBody, // Modified in EIP-4844 + body: BlindedBeaconBlockBody, // Modified in DENEB }, {typeName: "BlindedBeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} ); export const SignedBlindedBeaconBlock = new ContainerType( { - message: BlindedBeaconBlock, // Modified in EIP-4844 + message: BlindedBeaconBlock, // Modified in DENEB signature: BLSSignature, }, {typeName: "SignedBlindedBeaconBlock", jsonCase: "eth2"} @@ -230,7 +233,7 @@ export const BeaconState = new ContainerType( currentSyncCommittee: altairSsz.SyncCommittee, nextSyncCommittee: altairSsz.SyncCommittee, // Execution - latestExecutionPayloadHeader: ExecutionPayloadHeader, // Modified in EIP-4844 + latestExecutionPayloadHeader: ExecutionPayloadHeader, // Modified in DENEB // Withdrawals nextWithdrawalIndex: capellaSsz.BeaconState.fields.nextWithdrawalIndex, nextWithdrawalValidatorIndex: capellaSsz.BeaconState.fields.nextWithdrawalValidatorIndex, @@ -239,3 +242,62 @@ export const BeaconState = new ContainerType( }, {typeName: "BeaconState", jsonCase: "eth2"} ); + +export const LightClientHeader = new ContainerType( + { + beacon: phase0Ssz.BeaconBlockHeader, + execution: ExecutionPayloadHeader, + executionBranch: new VectorCompositeType(Bytes32, EXECUTION_PAYLOAD_DEPTH), + }, + {typeName: "LightClientHeader", jsonCase: "eth2"} +); + +export const LightClientBootstrap = new ContainerType( + { + header: LightClientHeader, + currentSyncCommittee: altairSsz.SyncCommittee, + currentSyncCommitteeBranch: altairSsz.LightClientBootstrap.fields.currentSyncCommitteeBranch, + }, + {typeName: "LightClientBootstrap", jsonCase: "eth2"} +); + +export const LightClientUpdate = new ContainerType( + { + attestedHeader: LightClientHeader, + nextSyncCommittee: altairSsz.SyncCommittee, + nextSyncCommitteeBranch: altairSsz.LightClientUpdate.fields.nextSyncCommitteeBranch, + finalizedHeader: LightClientHeader, + finalityBranch: altairSsz.LightClientUpdate.fields.finalityBranch, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, + }, + {typeName: "LightClientUpdate", jsonCase: "eth2"} +); + +export const LightClientFinalityUpdate = new ContainerType( + { + attestedHeader: LightClientHeader, + finalizedHeader: LightClientHeader, + finalityBranch: altairSsz.LightClientFinalityUpdate.fields.finalityBranch, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, + }, + {typeName: "LightClientFinalityUpdate", jsonCase: "eth2"} +); + +export const LightClientOptimisticUpdate = new ContainerType( + { + attestedHeader: LightClientHeader, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, + }, + {typeName: "LightClientOptimisticUpdate", jsonCase: "eth2"} +); + +export const LightClientStore = new ContainerType( + { + snapshot: LightClientBootstrap, + validUpdates: new ListCompositeType(LightClientUpdate, EPOCHS_PER_SYNC_COMMITTEE_PERIOD * SLOTS_PER_EPOCH), + }, + {typeName: "LightClientStore", jsonCase: "eth2"} +); diff --git a/packages/types/src/eip4844/types.ts b/packages/types/src/deneb/types.ts similarity index 79% rename from packages/types/src/eip4844/types.ts rename to packages/types/src/deneb/types.ts index b185d1afad0c..bb419bd3d00a 100644 --- a/packages/types/src/eip4844/types.ts +++ b/packages/types/src/deneb/types.ts @@ -32,3 +32,10 @@ export type FullOrBlindedExecutionPayload = ExecutionPayload | ExecutionPayloadH export type BuilderBid = ValueOf; export type SignedBuilderBid = 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 de120240d687..2a7df948a447 100644 --- a/packages/types/src/sszTypes.ts +++ b/packages/types/src/sszTypes.ts @@ -3,10 +3,11 @@ 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 eip4844} from "./eip4844/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; diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index ef9663c2b756..b3837510401d 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -3,7 +3,7 @@ export {ts as phase0} from "./phase0/index.js"; 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 eip4844} from "./eip4844/index.js"; +export {ts as deneb} from "./deneb/index.js"; export {ts as allForks} from "./allForks/index.js"; diff --git a/packages/types/src/utils/StringType.ts b/packages/types/src/utils/StringType.ts index ec9604c360d2..b3ef4f2f1f18 100644 --- a/packages/types/src/utils/StringType.ts +++ b/packages/types/src/utils/StringType.ts @@ -1,7 +1,5 @@ import {BasicType} from "@chainsafe/ssz"; -/* eslint-disable @typescript-eslint/naming-convention */ - export class StringType extends BasicType { readonly typeName = "string"; byteLength = 0; diff --git a/packages/types/test/unit/constants.test.ts b/packages/types/test/unit/constants.test.ts index 48051c36f566..d637eb72232c 100644 --- a/packages/types/test/unit/constants.test.ts +++ b/packages/types/test/unit/constants.test.ts @@ -2,8 +2,6 @@ import {expect} from "chai"; import * as constants from "@lodestar/params"; import {ssz} from "../../src/index.js"; -/* eslint-disable @typescript-eslint/naming-convention */ - // NOTE: This test is here and not in lodestar-params, to prevent lodestar-params depending on SSZ // Since lodestar-params and lodestar-types are in the same mono-repo, running this test here is enough // guarantee that these constants are correct. diff --git a/packages/utils/package.json b/packages/utils/package.json index d4817c626303..2581a37d40e8 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.4.3", + "version": "1.5.0", "type": "module", "exports": "./lib/index.js", "files": [ diff --git a/packages/utils/src/logger/format.ts b/packages/utils/src/logger/format.ts index 3671c5d184b9..2f5ad7dac429 100644 --- a/packages/utils/src/logger/format.ts +++ b/packages/utils/src/logger/format.ts @@ -58,7 +58,6 @@ function formatTimestamp(opts: ILoggerOptions): Format { function jsonLogFormat(opts: ILoggerOptions): Format { return format.combine( ...(opts.hideTimestamp ? [] : [format.timestamp()]), - // eslint-disable-next-line @typescript-eslint/naming-convention format((_info) => { const info = _info as IWinstonInfoArg; info.context = logCtxToJson(info.context); @@ -72,7 +71,7 @@ function jsonLogFormat(opts: ILoggerOptions): Format { /** * Winston template function print a human readable string given a log object */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/naming-convention +// eslint-disable-next-line @typescript-eslint/no-explicit-any function humanReadableTemplateFn(_info: {[key: string]: any; level: string; message: string}): string { const info = _info as IWinstonInfoArg; diff --git a/packages/validator/package.json b/packages/validator/package.json index acc030af9821..7816a4d5b160 100644 --- a/packages/validator/package.json +++ b/packages/validator/package.json @@ -1,6 +1,6 @@ { "name": "@lodestar/validator", - "version": "1.4.3", + "version": "1.5.0", "description": "A Typescript implementation of the validator client", "author": "ChainSafe Systems", "license": "LGPL-3.0", @@ -51,13 +51,13 @@ "dependencies": { "@chainsafe/bls": "7.1.1", "@chainsafe/ssz": "^0.9.2", - "@lodestar/api": "^1.4.3", - "@lodestar/config": "^1.4.3", - "@lodestar/db": "^1.4.3", - "@lodestar/params": "^1.4.3", - "@lodestar/state-transition": "^1.4.3", - "@lodestar/types": "^1.4.3", - "@lodestar/utils": "^1.4.3", + "@lodestar/api": "^1.5.0", + "@lodestar/config": "^1.5.0", + "@lodestar/db": "^1.5.0", + "@lodestar/params": "^1.5.0", + "@lodestar/state-transition": "^1.5.0", + "@lodestar/types": "^1.5.0", + "@lodestar/utils": "^1.5.0", "bigint-buffer": "^1.1.5", "cross-fetch": "^3.1.4", "strict-event-emitter-types": "^2.0.0" diff --git a/packages/validator/src/index.ts b/packages/validator/src/index.ts index 0663b59aced0..03a124e7f593 100644 --- a/packages/validator/src/index.ts +++ b/packages/validator/src/index.ts @@ -8,6 +8,7 @@ export { ValidatorProposerConfig, defaultOptions, ProposerConfig, + BuilderSelection, } from "./services/validatorStore.js"; export {waitForGenesis} from "./genesis.js"; export {getMetrics, Metrics, MetricsRegister} from "./metrics.js"; diff --git a/packages/validator/src/metrics.ts b/packages/validator/src/metrics.ts index 98fd9da4f600..5c976dcbc090 100644 --- a/packages/validator/src/metrics.ts +++ b/packages/validator/src/metrics.ts @@ -83,7 +83,7 @@ export type LodestarGitData = { /** * A collection of metrics used throughout the Gossipsub behaviour. */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function getMetrics(register: MetricsRegister, gitData: LodestarGitData) { // Using function style instead of class to prevent having to re-declare all MetricsPrometheus types. diff --git a/packages/validator/src/services/block.ts b/packages/validator/src/services/block.ts index 6e38d6033281..6e6088e2000b 100644 --- a/packages/validator/src/services/block.ts +++ b/packages/validator/src/services/block.ts @@ -7,9 +7,17 @@ import {Api, ApiError, ServerApi} from "@lodestar/api"; import {IClock, ILoggerVc} from "../util/index.js"; import {PubkeyHex} from "../types.js"; import {Metrics} from "../metrics.js"; -import {ValidatorStore} from "./validatorStore.js"; +import {ValidatorStore, BuilderSelection} from "./validatorStore.js"; import {BlockDutiesService, GENESIS_SLOT} from "./blockDuties.js"; +const ETH_TO_WEI = BigInt("1000000000000000000"); + +type ProduceBlockOpts = { + expectedFeeRecipient: string; + strictFeeRecipientCheck: boolean; + isBuilderEnabled: boolean; + builderSelection: BuilderSelection; +}; /** * Service that sets up and handles validator block proposal duties. */ @@ -74,12 +82,14 @@ export class BlockProposingService { const strictFeeRecipientCheck = this.validatorStore.strictFeeRecipientCheck(pubkeyHex); const isBuilderEnabled = this.validatorStore.isBuilderEnabled(pubkeyHex); + const builderSelection = this.validatorStore.getBuilderSelection(pubkeyHex); const expectedFeeRecipient = this.validatorStore.getFeeRecipient(pubkeyHex); const block = await this.produceBlockWrapper(slot, randaoReveal, graffiti, { expectedFeeRecipient, strictFeeRecipientCheck, isBuilderEnabled, + builderSelection, }).catch((e: Error) => { this.metrics?.blockProposingErrors.inc({error: "produce"}); throw extendError(e, "Failed to produce block"); @@ -115,14 +125,10 @@ export class BlockProposingService { slot: Slot, randaoReveal: BLSSignature, graffiti: string, - { - expectedFeeRecipient, - strictFeeRecipientCheck, - isBuilderEnabled, - }: {expectedFeeRecipient: string; strictFeeRecipientCheck: boolean; isBuilderEnabled: boolean} + {expectedFeeRecipient, strictFeeRecipientCheck, isBuilderEnabled, builderSelection}: ProduceBlockOpts ): Promise<{data: allForks.FullOrBlindedBeaconBlock} & {debugLogCtx: Record}> => { const blindedBlockPromise = isBuilderEnabled - ? this.api.validator.produceBlindedBlock(slot, randaoReveal, graffiti).catch((e: Error) => { + ? this.produceBlindedBlock(slot, randaoReveal, graffiti).catch((e: Error) => { this.logger.error("Failed to produce builder block", {}, e as Error); return null; }) @@ -136,51 +142,88 @@ export class BlockProposingService { await Promise.all([blindedBlockPromise, fullBlockPromise]); const blindedBlock = await blindedBlockPromise; + const builderBlockValue = blindedBlock?.blockValue ?? BigInt(0); + const fullBlock = await fullBlockPromise; + const engineBlockValue = fullBlock?.blockValue ?? BigInt(0); + + const feeRecipientCheck = {expectedFeeRecipient, strictFeeRecipientCheck}; + + if (fullBlock && blindedBlock) { + let selectedSource: string; + let selectedBlock; + switch (builderSelection) { + case BuilderSelection.MaxProfit: { + if (engineBlockValue >= builderBlockValue) { + selectedSource = "engine"; + selectedBlock = fullBlock; + } else { + selectedSource = "builder"; + selectedBlock = blindedBlock; + } + break; + } - // A metric on the choice between blindedBlock and normal block can be applied - // TODO: compare the blockValue that has been obtained in each of full or blinded block - if (blindedBlock && blindedBlock.ok) { - const debugLogCtx = {source: "builder", blockValue: blindedBlock.response.blockValue.toString()}; - return {...blindedBlock.response, debugLogCtx}; - } else { - if (!fullBlock) { - throw Error("Failed to produce engine or builder block"); + case BuilderSelection.BuilderAlways: + default: { + selectedSource = "builder"; + selectedBlock = blindedBlock; + } } - const debugLogCtx = {source: "engine", blockValue: fullBlock.blockValue.toString()}; - const blockFeeRecipient = (fullBlock.data as bellatrix.BeaconBlock).body.executionPayload?.feeRecipient; - const feeRecipient = blockFeeRecipient !== undefined ? toHexString(blockFeeRecipient) : undefined; + this.logger.debug(`Selected ${selectedSource} block`, { + builderSelection, + // winston logger doesn't like bigint + engineBlockValue: `${engineBlockValue}`, + builderBlockValue: `${builderBlockValue}`, + }); + return this.getBlockWithDebugLog(selectedBlock, selectedSource, feeRecipientCheck); + } else if (fullBlock && !blindedBlock) { + this.logger.debug("Selected engine block: no builder block produced", { + // winston logger doesn't like bigint + engineBlockValue: `${engineBlockValue}`, + }); + return this.getBlockWithDebugLog(fullBlock, "engine", feeRecipientCheck); + } else if (blindedBlock && !fullBlock) { + this.logger.debug("Selected builder block: no engine block produced", { + // winston logger doesn't like bigint + builderBlockValue: `${builderBlockValue}`, + }); + return this.getBlockWithDebugLog(blindedBlock, "builder", feeRecipientCheck); + } else { + throw Error("Failed to produce engine or builder block"); + } + }; + + private getBlockWithDebugLog( + fullOrBlindedBlock: {data: allForks.FullOrBlindedBeaconBlock; blockValue: Wei}, + source: string, + {expectedFeeRecipient, strictFeeRecipientCheck}: {expectedFeeRecipient: string; strictFeeRecipientCheck: boolean} + ): {data: allForks.FullOrBlindedBeaconBlock} & {debugLogCtx: Record} { + const debugLogCtx = { + source: source, + // winston logger doesn't like bigint + "blockValue(eth)": `${fullOrBlindedBlock.blockValue / ETH_TO_WEI}`, + }; + const blockFeeRecipient = (fullOrBlindedBlock.data as bellatrix.BeaconBlock).body.executionPayload?.feeRecipient; + const feeRecipient = blockFeeRecipient !== undefined ? toHexString(blockFeeRecipient) : undefined; + + if (source === "engine") { if (feeRecipient !== undefined) { - // In Mev Builder, the feeRecipient could differ and rewards to the feeRecipient - // might be included in the block transactions as indicated by the BuilderBid - // Address this appropriately in the Mev boost PR - // - // Even for engine, there could be divergence of feeRecipient the argument being - // that the bn <> engine setup has implied trust and are user-agents of the same entity. - // A better approach would be to have engine also provide something akin to BuilderBid - // - // The following conversation in the interop R&D channel can provide some context - // https://discord.com/channels/595666850260713488/892088344438255616/978374892678426695 - // - // For now providing a strick check flag to enable disable this if (feeRecipient !== expectedFeeRecipient && strictFeeRecipientCheck) { throw Error(`Invalid feeRecipient=${feeRecipient}, expected=${expectedFeeRecipient}`); } - const transactions = (fullBlock.data as bellatrix.BeaconBlock).body.executionPayload?.transactions.length; - const withdrawals = (fullBlock.data as capella.BeaconBlock).body.executionPayload?.withdrawals?.length; - Object.assign(debugLogCtx, {feeRecipient, transactions}, withdrawals !== undefined ? {withdrawals} : {}); } - return {...fullBlock, debugLogCtx}; - // throw Error("random") } - }; + + const transactions = (fullOrBlindedBlock.data as bellatrix.BeaconBlock).body.executionPayload?.transactions.length; + const withdrawals = (fullOrBlindedBlock.data as capella.BeaconBlock).body.executionPayload?.withdrawals?.length; + Object.assign(debugLogCtx, {feeRecipient, transactions}, withdrawals !== undefined ? {withdrawals} : {}); + + return {...fullOrBlindedBlock, debugLogCtx}; + } /** Wrapper around the API's different methods for producing blocks across forks */ - private produceBlock: ServerApi["produceBlock"] = async ( - slot, - randaoReveal, - graffiti - ): Promise<{data: allForks.BeaconBlock; blockValue: Wei}> => { + private produceBlock: ServerApi["produceBlock"] = async (slot, randaoReveal, graffiti) => { switch (this.config.getForkName(slot)) { case ForkName.phase0: { const res = await this.api.validator.produceBlock(slot, randaoReveal, graffiti); @@ -196,4 +239,14 @@ export class BlockProposingService { } } }; + + private produceBlindedBlock: ServerApi["produceBlindedBlock"] = async ( + slot, + randaoReveal, + graffiti + ) => { + const res = await this.api.validator.produceBlindedBlock(slot, randaoReveal, graffiti); + ApiError.assert(res, "Failed to produce block: validator.produceBlindedBlock"); + return res.response; + }; } diff --git a/packages/validator/src/services/validatorStore.ts b/packages/validator/src/services/validatorStore.ts index 84b59acbddc8..5a46ed40509b 100644 --- a/packages/validator/src/services/validatorStore.ts +++ b/packages/validator/src/services/validatorStore.ts @@ -62,6 +62,11 @@ export type SignerRemote = { pubkey: PubkeyHex; }; +export enum BuilderSelection { + BuilderAlways = "builderalways", + MaxProfit = "maxprofit", +} + type DefaultProposerConfig = { graffiti: string; strictFeeRecipientCheck: boolean; @@ -69,6 +74,7 @@ type DefaultProposerConfig = { builder: { enabled: boolean; gasLimit: number; + selection: BuilderSelection; }; }; @@ -79,6 +85,7 @@ export type ProposerConfig = { builder?: { enabled?: boolean; gasLimit?: number; + selection?: BuilderSelection; }; }; @@ -113,6 +120,7 @@ type ValidatorData = ProposerConfig & { export const defaultOptions = { suggestedFeeRecipient: "0x0000000000000000000000000000000000000000", defaultGasLimit: 30_000_000, + builderSelection: BuilderSelection.BuilderAlways, }; /** @@ -142,6 +150,7 @@ export class ValidatorStore { builder: { enabled: defaultConfig.builder?.enabled ?? false, gasLimit: defaultConfig.builder?.gasLimit ?? defaultOptions.defaultGasLimit, + selection: defaultConfig.builder?.selection ?? defaultOptions.builderSelection, }, }; @@ -206,7 +215,11 @@ export class ValidatorStore { } isBuilderEnabled(pubkeyHex: PubkeyHex): boolean { - return (this.validators.get(pubkeyHex)?.builder || {}).enabled ?? this.defaultProposerConfig?.builder.enabled; + return (this.validators.get(pubkeyHex)?.builder || {}).enabled ?? this.defaultProposerConfig.builder.enabled; + } + + getBuilderSelection(pubkeyHex: PubkeyHex): BuilderSelection { + return (this.validators.get(pubkeyHex)?.builder || {}).selection ?? this.defaultProposerConfig.builder.selection; } strictFeeRecipientCheck(pubkeyHex: PubkeyHex): boolean { diff --git a/packages/validator/src/slashingProtection/attestation/attestationLowerBoundRepository.ts b/packages/validator/src/slashingProtection/attestation/attestationLowerBoundRepository.ts index 1b89f76036b3..6de901e470c4 100644 --- a/packages/validator/src/slashingProtection/attestation/attestationLowerBoundRepository.ts +++ b/packages/validator/src/slashingProtection/attestation/attestationLowerBoundRepository.ts @@ -4,7 +4,6 @@ import {ContainerType, Type} from "@chainsafe/ssz"; import {LodestarValidatorDatabaseController} from "../../types.js"; // Only used locally here -// eslint-disable-next-line @typescript-eslint/naming-convention export interface SlashingProtectionLowerBound { minSourceEpoch: Epoch; minTargetEpoch: Epoch; diff --git a/packages/validator/src/slashingProtection/types.ts b/packages/validator/src/slashingProtection/types.ts index 584c66e665d4..03e3b850d7b0 100644 --- a/packages/validator/src/slashingProtection/types.ts +++ b/packages/validator/src/slashingProtection/types.ts @@ -1,7 +1,5 @@ import {Epoch, Root, Slot} from "@lodestar/types"; -/* eslint-disable @typescript-eslint/naming-convention */ - export interface SlashingProtectionBlock { slot: Slot; signingRoot: Root; diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 44a0cbf20276..2abdfb7630dc 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -72,7 +72,7 @@ function getSpecCriticalParams(localConfig: IChainConfig): Record { const dbLocation = "./.__testdb_2"; const controller = new LevelDbController({name: dbLocation}, {}); diff --git a/packages/validator/test/spec/spec.test.ts b/packages/validator/test/spec/spec.test.ts index 02783a5a3500..343870a5d488 100644 --- a/packages/validator/test/spec/spec.test.ts +++ b/packages/validator/test/spec/spec.test.ts @@ -16,8 +16,6 @@ import { import {loadTestCases} from "../utils/spec.js"; import {SPEC_TEST_LOCATION} from "./params.js"; -/* eslint-disable no-console */ - describe("slashing-protection-interchange-tests", () => { const testCases = loadTestCases(path.join(SPEC_TEST_LOCATION, "/tests/generated")); const dbLocation = "./.__testdb"; diff --git a/packages/validator/test/unit/services/produceBlockwrapper.test.ts b/packages/validator/test/unit/services/produceBlockwrapper.test.ts new file mode 100644 index 000000000000..b7c0ba9d1355 --- /dev/null +++ b/packages/validator/test/unit/services/produceBlockwrapper.test.ts @@ -0,0 +1,98 @@ +import {expect} from "chai"; +import sinon from "sinon"; +import {createIChainForkConfig} from "@lodestar/config"; +import {config as mainnetConfig} from "@lodestar/config/default"; +import {ssz} from "@lodestar/types"; +import {ForkName} from "@lodestar/params"; +import {HttpStatusCode} from "@lodestar/api"; + +import {BlockProposingService} from "../../../src/services/block.js"; +import {ValidatorStore, BuilderSelection} from "../../../src/services/validatorStore.js"; +import {getApiClientStub} from "../../utils/apiStub.js"; +import {loggerVc} from "../../utils/logger.js"; +import {ClockMock} from "../../utils/clock.js"; + +describe("Produce Block with BuilderSelection", function () { + const sandbox = sinon.createSandbox(); + const api = getApiClientStub(sandbox); + const validatorStore = sinon.createStubInstance(ValidatorStore) as ValidatorStore & + sinon.SinonStubbedInstance; + + const config = createIChainForkConfig(mainnetConfig); + + const clock = new ClockMock(); + const blockService = new BlockProposingService(config, loggerVc, api, clock, validatorStore, null); + const produceBlockWrapper = blockService["produceBlockWrapper"]; + + const blindedBlock = ssz.bellatrix.BlindedBeaconBlock.defaultValue(); + const fullBlock = ssz.bellatrix.BeaconBlock.defaultValue(); + const randaoReveal = fullBlock.body.randaoReveal; + + let controller: AbortController; // To stop clock + beforeEach(() => (controller = new AbortController())); + afterEach(() => controller.abort()); + + // Testcase: BuilderSelection, builderBlockValue, engineBlockValue, selection + // null blockValue means the block was not produced + const testCases: [BuilderSelection, number | null, number | null, string][] = [ + [BuilderSelection.MaxProfit, 1, 0, "builder"], + [BuilderSelection.MaxProfit, 1, 2, "engine"], + [BuilderSelection.MaxProfit, null, 0, "engine"], + [BuilderSelection.MaxProfit, 0, null, "builder"], + + [BuilderSelection.BuilderAlways, 1, 2, "builder"], + [BuilderSelection.BuilderAlways, 1, 0, "builder"], + [BuilderSelection.BuilderAlways, null, 0, "engine"], + [BuilderSelection.BuilderAlways, 0, null, "builder"], + ]; + testCases.forEach(([builderSelection, builderBlockValue, engineBlockValue, finalSelection]) => { + it(`builder selection = ${builderSelection}, builder blockValue = ${builderBlockValue}, engine blockValue = ${engineBlockValue} - expected selection = ${finalSelection} `, async function () { + if (builderBlockValue !== null) { + api.validator.produceBlindedBlock.resolves({ + response: { + data: blindedBlock, + version: ForkName.bellatrix, + blockValue: BigInt(builderBlockValue), + }, + ok: true, + status: HttpStatusCode.OK, + }); + } else { + api.validator.produceBlindedBlock.throws(Error("not produced")); + } + + if (engineBlockValue !== null) { + api.validator.produceBlock.resolves({ + response: {data: fullBlock, blockValue: BigInt(engineBlockValue)}, + ok: true, + status: HttpStatusCode.OK, + }); + api.validator.produceBlockV2.resolves({ + response: { + data: fullBlock, + version: ForkName.bellatrix, + blockValue: BigInt(engineBlockValue), + }, + ok: true, + status: HttpStatusCode.OK, + }); + } else { + api.validator.produceBlock.throws(Error("not produced")); + api.validator.produceBlockV2.throws(Error("not produced")); + } + + const produceBlockOpts = { + strictFeeRecipientCheck: false, + expectedFeeRecipient: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + isBuilderEnabled: true, + builderSelection, + }; + const { + debugLogCtx: {source}, + } = ((await produceBlockWrapper(144897, randaoReveal, "", produceBlockOpts)) as unknown) as { + debugLogCtx: {source: string}; + }; + expect(source).to.equal(finalSelection, "blindedBlock must be returned"); + }); + }); +});