Skip to content

Commit

Permalink
Add more tests (#207)
Browse files Browse the repository at this point in the history
* Add more tests

* Add more by type tests

* Run all ssz spec tests

* Port full Lodestar types to SSZ

* Update script runners

* Fix unit tests

* Add container tests

* Test more empty List cases

* Use ContainerType in validator perf tests

* Add local copy of safeType
  • Loading branch information
dapplion authored Oct 12, 2021
1 parent b4463f8 commit 3944763
Show file tree
Hide file tree
Showing 52 changed files with 1,820 additions and 633 deletions.
37 changes: 34 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,38 @@ jobs:
run: yarn lint
- name: Tests
run: yarn test

# Download spec tests with cache
- name: Restore spec tests cache
uses: actions/cache@master
with:
path: packages/ssz/spec-tests
key: spec-test-data-${{ hashFiles('packages/ssz/test/specTestVersioning.ts') }}
- name: Download spec tests
run: lerna run download-spec-tests
- name: Spec tests
run: lerna run test:spec
run: yarn download-spec-tests
working-directory: packages/ssz

# Run them in different steps to quickly identifying which command failed
# Otherwise just doing `yarn test:spec` you can't tell which specific suite failed
# many of the suites have identical names for minimal and mainnet
- name: Spec tests general
run: ../../node_modules/.bin/mocha test/spec/generic/index.test.ts
working-directory: packages/ssz
- name: Spec tests phase0-minimal
run: LODESTAR_PRESET=minimal LODESTAR_FORK=phase0 ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
working-directory: packages/ssz
- name: Spec tests phase0-mainnet
run: LODESTAR_PRESET=mainnet LODESTAR_FORK=phase0 ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
working-directory: packages/ssz
- name: Spec tests altair-minimal
run: LODESTAR_PRESET=minimal LODESTAR_FORK=altair ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
working-directory: packages/ssz
- name: Spec tests altair-mainnet
run: LODESTAR_PRESET=mainnet LODESTAR_FORK=altair ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
working-directory: packages/ssz
- name: Spec tests merge-minimal
run: LODESTAR_PRESET=minimal LODESTAR_FORK=merge ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
working-directory: packages/ssz
- name: Spec tests merge-mainnet
run: LODESTAR_PRESET=mainnet LODESTAR_FORK=merge ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
working-directory: packages/ssz
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ yarn-error.log
.nyc_output/
.idea/
.vscode

packages/ssz/spec-tests
2 changes: 1 addition & 1 deletion packages/ssz/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ node_modules/
.vscode
yarn-error.log

test/spec-tests-data
spec-tests
10 changes: 7 additions & 3 deletions packages/ssz/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@
"benchmark:local": "yarn benchmark --local",
"test:perf": "mocha \"test/perf/**/*.test.ts\"",
"test:unit": "nyc -e .ts mocha \"test/unit/**/*.test.ts\"",
"test:spec": "mocha \"test/spec/**/*.test.ts\"",
"test:spec": "yarn test:spec-generic && yarn test:spec-static",
"test:spec-generic": "mocha \"test/spec/generic/**/*.test.ts\"",
"test:spec-static": "yarn test:spec-static-minimal && yarn test:spec-static-mainnet",
"test:spec-static-minimal": "LODESTAR_PRESET=minimal mocha test/spec/ssz_static.test.ts",
"test:spec-static-mainnet": "LODESTAR_PRESET=mainnet mocha test/spec/ssz_static.test.ts",
"download-spec-tests": "node -r ts-node/register test/spec/downloadTests.ts"
},
"types": "lib/index.d.ts",
Expand All @@ -37,8 +41,8 @@
"case": "^1.6.3"
},
"devDependencies": {
"@chainsafe/lodestar-params": "^0.28.1",
"@chainsafe/lodestar-spec-test-util": "^0.28.1"
"@chainsafe/lodestar-params": "^0.31.0",
"@chainsafe/lodestar-spec-test-util": "^0.31.0"
},
"keywords": [
"ethereum",
Expand Down
4 changes: 2 additions & 2 deletions packages/ssz/src/types/composite/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ export abstract class CompositeType<T extends CompositeValue> extends Type<T> {
return this.tree_serializeToBytes(tree, output, 0);
}

bytes_validate(data: Uint8Array, start: number, end: number): void {
bytes_validate(data: Uint8Array, start: number, end: number, emptyOk?: boolean): void {
if (!data) {
throw new Error("Data is null or undefined");
}
if (data.length === 0) {
if (data.length === 0 && !emptyOk) {
throw new Error("Data is empty");
}
if (start < 0) {
Expand Down
8 changes: 4 additions & 4 deletions packages/ssz/src/types/composite/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ export abstract class BasicArrayType<T extends ArrayLike<unknown>> extends Compo
return newValue;
}

struct_deserializeFromBytes(data: Uint8Array, start: number, end: number): T {
this.bytes_validate(data, start, end);
struct_deserializeFromBytes(data: Uint8Array, start: number, end: number, emptyOk?: boolean): T {
this.bytes_validate(data, start, end, emptyOk);
const elementSize = this.elementType.struct_getSerializedLength();
return Array.from({length: (end - start) / elementSize}, (_, i) =>
this.elementType.struct_deserializeFromBytes(data, start + i * elementSize)
Expand Down Expand Up @@ -387,8 +387,8 @@ export abstract class CompositeArrayType<T extends ArrayLike<unknown>> extends C
return newValue;
}

struct_deserializeFromBytes(data: Uint8Array, start: number, end: number): T {
this.bytes_validate(data, start, end);
struct_deserializeFromBytes(data: Uint8Array, start: number, end: number, emptyOk?: boolean): T {
this.bytes_validate(data, start, end, emptyOk);
if (start === end) {
return [] as unknown as T;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/ssz/src/types/composite/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@ export class BasicListType<T extends List<unknown> = List<unknown>> extends Basi
}

bytes_validate(data: Uint8Array, start: number, end: number): void {
super.bytes_validate(data, start, end);
super.bytes_validate(data, start, end, true);
if (end - start > this.getMaxSerializedLength()) {
throw new Error("Deserialized list length greater than limit");
}
}

struct_deserializeFromBytes(data: Uint8Array, start: number, end: number): T {
this.bytes_validate(data, start, end);
return super.struct_deserializeFromBytes(data, start, end);
return super.struct_deserializeFromBytes(data, start, end, true);
}

struct_getChunkCount(value: T): number {
Expand Down Expand Up @@ -379,8 +379,8 @@ export class CompositeListType<T extends List<unknown> = List<unknown>> extends
}

struct_deserializeFromBytes(data: Uint8Array, start: number, end: number): T {
this.bytes_validate(data, start, end);
const value = super.struct_deserializeFromBytes(data, start, end);
this.bytes_validate(data, start, end, true);
const value = super.struct_deserializeFromBytes(data, start, end, true);
if (value.length > this.limit) {
throw new Error(`Deserialized list length greater than limit: ${value.length} ${this.limit}`);
}
Expand Down
41 changes: 41 additions & 0 deletions packages/ssz/test/copyTypes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

####################################################
# Copy all Lodestar eth2.0 types to SSZ's test files
#
# Why?
# - To leverage consensus spec tests before release
# - To ensure Lodestar's types definitions are compatible before releasing
# - To benchmark usage a real eth2.0 types in SSZ for fast development
####################################################

# Allow the script to be run from any folder and not break paths
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
LODESTAR_EXECUTABLE=${SCRIPT_DIR}/../../packages/cli/bin/lodestar

# Assumes your directory structure is:
# i.e. you have cloned both the lodestar and ssz monorepo in the same parent directory
# .
# ├── lodestar
# │ └── packages
# │ └── types
# └── ssz
# └── packages
# └── ssz
#

LODESTAR_TYPES_SRC=${SCRIPT_DIR}/../../../../lodestar/packages/types/src
TYPES_SSZ_LOCATION=${SCRIPT_DIR}/lodestarTypes

# Clean up existing directory
rm -rf $TYPES_SSZ_LOCATION
mkdir -p $TYPES_SSZ_LOCATION

# Copy from lodestar types the entire source
cp -R $LODESTAR_TYPES_SRC/. $TYPES_SSZ_LOCATION

# Replace imports
find $TYPES_SSZ_LOCATION \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i 's/@chainsafe\/ssz/\.\.\/\.\.\/\.\.\/src/g'



2 changes: 2 additions & 0 deletions packages/ssz/test/lodestarTypes/allForks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * as ts from "./types";
export * as ssz from "./sszTypes";
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import {ForkName} from "@chainsafe/lodestar-params";

import {AllForksSSZTypes} from "./types";
import * as phase0 from "../phase0/sszTypes";
import * as altair from "../altair/sszTypes";
import {ssz as phase0} from "../phase0";
import {ssz as altair} from "../altair";
import {ssz as merge} from "../merge";

/**
* Index the ssz types that differ by fork
Expand All @@ -24,4 +25,11 @@ export const allForks: {[K in ForkName]: AllForksSSZTypes} = {
BeaconState: altair.BeaconState as AllForksSSZTypes["BeaconState"],
Metadata: altair.Metadata as AllForksSSZTypes["Metadata"],
},
merge: {
BeaconBlockBody: merge.BeaconBlockBody as AllForksSSZTypes["BeaconBlockBody"],
BeaconBlock: merge.BeaconBlock as AllForksSSZTypes["BeaconBlock"],
SignedBeaconBlock: merge.SignedBeaconBlock as AllForksSSZTypes["SignedBeaconBlock"],
BeaconState: merge.BeaconState as AllForksSSZTypes["BeaconState"],
Metadata: altair.Metadata as AllForksSSZTypes["Metadata"],
},
};
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {ContainerType} from "../../../src";

import * as phase0 from "../phase0/types";
import * as altair from "../altair/types";
import {ts as phase0} from "../phase0";
import {ts as altair} from "../altair";
import {ts as merge} from "../merge";

// Re-export union types for types that are _known_ to differ

export type BeaconBlockBody = phase0.BeaconBlockBody | altair.BeaconBlockBody;
export type BeaconBlock = phase0.BeaconBlock | altair.BeaconBlock;
export type SignedBeaconBlock = phase0.SignedBeaconBlock | altair.SignedBeaconBlock;
export type BeaconState = phase0.BeaconState | altair.BeaconState;
export type BeaconBlockBody = phase0.BeaconBlockBody | altair.BeaconBlockBody | merge.BeaconBlockBody;
export type BeaconBlock = phase0.BeaconBlock | altair.BeaconBlock | merge.BeaconBlock;
export type SignedBeaconBlock = phase0.SignedBeaconBlock | altair.SignedBeaconBlock | merge.SignedBeaconBlock;
export type BeaconState = phase0.BeaconState | altair.BeaconState | merge.BeaconState;
export type Metadata = phase0.Metadata | altair.Metadata;

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/ssz/test/lodestarTypes/altair/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * as ts from "./types";
export * as ssz from "./sszTypes";
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {BitVectorType, ContainerType, VectorType, ListType, RootType, Vector} from "../../../src";
import {
JUSTIFICATION_BITS_LENGTH,
FINALIZED_ROOT_INDEX_FLOORLOG2,
Expand All @@ -6,19 +7,16 @@ import {
SYNC_COMMITTEE_SIZE,
SLOTS_PER_HISTORICAL_ROOT,
HISTORICAL_ROOTS_LIMIT,
EPOCHS_PER_ETH1_VOTING_PERIOD,
SLOTS_PER_EPOCH,
VALIDATOR_REGISTRY_LIMIT,
EPOCHS_PER_HISTORICAL_VECTOR,
EPOCHS_PER_SLASHINGS_VECTOR,
EPOCHS_PER_SYNC_COMMITTEE_PERIOD,
} from "@chainsafe/lodestar-params";
import {BitVectorType, ContainerType, VectorType, ListType, RootType, Vector} from "../../../src";
import * as phase0Ssz from "../phase0/sszTypes";
import * as primitiveSsz from "../primitive/sszTypes";
import * as phase0 from "../phase0/types";
import {Root} from "../primitive/types";
import {LazyVariable} from "../lazyVar";
import {ssz as phase0Ssz, ts as phase0Types} from "../phase0";
import {ssz as primitiveSsz} from "../primitive";
import {LazyVariable} from "../utils/lazyVar";
import * as altair from "./types";

const {
Expand Down Expand Up @@ -124,7 +122,7 @@ export const HistoricalStateRoots = new VectorType<Vector<Root>>({
length: SLOTS_PER_HISTORICAL_ROOT,
});

export const HistoricalBatch = new ContainerType<phase0.HistoricalBatch>({
export const HistoricalBatch = new ContainerType<phase0Types.HistoricalBatch>({
fields: {
blockRoots: HistoricalBlockRoots,
stateRoots: HistoricalStateRoots,
Expand Down Expand Up @@ -156,8 +154,11 @@ export const SignedBeaconBlock = new ContainerType<altair.SignedBeaconBlock>({
},
});

//we don't reuse phase0.BeaconState fields since we need to replace some keys
//and we cannot keep order doing that
export const EpochParticipation = new ListType({elementType: ParticipationFlags, limit: VALIDATOR_REGISTRY_LIMIT});
export const InactivityScores = new ListType({elementType: Number64, limit: VALIDATOR_REGISTRY_LIMIT});

// we don't reuse phase0.BeaconState fields since we need to replace some keys
// and we cannot keep order doing that
export const BeaconState = new ContainerType<altair.BeaconState>({
fields: {
genesisTime: Number64,
Expand All @@ -174,33 +175,24 @@ export const BeaconState = new ContainerType<altair.BeaconState>({
}),
// Eth1
eth1Data: phase0Ssz.Eth1Data,
eth1DataVotes: new ListType({
elementType: phase0Ssz.Eth1Data,
limit: EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH,
}),
eth1DataVotes: phase0Ssz.Eth1DataVotes,
eth1DepositIndex: Number64,
// Registry
validators: new ListType({elementType: phase0Ssz.Validator, limit: VALIDATOR_REGISTRY_LIMIT}),
balances: new ListType({elementType: Gwei, limit: VALIDATOR_REGISTRY_LIMIT}),
balances: new ListType({elementType: Number64, limit: VALIDATOR_REGISTRY_LIMIT}),
randaoMixes: new VectorType({elementType: Bytes32, length: EPOCHS_PER_HISTORICAL_VECTOR}),
// Slashings
slashings: new VectorType({elementType: Gwei, length: EPOCHS_PER_SLASHINGS_VECTOR}),
// Participation
previousEpochParticipation: new ListType({
elementType: ParticipationFlags,
limit: VALIDATOR_REGISTRY_LIMIT,
}),
currentEpochParticipation: new ListType({
elementType: ParticipationFlags,
limit: VALIDATOR_REGISTRY_LIMIT,
}),
previousEpochParticipation: EpochParticipation,
currentEpochParticipation: EpochParticipation,
// Finality
justificationBits: new BitVectorType({length: JUSTIFICATION_BITS_LENGTH}),
previousJustifiedCheckpoint: phase0Ssz.Checkpoint,
currentJustifiedCheckpoint: phase0Ssz.Checkpoint,
finalizedCheckpoint: phase0Ssz.Checkpoint,
// Inactivity
inactivityScores: new ListType({elementType: Number64, limit: VALIDATOR_REGISTRY_LIMIT}),
inactivityScores: InactivityScores,
// Sync
currentSyncCommittee: SyncCommittee,
nextSyncCommittee: SyncCommittee,
Expand Down
Loading

0 comments on commit 3944763

Please sign in to comment.