diff --git a/packages/api/src/client/config.ts b/packages/api/src/client/config.ts index 38e74abda96..23fdc61759c 100644 --- a/packages/api/src/client/config.ts +++ b/packages/api/src/client/config.ts @@ -7,7 +7,7 @@ import {Api, ReqTypes, routesData, getReqSerializers, getReturnTypes} from "../r */ export function getClient(config: IChainForkConfig, httpClient: IHttpClient): Api { const reqSerializers = getReqSerializers(); - const returnTypes = getReturnTypes(config); + const returnTypes = getReturnTypes(); // All routes return JSON, use a client auto-generator return generateGenericJsonClient(routesData, reqSerializers, returnTypes, httpClient); } diff --git a/packages/api/src/routes/config.ts b/packages/api/src/routes/config.ts index 686b80007eb..6285073621f 100644 --- a/packages/api/src/routes/config.ts +++ b/packages/api/src/routes/config.ts @@ -1,9 +1,9 @@ -import {IBeaconPreset, BeaconPreset, activePreset} from "@chainsafe/lodestar-params"; -import {IChainConfig, ChainConfig} from "@chainsafe/lodestar-config"; +import {BeaconPreset} from "@chainsafe/lodestar-params"; +import {IChainConfig} from "@chainsafe/lodestar-config"; import {Bytes32, Number64, phase0, ssz} from "@chainsafe/lodestar-types"; import {mapValues} from "@chainsafe/lodestar-utils"; -import {ByteVectorType, ContainerType, Json, Type} from "@chainsafe/ssz"; -import {ArrayOf, ContainerData, ReqEmpty, reqEmpty, ReturnTypes, ReqSerializers, RoutesData, TypeJson} from "../utils"; +import {ByteVectorType, ContainerType} from "@chainsafe/ssz"; +import {ArrayOf, ContainerData, ReqEmpty, reqEmpty, ReturnTypes, ReqSerializers, RoutesData, sameType} from "../utils"; // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes @@ -12,16 +12,7 @@ export type DepositContract = { address: Bytes32; }; -export type ISpec = IBeaconPreset & IChainConfig; - -// eslint-disable-next-line @typescript-eslint/naming-convention -export const Spec = new ContainerType({ - fields: { - ...BeaconPreset.fields, - ...ChainConfig.fields, - }, - expectedCase: "notransform", -}); +export type Spec = BeaconPreset & IChainConfig; export type Api = { /** @@ -37,15 +28,16 @@ export type Api = { getForkSchedule(): Promise<{data: phase0.Fork[]}>; /** - * Get spec params. - * Retrieve specification configuration used on this node. - * [Specification params list](https://github.com/ethereum/eth2.0-specs/blob/v1.0.0-rc.0/configs/mainnet/phase0.yaml) + * Retrieve specification configuration used on this node. The configuration should include: + * - Constants for all hard forks known by the beacon node, for example the [phase 0](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#constants) and [altair](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#constants) values + * - Presets for all hard forks supplied to the beacon node, for example the [phase 0](https://github.com/ethereum/eth2.0-specs/blob/dev/presets/mainnet/phase0.yaml) and [altair](https://github.com/ethereum/eth2.0-specs/blob/dev/presets/mainnet/altair.yaml) values + * - Configuration for the beacon node, for example the [mainnet](https://github.com/ethereum/eth2.0-specs/blob/dev/configs/mainnet.yaml) values * * Values are returned with following format: * - any value starting with 0x in the spec is returned as a hex string * - numeric values are returned as a quoted integer */ - getSpec(): Promise<{data: ISpec}>; + getSpec(): Promise<{data: Record}>; }; /** @@ -63,15 +55,8 @@ export function getReqSerializers(): ReqSerializers { return mapValues(routesData, () => reqEmpty); } -function withJsonFilled(dataType: Type, fillWith: Json): TypeJson<{data: T}> { - return { - toJson: ({data}, opts) => ({data: dataType.toJson(data, opts)}), - fromJson: ({data}: {data: Json}, opts) => ({data: dataType.fromJson(Object.assign({}, fillWith, data), opts)}), - }; -} - /* eslint-disable @typescript-eslint/naming-convention */ -export function getReturnTypes(config: IChainConfig): ReturnTypes { +export function getReturnTypes(): ReturnTypes { const DepositContract = new ContainerType({ fields: { chainId: ssz.Number64, @@ -87,6 +72,6 @@ export function getReturnTypes(config: IChainConfig): ReturnTypes { return { getDepositContract: ContainerData(DepositContract), getForkSchedule: ContainerData(ArrayOf(ssz.phase0.Fork)), - getSpec: withJsonFilled(Spec, Spec.toJson({...config, ...activePreset})), + getSpec: ContainerData(sameType()), }; } diff --git a/packages/api/src/utils/types.ts b/packages/api/src/utils/types.ts index ccfe7aba146..0a54fca39c9 100644 --- a/packages/api/src/utils/types.ts +++ b/packages/api/src/utils/types.ts @@ -1,4 +1,4 @@ -import {ContainerType, IJsonOptions, Json, ListType, Type} from "@chainsafe/ssz"; +import {IJsonOptions, Json, ListType, Type} from "@chainsafe/ssz"; import {ForkName} from "@chainsafe/lodestar-params"; import {IChainForkConfig} from "@chainsafe/lodestar-config"; import {objectToExpectedCase} from "@chainsafe/lodestar-utils"; @@ -111,8 +111,17 @@ export function ArrayOf(elementType: Type, limit = 1e6): ListType { * data: T * ``` */ -export function ContainerData(dataType: Type): ContainerType<{data: T}> { - return new ContainerType({fields: {data: dataType}, expectedCase: "notransform"}); +export function ContainerData(dataType: TypeJson): TypeJson<{data: T}> { + return { + toJson: ({data}, opts) => ({ + data: dataType.toJson(data, opts), + }), + fromJson: ({data}: {data: Json}, opts) => { + return { + data: dataType.fromJson(data, opts), + }; + }, + }; } /** diff --git a/packages/api/test/unit/config.test.ts b/packages/api/test/unit/config.test.ts index e8ec73eea0d..b77c8ffbdce 100644 --- a/packages/api/test/unit/config.test.ts +++ b/packages/api/test/unit/config.test.ts @@ -1,17 +1,19 @@ import {ssz} from "@chainsafe/lodestar-types"; -import {config} from "@chainsafe/lodestar-config/default"; -import {Api, ReqTypes, Spec} from "../../src/routes/config"; +import {chainConfigToJson} from "@chainsafe/lodestar-config"; +import {config, chainConfig} from "@chainsafe/lodestar-config/default"; +import {activePreset, presetToJson} from "@chainsafe/lodestar-params"; +import {Api, ReqTypes, getReturnTypes} from "../../src/routes/config"; import {getClient} from "../../src/client/config"; import {getRoutes} from "../../src/server/config"; import {runGenericServerTest} from "../utils/genericServerTest"; import {expect} from "chai"; +/* eslint-disable @typescript-eslint/naming-convention */ + describe("config", () => { - it("Spec casing check", function () { - const defaultSpec = Spec.defaultValue(); - const specJson = Spec.toJson(defaultSpec) as Record; - expect(specJson["SLOTS_PER_EPOCH"] !== undefined).to.be.true; - }); + const configJson = chainConfigToJson(chainConfig); + const presetJson = presetToJson(activePreset); + const jsonSpec = {...configJson, ...presetJson}; runGenericServerTest(config, getClient, getRoutes, { getDepositContract: { @@ -29,7 +31,24 @@ describe("config", () => { }, getSpec: { args: [], - res: {data: Spec.defaultValue()}, + res: {data: jsonSpec}, }, }); + + it("Serialize Partial Spec object", () => { + const returnTypes = getReturnTypes(); + + const partialJsonSpec: Record = { + PRESET_BASE: "mainnet", + DEPOSIT_CONTRACT_ADDRESS: "0xff50ed3d0ec03ac01d4c79aad74928bff48a7b2b", + GENESIS_FORK_VERSION: "0x00001020", + TERMINAL_TOTAL_DIFFICULTY: "115792089237316195423570985008687907853269984665640564039457584007913129639936", + MIN_GENESIS_TIME: "1606824000", + }; + + const jsonRes = returnTypes.getSpec.toJson({data: partialJsonSpec}); + const specRes = returnTypes.getSpec.fromJson(jsonRes); + + expect(specRes).to.deep.equal({data: partialJsonSpec}, "Wrong toJson -> fromJson"); + }); }); diff --git a/packages/cli/src/config/beaconParams.ts b/packages/cli/src/config/beaconParams.ts index fcc9c5f523f..a1a96ebbd17 100644 --- a/packages/cli/src/config/beaconParams.ts +++ b/packages/cli/src/config/beaconParams.ts @@ -1,12 +1,11 @@ import { - ChainConfig, + IChainConfig, createIChainForkConfig, createIChainConfig, IChainForkConfig, - IChainConfig, - parsePartialIChainConfigJson, + chainConfigFromJson, } from "@chainsafe/lodestar-config"; -import {writeFile, readFile} from "../util"; +import {readFile} from "../util"; import {getNetworkBeaconParams, NetworkName} from "../networks"; import {getGlobalPaths, IGlobalPaths} from "../paths/global"; import {IBeaconParamsUnparsed} from "./types"; @@ -77,10 +76,10 @@ export function getBeaconParams({network, paramsFile, additionalParamsCli}: IBea }); } -export function writeBeaconParams(filepath: string, params: IChainConfig): void { - writeFile(filepath, ChainConfig.toJson(params)); -} - function readBeaconParams(filepath: string): IBeaconParamsUnparsed { return readFile(filepath) ?? {}; } + +export function parsePartialIChainConfigJson(input: Record): Partial { + return chainConfigFromJson(input); +} diff --git a/packages/cli/src/options/paramsOptions.ts b/packages/cli/src/options/paramsOptions.ts index 229f92bded3..fdd41239390 100644 --- a/packages/cli/src/options/paramsOptions.ts +++ b/packages/cli/src/options/paramsOptions.ts @@ -1,7 +1,7 @@ import {Options} from "yargs"; +import {IChainConfig, chainConfigTypes} from "@chainsafe/lodestar-config"; import {IBeaconParamsUnparsed} from "../config/types"; import {ObjectKeys, ICliCommandOptions} from "../util"; -import {ChainConfig, IChainConfig} from "@chainsafe/lodestar-config"; // No options are statically declared // If an arbitraty key notation is used, it removes typesafety on most of this CLI arg parsing code. @@ -17,17 +17,14 @@ export type IParamsArgs = Record & ITerminalPowArgs; const getArgKey = (key: keyof IBeaconParamsUnparsed): string => `params.${key}`; export function parseBeaconParamsArgs(args: Record): IBeaconParamsUnparsed { - return ((ObjectKeys(ChainConfig.fields) as unknown) as (keyof IChainConfig)[]).reduce( - (beaconParams: Partial, key) => { - const value = args[getArgKey(key)]; - if (value != null) beaconParams[key] = value; - return beaconParams; - }, - {} - ); + return ObjectKeys(chainConfigTypes).reduce((beaconParams: Partial, key) => { + const value = args[getArgKey(key)]; + if (value != null) beaconParams[key] = value; + return beaconParams; + }, {}); } -const paramsOptionsByName = ((ObjectKeys(ChainConfig.fields) as unknown) as (keyof IChainConfig)[]).reduce( +const paramsOptionsByName = ObjectKeys(chainConfigTypes).reduce( (options: Record, key): Record => ({ ...options, [getArgKey(key)]: { diff --git a/packages/config/README.md b/packages/config/README.md index 9ece40c4f61..0265e1ab083 100644 --- a/packages/config/README.md +++ b/packages/config/README.md @@ -8,7 +8,6 @@ > This package is part of [ChainSafe's Lodestar](https://lodestar.chainsafe.io) project - Lodestar defines all [network configuration variables](https://github.com/ethereum/eth2.0-specs/tree/dev/configs) defined in the [Ethereum Consensus / Eth2 spec](https://github.com/ethereum/eth2.0-specs). This tooling may be used to configure testnets, ingest mainnet/testnet config variables, or be used in downstream Lodestar libraries. ## Installation @@ -50,7 +49,7 @@ chainConfig.SECONDS_PER_SLOT === 12; There are also utility functions to help create a `IChainConfig` from unknown input and partial configs. ```typescript -import {createIChainConfig, IChainConfig, parsePartialIChainConfigJson} from "@chainsafe/lodestar-config"; +import {createIChainConfig, IChainConfig, chainConfigFromJson} from "@chainsafe/lodestar-config"; // example config let chainConfigObj: Record = { @@ -76,9 +75,9 @@ let chainConfigObj: Record = { INACTIVITY_SCORE_RECOVERY_RATE: 16, ALTAIR_FORK_VERSION: "0x01004811", ALTAIR_FORK_EPOCH: 10, -} +}; -const partialChainConfig: Partial = parsePartialIChainConfigJson(chainConfigObj); +const partialChainConfig: Partial = chainConfigFromJson(chainConfigObj); // Fill in the missing values with mainnet default values const chainConfig: IChainConfig = createIChainConfig(partialChainConfig); @@ -88,7 +87,7 @@ const chainConfig: IChainConfig = createIChainConfig(partialChainConfig); The variables described in the spec can be used to assemble a more structured 'fork schedule'. This info is organized as `IForkConfig` in the Lodestar config package. In practice, the `IChainConfig` and `IForkConfig` are usually combined as a `IChainForkConfig`. -A `IForkConfig` provides methods to select the fork info, fork name, fork version, or fork ssz types given a slot. +A `IForkConfig` provides methods to select the fork info, fork name, fork version, or fork ssz types given a slot. ```typescript import {GENESIS_SLOT} from "@chainsafe/lodestar-params"; diff --git a/packages/config/src/chainConfig/index.ts b/packages/config/src/chainConfig/index.ts index cfb5b697bd2..747412957e4 100644 --- a/packages/config/src/chainConfig/index.ts +++ b/packages/config/src/chainConfig/index.ts @@ -1,11 +1,9 @@ -import {Json} from "@chainsafe/ssz"; import {ACTIVE_PRESET} from "@chainsafe/lodestar-params"; import {IChainConfig} from "./types"; -import {ChainConfig} from "./sszTypes"; import {defaultChainConfig} from "./default"; +export {chainConfigToJson, chainConfigFromJson} from "./json"; export * from "./types"; -export * from "./sszTypes"; export * from "./default"; /** @@ -27,20 +25,3 @@ export function createIChainConfig(input: Partial): IChainConfig { } return config; } - -export function parsePartialIChainConfigJson(input?: Record): Partial { - if (!input) { - return {}; - } - - const config = {}; - - // Parse config input values, if they exist - for (const [fieldName, fieldType] of Object.entries(ChainConfig.fields)) { - if (input[fieldName] != null) { - (config as Record)[fieldName] = fieldType.fromJson(input[fieldName] as Json); - } - } - - return config; -} diff --git a/packages/config/src/chainConfig/json.ts b/packages/config/src/chainConfig/json.ts new file mode 100644 index 00000000000..751e087d9b8 --- /dev/null +++ b/packages/config/src/chainConfig/json.ts @@ -0,0 +1,78 @@ +import {fromHexString, toHexString} from "@chainsafe/ssz"; +import {IChainConfig, chainConfigTypes, SpecValue, SpecValueTypeName} from "./types"; + +const MAX_UINT64_JSON = "18446744073709551615"; + +export function chainConfigToJson(config: IChainConfig): Record { + const json: Record = {}; + + for (const key of Object.keys(config) as (keyof IChainConfig)[]) { + json[key] = serializeSpecValue(config[key], chainConfigTypes[key]); + } + + return json; +} + +export function chainConfigFromJson(json: Record): IChainConfig { + const config = {} as IChainConfig; + + for (const key of Object.keys(json) as (keyof IChainConfig)[]) { + config[key] = deserializeSpecValue(json[key], chainConfigTypes[key]) as never; + } + + return config; +} + +export function serializeSpecValue(value: SpecValue, typeName: SpecValueTypeName): string { + switch (typeName) { + case "number": + if (typeof value !== "number") { + throw Error(`Invalid value ${value} expected number`); + } + if (value === Infinity) { + return MAX_UINT64_JSON; + } + return value.toString(10); + + case "bigint": + if (typeof value !== "bigint") { + throw Error(`Invalid value ${value} expected bigint`); + } + return value.toString(10); + + case "bytes": + if (!(value instanceof Uint8Array)) { + throw Error(`Invalid value ${value} expected Uint8Array`); + } + return toHexString(value); + + case "string": + if (typeof value !== "string") { + throw Error(`Invalid value ${value} expected string`); + } + return value; + } +} + +export function deserializeSpecValue(valueStr: unknown, typeName: SpecValueTypeName): SpecValue { + if (typeof valueStr !== "string") { + throw Error(`Invalid value ${valueStr} expected string`); + } + + switch (typeName) { + case "number": + if (valueStr === MAX_UINT64_JSON) { + return Infinity; + } + return parseInt(valueStr, 10); + + case "bigint": + return BigInt(valueStr); + + case "bytes": + return fromHexString(valueStr); + + case "string": + return valueStr; + } +} diff --git a/packages/config/src/chainConfig/networks/mainnet.ts b/packages/config/src/chainConfig/networks/mainnet.ts index b382c986613..ebdd6a174dd 100644 --- a/packages/config/src/chainConfig/networks/mainnet.ts +++ b/packages/config/src/chainConfig/networks/mainnet.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {fromHexString as b} from "@chainsafe/ssz"; -import {IChainConfig} from ".."; +import {IChainConfig} from "../types"; import {chainConfig as mainnet} from "../presets/mainnet"; /* eslint-disable max-len */ diff --git a/packages/config/src/chainConfig/networks/prater.ts b/packages/config/src/chainConfig/networks/prater.ts index 4c4f8e472a2..085d1c9231b 100644 --- a/packages/config/src/chainConfig/networks/prater.ts +++ b/packages/config/src/chainConfig/networks/prater.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {fromHexString as b} from "@chainsafe/ssz"; -import {IChainConfig} from ".."; +import {IChainConfig} from "../types"; import {chainConfig as mainnet} from "../presets/mainnet"; /* eslint-disable max-len */ diff --git a/packages/config/src/chainConfig/networks/pyrmont.ts b/packages/config/src/chainConfig/networks/pyrmont.ts index 1c2889e0837..168290622b4 100644 --- a/packages/config/src/chainConfig/networks/pyrmont.ts +++ b/packages/config/src/chainConfig/networks/pyrmont.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {fromHexString as b} from "@chainsafe/ssz"; -import {IChainConfig} from ".."; +import {IChainConfig} from "../types"; import {chainConfig as mainnet} from "../presets/mainnet"; /* eslint-disable max-len */ diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index a7dfc16bfd3..b5a03aa5981 100644 --- a/packages/config/src/chainConfig/types.ts +++ b/packages/config/src/chainConfig/types.ts @@ -1,10 +1,11 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import {PresetName} from "@chainsafe/lodestar-params"; +/* eslint-disable @typescript-eslint/naming-convention */ + /** * Run-time chain configuration */ -export interface IChainConfig { +export type IChainConfig = { PRESET_BASE: PresetName; // Transition @@ -42,10 +43,81 @@ export interface IChainConfig { EJECTION_BALANCE: number; MIN_PER_EPOCH_CHURN_LIMIT: number; CHURN_LIMIT_QUOTIENT: number; + + // Proposer boost PROPOSER_SCORE_BOOST: number; // Deposit contract DEPOSIT_CHAIN_ID: number; DEPOSIT_NETWORK_ID: number; DEPOSIT_CONTRACT_ADDRESS: Uint8Array; -} +}; + +export const chainConfigTypes: SpecTypes = { + PRESET_BASE: "string", + + // Transition + TERMINAL_TOTAL_DIFFICULTY: "bigint", + TERMINAL_BLOCK_HASH: "bytes", + TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: "number", + + // Genesis + MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: "number", + MIN_GENESIS_TIME: "number", + GENESIS_FORK_VERSION: "bytes", + GENESIS_DELAY: "number", + + // Forking + // Altair + ALTAIR_FORK_VERSION: "bytes", + ALTAIR_FORK_EPOCH: "number", + // Bellatrix + BELLATRIX_FORK_VERSION: "bytes", + BELLATRIX_FORK_EPOCH: "number", + // Sharding + SHARDING_FORK_VERSION: "bytes", + SHARDING_FORK_EPOCH: "number", + + // Time parameters + SECONDS_PER_SLOT: "number", + SECONDS_PER_ETH1_BLOCK: "number", + MIN_VALIDATOR_WITHDRAWABILITY_DELAY: "number", + SHARD_COMMITTEE_PERIOD: "number", + ETH1_FOLLOW_DISTANCE: "number", + + // Validator cycle + INACTIVITY_SCORE_BIAS: "number", + INACTIVITY_SCORE_RECOVERY_RATE: "number", + EJECTION_BALANCE: "number", + MIN_PER_EPOCH_CHURN_LIMIT: "number", + CHURN_LIMIT_QUOTIENT: "number", + + // Proposer boost + PROPOSER_SCORE_BOOST: "number", + + // Deposit contract + DEPOSIT_CHAIN_ID: "number", + DEPOSIT_NETWORK_ID: "number", + DEPOSIT_CONTRACT_ADDRESS: "bytes", +}; + +/** Allows values in a Spec file */ +export type SpecValue = number | bigint | Uint8Array | string; + +/** Type value name of each spec field. Numbers are ignored since they are the most common */ +export type SpecValueType = V extends number + ? "number" + : V extends bigint + ? "bigint" + : V extends Uint8Array + ? "bytes" + : V extends string + ? "string" + : never; + +/** All possible type names for a SpecValue */ +export type SpecValueTypeName = SpecValueType; + +export type SpecTypes> = { + [K in keyof Spec]: SpecValueType; +}; diff --git a/packages/config/test/unit/json.test.ts b/packages/config/test/unit/json.test.ts new file mode 100644 index 00000000000..9b614baebb9 --- /dev/null +++ b/packages/config/test/unit/json.test.ts @@ -0,0 +1,12 @@ +import {expect} from "chai"; +import {chainConfigFromJson, chainConfigToJson} from "../../src"; +import {chainConfig} from "../../src/default"; + +describe("chainConfig JSON", () => { + it("Convert to and from JSON", () => { + const json = chainConfigToJson(chainConfig); + const chainConfigRes = chainConfigFromJson(json); + + expect(chainConfigRes).to.deep.equal(chainConfig); + }); +}); diff --git a/packages/lodestar/src/api/impl/config/index.ts b/packages/lodestar/src/api/impl/config/index.ts index ed8d5ea69a2..645eed237c8 100644 --- a/packages/lodestar/src/api/impl/config/index.ts +++ b/packages/lodestar/src/api/impl/config/index.ts @@ -1,17 +1,27 @@ import {routes} from "@chainsafe/lodestar-api"; -import {ChainConfig} from "@chainsafe/lodestar-config"; -import * as params from "@chainsafe/lodestar-params"; +import {chainConfigToJson} from "@chainsafe/lodestar-config"; +import {activePreset, presetToJson} from "@chainsafe/lodestar-params"; import {ApiModules} from "../types"; export function getConfigApi({config}: Pick): routes.config.Api { - const specWithExtraKeys = {...params, ...config}; - const spec = {} as routes.config.ISpec; - ([ - ...Object.keys(ChainConfig.fields), - ...Object.keys(params.BeaconPreset.fields), - ] as (keyof routes.config.ISpec)[]).forEach((fieldName) => { - ((spec as unknown) as Record)[fieldName] = specWithExtraKeys[fieldName]; - }); + // Retrieve specification configuration used on this node. The configuration should include: + // - Constants for all hard forks known by the beacon node, for example the + // [phase 0](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#constants) and + // [altair](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#constants) values + // - Presets for all hard forks supplied to the beacon node, for example the + // [phase 0](https://github.com/ethereum/eth2.0-specs/blob/dev/presets/mainnet/phase0.yaml) and + // [altair](https://github.com/ethereum/eth2.0-specs/blob/dev/presets/mainnet/altair.yaml) values + // - Configuration for the beacon node, for example the [mainnet](https://github.com/ethereum/eth2.0-specs/blob/dev/configs/mainnet.yaml) values + + let jsonSpec: Record | null = null; + function getJsonSpec(): Record { + // TODO: Include static constants exported in @chainsafe/lodestar-params (i.e. DOMAIN_BEACON_PROPOSER) + const configJson = chainConfigToJson(config); + const presetJson = presetToJson(activePreset); + jsonSpec = {...configJson, ...presetJson}; + return jsonSpec; + } + return { async getForkSchedule() { const forkInfos = Object.values(config.forks); @@ -34,7 +44,7 @@ export function getConfigApi({config}: Pick): routes.confi async getSpec() { return { - data: spec, + data: getJsonSpec(), }; }, }; diff --git a/packages/lodestar/test/unit/api/impl/config/config.test.ts b/packages/lodestar/test/unit/api/impl/config/config.test.ts index aabb8583c3b..991ddd70e04 100644 --- a/packages/lodestar/test/unit/api/impl/config/config.test.ts +++ b/packages/lodestar/test/unit/api/impl/config/config.test.ts @@ -1,9 +1,6 @@ -// eslint-disable-next-line no-restricted-imports -import {IChainConfig} from "@chainsafe/lodestar-config"; import {config} from "@chainsafe/lodestar-config/default"; import {expect} from "chai"; import {getConfigApi} from "../../../../../src/api/impl/config"; -import {toJson} from "@chainsafe/lodestar-utils"; describe("config api implementation", function () { let api: ReturnType; @@ -29,11 +26,10 @@ describe("config api implementation", function () { describe("getSpec", function () { it("should get the spec", async function () { - const {data: spec} = await api.getSpec(); - const specJson = (spec as unknown) as IChainConfig; + const {data: specJson} = await api.getSpec(); - expect(toJson(specJson.SECONDS_PER_ETH1_BLOCK)).to.equal(14, "Wrong SECONDS_PER_ETH1_BLOCK"); - expect(toJson(specJson.DEPOSIT_CONTRACT_ADDRESS)).to.equal( + expect(specJson.SECONDS_PER_ETH1_BLOCK).to.equal("14", "Wrong SECONDS_PER_ETH1_BLOCK"); + expect(specJson.DEPOSIT_CONTRACT_ADDRESS).to.equal( "0x1234567890123456789012345678901234567890", "Wrong DEPOSIT_CONTRACT_ADDRESS" ); diff --git a/packages/params/package.json b/packages/params/package.json index a8feeb9158c..60fb0f80757 100644 --- a/packages/params/package.json +++ b/packages/params/package.json @@ -42,9 +42,7 @@ "beacon", "blockchain" ], - "dependencies": { - "@chainsafe/ssz": "^0.8.20" - }, + "dependencies": {}, "devDependencies": { "@types/js-yaml": "^4.0.3", "axios": "^0.21.0", diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index 3348aecb6f8..faf11b59251 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -1,11 +1,13 @@ -import {PresetName} from "./preset"; +import {PresetName} from "./presetName"; import {preset as mainnet} from "./presets/mainnet"; import {preset as minimal} from "./presets/minimal"; import {presetStatus} from "./presetStatus"; import {userSelectedPreset} from "./setPreset"; -export * from "./preset"; +export * from "./interface"; export {ForkName} from "./forkName"; +export {presetToJson} from "./json"; +export {PresetName}; const presets = { [PresetName.mainnet]: mainnet, @@ -23,7 +25,7 @@ presetStatus.frozen = true; * * The active preset can be manually overridden with `setActivePreset` */ -export const ACTIVE_PRESET: PresetName = +export const ACTIVE_PRESET = userSelectedPreset || PresetName[process?.env?.LODESTAR_PRESET as PresetName] || PresetName.mainnet; export const activePreset = presets[ACTIVE_PRESET]; diff --git a/packages/params/src/preset/altair/interface.ts b/packages/params/src/interface/altair.ts similarity index 90% rename from packages/params/src/preset/altair/interface.ts rename to packages/params/src/interface/altair.ts index a2b6c89a917..e0b28756001 100644 --- a/packages/params/src/preset/altair/interface.ts +++ b/packages/params/src/interface/altair.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ - -export interface IAltairPreset { +export type AltairPreset = { SYNC_COMMITTEE_SIZE: number; EPOCHS_PER_SYNC_COMMITTEE_PERIOD: number; INACTIVITY_PENALTY_QUOTIENT_ALTAIR: number; @@ -8,4 +7,4 @@ export interface IAltairPreset { PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: number; MIN_SYNC_COMMITTEE_PARTICIPANTS: number; UPDATE_TIMEOUT: number; -} +}; diff --git a/packages/params/src/preset/bellatrix/interface.ts b/packages/params/src/interface/bellatrix.ts similarity index 90% rename from packages/params/src/preset/bellatrix/interface.ts rename to packages/params/src/interface/bellatrix.ts index d40d639737e..e250e4cbda2 100644 --- a/packages/params/src/preset/bellatrix/interface.ts +++ b/packages/params/src/interface/bellatrix.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ - -export interface IBellatrixPreset { +export type BellatrixPreset = { INACTIVITY_PENALTY_QUOTIENT_BELLATRIX: number; MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX: number; PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX: number; @@ -8,4 +7,4 @@ export interface IBellatrixPreset { MAX_TRANSACTIONS_PER_PAYLOAD: number; BYTES_PER_LOGS_BLOOM: number; MAX_EXTRA_DATA_BYTES: number; -} +}; diff --git a/packages/params/src/interface/index.ts b/packages/params/src/interface/index.ts new file mode 100644 index 00000000000..8b344c57a41 --- /dev/null +++ b/packages/params/src/interface/index.ts @@ -0,0 +1,5 @@ +import {Phase0Preset} from "./phase0"; +import {AltairPreset} from "./altair"; +import {BellatrixPreset} from "./bellatrix"; + +export type BeaconPreset = Phase0Preset & AltairPreset & BellatrixPreset; diff --git a/packages/params/src/preset/phase0/interface.ts b/packages/params/src/interface/phase0.ts similarity index 95% rename from packages/params/src/preset/phase0/interface.ts rename to packages/params/src/interface/phase0.ts index 16935f9ff4f..bbf4189bfdf 100644 --- a/packages/params/src/preset/phase0/interface.ts +++ b/packages/params/src/interface/phase0.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ - -export interface IPhase0Preset { +export type Phase0Preset = { // Misc MAX_COMMITTEES_PER_SLOT: number; TARGET_COMMITTEE_SIZE: number; @@ -16,7 +15,7 @@ export interface IPhase0Preset { SAFE_SLOTS_TO_UPDATE_JUSTIFIED: number; // Gwei Values - MIN_DEPOSIT_AMOUNT: bigint; + MIN_DEPOSIT_AMOUNT: number; MAX_EFFECTIVE_BALANCE: number; EFFECTIVE_BALANCE_INCREMENT: number; @@ -49,4 +48,4 @@ export interface IPhase0Preset { MAX_ATTESTATIONS: number; MAX_DEPOSITS: number; MAX_VOLUNTARY_EXITS: number; -} +}; diff --git a/packages/params/src/json.ts b/packages/params/src/json.ts new file mode 100644 index 00000000000..99abc3730bd --- /dev/null +++ b/packages/params/src/json.ts @@ -0,0 +1,23 @@ +import {BeaconPreset} from "./interface"; + +/** + * Render BeaconPreset to JSON strings + * - Numbers: Render as a quoted decimal string + */ +export function presetToJson(preset: BeaconPreset): Record { + const json: Record = {}; + + for (const key of Object.keys(preset) as (keyof BeaconPreset)[]) { + json[key] = serializePresetValue(preset[key]); + } + + return json; +} + +/** + * Type Wrapper to ensure that all values of BeaconPreset are number. + * If there are more types, expand this function with a type switch + */ +function serializePresetValue(value: number): string { + return value.toString(10); +} diff --git a/packages/params/src/preset/altair/index.ts b/packages/params/src/preset/altair/index.ts deleted file mode 100644 index 20616ee7a7f..00000000000 --- a/packages/params/src/preset/altair/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./interface"; -export * from "./ssz"; diff --git a/packages/params/src/preset/altair/ssz.ts b/packages/params/src/preset/altair/ssz.ts deleted file mode 100644 index ce0e303cda7..00000000000 --- a/packages/params/src/preset/altair/ssz.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ - -import {ContainerType, NumberUintType} from "@chainsafe/ssz"; - -import {IAltairPreset} from "./interface"; - -const Number64 = new NumberUintType({byteLength: 8}); - -export const AltairPreset = new ContainerType({ - fields: { - SYNC_COMMITTEE_SIZE: Number64, - EPOCHS_PER_SYNC_COMMITTEE_PERIOD: Number64, - INACTIVITY_PENALTY_QUOTIENT_ALTAIR: Number64, - MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR: Number64, - PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: Number64, - MIN_SYNC_COMMITTEE_PARTICIPANTS: Number64, - UPDATE_TIMEOUT: Number64, - }, - // Expected and container fields are the same here - expectedCase: "notransform", -}); diff --git a/packages/params/src/preset/bellatrix/index.ts b/packages/params/src/preset/bellatrix/index.ts deleted file mode 100644 index 20616ee7a7f..00000000000 --- a/packages/params/src/preset/bellatrix/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./interface"; -export * from "./ssz"; diff --git a/packages/params/src/preset/bellatrix/ssz.ts b/packages/params/src/preset/bellatrix/ssz.ts deleted file mode 100644 index 5080e8cbf9d..00000000000 --- a/packages/params/src/preset/bellatrix/ssz.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ - -import {ContainerType, NumberUintType} from "@chainsafe/ssz"; - -import {IBellatrixPreset} from "./interface"; - -const Number64 = new NumberUintType({byteLength: 8}); - -export const BellatrixPreset = new ContainerType({ - fields: { - INACTIVITY_PENALTY_QUOTIENT_BELLATRIX: Number64, - MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX: Number64, - PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX: Number64, - MAX_BYTES_PER_TRANSACTION: Number64, - MAX_TRANSACTIONS_PER_PAYLOAD: Number64, - BYTES_PER_LOGS_BLOOM: Number64, - MAX_EXTRA_DATA_BYTES: Number64, - }, - // Expected and container fields are the same here - expectedCase: "notransform", -}); diff --git a/packages/params/src/preset/index.ts b/packages/params/src/preset/index.ts deleted file mode 100644 index 803a60a277e..00000000000 --- a/packages/params/src/preset/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./presetName"; -export * from "./interface"; -export * from "./ssz"; - -export * from "./phase0"; -export * from "./altair"; -export * from "./bellatrix"; diff --git a/packages/params/src/preset/interface.ts b/packages/params/src/preset/interface.ts deleted file mode 100644 index ece659102d0..00000000000 --- a/packages/params/src/preset/interface.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/** - * @module params - */ - -import {IPhase0Preset} from "./phase0"; -import {IAltairPreset} from "./altair"; -import {IBellatrixPreset} from "./bellatrix"; - -export type IBeaconPreset = IPhase0Preset & IAltairPreset & IBellatrixPreset; diff --git a/packages/params/src/preset/phase0/index.ts b/packages/params/src/preset/phase0/index.ts deleted file mode 100644 index 20616ee7a7f..00000000000 --- a/packages/params/src/preset/phase0/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./interface"; -export * from "./ssz"; diff --git a/packages/params/src/preset/phase0/ssz.ts b/packages/params/src/preset/phase0/ssz.ts deleted file mode 100644 index 40f54595664..00000000000 --- a/packages/params/src/preset/phase0/ssz.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ - -import {IPhase0Preset} from "./interface"; -import {ContainerType, BigIntUintType, NumberUintType} from "@chainsafe/ssz"; - -const Number64 = new NumberUintType({byteLength: 8}); -const BigInt64 = new BigIntUintType({byteLength: 8}); - -export const Phase0Preset = new ContainerType({ - fields: { - // Misc - MAX_COMMITTEES_PER_SLOT: Number64, - TARGET_COMMITTEE_SIZE: Number64, - MAX_VALIDATORS_PER_COMMITTEE: Number64, - SHUFFLE_ROUND_COUNT: Number64, - HYSTERESIS_QUOTIENT: Number64, - HYSTERESIS_DOWNWARD_MULTIPLIER: Number64, - HYSTERESIS_UPWARD_MULTIPLIER: Number64, - - // Fork choice - SAFE_SLOTS_TO_UPDATE_JUSTIFIED: Number64, - - // Gwei Values - MIN_DEPOSIT_AMOUNT: BigInt64, - MAX_EFFECTIVE_BALANCE: Number64, - EFFECTIVE_BALANCE_INCREMENT: Number64, - - // Time parameters - MIN_ATTESTATION_INCLUSION_DELAY: Number64, - SLOTS_PER_EPOCH: Number64, - MIN_SEED_LOOKAHEAD: Number64, - MAX_SEED_LOOKAHEAD: Number64, - EPOCHS_PER_ETH1_VOTING_PERIOD: Number64, - SLOTS_PER_HISTORICAL_ROOT: Number64, - MIN_EPOCHS_TO_INACTIVITY_PENALTY: Number64, - - // State vector lengths - EPOCHS_PER_HISTORICAL_VECTOR: Number64, - EPOCHS_PER_SLASHINGS_VECTOR: Number64, - HISTORICAL_ROOTS_LIMIT: Number64, - VALIDATOR_REGISTRY_LIMIT: Number64, - - // Reward and penalty quotients - BASE_REWARD_FACTOR: Number64, - WHISTLEBLOWER_REWARD_QUOTIENT: Number64, - PROPOSER_REWARD_QUOTIENT: Number64, - INACTIVITY_PENALTY_QUOTIENT: Number64, - MIN_SLASHING_PENALTY_QUOTIENT: Number64, - PROPORTIONAL_SLASHING_MULTIPLIER: Number64, - - // Max operations per block - MAX_PROPOSER_SLASHINGS: Number64, - MAX_ATTESTER_SLASHINGS: Number64, - MAX_ATTESTATIONS: Number64, - MAX_DEPOSITS: Number64, - MAX_VOLUNTARY_EXITS: Number64, - }, - // Expected and container fields are the same here - expectedCase: "notransform", -}); diff --git a/packages/params/src/preset/ssz.ts b/packages/params/src/preset/ssz.ts deleted file mode 100644 index afbf3c9588b..00000000000 --- a/packages/params/src/preset/ssz.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ - -import {ContainerType} from "@chainsafe/ssz"; - -import {IBeaconPreset} from "./interface"; -import {Phase0Preset} from "./phase0"; -import {AltairPreset} from "./altair"; -import {BellatrixPreset} from "./bellatrix"; - -export const BeaconPreset = new ContainerType({ - fields: { - ...Phase0Preset.fields, - ...AltairPreset.fields, - ...BellatrixPreset.fields, - }, - expectedCase: "notransform", -}); diff --git a/packages/params/src/preset/presetName.ts b/packages/params/src/presetName.ts similarity index 100% rename from packages/params/src/preset/presetName.ts rename to packages/params/src/presetName.ts diff --git a/packages/params/src/presets/mainnet/altair.ts b/packages/params/src/presets/mainnet/altair.ts index 04badb8d252..dd1b4f2b7b9 100644 --- a/packages/params/src/presets/mainnet/altair.ts +++ b/packages/params/src/presets/mainnet/altair.ts @@ -1,7 +1,7 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import {IAltairPreset} from "../../preset"; +import {AltairPreset} from "../../interface/altair"; -export const altair: IAltairPreset = { +/* eslint-disable @typescript-eslint/naming-convention */ +export const altair: AltairPreset = { SYNC_COMMITTEE_SIZE: 512, EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 256, INACTIVITY_PENALTY_QUOTIENT_ALTAIR: 50331648, diff --git a/packages/params/src/presets/mainnet/bellatrix.ts b/packages/params/src/presets/mainnet/bellatrix.ts index 7e2d146d74e..9923389949e 100644 --- a/packages/params/src/presets/mainnet/bellatrix.ts +++ b/packages/params/src/presets/mainnet/bellatrix.ts @@ -1,7 +1,7 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import {IBellatrixPreset} from "../../preset"; +import {BellatrixPreset} from "../../interface/bellatrix"; -export const bellatrix: IBellatrixPreset = { +/* eslint-disable @typescript-eslint/naming-convention */ +export const bellatrix: BellatrixPreset = { INACTIVITY_PENALTY_QUOTIENT_BELLATRIX: 16777216, MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX: 32, PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX: 3, diff --git a/packages/params/src/presets/mainnet/index.ts b/packages/params/src/presets/mainnet/index.ts index 95822b8f849..d2af3ded2d6 100644 --- a/packages/params/src/presets/mainnet/index.ts +++ b/packages/params/src/presets/mainnet/index.ts @@ -1,12 +1,11 @@ -import {IBeaconPreset} from "../../preset"; - import {phase0} from "./phase0"; import {altair} from "./altair"; import {bellatrix} from "./bellatrix"; +import {BeaconPreset} from "../../interface"; export const commit = "v1.1.8"; -export const preset: IBeaconPreset = { +export const preset: BeaconPreset = { ...phase0, ...altair, ...bellatrix, diff --git a/packages/params/src/presets/mainnet/phase0.ts b/packages/params/src/presets/mainnet/phase0.ts index 65083f73f39..469499ec7c4 100644 --- a/packages/params/src/presets/mainnet/phase0.ts +++ b/packages/params/src/presets/mainnet/phase0.ts @@ -1,9 +1,7 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -// Mainnet preset - -import {IPhase0Preset} from "../../preset"; +import {Phase0Preset} from "../../interface/phase0"; -export const phase0: IPhase0Preset = { +/* eslint-disable @typescript-eslint/naming-convention */ +export const phase0: Phase0Preset = { // Misc // --------------------------------------------------------------- // 2**6 (= 64) @@ -29,7 +27,7 @@ export const phase0: IPhase0Preset = { // Gwei values // --------------------------------------------------------------- // 2**0 * 10**9 (= 1,000,000,000) Gwei - MIN_DEPOSIT_AMOUNT: BigInt(1000000000), + MIN_DEPOSIT_AMOUNT: 1000000000, // 2**5 * 10**9 (= 32,000,000,000) Gwei MAX_EFFECTIVE_BALANCE: 32000000000, // 2**0 * 10**9 (= 1,000,000,000) Gwei diff --git a/packages/params/src/presets/mainnet/phase1.ts b/packages/params/src/presets/mainnet/phase1.ts index 74fa67bfda1..7f255c6ff20 100644 --- a/packages/params/src/presets/mainnet/phase1.ts +++ b/packages/params/src/presets/mainnet/phase1.ts @@ -1,6 +1,4 @@ /* eslint-disable @typescript-eslint/naming-convention */ - -// Mainnet preset - phase 1 export const phase1Json = { CONFIG_NAME: "mainnet", diff --git a/packages/params/src/presets/minimal/altair.ts b/packages/params/src/presets/minimal/altair.ts index a2c6ded4726..0c6713ea6a0 100644 --- a/packages/params/src/presets/minimal/altair.ts +++ b/packages/params/src/presets/minimal/altair.ts @@ -1,7 +1,7 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import {IAltairPreset} from "../../preset"; +import {AltairPreset} from "../../interface/altair"; -export const altair: IAltairPreset = { +/* eslint-disable @typescript-eslint/naming-convention */ +export const altair: AltairPreset = { SYNC_COMMITTEE_SIZE: 32, EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 8, INACTIVITY_PENALTY_QUOTIENT_ALTAIR: 50331648, diff --git a/packages/params/src/presets/minimal/bellatrix.ts b/packages/params/src/presets/minimal/bellatrix.ts index 7e2d146d74e..9923389949e 100644 --- a/packages/params/src/presets/minimal/bellatrix.ts +++ b/packages/params/src/presets/minimal/bellatrix.ts @@ -1,7 +1,7 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import {IBellatrixPreset} from "../../preset"; +import {BellatrixPreset} from "../../interface/bellatrix"; -export const bellatrix: IBellatrixPreset = { +/* eslint-disable @typescript-eslint/naming-convention */ +export const bellatrix: BellatrixPreset = { INACTIVITY_PENALTY_QUOTIENT_BELLATRIX: 16777216, MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX: 32, PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX: 3, diff --git a/packages/params/src/presets/minimal/index.ts b/packages/params/src/presets/minimal/index.ts index 95822b8f849..d2af3ded2d6 100644 --- a/packages/params/src/presets/minimal/index.ts +++ b/packages/params/src/presets/minimal/index.ts @@ -1,12 +1,11 @@ -import {IBeaconPreset} from "../../preset"; - import {phase0} from "./phase0"; import {altair} from "./altair"; import {bellatrix} from "./bellatrix"; +import {BeaconPreset} from "../../interface"; export const commit = "v1.1.8"; -export const preset: IBeaconPreset = { +export const preset: BeaconPreset = { ...phase0, ...altair, ...bellatrix, diff --git a/packages/params/src/presets/minimal/phase0.ts b/packages/params/src/presets/minimal/phase0.ts index 65b9640cb55..1e92726784f 100644 --- a/packages/params/src/presets/minimal/phase0.ts +++ b/packages/params/src/presets/minimal/phase0.ts @@ -1,9 +1,7 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -// Minimal preset - -import {IPhase0Preset} from "../../preset"; +import {Phase0Preset} from "../../interface/phase0"; -export const phase0: IPhase0Preset = { +/* eslint-disable @typescript-eslint/naming-convention */ +export const phase0: Phase0Preset = { // Misc // --------------------------------------------------------------- // [customized] Just 4 committees for slot for testing purposes @@ -29,7 +27,7 @@ export const phase0: IPhase0Preset = { // Gwei values // --------------------------------------------------------------- // 2**0 * 10**9 (= 1,000,000,000) Gwei - MIN_DEPOSIT_AMOUNT: BigInt(1000000000), + MIN_DEPOSIT_AMOUNT: 1000000000, // 2**5 * 10**9 (= 32,000,000,000) Gwei MAX_EFFECTIVE_BALANCE: 32000000000, // 2**0 * 10**9 (= 1,000,000,000) Gwei diff --git a/packages/params/src/presets/minimal/phase1.ts b/packages/params/src/presets/minimal/phase1.ts index 93173568752..b2bfa8b51be 100644 --- a/packages/params/src/presets/minimal/phase1.ts +++ b/packages/params/src/presets/minimal/phase1.ts @@ -1,6 +1,4 @@ /* eslint-disable @typescript-eslint/naming-convention */ - -// Minimal preset - phase 1 export const phase1Json = { CONFIG_NAME: "minimal", diff --git a/packages/params/src/setPreset.ts b/packages/params/src/setPreset.ts index ad78eaf68db..79315263471 100644 --- a/packages/params/src/setPreset.ts +++ b/packages/params/src/setPreset.ts @@ -1,4 +1,4 @@ -import {PresetName} from "./preset"; +import {PresetName} from "./presetName"; import {presetStatus} from "./presetStatus"; export {PresetName}; diff --git a/packages/params/src/utils.ts b/packages/params/src/utils.ts deleted file mode 100644 index 068449bb966..00000000000 --- a/packages/params/src/utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {Json} from "@chainsafe/ssz"; - -import {IBeaconPreset, BeaconPreset} from "./preset"; - -export function createIBeaconPreset(input: Record): Partial { - const params: Partial = {}; - for (const [fieldName, fieldType] of Object.entries(BeaconPreset.fields)) { - if (input[fieldName] != null) { - try { - (params as Record)[fieldName] = fieldType.fromJson(input[fieldName] as Json) as unknown; - } catch (e) { - (e as Error).message = `Error parsing '${fieldName}': ${(e as Error).message}`; - throw e; - } - } - } - return params; -} 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 e6792b268aa..a289c2c93f2 100644 --- a/packages/params/test/e2e/ensure-config-is-synced.test.ts +++ b/packages/params/test/e2e/ensure-config-is-synced.test.ts @@ -1,14 +1,36 @@ import {expect} from "chai"; import axios from "axios"; -import {createIBeaconPreset} from "../../src/utils"; import * as mainnet from "../../src/presets/mainnet"; import * as minimal from "../../src/presets/minimal"; -import {ForkName} from "../../src"; +import {ForkName, BeaconPreset} from "../../src"; import {loadConfigYaml} from "../yaml"; // Not e2e, but slow. Run with e2e tests -async function downloadRemoteConfig(preset: "mainnet" | "minimal", commit: string): Promise> { +describe("Ensure config is synced", function () { + this.timeout(60 * 1000); + + it("mainnet", async function () { + const remotePreset = await downloadRemoteConfig("mainnet", mainnet.commit); + assertCorrectPreset({...mainnet.preset}, remotePreset); + }); + + it("minimal", async function () { + const remotePreset = await downloadRemoteConfig("minimal", minimal.commit); + assertCorrectPreset({...minimal.preset}, remotePreset); + }); +}); + +function assertCorrectPreset(localPreset: BeaconPreset, remotePreset: BeaconPreset): void { + // Check each key for better debuggability + for (const key of Object.keys(remotePreset) as (keyof BeaconPreset)[]) { + expect(localPreset[key]).to.equal(remotePreset[key], `Wrong ${key} value`); + } + + expect(localPreset).to.deep.equal(remotePreset); +} + +async function downloadRemoteConfig(preset: "mainnet" | "minimal", commit: string): Promise { const urlByFork: Record = { [ForkName.phase0]: `https://raw.githubusercontent.com/ethereum/eth2.0-specs/${commit}/presets/${preset}/phase0.yaml`, [ForkName.altair]: `https://raw.githubusercontent.com/ethereum/eth2.0-specs/${commit}/presets/${preset}/altair.yaml`, @@ -23,38 +45,15 @@ async function downloadRemoteConfig(preset: "mainnet" | "minimal", commit: strin // Merge all the fetched yamls for the different forks // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const configParams = Object.assign(...((downloadedParams as unknown) as [input: Record])); - - return createIBeaconPreset(configParams); -} - -describe("Ensure config is synced", function () { - this.timeout(60 * 1000); + const beaconPresetRaw: Record = Object.assign( + ...((downloadedParams as unknown) as [input: Record]) + ); - // TODO: Remove items from this list as the specs are updated - // Items added here are intentionally either not added, or are different - // eslint-disable-next-line prettier/prettier - const blacklist: string[] = [ - "SYNC_COMMITTEE_SIZE" - ]; + // As of December 2021 the presets don't include any hex strings + const beaconPreset = {} as BeaconPreset; + for (const key of Object.keys(beaconPresetRaw)) { + beaconPreset[key as keyof BeaconPreset] = parseInt(beaconPresetRaw[key] as string, 10); + } - it("mainnet", async function () { - const remotePreset = await downloadRemoteConfig("mainnet", mainnet.commit); - const localPreset = {...mainnet.preset}; - for (const param of blacklist) { - delete remotePreset[param]; - delete (localPreset as Record)[param]; - } - expect(localPreset).to.deep.equal(remotePreset); - }); - - it("minimal", async function () { - const remotePreset = await downloadRemoteConfig("minimal", minimal.commit); - const localPreset = {...minimal.preset}; - for (const param of blacklist) { - delete remotePreset[param]; - delete (localPreset as Record)[param]; - } - expect(localPreset).to.deep.equal(remotePreset); - }); -}); + return beaconPreset; +} diff --git a/packages/params/test/unit/activePreset.test.ts b/packages/params/test/unit/activePreset.test.ts index 810751574c9..774a14241f6 100644 --- a/packages/params/test/unit/activePreset.test.ts +++ b/packages/params/test/unit/activePreset.test.ts @@ -1,7 +1,8 @@ import {preset as mainnetParams} from "../../src/presets/mainnet"; import {preset as minimalParams} from "../../src/presets/minimal"; import {ACTIVE_PRESET, PresetName} from "../../src"; -import {setActivePreset} from "../../setPreset"; +import {setActivePreset} from "../../src/setPreset"; +import {setActivePreset as setActivePresetLib} from "../../setPreset"; import {expect} from "chai"; describe("active preset", () => { @@ -31,7 +32,10 @@ describe("active preset", () => { it("Should not allow to change preset", () => { expect(() => { + // I'm not sure if mocha is requiring from src or lib. Each file has different state. + // To ensure this throws, call setActivePreset on both the src and lib file. setActivePreset(PresetName.minimal); + setActivePresetLib(PresetName.minimal); }).to.throw(); }); }); diff --git a/packages/params/test/unit/constants.test.ts b/packages/types/test/unit/constants.test.ts similarity index 77% rename from packages/params/test/unit/constants.test.ts rename to packages/types/test/unit/constants.test.ts index 4ce7075adbc..67540b4bc4a 100644 --- a/packages/params/test/unit/constants.test.ts +++ b/packages/types/test/unit/constants.test.ts @@ -1,9 +1,13 @@ -import * as constants from "../../src"; -import {ssz} from "@chainsafe/lodestar-types"; +import * as constants from "@chainsafe/lodestar-params"; +import {ssz} from "../../src"; import {expect} from "chai"; /* 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. + describe("Lightclient pre-computed constants", () => { const FINALIZED_ROOT_GINDEX = Number(ssz.altair.BeaconState.getPathGindex(["finalizedCheckpoint", "root"])); const FINALIZED_ROOT_DEPTH = floorlog2(FINALIZED_ROOT_GINDEX); diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 57e31f8887f..669418c967c 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -1,20 +1,18 @@ -import {ChainConfig, IChainConfig} from "@chainsafe/lodestar-config"; +import {IChainConfig, chainConfigToJson} from "@chainsafe/lodestar-config"; export class NotEqualParamsError extends Error {} /** - * Assert that two IBeaconParams are identical. Throws error otherwise + * Assert localConfig values match externalSpecJson. externalSpecJson may contain more values than localConfig. */ -export function assertEqualParams(currentParams: IChainConfig, expectedParams: IChainConfig): void { - const params1Json = ChainConfig.toJson(currentParams) as Record; - const params2Json = ChainConfig.toJson(expectedParams) as Record; - const keys = new Set([...Object.keys(params1Json), ...Object.keys(params2Json)]); +export function assertEqualParams(localConfig: IChainConfig, externalSpecJson: Record): void { + const params1Json = chainConfigToJson(localConfig) as Record; + const params2Json = externalSpecJson; const errors: string[] = []; - for (const key of keys) { - if (!params1Json[key]) errors.push(`${key} not in current params`); - if (!params2Json[key]) errors.push(`${key} not in expected params`); + // Ensure only that the localConfig values match the remote spec + for (const key of Object.keys(params1Json)) { if (params1Json[key] !== params2Json[key]) errors.push(`${key} different value: ${params1Json[key]} != ${params2Json[key]}`); } diff --git a/packages/validator/src/validator.ts b/packages/validator/src/validator.ts index 73883642110..b1e023e8054 100644 --- a/packages/validator/src/validator.ts +++ b/packages/validator/src/validator.ts @@ -94,11 +94,13 @@ export class Validator { const genesis = await waitForGenesis(api, opts.logger, signal); opts.logger.info("Genesis available"); - const {data: nodeParams} = await api.config.getSpec(); - assertEqualParams(config, nodeParams); + const {data: externalSpecJson} = await api.config.getSpec(); + assertEqualParams(config, externalSpecJson); opts.logger.info("Verified node and validator have same config"); + await assertEqualGenesis(opts, genesis); opts.logger.info("Verified node and validator have same genesisValidatorRoot"); + return new Validator(opts, genesis); } diff --git a/packages/validator/test/unit/utils/params.test.ts b/packages/validator/test/unit/utils/params.test.ts index 682bccac22e..2e1ae9c53b0 100644 --- a/packages/validator/test/unit/utils/params.test.ts +++ b/packages/validator/test/unit/utils/params.test.ts @@ -1,15 +1,21 @@ -import {createIChainConfig} from "@chainsafe/lodestar-config"; +import {chainConfigToJson} from "@chainsafe/lodestar-config"; import {chainConfig} from "@chainsafe/lodestar-config/default"; import {expect} from "chai"; import {assertEqualParams, NotEqualParamsError} from "../../../src/util/params"; describe("utils / params / assertEqualParams", () => { it("default == default", () => { - assertEqualParams(chainConfig, chainConfig); + const chainConfigJson = chainConfigToJson(chainConfig); + assertEqualParams(chainConfig, chainConfigJson); }); + it("default != other", () => { + const chainConfigJson = chainConfigToJson(chainConfig); + + // Force ALTAIR_FORK_EPOCH value to be different // eslint-disable-next-line @typescript-eslint/naming-convention - const otherConfig = createIChainConfig({...chainConfig, ALTAIR_FORK_EPOCH: 0}); + const otherConfig = {...chainConfigJson, ALTAIR_FORK_EPOCH: String(chainConfig.ALTAIR_FORK_EPOCH + 1)}; + expect(() => assertEqualParams(chainConfig, otherConfig)).to.throw(NotEqualParamsError); }); });