Skip to content

Commit

Permalink
Refactor following ssz repo
Browse files Browse the repository at this point in the history
  • Loading branch information
twoeths committed Dec 3, 2021
1 parent b01e3de commit 0785b9e
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 266 deletions.
137 changes: 83 additions & 54 deletions packages/spec-test-runner/test/spec/ssz/generic/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,102 @@
import {expect} from "chai";
import {join} from "path";
import path from "path";
import fs from "fs";
// eslint-disable-next-line no-restricted-imports
import {getInvalidTestcases, getValidTestcases} from "@chainsafe/lodestar-spec-test-util/lib/sszGeneric";
import {CompositeValue, isCompositeType} from "@chainsafe/ssz";
import {parseInvalidTestcase, parseValidTestcase} from "@chainsafe/lodestar-spec-test-util/lib/sszGeneric";
import {CompositeType, isCompositeType, toHexString, Type} from "@chainsafe/ssz";
import {SPEC_TEST_LOCATION} from "../../../specTestVersioning";

// Test types defined here
import {types} from "./types";
import {getTestType} from "./types";

for (const type of types) {
// valid testcases
describe(`ssz generic - valid - ${type.prefix}`, () => {
for (const testcase of getValidTestcases(join(type.path, "valid"), type.prefix, type.type)) {
it(`${testcase.path.split("/").pop()}`, () => {
// test struct round trip serialization/deserialization
expect(
type.type.deserialize(type.type.serialize(testcase.value)),
"Invalid struct round-trip serialization/deserialization"
).to.deep.equal(testcase.value);
const rootGenericSszPath = path.join(SPEC_TEST_LOCATION, "tests", "general", "phase0", "ssz_generic");

// test struct serialization
expect(type.type.serialize(testcase.value), "Invalid struct serialization").to.deep.equal(testcase.serialized);
// ssz_generic
// | basic_vector
// | invalid
// | vec_bool_0
// | serialized.ssz_snappy
// | valid
// | vec_bool_1_max
// | meta.yaml
// | serialized.ssz_snappy
// | value.yaml
//
// Docs: https://github.com/ethereum/eth2.0-specs/blob/master/tests/formats/ssz_generic/README.md

// test deserialization to struct
expect(type.type.deserialize(testcase.serialized), "Invalid deserialization to struct").to.deep.equal(
testcase.value
);
for (const testType of fs.readdirSync(rootGenericSszPath)) {
const testTypePath = path.join(rootGenericSszPath, testType);

// test struct merkleization
expect(type.type.hashTreeRoot(testcase.value), "Invalid struct merkleization").to.deep.equal(testcase.root);
describe(`${testType} invalid`, () => {
const invalidCasesPath = path.join(testTypePath, "invalid");
for (const invalidCase of fs.readdirSync(invalidCasesPath)) {
it(invalidCase, () => {
const type = getTestType(testType, invalidCase);
const testData = parseInvalidTestcase(path.join(invalidCasesPath, invalidCase));

// If the type is composite, test tree-backed ops
if (isCompositeType(type.type)) {
const structValue = testcase.value as CompositeValue;
const treebackedValue = type.type.createTreeBackedFromStruct(structValue);
// Unlike the valid suite, invalid encodings do not have any value or hash tree root. The serialized data
// should simply not be decoded without raising an error.
// Note that for some type declarations in the invalid suite, the type itself may technically be invalid.
// This is a valid way of detecting invalid data too. E.g. a 0-length basic vector.
expect(() => type.deserialize(testData.serialized), "Must throw on deserialize").to.throw();
});
}
});

// test struct / tree-backed equality
expect(type.type.equals(structValue, treebackedValue), "Struct and tree-backed not equal").to.be.true;
describe(`${testType} valid`, () => {
const validCasesPath = path.join(testTypePath, "valid");
for (const validCase of fs.readdirSync(validCasesPath)) {
// NOTE: ComplexTestStruct tests are not correctly generated.
// where deserialized .d value is D: '0x00'. However the tests guide mark that field as D: Bytes[256].
// Those test won't be fixed since most implementations staticly compile types.
if (validCase.startsWith("ComplexTestStruct")) {
continue;
}

// test tree-backed to struct
expect(
type.type.tree_convertToStruct(treebackedValue.tree),
"Tree-backed to struct conversion resulted in unequal value"
).to.deep.equal(structValue);
it(validCase, () => {
const type = getTestType(testType, validCase);

// test tree-backed serialization
expect(treebackedValue.serialize(), "Invalid tree-backed serialization").to.deep.equal(testcase.serialized);
const testData = parseValidTestcase(path.join(validCasesPath, validCase), type);
const testDataSerialized = toHexString(testData.serialized);
const testDataRoot = toHexString(testData.root);

// test deserialization to tree-backed
expect(
type.type.tree_convertToStruct(type.type.tree_deserialize(testcase.serialized)),
"Invalid deserialization to tree-backed"
).to.deep.equal(structValue);
const serialized = wrapErr(() => type.serialize(testData.value), "type.serialize()");
const value = wrapErr(() => type.deserialize(testData.serialized), "type.deserialize()");
const root = wrapErr(() => type.hashTreeRoot(testData.value), "type.hashTreeRoot()");
const valueSerdes = wrapErr(() => type.deserialize(serialized), "type.deserialize(serialized)");

// test tree-backed merkleization
expect(treebackedValue.hashTreeRoot(), "Invalid tree-backed merkleization").to.deep.equal(testcase.root);
}
});
}
});
expect(valueSerdes).to.deep.equal(testData.value, "round trip serdes");
expect(toHexString(serialized)).to.equal(testDataSerialized, "struct serialize");
expect(value).to.deep.equal(testData.value, "struct deserialize");
expect(toHexString(root)).to.equal(testDataRoot, "struct hashTreeRoot");

// If the type is composite, test tree-backed ops
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!isCompositeType(type as Type<any>)) return;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const compositeType = type as CompositeType<any>;

// invalid testcases
describe(`ssz generic - invalid - ${type.prefix}`, () => {
for (const testcase of getInvalidTestcases(join(type.path, "invalid"), type.prefix)) {
it(`${testcase.path.split("/").pop()}`, () => {
// test struct round trip serialization/deserialization
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
expect(() => type.type.deserialize(testcase.serialized), "Invalid data should error during deserialization").to
.throw;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const treebackedValue = compositeType.createTreeBackedFromStruct(testData.value);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const treeToStruct = compositeType.tree_convertToStruct(treebackedValue.tree);

expect(treeToStruct).to.deep.equal(testData.value, "tree-backed to struct");
expect(type.equals(testData.value, treebackedValue), "struct - tree-backed type.equals()").to.be.true;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
expect(toHexString(treebackedValue.serialize())).to.equal(testDataSerialized, "tree-backed serialize");
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
expect(toHexString(treebackedValue.hashTreeRoot())).to.equal(testDataRoot, "tree-backed hashTreeRoot");
});
}
});
}

function wrapErr<T>(fn: () => T, prefix: string): T {
try {
return fn();
} catch (e) {
(e as Error).message = `${prefix}: ${(e as Error).message}`;
throw e;
}
}
Loading

0 comments on commit 0785b9e

Please sign in to comment.