V0.8.3 tests part 3 - sanity checks for slots and blocks (#375)

* Add sanity check for slot processing (also impacted by https://github.com/status-im/nim-beacon-chain/issues/373)

* use reportDiff also for all state tests vs EF

* initial sanity checks for blocks - workaround zero signature in block headers: https://github.com/status-im/nim-beacon-chain/issues/374

* Remove generic object variant compare commented code

* Add the one block state transition sanity checks

* generalize blocks test to multiple blocks

* simplify slots test runner

* Add official epoch transitions, sanity blocks, sanity slots to the test suite

* Fix index out-of-bounds in initiate_validator_exit - enable proposer slashings unittest
This commit is contained in:
Mamy Ratsimbazafy 2019-09-03 20:02:21 +02:00 committed by Dustin Brody
parent 297e9079d4
commit c11f37e550
13 changed files with 228 additions and 64 deletions

View File

@ -51,10 +51,12 @@ task test, "Run all tests":
buildBinary "test_fixture_ssz_static", "tests/official/", "-r -d:release -d:chronicles_log_level=DEBUG -d:const_preset=minimal"
buildBinary "test_fixture_ssz_static", "tests/official/", "-r -d:release -d:chronicles_log_level=DEBUG -d:const_preset=mainnet"
buildBinary "all_fixtures_require_ssz", "tests/official/", "-r -d:release -d:chronicles_log_level=DEBUG -d:const_preset=minimal"
buildBinary "all_fixtures_require_ssz", "tests/official/", "-r -d:release -d:chronicles_log_level=DEBUG -d:const_preset=mainnet"
# State sim; getting into 3rd epoch useful
buildBinary "state_sim", "research/", "-r -d:release", "--validators=128 --slots=24"
task sync_lfs_tests, "Sync LFS json tests":
# Syncs the json test files (but not the EF yaml tests)
exec "scripts/process_lfs.sh"

View File

@ -128,12 +128,11 @@ func initiate_validator_exit*(state: var BeaconState,
# Compute exit queue epoch
# TODO try zero-functional here
let exit_epochs = mapIt(
var exit_epochs = mapIt(
filterIt(state.validators, it.exit_epoch != FAR_FUTURE_EPOCH),
it.exit_epoch)
var exit_queue_epoch =
max(max(exit_epochs),
compute_activation_exit_epoch(get_current_epoch(state)))
exit_epochs.add compute_activation_exit_epoch(get_current_epoch(state))
var exit_queue_epoch = max(exit_epochs)
let exit_queue_churn = foldl(
state.validators,
a + (if b.exit_epoch == exit_queue_epoch: 1'u64 else: 0'u64),

View File

@ -53,7 +53,10 @@ import
export
json_serialization
export blscurve.init, blscurve.getBytes, blscurve.combine, blscurve.`$`, blscurve.`==`
export
blscurve.init, blscurve.getBytes, blscurve.combine,
blscurve.`$`, blscurve.`==`,
blscurve.Signature
type
BlsValueType* = enum
@ -232,8 +235,13 @@ proc readValue*(reader: var JsonReader, value: var ValidatorPubKey) {.inline.} =
proc writeValue*(writer: var JsonWriter, value: ValidatorSig) {.inline.} =
when value is BlsValue:
doAssert value.kind == Real
writer.writeValue($value.blsValue)
if value.kind == Real:
writer.writeValue($value.blsValue)
else:
# Workaround: https://github.com/status-im/nim-beacon-chain/issues/374
let asHex = toHex(value.blob, true)
# echo "[Warning] writing raw opaque signature: ", asHex
writer.writeValue(asHex)
else:
writer.writeValue($value)
@ -293,4 +301,3 @@ proc writeValue*(writer: var JsonWriter, value: Signature) {.inline.} =
proc readValue*(reader: var JsonReader, value: var Signature) {.inline.} =
value = Signature.init(reader.readValue(string))

View File

@ -64,8 +64,11 @@ proc processBlockHeader(
# state_root: zeroed, overwritten in the next `process_slot` call
body_root: hash_tree_root(blck.body),
# signature is always zeroed
# TODO - Pure BLSSig cannot be zero: https://github.com/status-im/nim-beacon-chain/issues/374
signature: BlsValue[Signature](kind: OpaqueBlob)
)
# Verify proposer is not slashed
let proposer =
state.validators[get_beacon_proposer_index(state, stateCache)]

View File

@ -24,7 +24,8 @@ import # Refactor state transition unit tests
./spec_block_processing/test_process_attestation,
./spec_epoch_processing/test_process_crosslinks
import # Official fixtures
import # Official fixtures that don't require SSZ parsing of invalid BLS signatures
# https://github.com/status-im/nim-beacon-chain/issues/374
./official/test_fixture_shuffling,
./official/test_fixture_bls,
./official/test_fixture_ssz_uint

View File

@ -7,66 +7,30 @@
import
macros,
nimcrypto/utils,
../../beacon_chain/spec/[datatypes, crypto, digest]
# digest is necessary for them to be printed as hex
# Define comparison of object variants for BLSValue
# https://github.com/nim-lang/Nim/issues/6676
# (fully generic available - see also https://github.com/status-im/nim-beacon-chain/commit/993789bad684721bd7c74ea14b35c2d24dbb6e51)
# ----------------------------------------------------------------
proc processNode(arg, a,b, result: NimNode) =
case arg.kind
of nnkIdentDefs:
let field = arg[0]
result.add quote do:
if `a`.`field` != `b`.`field`:
return false
of nnkRecCase:
let kindField = arg[0][0]
processNode(arg[0], a,b, result)
let caseStmt = nnkCaseStmt.newTree(newDotExpr(a, kindField))
for i in 1 ..< arg.len:
let inputBranch = arg[i]
let outputBranch = newTree(inputBranch.kind)
let body = newStmtList()
if inputBranch.kind == nnkOfBranch:
outputBranch.add inputBranch[0]
processNode(inputBranch[1], a,b, body)
else:
inputBranch.expectKind nnkElse
processNode(inputBranch[0], a,b, body)
outputBranch.add body
caseStmt.add outputBranch
result.add caseStmt
of nnkRecList:
for child in arg:
child.expectKind {nnkIdentDefs, nnkRecCase}
processNode(child, a,b, result)
proc `==`*[T](a, b: BlsValue[T]): bool =
## We sometimes need to compare real BlsValue
## from parsed opaque blobs that are not really on the BLS curve
## and full of zeros
if a.kind == Real:
if b.kind == Real:
a.blsvalue == b.blsValue
else:
$a.blsvalue == toHex(b.blob, true)
else:
arg.expectKind {nnkIdentDefs, nnkRecCase, nnkRecList}
if b.kind == Real:
toHex(a.blob, true) == $b.blsValue
else:
a.blob == b.blob
macro myCompareImpl(a,b: typed): untyped =
a.expectKind nnkSym
b.expectKind nnkSym
assert sameType(a, b)
let typeImpl = a.getTypeImpl
var checks = newSeq[NimNode]()
# uncomment to debug
# echo typeImpl.treeRepr
result = newStmtList()
processNode(typeImpl[2], a, b, result)
result.add quote do:
return true
# uncomment to debug
# echo result.repr
proc `==`*[T](a,b: BlsValue[T]): bool =
myCompareImpl(a,b)
# ---------------------------------------------------------------------
# This tool inspects and compare 2 instances of a type recursively

View File

@ -0,0 +1,15 @@
# 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.
# All non-pure SSZ tests that require the -d:ssz_testing
# to ignore invalid BLS signature in EF test vectors
# https://github.com/status-im/nim-beacon-chain/issues/374
import
./test_fixture_sanity_slots,
./test_fixture_sanity_blocks,
./test_fixture_state_transition_epoch

View File

@ -0,0 +1 @@
-d:"ssz_testing"

View File

@ -0,0 +1,111 @@
# beacon_chain
# Copyright (c) 2018-Present 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, unittest, strutils,
# Beacon chain internals
../../beacon_chain/spec/[datatypes],
../../beacon_chain/[ssz, state_transition, extras],
# Test utilities
../testutil,
./fixtures_utils,
../helpers/debug_state,
../mocking/mock_blocks
const SanityBlocksDir = SszTestsDir/const_preset/"phase0"/"sanity"/"blocks"/"pyspec_tests"
template runValidTest(testName: string, identifier: untyped, num_blocks: int): untyped =
# We wrap the tests in a proc to avoid running out of globals
# in the future: Nim supports up to 3500 globals
# but unittest with the macro/templates put everything as globals
# https://github.com/nim-lang/Nim/issues/12084#issue-486866402
const testDir = SanityBlocksDir / astToStr(identifier)
proc `testImpl _ blck _ identifier`() =
test "[Valid] " & testName & " (" & astToStr(identifier) & ")":
var stateRef, postRef: ref BeaconState
new stateRef
new postRef
stateRef[] = parseTest(testDir/"pre.ssz", SSZ, BeaconState)
postRef[] = parseTest(testDir/"post.ssz", SSZ, BeaconState)
var success = true
for i in 0 ..< num_blocks:
let blck = parseTest(testDir/"blocks_" & $i & ".ssz", SSZ, BeaconBlock)
# TODO: The EF is using invalid BLS keys so we can't verify them
let success = state_transition(stateRef[], blck, flags = {skipValidation})
doAssert success, "Failure when applying block " & $i
# Checks:
# check: stateRef.hash_tree_root() == postRef.hash_tree_root()
reportDiff(stateRef, postRef)
`testImpl _ blck _ identifier`()
suite "Official - Sanity - Blocks " & preset():
test "[Invalid] Previous slot block transition (prev_slot_block_transition)":
const testDir = SanityBlocksDir/"prev_slot_block_transition"
var stateRef: ref BeaconState
new stateRef
stateRef[] = parseTest(testDir/"pre.ssz", SSZ, BeaconState)
let blck = parseTest(testDir/"blocks_0.ssz", SSZ, BeaconBlock)
# Check that a block build for an old slot cannot be used for state transition
expect(AssertionError):
# assert in process_slots. This should not be triggered
# for blocks from block_pool/network
let done = state_transition(stateRef[], blck, flags = {skipValidation})
runValidTest("Same slot block transition", same_slot_block_transition, 1)
runValidTest("Empty block transition", empty_block_transition, 1)
when false: # TODO: we need more granular skipValidation
test "[Invalid] Invalid state root":
const testDir = SanityBlocksDir/"invalid_state_root"
var stateRef: ref BeaconState
new stateRef
stateRef[] = parseTest(testDir/"pre.ssz", SSZ, BeaconState)
let blck = parseTest(testDir/"blocks_0.ssz", SSZ, BeaconBlock)
expect(AssertionError):
let done = state_transition(stateRef[], blck, flags = {skipValidation})
runValidTest("Skipped Slots", skipped_slots, 1)
when false: # TODO: failing due to state_roots[8]
runValidTest("Empty epoch transition", empty_epoch_transition, 1)
runValidTest("Empty epoch transition not finalizing", empty_epoch_transition_not_finalizing, 1)
runValidTest("Proposer slashing", proposer_slashing, 1)
when false: # TODO: Assert spec/crypto.nim(156, 12) `x.kind == Real and other.kind == Real`
runValidTest("Attester slashing", attester_slashing, 1)
# TODO: Expected deposit in block
when false: # TODO: Assert .spec/crypto.nim(175, 14) `sig.kind == Real and pubkey.kind == Real`
runValidTest("Deposit in block", deposit_in_block, 1)
runValidTest("Deposit top up", deposit_top_up, 1)
when false: # TODO: Assert spec/crypto.nim(156, 12) `x.kind == Real and other.kind == Real`
runValidTest("Attestation", attestation, 2)
when false: # TODO: failing due to state_roots[8]
runValidTest("Voluntary exit", voluntary_exit, 2)
runValidTest("Balance-driven status transitions", balance_driven_status_transitions, 1)
when false: # TODO: `stateRef3946003.balances[idx3953625] == postRef3946005.balances[idx3953625]`
# stateRef3946003.balances[0] = 31998855136
# postRef3946005.balances[0] = 31997418334
runValidTest("Historical batch", historical_batch, 1)
when false: # TODO: `stateRef3870603.block_roots[idx3874628] == postRef3870605.block_roots[idx3874628]`
# stateRef3856003.block_roots[16] = 06013007F8A1D4E310344192C5DF6157B1F9F0F5B3A8404103ED822DF47CD85D
# postRef3856005.block_roots[16] = 73F47FF01C106CC82BF839C953C4171E019A22590D762076306F4CEE1CB77583
runValidTest("ETH1 data votes consensus", eth1_data_votes_consensus, 17)
runValidTest("ETH1 data votes no consensus", eth1_data_votes_no_consensus, 16)

View File

@ -0,0 +1 @@
-d:"ssz_testing"

View File

@ -0,0 +1,58 @@
# beacon_chain
# Copyright (c) 2018-Present 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, unittest, strutils,
# Beacon chain internals
../../beacon_chain/spec/datatypes,
../../beacon_chain/state_transition,
# Test utilities
../testutil,
./fixtures_utils,
../helpers/debug_state
const SanitySlotsDir = SszTestsDir/const_preset/"phase0"/"sanity"/"slots"/"pyspec_tests"
template runTest(testName: string, identifier: untyped, num_slots: uint64): untyped =
# We wrap the tests in a proc to avoid running out of globals
# in the future: Nim supports up to 3500 globals
# but unittest with the macro/templates put everything as globals
# https://github.com/nim-lang/Nim/issues/12084#issue-486866402
const testDir = SanitySlotsDir / astToStr(identifier)
proc `testImpl _ slots _ identifier`() =
test "Slots - " & testName & " (" & astToStr(identifier) & ")":
var stateRef, postRef: ref BeaconState
new stateRef
new postRef
stateRef[] = parseTest(testDir/"pre.ssz", SSZ, BeaconState)
postRef[] = parseTest(testDir/"post.ssz", SSZ, BeaconState)
process_slots(stateRef[], stateRef.slot + num_slots)
# check: stateRef.hash_tree_root() == postRef.hash_tree_root()
reportDiff(stateRef, postRef)
`testImpl _ slots _ identifier`()
# 1 slot
# ---------------------------------------------------------------
suite "Official - Sanity - Slots " & preset():
runTest("Advance 1 slot", slots_1, 1)
runTest("Advance 2 slots", slots_2, 2)
when false: # TODO: issue in active_index_roots - https://github.com/status-im/nim-beacon-chain/issues/373
runTest("Advance an empty epoch", empty_epoch, SLOTS_PER_EPOCH)
when false: # TODO: issue in state_roots
const DoubleEpoch = SLOTS_PER_EPOCH.uint64*2 # workaround undeclared identifier "double_empty_epoch"
runTest("Advance 2 empty epochs", double_empty_epoch, DoubleEpoch)
# This starts in the middle of an epoch
runTest("Advance over an epoch boundary", over_epoch_boundary, SLOTS_PER_EPOCH)

View File

@ -0,0 +1 @@
-d:"ssz_testing"

View File

@ -12,7 +12,8 @@ import
../../beacon_chain/spec/[datatypes, validator, state_transition_epoch],
# Test utilities
../testutil,
./fixtures_utils
./fixtures_utils,
../helpers/debug_state
from ../../beacon_chain/spec/beaconstate import process_registry_updates
# XXX: move to state_transition_epoch?
@ -30,7 +31,7 @@ template runSuite(suiteDir, testName: string, transitionProc: untyped{ident}, us
# https://github.com/nim-lang/Nim/issues/12084#issue-486866402
proc `suiteImpl _ transitionProc`() =
suite "Official - Epoch Processing - " & testName & " [Preset: " & preset():
suite "Official - Epoch Processing - " & testName & preset():
for testDir in walkDirRec(suiteDir, yieldFilter = {pcDir}):
let unitTestName = testDir.rsplit(DirSep, 1)[1]
@ -47,7 +48,7 @@ template runSuite(suiteDir, testName: string, transitionProc: untyped{ident}, us
else:
transitionProc(stateRef[])
check: stateRef.hash_tree_root() == postRef.hash_tree_root()
reportDiff(stateRef, postRef)
`suiteImpl _ transitionProc`()