mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-10 14:26:26 +00:00
ca4f29caca
* Introduce new mocking proc to replace:
- makeFakeValidatorPrivKey
- hackPrivKey
- getNextBeaconProposerIndex
- addBlock
- makeBlock
* Add comments on datastructure unsynced with the spec
* Add merkle tree constructor and initial mocking for deposits (missing merkle proofs)
* [Mock] Implement sparse merkle tree and merkle proof builder
* [Mocking] Genesis deposits
* Add compact_committees_roots init + mock genesis state
* [Tests] Add first deposit test using the new mocking procedures
* [Tests -deposits] add at and over 32 ETH deposit tests
* [Tests - deposits] Add test for validator top-up
* [Tests -deposits] Mention the TODO to test for invalid conditions
* [Tests] Add stub to test "is_valid_genesis_state"
* [Merkle proofs] Implement round-trip checks
* Deactivate roundtrips test
* SSZ - use EF convention for hash_tree_root / hashTreeRoot
* [Tests - Attestation] Attestation mocking + initial test
* Add mocking + 3 new tests for valid attestations + mention future invalid attestation tests
* Add crosslinks test (1 failing to attestations in block being duplicated in state transition)
* Single attestation crosslink test - workaround https://github.com/status-im/nim-beacon-chain/issues/361
* Add test for failed crosslink penalty
* Rebase fixes + add refactored tests to test suite
* justif-finalization helpers first batch
* Add 234 finalization tests
* Fix justif test, Rule I 234 finalization does not happen with sufficient support.
(Also unittest check template does not fail properly in some cases)
* Add tests for all finalization rules
* Properly delete nim-byteutils following c91727e7e5 (diff-7c3613dba5171cb6027c67835dd3b9d4)
* use digest helper for deposit root
216 lines
6.5 KiB
Nim
216 lines
6.5 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2018 Status Research & Development GmbH
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
import
|
|
# Standard library
|
|
os, strutils, strformat, tables, unittest, sequtils, typetraits,
|
|
# Status libs
|
|
stew/[byteutils, bitseqs], nimcrypto/hash,
|
|
serialization/testing/tracing,
|
|
json_serialization, json_serialization/lexer,
|
|
# Beacon chain internals
|
|
../../beacon_chain/ssz,
|
|
../../beacon_chain/spec/[datatypes, validator, digest, crypto],
|
|
# Test utilities
|
|
../testutil,
|
|
./fixtures_utils
|
|
|
|
const
|
|
failFast = defined(debug) and false
|
|
traceOnFailure = defined(debug)
|
|
|
|
type
|
|
SpecObject[T] = ref object of RootObj
|
|
obj: ref T
|
|
|
|
SszStaticTest* = object
|
|
obj: RootRef
|
|
objType, objJsonRepr: string
|
|
expectedBytes: seq[byte]
|
|
expectedRootHash, expectedSigHash: Eth2Digest
|
|
hasSigHash: bool
|
|
line: int
|
|
|
|
ReaderProc = proc(r: var JsonReader): RootRef {.cdecl, gcsafe.}
|
|
TestingProc = proc(file: string, test: SszStaticTest) {.cdecl, gcsafe.}
|
|
|
|
SpecTypeVtable = object
|
|
reader: ReaderProc
|
|
tester: TestingProc
|
|
|
|
let testsDir = JsonTestsDir / "ssz_static" / "core"
|
|
let minDevTestFile = getTempDir() / "minimal_ssz_test.json"
|
|
|
|
var specTypesRTTI = initTable[string, SpecTypeVtable]()
|
|
|
|
proc readerImpl[T](r: var JsonReader): RootRef {.cdecl, gcsafe.} =
|
|
var res = SpecObject[T](obj: new T)
|
|
res.obj[] = r.readValue(T)
|
|
RootRef(res)
|
|
|
|
# TODO:
|
|
# Fun fact: With mainnet settings, the BeaconState object
|
|
# is too large to safely exist as a stack variable. The
|
|
# `testerImpl` procedure below will trigger a segmentation
|
|
# fault on its very first line because of it.
|
|
#
|
|
# To work-around this issue, this file uses ref objects
|
|
# to store the loaded test cases, but we must compare them
|
|
# by value:
|
|
template valuesAreEqual[T](a, b: ref T): bool =
|
|
a[] == b[]
|
|
|
|
template valuesAreEqual[T](a, b: T): bool =
|
|
a == b
|
|
|
|
template `$`(x: ref auto): string =
|
|
$(x[])
|
|
|
|
proc readSszValueRef*(input: openarray[byte], T: type): ref T =
|
|
new result
|
|
result[] = readSszValue(input, T)
|
|
|
|
proc testerImpl[T](path: string, sszTest: SszStaticTest) {.cdecl, gcsafe.} =
|
|
doAssert sszTest.obj != nil
|
|
var obj = SpecObject[T](sszTest.obj)
|
|
|
|
test &"test case on line {sszTest.line}":
|
|
template execTest(testOpName, testOp, expectedRes) =
|
|
let ourRes = testOp
|
|
let success = valuesAreEqual(ourRes, expectedRes)
|
|
if not success and traceOnFailure:
|
|
{.gcsafe.}:
|
|
echo "====== ", testOpName, " failed ", path, ":", sszTest.line
|
|
echo " our result:"
|
|
echo " ", ourRes
|
|
echo " expected result:"
|
|
echo " ", expectedRes
|
|
when defined(serialization_tracing):
|
|
tracingEnabled = true
|
|
discard testOp
|
|
tracingEnabled = false
|
|
echo "======================================================"
|
|
if failFast: quit 1
|
|
|
|
# TODO BEWARE: Passing the boolean expression to `check` directly
|
|
# will trigger a Nim compilation bomb. This is most likely caused
|
|
# by a mis-behaving generics instantiations cache when a function
|
|
# is explicitly instantiated to get its address.
|
|
# There is a recursive instantiation loop of system's `$` operator.
|
|
check success
|
|
|
|
execTest "serialization",
|
|
(let ourBytes = SSZ.encode(obj.obj[]); ourBytes),
|
|
sszTest.expectedBytes
|
|
|
|
execTest "root hash check",
|
|
hash_tree_root(obj.obj[]),
|
|
sszTest.expectedRootHash
|
|
|
|
when hasSigningRoot(T):
|
|
doAssert sszTest.hasSigHash
|
|
execTest "sig hash check",
|
|
signingRoot(obj.obj[]),
|
|
sszTest.expectedSigHash
|
|
|
|
execTest "roundtrip",
|
|
readSszValueRef(sszTest.expectedBytes, T),
|
|
obj.obj
|
|
|
|
template addSpecTypeRTTI(T: type) =
|
|
var reader = readerImpl[T]
|
|
var tester = testerImpl[T]
|
|
specTypesRTTI.add(T.name, SpecTypeVtable(reader: reader,
|
|
tester: tester))
|
|
foreachSpecType(addSpecTypeRTTI)
|
|
|
|
proc runTest(path: string, test: SszStaticTest) =
|
|
if test.objType != "Unsupported":
|
|
specTypesRTTI[test.objType].tester(path, test)
|
|
|
|
proc advanceToClosingBrace(lexer: var JsonLexer, openedBraces = 1) =
|
|
var closedBraces = 0
|
|
while closedBraces < openedBraces:
|
|
while lexer.tok notin {tkCurlyLe, tkCurlyRi}:
|
|
lexer.next
|
|
if lexer.tok == tkCurlyLe:
|
|
dec closedBraces
|
|
else:
|
|
inc closedBraces
|
|
lexer.next
|
|
|
|
proc readValue*(r: var JsonReader, result: var SszStaticTest) {.gcsafe.} =
|
|
r.skipToken tkCurlyLe
|
|
|
|
if r.lexer.tok != tkString:
|
|
r.raiseUnexpectedToken(etString)
|
|
|
|
var reader: ReaderProc
|
|
let key = r.lexer.strVal
|
|
{.gcsafe.}:
|
|
if not specTypesRTTI.hasKey(key):
|
|
result.objType = "Unsupported"
|
|
r.lexer.advanceToClosingBrace
|
|
return
|
|
|
|
result.objType = key
|
|
result.line = r.lexer.line
|
|
reader = specTypesRTTI[key].reader
|
|
|
|
r.lexer.next
|
|
r.skipToken tkColon
|
|
r.skipToken tkCurlyLe
|
|
|
|
while r.lexer.tok == tkString:
|
|
# TODO: I was hit by a very nasty Nim bug here.
|
|
# If you use `let` on the next line, the variable will be
|
|
# aliased to `r.lexer.strVar` instead of being copied.
|
|
# This will create problems, because the value is modified
|
|
# on the next line.
|
|
var field = r.lexer.strVal
|
|
r.lexer.next
|
|
r.skipToken tkColon
|
|
|
|
case field
|
|
of "value":
|
|
result.obj = reader(r)
|
|
of "serialized":
|
|
result.expectedBytes = hexToSeqByte r.readValue(string)
|
|
of "root":
|
|
result.expectedRootHash = Eth2Digest.fromHex r.readValue(string)
|
|
of "signing_root":
|
|
result.expectedSigHash = Eth2Digest.fromHex r.readValue(string)
|
|
result.hasSigHash = true
|
|
else:
|
|
r.raiseUnexpectedField(field, type(result).name)
|
|
|
|
if r.lexer.tok == tkComma:
|
|
r.lexer.next()
|
|
else:
|
|
break
|
|
|
|
r.skipToken tkCurlyRi
|
|
r.skipToken tkCurlyRi
|
|
|
|
when failFast:
|
|
# This will produce faster failures in debug builds
|
|
{.gcsafe.}: runTest result
|
|
|
|
proc executeSuite(path: string) =
|
|
let sszSuite = path.parseTests SszStaticTest
|
|
suite &"{path}: {sszSuite.title}":
|
|
for sszTest in sszSuite.test_cases:
|
|
runTest path, sszTest
|
|
|
|
if fileExists(minDevTestFile):
|
|
executeSuite minDevTestFile
|
|
|
|
for kind, path in walkDir(testsDir):
|
|
if kind notin {pcFile, pcLinkToFile}: continue
|
|
if const_preset in path:
|
|
executeSuite path
|