-
-
Notifications
You must be signed in to change notification settings - Fork 317
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
232 additions
and
266 deletions.
There are no files selected for viewing
137 changes: 83 additions & 54 deletions
137
packages/spec-test-runner/test/spec/ssz/generic/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.