diff --git a/packages/lodestar/test/spec/allForks/epochProcessing.ts b/packages/lodestar/test/spec/allForks/epochProcessing.ts index 95cb237f6ebd..be5215df8eab 100644 --- a/packages/lodestar/test/spec/allForks/epochProcessing.ts +++ b/packages/lodestar/test/spec/allForks/epochProcessing.ts @@ -13,6 +13,9 @@ import {IBaseSpecTest} from "../type"; export type EpochProcessFn = (state: CachedBeaconStateAllForks, epochProcess: allForks.IEpochProcess) => void; +/** + * https://github.com/ethereum/consensus-specs/blob/dev/tests/formats/epoch_processing/README.md + */ type EpochProcessingStateTestCase = IBaseSpecTest & { pre: allForks.BeaconState; post: allForks.BeaconState; diff --git a/packages/lodestar/test/spec/allForks/finality.ts b/packages/lodestar/test/spec/allForks/finality.ts index f48a12f38d79..2ea8838a4b1b 100644 --- a/packages/lodestar/test/spec/allForks/finality.ts +++ b/packages/lodestar/test/spec/allForks/finality.ts @@ -2,14 +2,16 @@ import {join} from "node:path"; import {TreeBacked} from "@chainsafe/ssz"; import {CachedBeaconStateAllForks, allForks, altair} from "@chainsafe/lodestar-beacon-state-transition"; import {describeDirectorySpecTest} from "@chainsafe/lodestar-spec-test-util"; -import {bellatrix, ssz, Uint64} from "@chainsafe/lodestar-types"; +import {bellatrix, ssz} from "@chainsafe/lodestar-types"; import {ACTIVE_PRESET, ForkName} from "@chainsafe/lodestar-params"; import {SPEC_TEST_LOCATION} from "../specTestVersioning"; -import {IBaseSpecTest} from "../type"; +import {IBaseSpecTest, shouldVerify} from "../type"; import {expectEqualBeaconState, inputTypeSszTreeBacked} from "../util"; import {getConfig} from "./util"; import {generateBlocksSZZTypeMapping} from "./sanity"; +/* eslint-disable @typescript-eslint/naming-convention */ + export function finality(fork: ForkName): void { describeDirectorySpecTest( `${ACTIVE_PRESET}/${fork}/finality/finality`, @@ -19,8 +21,8 @@ export function finality(fork: ForkName): void { getConfig(fork), testcase.pre as TreeBacked ) as CachedBeaconStateAllForks; - const verify = testcase.meta !== undefined && testcase.meta.blsSetting === BigInt(1); - for (let i = 0; i < Number(testcase.meta.blocksCount); i++) { + const verify = shouldVerify(testcase); + for (let i = 0; i < testcase.meta.blocks_count; i++) { const signedBlock = testcase[`blocks_${i}`] as bellatrix.SignedBeaconBlock; wrappedState = allForks.stateTransition( @@ -52,11 +54,18 @@ export function finality(fork: ForkName): void { ); } +/** + * `meta.yaml` + * ``` + * {blocks_count: 16} + * ``` + * https://github.com/ethereum/consensus-specs/blob/dev/tests/formats/finality/README.md + */ interface IFinalityTestCase extends IBaseSpecTest { [k: string]: altair.SignedBeaconBlock | unknown | null | undefined; meta: { - blocksCount: Uint64; - blsSetting: BigInt; + blocks_count: number; + bls_setting: bigint; }; pre: altair.BeaconState; post?: altair.BeaconState; diff --git a/packages/lodestar/test/spec/allForks/forkChoice.ts b/packages/lodestar/test/spec/allForks/forkChoice.ts index fbef9273bcc4..2a76120abed4 100644 --- a/packages/lodestar/test/spec/allForks/forkChoice.ts +++ b/packages/lodestar/test/spec/allForks/forkChoice.ts @@ -23,11 +23,13 @@ import {ChainEventEmitter} from "@chainsafe/lodestar/lib/chain/emitter"; import {toHexString} from "@chainsafe/ssz"; import {CheckpointWithHex, IForkChoice} from "@chainsafe/lodestar-fork-choice"; import {ssz, RootHex} from "@chainsafe/lodestar-types"; +import {bnToNum} from "@chainsafe/lodestar-utils"; import {ACTIVE_PRESET, SLOTS_PER_EPOCH, ForkName} from "@chainsafe/lodestar-params"; import {SPEC_TEST_LOCATION} from "../specTestVersioning"; -import {IBaseSpecTest} from "../type"; import {getConfig} from "./util"; +/* 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]+)$"; @@ -57,7 +59,7 @@ export function forkChoiceTest(fork: ForkName): void { for (const [i, step] of steps.entries()) { if (isTick(step)) { - tickTime = Number(step.tick); + tickTime = bnToNum(step.tick); forkchoice.updateTime(Math.floor(tickTime / config.SECONDS_PER_SLOT)); } @@ -84,52 +86,43 @@ export function forkChoiceTest(fork: ForkName): void { // checks step else if (isCheck(step)) { - const { - head: expectedHead, - time: expectedTime, - justifiedCheckpoint, - finalizedCheckpoint, - bestJustifiedCheckpoint, - proposerBoostRoot: expectedProposerBoostRoot, - } = step.checks; - // Forkchoice head is computed lazily only on request const head = forkchoice.updateHead(); const proposerBootRoot = forkchoice.getProposerBoostRoot(); - if (expectedHead !== undefined) { - expect(head.slot).to.be.equal(Number(expectedHead.slot), `Invalid head slot at step ${i}`); - expect(head.blockRoot).to.be.equal(expectedHead.root, `Invalid head root at step ${i}`); + if (step.checks.head !== undefined) { + expect(head.slot).to.be.equal(bnToNum(step.checks.head.slot), `Invalid head slot at step ${i}`); + expect(head.blockRoot).to.be.equal(step.checks.head.root, `Invalid head root at step ${i}`); } - if (expectedProposerBoostRoot !== undefined) { + if (step.checks.proposer_boost_root !== undefined) { expect(proposerBootRoot).to.be.equal( - expectedProposerBoostRoot, + step.checks.proposer_boost_root, `Invalid proposer boost root at step ${i}` ); } // time in spec mapped to Slot in our forkchoice implementation. // Compare in slots because proposer boost steps doesn't always come on // slot boundary. - if (expectedTime !== undefined && expectedTime > 0) + if (step.checks.time !== undefined && step.checks.time > 0) expect(forkchoice.getTime()).to.be.equal( - Math.floor(Number(expectedTime) / config.SECONDS_PER_SLOT), + Math.floor(bnToNum(step.checks.time) / config.SECONDS_PER_SLOT), `Invalid forkchoice time at step ${i}` ); - if (justifiedCheckpoint) { + if (step.checks.justified_checkpoint) { expect(toSpecTestCheckpoint(forkchoice.getJustifiedCheckpoint())).to.be.deep.equal( - justifiedCheckpoint, + step.checks.justified_checkpoint, `Invalid justified checkpoint at step ${i}` ); } - if (finalizedCheckpoint) { + if (step.checks.finalized_checkpoint) { expect(toSpecTestCheckpoint(forkchoice.getFinalizedCheckpoint())).to.be.deep.equal( - finalizedCheckpoint, + step.checks.finalized_checkpoint, `Invalid finalized checkpoint at step ${i}` ); } - if (bestJustifiedCheckpoint) { + if (step.checks.best_justified_checkpoint) { expect(toSpecTestCheckpoint(forkchoice.getBestJustifiedCheckpoint())).to.be.deep.equal( - bestJustifiedCheckpoint, + step.checks.best_justified_checkpoint, `Invalid best justified checkpoint at step ${i}` ); } @@ -277,7 +270,7 @@ type SpecTestCheckpoint = {epoch: BigInt; root: string}; type OnTick = { /** to execute `on_tick(store, time)` */ - tick: number; + tick: bigint; /** optional, default to `true`. */ valid?: number; }; @@ -297,19 +290,22 @@ type OnBlock = { type Checks = { /** Value in the ForkChoice store to verify it's correct after being mutated by another step */ checks: { - head: {slot: number; root: string}; - time?: number; - justifiedCheckpoint?: SpecTestCheckpoint; - finalizedCheckpoint?: SpecTestCheckpoint; - bestJustifiedCheckpoint?: SpecTestCheckpoint; - proposerBoostRoot?: RootHex; + head?: { + slot: bigint; + root: string; + }; + time?: bigint; + justified_checkpoint?: SpecTestCheckpoint; + finalized_checkpoint?: SpecTestCheckpoint; + best_justified_checkpoint?: SpecTestCheckpoint; + proposer_boost_root?: RootHex; }; }; -interface IForkChoiceTestCase extends IBaseSpecTest { +interface IForkChoiceTestCase { meta?: { description?: string; - blsSetting: BigInt; + bls_setting: BigInt; }; anchorState: allForks.BeaconState; anchorBlock: allForks.BeaconBlock; diff --git a/packages/lodestar/test/spec/allForks/genesis.ts b/packages/lodestar/test/spec/allForks/genesis.ts index 1a856057f0e8..7f4258ee19f4 100644 --- a/packages/lodestar/test/spec/allForks/genesis.ts +++ b/packages/lodestar/test/spec/allForks/genesis.ts @@ -4,6 +4,7 @@ import {phase0, Uint64, Root, ssz, allForks, bellatrix} from "@chainsafe/lodesta import {TreeBacked} from "@chainsafe/ssz"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; import {initializeBeaconStateFromEth1, isValidGenesisState} from "@chainsafe/lodestar-beacon-state-transition"; +import {bnToNum} from "@chainsafe/lodestar-utils"; import {ACTIVE_PRESET, ForkName} from "@chainsafe/lodestar-params"; import {SPEC_TEST_LOCATION} from "../specTestVersioning"; import {expectEqualBeaconState} from "../util"; @@ -18,7 +19,7 @@ export function genesis(fork: ForkName): void { join(SPEC_TEST_LOCATION, `/tests/${ACTIVE_PRESET}/${fork}/genesis/initialization/pyspec_tests`), (testcase) => { const deposits: phase0.Deposit[] = []; - for (let i = 0; i < Number(testcase.meta.depositsCount); i++) { + for (let i = 0; i < testcase.meta.deposits_count; i++) { deposits.push(testcase[`deposits_${i}`] as phase0.Deposit); } let executionPayloadHeader: TreeBacked | undefined = undefined; @@ -29,8 +30,8 @@ export function genesis(fork: ForkName): void { } return initializeBeaconStateFromEth1( getConfig(fork), - ssz.Root.fromJson((testcase.eth1 as IGenesisInitCase).eth1BlockHash), - Number((testcase.eth1 as IGenesisInitCase).eth1Timestamp), + ssz.Root.fromJson((testcase.eth1 as IGenesisInitCase).eth1_block_hash), + bnToNum((testcase.eth1 as IGenesisInitCase).eth1_timestamp), deposits, undefined, executionPayloadHeader @@ -90,15 +91,15 @@ interface IGenesisInitSpecTest { eth1_block_hash: Root; eth1_timestamp: Uint64; meta: { - depositsCount: Uint64; + deposits_count: number; }; execution_payload_header?: bellatrix.ExecutionPayloadHeader; state: phase0.BeaconState; } interface IGenesisInitCase { - eth1BlockHash: any; - eth1Timestamp: any; + eth1_block_hash: string; + eth1_timestamp: bigint; } interface IGenesisValidityTestCase extends IBaseSpecTest { diff --git a/packages/lodestar/test/spec/allForks/merkle.ts b/packages/lodestar/test/spec/allForks/merkle.ts index 7c8f975dd5e2..b7595fd184b8 100644 --- a/packages/lodestar/test/spec/allForks/merkle.ts +++ b/packages/lodestar/test/spec/allForks/merkle.ts @@ -9,6 +9,8 @@ import {IBaseSpecTest} from "../type"; import {verifyMerkleBranch} from "@chainsafe/lodestar-utils"; import {expect} from "chai"; +/* eslint-disable @typescript-eslint/naming-convention */ + export function merkle(fork: ForkName): void { describeDirectorySpecTest( `${ACTIVE_PRESET}/${fork}/transition`, @@ -19,16 +21,17 @@ export function merkle(fork: ForkName): void { const stateRoot = stateTB.hashTreeRoot(); const leaf = fromHexString(specTestProof.leaf); const branch = specTestProof.branch.map((item) => fromHexString(item)); - const depth = Math.floor(Math.log2(Number(specTestProof.leafIndex))); - const verified = verifyMerkleBranch(leaf, branch, depth, Number(specTestProof.leafIndex) % 2 ** depth, stateRoot); + const leafIndex = Number(specTestProof.leaf_index); + const depth = Math.floor(Math.log2(leafIndex)); + const verified = verifyMerkleBranch(leaf, branch, depth, leafIndex % 2 ** depth, stateRoot); expect(verified, "cannot verify merkle branch").to.be.true; const lodestarProof = stateTB.tree.getProof({ - gindex: specTestProof.leafIndex, + gindex: specTestProof.leaf_index, type: ProofType.single, }) as SingleProof; return { leaf: toHexString(lodestarProof.leaf), - leafIndex: lodestarProof.gindex, + leaf_index: lodestarProof.gindex, branch: lodestarProof.witnesses.map(toHexString), }; }, @@ -57,7 +60,7 @@ export function merkle(fork: ForkName): void { interface IProof { leaf: string; - leafIndex: bigint; + leaf_index: bigint; branch: string[]; } } diff --git a/packages/lodestar/test/spec/allForks/operations.ts b/packages/lodestar/test/spec/allForks/operations.ts index 083d576917b2..17b8c9a565a4 100644 --- a/packages/lodestar/test/spec/allForks/operations.ts +++ b/packages/lodestar/test/spec/allForks/operations.ts @@ -20,7 +20,7 @@ export type BlockProcessFn = ( export type OperationsTestCase = IBaseSpecTest & { pre: BeaconState; post: BeaconState; - execution: {executionValid: boolean}; + execution: {execution_valid: boolean}; }; export function operations( diff --git a/packages/lodestar/test/spec/allForks/sanity.ts b/packages/lodestar/test/spec/allForks/sanity.ts index f48d8d38c237..a5258d86d902 100644 --- a/packages/lodestar/test/spec/allForks/sanity.ts +++ b/packages/lodestar/test/spec/allForks/sanity.ts @@ -2,13 +2,16 @@ import {join} from "node:path"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; import {allForks} from "@chainsafe/lodestar-beacon-state-transition"; import {TreeBacked} from "@chainsafe/ssz"; -import {bellatrix, ssz, Uint64} from "@chainsafe/lodestar-types"; +import {bellatrix, ssz} from "@chainsafe/lodestar-types"; import {ACTIVE_PRESET, ForkName} from "@chainsafe/lodestar-params"; +import {bnToNum} from "@chainsafe/lodestar-utils"; import {expectEqualBeaconState, inputTypeSszTreeBacked} from "../util"; import {SPEC_TEST_LOCATION} from "../specTestVersioning"; -import {IBaseSpecTest} from "../type"; +import {IBaseSpecTest, shouldVerify} from "../type"; import {getConfig} from "./util"; +/* eslint-disable @typescript-eslint/naming-convention */ + export function sanity(fork: ForkName): void { sanitySlot(fork); sanityBlock(fork, `/tests/${ACTIVE_PRESET}/${fork}/sanity/blocks/pyspec_tests`); @@ -21,7 +24,7 @@ export function sanitySlot(fork: ForkName): void { (testcase) => { const stateTB = (testcase.pre as TreeBacked).clone(); const state = allForks.createCachedBeaconState(getConfig(fork), stateTB); - const postState = allForks.processSlots(state, state.slot + Number(testcase.slots)); + const postState = allForks.processSlots(state, state.slot + bnToNum(testcase.slots)); return postState.type.createTreeBacked(postState.tree); }, { @@ -47,8 +50,8 @@ export function sanityBlock(fork: ForkName, testPath: string): void { (testcase) => { const stateTB = testcase.pre as TreeBacked; let wrappedState = allForks.createCachedBeaconState(getConfig(fork), stateTB); - const verify = testcase.meta !== undefined && testcase.meta.blsSetting === BigInt(1); - for (let i = 0; i < Number(testcase.meta.blocksCount); i++) { + const verify = shouldVerify(testcase); + for (let i = 0; i < testcase.meta.blocks_count; i++) { const signedBlock = testcase[`blocks_${i}`] as bellatrix.SignedBeaconBlock; wrappedState = allForks.stateTransition( wrappedState, @@ -92,8 +95,8 @@ export function generateBlocksSZZTypeMapping(fork: ForkName, n: number): BlocksS interface IBlockSanityTestCase extends IBaseSpecTest { [k: string]: allForks.SignedBeaconBlock | unknown | null | undefined; meta: { - blocksCount: Uint64; - blsSetting: BigInt; + blocks_count: number; + bls_setting: bigint; }; pre: allForks.BeaconState; post: allForks.BeaconState; @@ -102,5 +105,5 @@ interface IBlockSanityTestCase extends IBaseSpecTest { interface IProcessSlotsTestCase extends IBaseSpecTest { pre: allForks.BeaconState; post?: allForks.BeaconState; - slots: Uint64; + slots: bigint; } diff --git a/packages/lodestar/test/spec/allForks/transition.ts b/packages/lodestar/test/spec/allForks/transition.ts index 183a1f2c496b..32d8537f8163 100644 --- a/packages/lodestar/test/spec/allForks/transition.ts +++ b/packages/lodestar/test/spec/allForks/transition.ts @@ -1,6 +1,6 @@ import {join} from "node:path"; import {allForks} from "@chainsafe/lodestar-beacon-state-transition"; -import {Uint64, Epoch, ssz, phase0} from "@chainsafe/lodestar-types"; +import {ssz, phase0} from "@chainsafe/lodestar-types"; import {describeDirectorySpecTest} from "@chainsafe/lodestar-spec-test-util"; import {createIChainForkConfig, IChainConfig} from "@chainsafe/lodestar-config"; import {ForkName, ACTIVE_PRESET} from "@chainsafe/lodestar-params"; @@ -8,6 +8,7 @@ import {TreeBacked} from "@chainsafe/ssz"; import {SPEC_TEST_LOCATION} from "../specTestVersioning"; import {IBaseSpecTest} from "../type"; import {expectEqualBeaconState, inputTypeSszTreeBacked} from "../util"; +import {bnToNum} from "@chainsafe/lodestar-utils"; type CreateTreeBackedSignedBlock = (block: allForks.SignedBeaconBlock) => TreeBacked; const createTreeBackedSignedBlockByFork: Record = { @@ -33,13 +34,12 @@ export function transition( join(SPEC_TEST_LOCATION, `/tests/${ACTIVE_PRESET}/${fork}/transition/core/pyspec_tests`), (testcase) => { const meta = testcase.meta; - const {forkEpoch, blocksCount, forkBlock} = meta; // testConfig is used here to load forkEpoch from meta.yaml - const testConfig = createIChainForkConfig(forkConfig(Number(forkEpoch))); + const testConfig = createIChainForkConfig(forkConfig(bnToNum(meta.fork_epoch))); let wrappedState = allForks.createCachedBeaconState(testConfig, testcase.pre as TreeBacked); - for (let i = 0; i < Number(blocksCount); i++) { + for (let i = 0; i < meta.blocks_count; i++) { let tbSignedBlock: TreeBacked; - if (i <= forkBlock) { + if (i <= meta.fork_block) { const signedBlock = testcase[`blocks_${i}`] as allForks.SignedBeaconBlock; tbSignedBlock = createTreeBackedSignedBlockByFork[pre](signedBlock); } else { @@ -81,8 +81,8 @@ export function transition( } const blocksMapping: BlocksSZZTypeMapping = {}; // The fork_block is the index in the test data of the last block of the initial fork. - for (let i = 0; i < meta.blocksCount; i++) { - blocksMapping[`blocks_${i}`] = i <= meta.forkBlock ? ssz[pre].SignedBeaconBlock : ssz[fork].SignedBeaconBlock; + for (let i = 0; i < meta.blocks_count; i++) { + blocksMapping[`blocks_${i}`] = i <= meta.fork_block ? ssz[pre].SignedBeaconBlock : ssz[fork].SignedBeaconBlock; } return blocksMapping; } @@ -91,14 +91,15 @@ export function transition( type BlocksSZZTypeMapping = Record; type PostBeaconState = Exclude; +/* eslint-disable @typescript-eslint/naming-convention */ interface ITransitionTestCase extends IBaseSpecTest { [k: string]: allForks.SignedBeaconBlock | unknown | null | undefined; meta: { - postFork: ForkName; - forkEpoch: Epoch; - forkBlock: Uint64; - blocksCount: Uint64; - blsSetting?: BigInt; + post_fork: ForkName; + fork_epoch: bigint; + fork_block: bigint; + blocks_count: bigint; + bls_setting?: bigint; }; pre: allForks.BeaconState; post: PostBeaconState; diff --git a/packages/lodestar/test/spec/altair/operations.test.ts b/packages/lodestar/test/spec/altair/operations.test.ts index 4d2b16782f47..cacb20ad546d 100644 --- a/packages/lodestar/test/spec/altair/operations.test.ts +++ b/packages/lodestar/test/spec/altair/operations.test.ts @@ -1,7 +1,7 @@ import {CachedBeaconStateAllForks, allForks, altair} from "@chainsafe/lodestar-beacon-state-transition"; import {phase0, ssz} from "@chainsafe/lodestar-types"; import {ForkName} from "@chainsafe/lodestar-params"; -import {IBaseSpecTest} from "../type"; +import {IBaseSpecTest, shouldVerify} from "../type"; import {operations, BlockProcessFn} from "../allForks/operations"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -26,8 +26,7 @@ operations(ForkName.altair, { }, attester_slashing: (state, testCase: IBaseSpecTest & {attester_slashing: phase0.AttesterSlashing}) => { - const verify = !!testCase.meta && !!testCase.meta.blsSetting && testCase.meta.blsSetting === BigInt(1); - altair.processAttesterSlashing(state, testCase.attester_slashing, verify); + altair.processAttesterSlashing(state, testCase.attester_slashing, shouldVerify(testCase)); }, block_header: (state, testCase: IBaseSpecTest & {block: altair.BeaconBlock}) => { diff --git a/packages/lodestar/test/spec/bellatrix/operations.test.ts b/packages/lodestar/test/spec/bellatrix/operations.test.ts index 606686da6c01..cbdcf492471d 100644 --- a/packages/lodestar/test/spec/bellatrix/operations.test.ts +++ b/packages/lodestar/test/spec/bellatrix/operations.test.ts @@ -8,7 +8,7 @@ import { } from "@chainsafe/lodestar-beacon-state-transition"; import {phase0, ssz} from "@chainsafe/lodestar-types"; import {ForkName} from "@chainsafe/lodestar-params"; -import {IBaseSpecTest} from "../type"; +import {IBaseSpecTest, shouldVerify} from "../type"; import {operations, BlockProcessFn} from "../allForks/operations"; // eslint-disable-next-line no-restricted-imports import {processExecutionPayload} from "@chainsafe/lodestar-beacon-state-transition/lib/bellatrix/block/processExecutionPayload"; @@ -35,8 +35,7 @@ operations(ForkName.bellatrix, { }, attester_slashing: (state, testCase: IBaseSpecTest & {attester_slashing: phase0.AttesterSlashing}) => { - const verify = !!testCase.meta && !!testCase.meta.blsSetting && testCase.meta.blsSetting === BigInt(1); - bellatrix.processAttesterSlashing(state, testCase.attester_slashing, verify); + bellatrix.processAttesterSlashing(state, testCase.attester_slashing, shouldVerify(testCase)); }, block_header: (state, testCase: IBaseSpecTest & {block: altair.BeaconBlock}) => { @@ -60,10 +59,10 @@ operations(ForkName.bellatrix, { execution_payload: ( state, - testCase: IBaseSpecTest & {execution_payload: bellatrix.ExecutionPayload; execution: {executionValid: boolean}} + testCase: IBaseSpecTest & {execution_payload: bellatrix.ExecutionPayload; execution: {execution_valid: boolean}} ) => { processExecutionPayload((state as unknown) as CachedBeaconStateBellatrix, testCase.execution_payload, { - executePayload: () => testCase.execution.executionValid, + executePayload: () => testCase.execution.execution_valid, }); }, }); diff --git a/packages/lodestar/test/spec/phase0/operations.test.ts b/packages/lodestar/test/spec/phase0/operations.test.ts index 6de5f484a3b3..ac0865d6223c 100644 --- a/packages/lodestar/test/spec/phase0/operations.test.ts +++ b/packages/lodestar/test/spec/phase0/operations.test.ts @@ -1,6 +1,6 @@ import {CachedBeaconStateAllForks, allForks, phase0} from "@chainsafe/lodestar-beacon-state-transition"; import {ForkName} from "@chainsafe/lodestar-params"; -import {IBaseSpecTest} from "../type"; +import {IBaseSpecTest, shouldVerify} from "../type"; import {operations} from "../allForks/operations"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -12,8 +12,7 @@ operations(ForkName.phase0, { }, attester_slashing: (state, testCase: IBaseSpecTest & {attester_slashing: phase0.AttesterSlashing}) => { - const verify = !!testCase.meta && !!testCase.meta.blsSetting && testCase.meta.blsSetting === BigInt(1); - phase0.processAttesterSlashing(state, testCase.attester_slashing, verify); + phase0.processAttesterSlashing(state, testCase.attester_slashing, shouldVerify(testCase)); }, block_header: (state, testCase: IBaseSpecTest & {block: phase0.BeaconBlock}) => { diff --git a/packages/lodestar/test/spec/ssz/generic/index.test.ts b/packages/lodestar/test/spec/ssz/generic/index.test.ts index 00c9e84be6ce..559f59d0a4f4 100644 --- a/packages/lodestar/test/spec/ssz/generic/index.test.ts +++ b/packages/lodestar/test/spec/ssz/generic/index.test.ts @@ -58,7 +58,7 @@ for (const testType of fs.readdirSync(rootGenericSszPath)) { const testData = parseValidTestcase(path.join(validCasesPath, validCase), type); const testDataSerialized = toHexString(testData.serialized); - const testDataRoot = toHexString(testData.root); + const testDataRoot = testData.root; const serialized = wrapErr(() => type.serialize(testData.value), "type.serialize()"); const value = wrapErr(() => type.deserialize(testData.serialized), "type.deserialize()"); diff --git a/packages/lodestar/test/spec/type.ts b/packages/lodestar/test/spec/type.ts index 3b0ed2dc6fe9..c03dc7a26169 100644 --- a/packages/lodestar/test/spec/type.ts +++ b/packages/lodestar/test/spec/type.ts @@ -1,5 +1,11 @@ +/* eslint-disable @typescript-eslint/naming-convention */ + export interface IBaseSpecTest { meta?: { - blsSetting?: BigInt; + bls_setting?: bigint; }; } + +export function shouldVerify(testCase: IBaseSpecTest): boolean { + return testCase.meta?.bls_setting === BigInt(1); +} diff --git a/packages/spec-test-util/src/index.ts b/packages/spec-test-util/src/index.ts index 7ac35516fb36..157f29b3552c 100644 --- a/packages/spec-test-util/src/index.ts +++ b/packages/spec-test-util/src/index.ts @@ -1,5 +1,4 @@ export * from "./downloadTests"; export * from "./multi"; export * from "./single"; -export * from "./util"; export * from "./transform"; diff --git a/packages/spec-test-util/src/multi.ts b/packages/spec-test-util/src/multi.ts index cc0a657ba078..a3a486f9f255 100644 --- a/packages/spec-test-util/src/multi.ts +++ b/packages/spec-test-util/src/multi.ts @@ -1,5 +1,6 @@ +import fs from "node:fs"; +import {loadYaml} from "@chainsafe/lodestar-utils"; import {expect} from "chai"; -import {loadYamlFile} from "./util"; /* eslint-disable @typescript-eslint/no-unsafe-call, @@ -8,6 +9,7 @@ import {loadYamlFile} from "./util"; @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars, + @typescript-eslint/naming-convention, func-names */ export interface IBaseCase { @@ -22,12 +24,12 @@ export interface IBaseCase { interface TestSpec { title: string; summary: string; - forksTimeline: string; + forks_timeline: string; forks: string; config: string; runner: string; handler: string; - testCases: TestCase[]; + test_cases: TestCase[]; } /** @@ -59,13 +61,13 @@ export function describeMultiSpec( expectFunc = (testCase: TestCase, expect: any, expected: any, actual: any) => expect(actual).to.be.equal(expected), timeout = 10 * 60 * 1000 ): void { - const testSpec = (loadYamlFile(testYamlPath) as unknown) as TestSpec; + const testSpec = loadYaml>(fs.readFileSync(testYamlPath, "utf8")); const testSuiteName = `${testSpec.runner} - ${testSpec.handler} - ${testSpec.title} - ${testSpec.config}`; describe(testSuiteName, function () { this.timeout(timeout); - for (const [index, testCase] of testSpec.testCases.entries()) { + for (const [index, testCase] of testSpec.test_cases.entries()) { if (shouldSkip(testCase, index)) { continue; } diff --git a/packages/spec-test-util/src/single.ts b/packages/spec-test-util/src/single.ts index 4f523898e676..600300ce948f 100644 --- a/packages/spec-test-util/src/single.ts +++ b/packages/spec-test-util/src/single.ts @@ -1,9 +1,9 @@ import {expect} from "chai"; -import {readdirSync, readFileSync, existsSync} from "node:fs"; +import fs, {readdirSync, readFileSync, existsSync} from "node:fs"; import {basename, join, parse} from "node:path"; import {Type, CompositeType} from "@chainsafe/ssz"; import {uncompress} from "snappyjs"; -import {isDirectory, loadYamlFile} from "./util"; +import {loadYaml} from "@chainsafe/lodestar-utils"; /* eslint-disable @typescript-eslint/no-unsafe-assignment, @@ -33,6 +33,10 @@ export function toExpandedInputType(inputType: InputType | ExpandedInputType): E }; } +function isDirectory(path: string): boolean { + return fs.lstatSync(path).isDirectory(); +} + export interface ISpecTestOptions { /** * If directory contains both ssz or yaml file version, @@ -126,7 +130,7 @@ function generateTestCase( const metaFilePath = join(testCaseDirectoryPath, "meta.yaml"); let meta: TestCase["meta"] = undefined; if (existsSync(metaFilePath)) { - meta = loadYamlFile(metaFilePath); + meta = loadYaml(fs.readFileSync(metaFilePath, "utf8")); } let testCase = loadInputFiles(testCaseDirectoryPath, options, meta); if (options.mapToTestCase) testCase = options.mapToTestCase(testCase); @@ -209,7 +213,7 @@ function deserializeInputFile( meta?: TestCase["meta"] ): any { if (inputType === InputType.YAML) { - return loadYamlFile(file); + return loadYaml(fs.readFileSync(file, "utf8")); } else if (inputType === InputType.SSZ || inputType === InputType.SSZ_SNAPPY) { const sszTypes = options.getSszTypes ? options.getSszTypes(meta) : options.sszTypes; if (!sszTypes) throw Error("sszTypes is not defined"); diff --git a/packages/spec-test-util/src/sszGeneric.ts b/packages/spec-test-util/src/sszGeneric.ts index c58917fe4a6c..8fb4f5337ab5 100644 --- a/packages/spec-test-util/src/sszGeneric.ts +++ b/packages/spec-test-util/src/sszGeneric.ts @@ -1,12 +1,11 @@ -import {join} from "node:path"; -import {readFileSync, readdirSync} from "node:fs"; -import {fromHexString, Json, Type} from "@chainsafe/ssz"; +import path, {join} from "node:path"; +import fs, {readFileSync, readdirSync} from "node:fs"; +import {Json, Type} from "@chainsafe/ssz"; +import {loadYaml, objectToExpectedCase} from "@chainsafe/lodestar-utils"; import {uncompress} from "snappyjs"; -import {loadYamlFile} from "./util"; export interface IValidTestcase { - path: string; - root: Uint8Array; + root: string; serialized: Uint8Array; value: T; } @@ -16,21 +15,24 @@ export interface IInvalidTestcase { serialized: Uint8Array; } -export function parseValidTestcase(path: string, type: Type): IValidTestcase { +export function parseValidTestcase(dirpath: string, type: Type): IValidTestcase { // The root is stored in meta.yml as: // root: 0xDEADBEEF - const meta = loadYamlFile(join(path, "meta.yaml")); - const root = fromHexString(meta.root as string); - + const metaStr = fs.readFileSync(path.join(dirpath, "meta.yaml"), "utf8"); + const meta = loadYaml<{root: string}>(metaStr); + if (typeof meta.root !== "string") { + throw Error(`meta.root not a string: ${meta.root}\n${fs}`); + } // The serialized value is stored in serialized.ssz_snappy - const serialized = uncompress(readFileSync(join(path, "serialized.ssz_snappy"))); + const serialized = uncompress(readFileSync(join(dirpath, "serialized.ssz_snappy"))); // The value is stored in value.yml - const value = type.fromJson(loadYamlFile(join(path, "value.yaml")) as Json) as T; + const yamlSnake = loadYaml(fs.readFileSync(join(dirpath, "value.yaml"), "utf8")); + const yamlCamel = objectToExpectedCase(yamlSnake, "camel"); + const value = type.fromJson(yamlCamel as Json); return { - path, - root, + root: meta.root, serialized, value, }; diff --git a/packages/spec-test-util/src/util.ts b/packages/spec-test-util/src/util.ts deleted file mode 100644 index 56db11df9aa5..000000000000 --- a/packages/spec-test-util/src/util.ts +++ /dev/null @@ -1,10 +0,0 @@ -import fs from "node:fs"; -import {loadYaml} from "@chainsafe/lodestar-utils"; - -export function isDirectory(path: string): boolean { - return fs.lstatSync(path).isDirectory(); -} - -export function loadYamlFile(path: string): Record { - return loadYaml(fs.readFileSync(path, "utf8")); -} 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 a16d330289f4..34934c72ea50 100644 --- a/packages/spec-test-util/test/e2e/single/index.test.ts +++ b/packages/spec-test-util/test/e2e/single/index.test.ts @@ -1,11 +1,13 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import {unlinkSync, writeFileSync} from "node:fs"; +import fs, {unlinkSync, writeFileSync} from "node:fs"; import {join} from "node:path"; import {ContainerType, Type, Json} from "@chainsafe/ssz"; import {ssz} from "@chainsafe/lodestar-types"; import {describeDirectorySpecTest, InputType} from "../../../src/single"; -import {loadYamlFile} from "../../../src/util"; +import {loadYaml} from "@chainsafe/lodestar-utils"; + +/* eslint-disable @typescript-eslint/naming-convention */ export interface ISimpleStruct { test: boolean; @@ -16,7 +18,7 @@ export interface ISimpleCase extends Iterable { input: ISimpleStruct; output: number; meta?: { - blsSetting?: BigInt; + bls_setting?: BigInt; }; } @@ -63,7 +65,7 @@ describeDirectorySpecTest( function yamlToSSZ(file: string, sszSchema: Type): void { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const input: any = sszSchema.fromJson(loadYamlFile(file) as Json); + const input: any = sszSchema.fromJson(loadYaml(fs.readFileSync(file, "utf8"))); if (input.number) { input.number = Number(input.number); } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 4fbe38e12dad..a19f61aeeaa3 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -10,6 +10,6 @@ export * from "./notNullish"; export * from "./sleep"; export * from "./sort"; export * from "./timeout"; -export * from "./types"; +export {RecursivePartial, bnToNum} from "./types"; export * from "./verifyMerkleBranch"; export * from "./json"; diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index ecf36c4fdc47..f302c9a4c80c 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -9,3 +9,8 @@ export type RecursivePartial = { ? Readonly>[] : RecursivePartial; }; + +/** Type safe wrapper for Number constructor that takes 'any' */ +export function bnToNum(bn: bigint): number { + return Number(bn); +} diff --git a/packages/utils/src/yaml/index.ts b/packages/utils/src/yaml/index.ts index ac0cb1394edc..c72b47d7f56c 100644 --- a/packages/utils/src/yaml/index.ts +++ b/packages/utils/src/yaml/index.ts @@ -1,9 +1,8 @@ import {load, dump} from "js-yaml"; import {schema} from "./schema"; -import {objectToExpectedCase} from "../objects"; -export function loadYaml(yaml: string): Record { - return objectToExpectedCase>(load(yaml, {schema})); +export function loadYaml>(yaml: string): T { + return load(yaml, {schema}) as T; } export function dumpYaml(yaml: unknown): string {