From e5047d93a44b680da50f572d55b32a256570477c Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Fri, 10 May 2019 10:14:01 +0200 Subject: [PATCH] [Tests] SHA256 + official shuffling vectors (#263) * Change digests to SHA2-256 (from Keccak256) * Fix sha256 digest initialization - pass shuffling test * Add comments + remove old shuffling tests * Remove stale tree hashing tests * Small toOpenArray optim + notes on in-place hashing * Revert "Revert "Update test repo"" This reverts commit f385467cd3312058ec0ccf70be83e80f400a41c5. * Revert "Revert "Fix SIGFPE on shuffling for 0"" This reverts commit 226d380f8733663c033edee94fdd8427c4ddb2a5. * Revert "Revert "Implement shuffling test (pending typedesc and shuffling algo fix)"" This reverts commit 813cb6fbb8cdbb768a894c35173c3adc1e5d6c59. * withEth2hash templates now needs ctx.init() * Use init-update-finish to avoid burnMem --- beacon_chain/spec/digest.nim | 41 +++++----- beacon_chain/spec/validator.nim | 15 +++- beacon_chain/ssz.nim | 2 +- tests/all_tests.nim | 10 ++- tests/official/fixtures | 2 +- ...tate_test_utils.nim => fixtures_utils.nim} | 46 ++++++++++-- tests/official/test_fixture_shuffling.nim | 30 ++++++++ tests/official/test_fixture_state.nim | 6 +- tests/test_ssz.nim | 26 +++---- tests/test_validator.nim | 74 +------------------ 10 files changed, 131 insertions(+), 121 deletions(-) rename tests/official/{state_test_utils.nim => fixtures_utils.nim} (75%) create mode 100644 tests/official/test_fixture_shuffling.nim diff --git a/beacon_chain/spec/digest.nim b/beacon_chain/spec/digest.nim index 15c46d2df..dd87eea05 100644 --- a/beacon_chain/spec/digest.nim +++ b/beacon_chain/spec/digest.nim @@ -7,21 +7,20 @@ # Serenity hash function / digest # -# https://github.com/ethereum/eth2.0-specs/blob/v0.6.0/specs/core/0_beacon-chain.md#hash +# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#hash # -# In Phase 0 the beacon chain is deployed with the same hash function as -# Ethereum 1.0, i.e. Keccak-256 (also incorrectly known as SHA3). +# In Phase 0 the beacon chain is deployed with SHA256 (SHA2-256). +# Note that is is different from Keccak256 (often mistakenly called SHA3-256) +# and SHA3-256. # -# Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future -# Ethereum 2.0 deployment phase. +# In Eth1.0, the default hash function is Keccak256 and SHA256 is available as a precompiled contract. # -# https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub -# -# In our code base, to enable a smooth transition, we call this function -# `eth2hash`, and it outputs a `Eth2Digest`. Easy to sed :) +# In our code base, to enable a smooth transition +# (already did Blake2b --> Keccak256 --> SHA2-256), +# we call this function `eth2hash`, and it outputs a `Eth2Digest`. Easy to sed :) import - nimcrypto/[keccak, hash], eth/common/eth_types_json_serialization, + nimcrypto/[sha2, hash], eth/common/eth_types_json_serialization, hashes export @@ -29,17 +28,21 @@ export type Eth2Digest* = MDigest[32 * 8] ## `hash32` from spec - Eth2Hash* = keccak256 ## Context for hash function + Eth2Hash* = sha256 ## Context for hash function func shortLog*(x: Eth2Digest): string = # result = is needed to fix https://github.com/status-im/nim-beacon-chain/issues/209 result = ($x)[0..7] -func eth2hash*(v: openArray[byte]): Eth2Digest = - var ctx: keccak256 # use explicit type so we can rely on init being useless - # We can avoid this step for Keccak/SHA3 digests because `ctx` is already - # empty, but if digest will be changed next line must be enabled. - # ctx.init() +# TODO: expose an in-place digest function +# when hashing in loop or into a buffer +# See: https://github.com/cheatfate/nimcrypto/blob/b90ba3abd/nimcrypto/sha2.nim#L570 +func eth2hash*(v: openArray[byte]): Eth2Digest {.inline.} = + # We use the init-update-finish interface to avoid + # the expensive burning/clearing memory (20~30% perf) + # TODO: security implication? + var ctx: sha256 + ctx.init() ctx.update(v) result = ctx.finish() @@ -47,14 +50,14 @@ template withEth2Hash*(body: untyped): Eth2Digest = ## This little helper will init the hash function and return the sliced ## hash: ## let hashOfData = withHash: h.update(data) - var h {.inject.}: keccak256 - # TODO no need, as long as using keccak256: h.init() + var h {.inject.}: sha256 + h.init() body var res = h.finish() res func hash*(x: Eth2Digest): Hash = - ## Hash for Keccak digests for Nim hash tables + ## Hash for digests for Nim hash tables # Stub for BeaconChainDB # We just slice the first 4 or 8 bytes of the block hash diff --git a/beacon_chain/spec/validator.nim b/beacon_chain/spec/validator.nim index 5425ba50b..0e98c9a70 100644 --- a/beacon_chain/spec/validator.nim +++ b/beacon_chain/spec/validator.nim @@ -12,8 +12,9 @@ import ../ssz, ../beacon_node_types, ./crypto, ./datatypes, ./digest, ./helpers -# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#get_shuffling -# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#get_permuted_index +# TODO: Proceed to renaming and signature changes +# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_shuffled_index +# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#compute_committee func get_shuffled_seq*(seed: Eth2Digest, list_size: uint64, ): seq[ValidatorIndex] = @@ -25,8 +26,16 @@ func get_shuffled_seq*(seed: Eth2Digest, ## ## Invert the inner/outer loops from the spec, essentially. Most useful ## hash result re-use occurs within a round. + + # Empty size -> empty list. + if list_size == 0: + return + var # Share these buffers. + # TODO: Redo to follow spec. + # We can have an "Impl" private version that takes buffer as parameters + # so that we avoid alloc on repeated calls from compute_committee pivot_buffer: array[(32+1), byte] source_buffer: array[(32+1+4), byte] shuffled_active_validator_indices = mapIt( @@ -44,7 +53,7 @@ func get_shuffled_seq*(seed: Eth2Digest, source_buffer[32] = round_bytes1 # Only one pivot per round. - let pivot = bytes_to_int(eth2hash(pivot_buffer).data[0..7]) mod list_size + let pivot = bytes_to_int(eth2hash(pivot_buffer).data.toOpenArray(0, 7)) mod list_size ## Only need to run, per round, position div 256 hashes, so precalculate ## them. This consumes memory, but for low-memory devices, it's possible diff --git a/beacon_chain/ssz.nim b/beacon_chain/ssz.nim index faabdaad3..f0098679c 100644 --- a/beacon_chain/ssz.nim +++ b/beacon_chain/ssz.nim @@ -10,7 +10,7 @@ import endians, typetraits, options, algorithm, math, - faststreams/input_stream, serialization, eth/common, nimcrypto/keccak, + faststreams/input_stream, serialization, eth/common, nimcrypto/sha2, ./spec/[bitfield, crypto, datatypes, digest] # ################### Helper functions ################################### diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 6b7ad24e0..1d52f3de1 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -5,7 +5,7 @@ # * 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 +import # Unit test ./test_attestation_pool, ./test_beacon_chain_db, ./test_beacon_node, @@ -15,8 +15,10 @@ import ./test_helpers, ./test_ssz, ./test_state_transition, - ./test_sync_protocol, - ./test_validator + ./test_sync_protocol + # ./test_validator # Empty! - # TODO - re-enable once official test fixtures arrive +import # Official fixtures + # TODO - re-enable #./official/test_fixture_state + ./official/test_fixture_shuffling \ No newline at end of file diff --git a/tests/official/fixtures b/tests/official/fixtures index da49e682b..80c365279 160000 --- a/tests/official/fixtures +++ b/tests/official/fixtures @@ -1 +1 @@ -Subproject commit da49e682bf93c43ffc207e4b1b948962c5068196 +Subproject commit 80c3652796488e62c37087a9bffae60c6b1cefa5 diff --git a/tests/official/state_test_utils.nim b/tests/official/fixtures_utils.nim similarity index 75% rename from tests/official/state_test_utils.nim rename to tests/official/fixtures_utils.nim index fcb829d49..8ab1ba454 100644 --- a/tests/official/state_test_utils.nim +++ b/tests/official/fixtures_utils.nim @@ -12,14 +12,15 @@ export nimcrypto.toHex type # TODO: use ref object to avoid allocating # so much on the stack - pending https://github.com/status-im/nim-json-serialization/issues/3 - StateTest* = object + StateTests* = object title*: string summary*: string test_suite*: string fork*: string - test_cases*: seq[TestCase] + test_cases*: seq[StateTestCase] TestConstants* = object + # TODO - 0.5.1 constants SHARD_COUNT*: int TARGET_COMMITTEE_SIZE*: int MAX_BALANCE_CHURN_QUOTIENT*: int @@ -66,13 +67,30 @@ type DOMAIN_VOLUNTARY_EXIT*: SignatureDomain DOMAIN_TRANSFER*: SignatureDomain - TestCase* = object + StateTestCase* = object name*: string config*: TestConstants verify_signatures*: bool initial_state*: BeaconState blocks*: seq[BeaconBlock] expected_state*: BeaconState + + ShufflingTests* = object + title*: string + summary*: string + forks_timeline*: string + forks*: seq[string] + config*: string + runner*: string + handler*: string + test_cases*: seq[ShufflingTestCase] + + ShufflingTestCase* = object + seed*: Eth2Digest + count*: uint64 + shuffled*: seq[ValidatorIndex] + + Tests* = StateTests or ShufflingTests # ####################### # Default init @@ -89,9 +107,27 @@ proc readValue*[N: static int](r: var JsonReader, a: var array[N, byte]) {.inlin # if so export that to nim-eth hexToByteArray(r.readValue(string), a) -proc parseStateTests*(jsonPath: string): StateTest = +proc readValue*(r: var JsonReader, a: var ValidatorIndex) {.inline.} = + a = r.readValue(uint32) + +# TODO: cannot pass a typedesc +# proc parseTests*(jsonPath: string, T: typedesc[Tests]): T = +# # TODO: due to generic early symbol resolution +# # we cannot use a generic proc +# # Otherwise we get: +# # "Error: undeclared identifier: 'ReaderType'" +# # Templates, even untyped don't work +# try: +# result = Json.loadFile(jsonPath, T) +# except SerializationError as err: +# writeStackTrace() +# stderr.write "Json load issue for file \"", jsonPath, "\"\n" +# stderr.write err.formatMsg(jsonPath), "\n" +# quit 1 + +proc parseTests*(jsonPath: string): ShufflingTests = try: - result = Json.loadFile(jsonPath, StateTest) + result = Json.loadFile(jsonPath, ShufflingTests) except SerializationError as err: writeStackTrace() stderr.write "Json load issue for file \"", jsonPath, "\"\n" diff --git a/tests/official/test_fixture_shuffling.nim b/tests/official/test_fixture_shuffling.nim new file mode 100644 index 000000000..cdfab8565 --- /dev/null +++ b/tests/official/test_fixture_shuffling.nim @@ -0,0 +1,30 @@ +# 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 libs + ospaths, strutils, json, unittest, + # Third parties + + # Beacon chain internals + ../../beacon_chain/spec/validator, + # Test utilities + ./fixtures_utils + +const TestFolder = currentSourcePath.rsplit(DirSep, 1)[0] +const TestsPath = "fixtures" / "json_tests" / "shuffling" / "core" / "shuffling_full.json" + +var shufflingTests: ShufflingTests + +suite "Official - Shuffling tests": + test "Parsing the official shuffling tests": + shufflingTests = parseTests(TestFolder / TestsPath) + + test "Shuffling a sequence of N validators": + for t in shufflingTests.test_cases: + let implResult = get_shuffled_seq(t.seed, t.count) + check: implResult == t.shuffled \ No newline at end of file diff --git a/tests/official/test_fixture_state.nim b/tests/official/test_fixture_state.nim index 00f86ad6e..11c44c7bf 100644 --- a/tests/official/test_fixture_state.nim +++ b/tests/official/test_fixture_state.nim @@ -14,17 +14,17 @@ import ../../beacon_chain/spec/[datatypes, crypto, digest, beaconstate], ../../beacon_chain/[ssz, state_transition], # Test utilities - ./state_test_utils + ./fixtures_utils const TestFolder = currentSourcePath.rsplit(DirSep, 1)[0] const TestsPath = "fixtures" / "json_tests" / "state" / "sanity-check_default-config_100-vals.json" -var stateTests: StateTest +var stateTests: StateTests suite "Official - State tests": # Initializing a beacon state from the deposits # Source: https://github.com/ethereum/eth2.0-specs/blob/2baa242ac004b0475604c4c4ef4315e14f56c5c7/tests/phase0/test_sanity.py#L55-L460 test "Parsing the official state tests into Nimbus beacon types": - stateTests = parseStateTests(TestFolder / TestsPath) + stateTests = parseTests(TestFolder / TestsPath, StateTests) # TODO pending typedesc fix in fixture_utils.nim doAssert $stateTests.test_cases[0].name == "test_empty_block_transition" test "[For information - Non-blocking] Block root signing": diff --git a/tests/test_ssz.nim b/tests/test_ssz.nim index 958c5cd63..7bd0f7463 100644 --- a/tests/test_ssz.nim +++ b/tests/test_ssz.nim @@ -78,18 +78,18 @@ suite "Simple serialization": slot: 42.Slot, signature: sign(SigKey.random(), 0'u64, "")) SSZ.roundripTest BeaconState(slot: 42.Slot) -suite "Tree hashing": - # TODO The test values are taken from an earlier version of SSZ and have - # nothing to do with upstream - needs verification and proper test suite +# suite "Tree hashing": +# # TODO The test values are taken from an earlier version of SSZ and have +# # nothing to do with upstream - needs verification and proper test suite - test "Hash BeaconBlock": - let vr = BeaconBlock() - check: - $hash_tree_root(vr) == - "8951C9C64ABA469EBA78F5D9F9A0666FB697B8C4D86901445777E4445D0B1543" +# test "Hash BeaconBlock": +# let vr = BeaconBlock() +# check: +# $hash_tree_root(vr) == +# "8951C9C64ABA469EBA78F5D9F9A0666FB697B8C4D86901445777E4445D0B1543" - test "Hash BeaconState": - let vr = BeaconState() - check: - $hash_tree_root(vr) == - "66F9BF92A690F1FBD36488D98BE70DA6C84100EDF935BC6D0B30FF14A2976455" +# test "Hash BeaconState": +# let vr = BeaconState() +# check: +# $hash_tree_root(vr) == +# "66F9BF92A690F1FBD36488D98BE70DA6C84100EDF935BC6D0B30FF14A2976455" diff --git a/tests/test_validator.nim b/tests/test_validator.nim index f31228d4e..aac9c754d 100644 --- a/tests/test_validator.nim +++ b/tests/test_validator.nim @@ -9,76 +9,6 @@ import ../beacon_chain/spec/[helpers, datatypes, digest, validator, beaconstate], ../beacon_chain/extras -func sumCommittees(v: openArray[seq[ValidatorIndex]], reqCommitteeLen: int): int = - for x in v: - ## This only holds when num_validators is divisible by - ## SLOTS_PER_EPOCH * get_committee_count_per_slot(len(validators)) - ## as, in general, not all committees can be equally sized. - doAssert x.len == reqCommitteeLen - inc result, x.len - -func checkPermutation(index: int, list_size: uint64, - permuted_index: int, seed: Eth2Digest): bool = - let - s = get_shuffled_seq(seed, list_size) - s[index] == permuted_index.ValidatorIndex - suite "Validators": - ## For now just test that we can compile and execute block processing with mock data. - ## https://github.com/status-im/nim-beacon-chain/issues/1 - ## https://github.com/sigp/lighthouse/blob/ba548e49a52687a655c61b443b6835d79c6d4236/beacon_chain/validator_shuffling/src/shuffle.rs - test "Validator shuffling": - let - num_validators = 4096 - genState = get_genesis_beacon_state( - makeInitialDeposits(num_validators, flags = {skipValidation}), 0, - Eth1Data(), {skipValidation}) - s = get_shuffling( - Eth2Digest(), genState.validator_registry, GENESIS_EPOCH) - committees = get_epoch_committee_count(genState, GENESIS_EPOCH).int - check: - # def b(s): return "Eth2Digest(data: [0x" + "'u8, 0x".join((s[i:i+2] for i in range(0, 64, 2))) + "'u8])" - # TODO read YAML file directly from test case generator - checkPermutation(0, 1, 0, Eth2Digest(data: [0xc0'u8, 0xc7'u8, 0xf2'u8, 0x26'u8, 0xfb'u8, 0xd5'u8, 0x74'u8, 0xa8'u8, 0xc6'u8, 0x3d'u8, 0xc2'u8, 0x68'u8, 0x64'u8, 0xc2'u8, 0x78'u8, 0x33'u8, 0xea'u8, 0x93'u8, 0x1e'u8, 0x7c'u8, 0x70'u8, 0xb3'u8, 0x44'u8, 0x09'u8, 0xba'u8, 0x76'u8, 0x5f'u8, 0x3d'u8, 0x20'u8, 0x31'u8, 0x63'u8, 0x3d'u8])) - checkPermutation(0, 2, 0, Eth2Digest(data: [0xb2'u8, 0x04'u8, 0x20'u8, 0xb2'u8, 0xb7'u8, 0xb1'u8, 0xc6'u8, 0x46'u8, 0x00'u8, 0xcb'u8, 0xe9'u8, 0x62'u8, 0x54'u8, 0x40'u8, 0x52'u8, 0xd0'u8, 0xbb'u8, 0xe1'u8, 0x3d'u8, 0xa4'u8, 0x03'u8, 0x95'u8, 0x0d'u8, 0x19'u8, 0x8d'u8, 0x4f'u8, 0x4e'u8, 0xa2'u8, 0x87'u8, 0x62'u8, 0x95'u8, 0x3f'u8])) - checkPermutation(1, 2, 0, Eth2Digest(data: [0x11'u8, 0xf1'u8, 0x32'u8, 0x2c'u8, 0x3a'u8, 0x4c'u8, 0xfc'u8, 0xe2'u8, 0x0e'u8, 0xfb'u8, 0x7d'u8, 0x7e'u8, 0xca'u8, 0x50'u8, 0x29'u8, 0x14'u8, 0x70'u8, 0x04'u8, 0x3d'u8, 0x6e'u8, 0x8a'u8, 0x2c'u8, 0x62'u8, 0x95'u8, 0x6e'u8, 0x68'u8, 0x75'u8, 0x71'u8, 0x60'u8, 0x7d'u8, 0x3f'u8, 0x0e'u8])) - checkPermutation(0, 3, 2, Eth2Digest(data: [0x5b'u8, 0xd0'u8, 0xaf'u8, 0x3f'u8, 0x74'u8, 0xfe'u8, 0x69'u8, 0x86'u8, 0xbb'u8, 0x99'u8, 0xb3'u8, 0xec'u8, 0xc0'u8, 0xea'u8, 0x15'u8, 0xa4'u8, 0x03'u8, 0x45'u8, 0x6c'u8, 0xe7'u8, 0x08'u8, 0xc0'u8, 0x5c'u8, 0xee'u8, 0xed'u8, 0xdc'u8, 0x0a'u8, 0x42'u8, 0x05'u8, 0xca'u8, 0xf0'u8, 0x72'u8])) - checkPermutation(1, 3, 1, Eth2Digest(data: [0xba'u8, 0x06'u8, 0xff'u8, 0x9b'u8, 0xde'u8, 0x03'u8, 0xf3'u8, 0x7e'u8, 0xdd'u8, 0xea'u8, 0xcb'u8, 0x26'u8, 0x1a'u8, 0x51'u8, 0x10'u8, 0x96'u8, 0x76'u8, 0xd5'u8, 0x49'u8, 0xc1'u8, 0xbe'u8, 0xa3'u8, 0xb8'u8, 0x1e'u8, 0xdd'u8, 0x82'u8, 0xdf'u8, 0x68'u8, 0xcc'u8, 0x03'u8, 0xa9'u8, 0x7f'u8])) - checkPermutation(2, 3, 2, Eth2Digest(data: [0xf5'u8, 0x8a'u8, 0x89'u8, 0x70'u8, 0xc6'u8, 0x3c'u8, 0xa8'u8, 0x6d'u8, 0xd3'u8, 0xb8'u8, 0xb8'u8, 0xa6'u8, 0x15'u8, 0x30'u8, 0x2e'u8, 0xc0'u8, 0x6c'u8, 0xdd'u8, 0xea'u8, 0x12'u8, 0x79'u8, 0xbf'u8, 0x4a'u8, 0x27'u8, 0x25'u8, 0xc7'u8, 0x81'u8, 0xce'u8, 0x6a'u8, 0xba'u8, 0x34'u8, 0x8d'u8])) - checkPermutation(0, 1024, 1005, Eth2Digest(data: [0x38'u8, 0x35'u8, 0x56'u8, 0xe2'u8, 0x3f'u8, 0xcb'u8, 0x9e'u8, 0x73'u8, 0xc2'u8, 0x3a'u8, 0xd3'u8, 0x3c'u8, 0xfb'u8, 0x50'u8, 0xf4'u8, 0xc0'u8, 0x98'u8, 0xf4'u8, 0x96'u8, 0x88'u8, 0xa8'u8, 0x4b'u8, 0x12'u8, 0x8c'u8, 0x28'u8, 0x85'u8, 0x96'u8, 0x0e'u8, 0x5f'u8, 0x1b'u8, 0x39'u8, 0x82'u8])) - checkPermutation(1023, 1024, 934, Eth2Digest(data: [0x2e'u8, 0xe5'u8, 0xda'u8, 0xb3'u8, 0x0a'u8, 0xd1'u8, 0x58'u8, 0x0c'u8, 0xda'u8, 0xbb'u8, 0x17'u8, 0x5a'u8, 0x4b'u8, 0x15'u8, 0x12'u8, 0xca'u8, 0xc5'u8, 0x56'u8, 0x68'u8, 0x66'u8, 0xd6'u8, 0x5a'u8, 0x15'u8, 0xe9'u8, 0xe2'u8, 0x2c'u8, 0x84'u8, 0x44'u8, 0xf4'u8, 0x60'u8, 0xc9'u8, 0xdc'u8])) - checkPermutation(3925, 4040, 32, Eth2Digest(data: [0x34'u8, 0xa3'u8, 0xc1'u8, 0x3f'u8, 0x21'u8, 0x1e'u8, 0x63'u8, 0xc5'u8, 0x6e'u8, 0x9e'u8, 0x11'u8, 0x87'u8, 0xf3'u8, 0x1a'u8, 0x56'u8, 0xa4'u8, 0x23'u8, 0x0d'u8, 0x8d'u8, 0x5b'u8, 0xf5'u8, 0xe5'u8, 0x84'u8, 0xf0'u8, 0xe4'u8, 0xfe'u8, 0x93'u8, 0x94'u8, 0x6a'u8, 0xf9'u8, 0x1c'u8, 0xce'u8])) - checkPermutation(885, 2417, 1822, Eth2Digest(data: [0x13'u8, 0x46'u8, 0xe3'u8, 0x97'u8, 0x08'u8, 0x15'u8, 0x10'u8, 0x71'u8, 0x54'u8, 0xb5'u8, 0x8b'u8, 0x1e'u8, 0xff'u8, 0x41'u8, 0x1b'u8, 0xfc'u8, 0xa3'u8, 0x34'u8, 0x2e'u8, 0xa0'u8, 0xd8'u8, 0x28'u8, 0x2a'u8, 0x86'u8, 0x30'u8, 0x4d'u8, 0x79'u8, 0xd6'u8, 0x2d'u8, 0x5f'u8, 0x3c'u8, 0x52'u8])) - checkPermutation(840, 1805, 808, Eth2Digest(data: [0x08'u8, 0x10'u8, 0xc1'u8, 0x04'u8, 0xb7'u8, 0x5e'u8, 0x25'u8, 0xbf'u8, 0x89'u8, 0xc0'u8, 0x06'u8, 0x6d'u8, 0xee'u8, 0xbc'u8, 0x34'u8, 0x61'u8, 0x93'u8, 0x7f'u8, 0xc0'u8, 0xe7'u8, 0x2a'u8, 0xe0'u8, 0x4e'u8, 0xe7'u8, 0x4f'u8, 0x24'u8, 0x56'u8, 0x16'u8, 0xc1'u8, 0x57'u8, 0x18'u8, 0xdf'u8])) - checkPermutation(881, 1788, 582, Eth2Digest(data: [0x34'u8, 0xad'u8, 0xb3'u8, 0x5f'u8, 0x3f'u8, 0xc2'u8, 0x88'u8, 0x0d'u8, 0x22'u8, 0x0e'u8, 0x52'u8, 0x01'u8, 0x20'u8, 0xa0'u8, 0x32'u8, 0xbb'u8, 0xaa'u8, 0x0f'u8, 0x4b'u8, 0xd7'u8, 0xa5'u8, 0xfc'u8, 0xf1'u8, 0xc2'u8, 0x26'u8, 0x9d'u8, 0xe2'u8, 0x10'u8, 0x75'u8, 0xe7'u8, 0xa4'u8, 0x64'u8])) - checkPermutation(1362, 1817, 1018, Eth2Digest(data: [0xc9'u8, 0xb0'u8, 0xc7'u8, 0x6e'u8, 0x11'u8, 0xf4'u8, 0xc3'u8, 0xc3'u8, 0xc3'u8, 0x8b'u8, 0x44'u8, 0x7a'u8, 0xca'u8, 0x53'u8, 0x52'u8, 0xd9'u8, 0x31'u8, 0x32'u8, 0xad'u8, 0x56'u8, 0x78'u8, 0xda'u8, 0x42'u8, 0x0c'u8, 0xa2'u8, 0xe6'u8, 0x9d'u8, 0x92'u8, 0x58'u8, 0x8e'u8, 0x0f'u8, 0xba'u8])) - checkPermutation(28, 111, 0, Eth2Digest(data: [0x29'u8, 0x31'u8, 0x45'u8, 0xc3'u8, 0x1a'u8, 0xeb'u8, 0x3e'u8, 0xb2'u8, 0x9c'u8, 0xcd'u8, 0xf3'u8, 0x32'u8, 0x7d'u8, 0x0f'u8, 0x3d'u8, 0xd4'u8, 0x59'u8, 0x2c'u8, 0xdf'u8, 0xb2'u8, 0xfa'u8, 0xd3'u8, 0x70'u8, 0x32'u8, 0x29'u8, 0xc6'u8, 0xc2'u8, 0xe7'u8, 0x20'u8, 0xdc'u8, 0x79'u8, 0x2f'u8])) - checkPermutation(959, 2558, 2094, Eth2Digest(data: [0xc9'u8, 0xf4'u8, 0xc5'u8, 0xfb'u8, 0xb2'u8, 0xa3'u8, 0x97'u8, 0xfd'u8, 0x8e'u8, 0xa3'u8, 0x6d'u8, 0xbf'u8, 0xce'u8, 0xc0'u8, 0xd7'u8, 0x33'u8, 0xd0'u8, 0xaf'u8, 0x7e'u8, 0xc3'u8, 0xa0'u8, 0x3d'u8, 0x78'u8, 0x9a'u8, 0x66'u8, 0x23'u8, 0x1f'u8, 0x3b'u8, 0xc7'u8, 0xca'u8, 0xfa'u8, 0x5e'u8])) - checkPermutation(887, 2406, 831, Eth2Digest(data: [0x56'u8, 0x57'u8, 0x29'u8, 0xe0'u8, 0xd5'u8, 0xde'u8, 0x52'u8, 0x4e'u8, 0x6d'u8, 0xee'u8, 0x54'u8, 0xd1'u8, 0xb8'u8, 0xb5'u8, 0x88'u8, 0x2a'u8, 0xd8'u8, 0xe5'u8, 0x5c'u8, 0x18'u8, 0xa3'u8, 0x04'u8, 0x62'u8, 0xac'u8, 0x02'u8, 0xc4'u8, 0xbb'u8, 0x86'u8, 0xc2'u8, 0x7d'u8, 0x26'u8, 0xcb'u8])) - checkPermutation(3526, 3674, 3531, Eth2Digest(data: [0x29'u8, 0x51'u8, 0x39'u8, 0x5b'u8, 0x1a'u8, 0x1b'u8, 0xbd'u8, 0xa8'u8, 0xd5'u8, 0x3b'u8, 0x77'u8, 0x6c'u8, 0x7f'u8, 0xc8'u8, 0xbd'u8, 0xad'u8, 0x60'u8, 0x30'u8, 0xde'u8, 0x94'u8, 0x3c'u8, 0x4e'u8, 0x3f'u8, 0x93'u8, 0x82'u8, 0x02'u8, 0xac'u8, 0x55'u8, 0x3f'u8, 0x44'u8, 0x38'u8, 0x1d'u8])) - checkPermutation(978, 3175, 2257, Eth2Digest(data: [0x74'u8, 0xaa'u8, 0xc2'u8, 0x35'u8, 0x23'u8, 0xcb'u8, 0x45'u8, 0xb7'u8, 0xee'u8, 0x52'u8, 0xd5'u8, 0xd2'u8, 0xf7'u8, 0xb2'u8, 0xd2'u8, 0x4e'u8, 0xbc'u8, 0x6b'u8, 0xf2'u8, 0xd6'u8, 0x3e'u8, 0xf1'u8, 0x89'u8, 0xef'u8, 0xcc'u8, 0xab'u8, 0xc4'u8, 0xa1'u8, 0x6b'u8, 0xb1'u8, 0x7c'u8, 0xd8'u8])) - checkPermutation(37, 231, 48, Eth2Digest(data: [0xe4'u8, 0x08'u8, 0x3e'u8, 0x61'u8, 0xb3'u8, 0x19'u8, 0x31'u8, 0xba'u8, 0xd6'u8, 0x62'u8, 0x39'u8, 0x27'u8, 0x58'u8, 0xe8'u8, 0xbc'u8, 0x30'u8, 0xa4'u8, 0xce'u8, 0x7b'u8, 0x26'u8, 0xb6'u8, 0x89'u8, 0x7c'u8, 0x22'u8, 0x21'u8, 0xa3'u8, 0x35'u8, 0x8f'u8, 0x25'u8, 0xfd'u8, 0xc1'u8, 0xd8'u8])) - checkPermutation(340, 693, 234, Eth2Digest(data: [0x80'u8, 0x89'u8, 0xc1'u8, 0xf2'u8, 0x42'u8, 0xaa'u8, 0x48'u8, 0xc6'u8, 0x61'u8, 0x11'u8, 0x80'u8, 0xf2'u8, 0x21'u8, 0xc1'u8, 0x20'u8, 0xe9'u8, 0x30'u8, 0xad'u8, 0xee'u8, 0xca'u8, 0xf3'u8, 0x08'u8, 0x4b'u8, 0x2b'u8, 0x85'u8, 0xf9'u8, 0xb1'u8, 0xdf'u8, 0xeb'u8, 0xe3'u8, 0x4f'u8, 0x63'u8])) - checkPermutation(0, 9, 1, Eth2Digest(data: [0x7f'u8, 0xda'u8, 0x0a'u8, 0xb6'u8, 0xa7'u8, 0x46'u8, 0xb6'u8, 0xb0'u8, 0x20'u8, 0x6f'u8, 0xeb'u8, 0xb8'u8, 0x25'u8, 0x98'u8, 0x91'u8, 0xe0'u8, 0xe6'u8, 0xf8'u8, 0x8b'u8, 0xf5'u8, 0x21'u8, 0x43'u8, 0xb2'u8, 0x0d'u8, 0x6c'u8, 0x78'u8, 0xca'u8, 0xf7'u8, 0xca'u8, 0xf8'u8, 0xe7'u8, 0xb3'u8])) - checkPermutation(200, 1108, 952, Eth2Digest(data: [0x87'u8, 0xb2'u8, 0x10'u8, 0xd0'u8, 0x00'u8, 0xb5'u8, 0xf5'u8, 0x7e'u8, 0x98'u8, 0x34'u8, 0x38'u8, 0x8d'u8, 0x4b'u8, 0xc2'u8, 0xb8'u8, 0x6a'u8, 0xe8'u8, 0xb3'u8, 0x13'u8, 0x83'u8, 0xfa'u8, 0x10'u8, 0xa3'u8, 0x4b'u8, 0x02'u8, 0x95'u8, 0x46'u8, 0xc2'u8, 0xeb'u8, 0xab'u8, 0xb8'u8, 0x07'u8])) - checkPermutation(1408, 1531, 584, Eth2Digest(data: [0x06'u8, 0x70'u8, 0xa7'u8, 0x8b'u8, 0x38'u8, 0xe0'u8, 0x41'u8, 0x9a'u8, 0xae'u8, 0xad'u8, 0x5d'u8, 0x1c'u8, 0xc8'u8, 0xf4'u8, 0x0f'u8, 0x58'u8, 0x04'u8, 0x4b'u8, 0x70'u8, 0x76'u8, 0xce'u8, 0xd8'u8, 0x19'u8, 0x3c'u8, 0x08'u8, 0xb5'u8, 0x80'u8, 0xdd'u8, 0x95'u8, 0xa1'u8, 0x35'u8, 0x55'u8])) - checkPermutation(1704, 1863, 1022, Eth2Digest(data: [0xdb'u8, 0xf7'u8, 0x86'u8, 0x65'u8, 0x19'u8, 0x0a'u8, 0x61'u8, 0x33'u8, 0x19'u8, 0x1e'u8, 0x91'u8, 0xab'u8, 0x35'u8, 0xb1'u8, 0x10'u8, 0x6e'u8, 0x89'u8, 0x84'u8, 0xdf'u8, 0xc0'u8, 0xdf'u8, 0xa3'u8, 0x60'u8, 0x18'u8, 0x00'u8, 0x4f'u8, 0x88'u8, 0x0b'u8, 0x43'u8, 0x1c'u8, 0x2a'u8, 0x14'u8])) - checkPermutation(793, 3938, 2607, Eth2Digest(data: [0x54'u8, 0xbf'u8, 0x01'u8, 0x92'u8, 0x29'u8, 0x2f'u8, 0xfa'u8, 0xe0'u8, 0xbf'u8, 0x39'u8, 0xb3'u8, 0x9f'u8, 0x12'u8, 0xe0'u8, 0x54'u8, 0x0b'u8, 0x97'u8, 0x59'u8, 0x1a'u8, 0xf0'u8, 0xa2'u8, 0x98'u8, 0x0d'u8, 0x32'u8, 0xf2'u8, 0x77'u8, 0xbd'u8, 0x33'u8, 0x20'u8, 0x13'u8, 0x95'u8, 0xd3'u8])) - checkPermutation(14, 28, 10, Eth2Digest(data: [0x43'u8, 0x05'u8, 0x44'u8, 0x17'u8, 0xc6'u8, 0x05'u8, 0x64'u8, 0x04'u8, 0xc5'u8, 0x86'u8, 0xc9'u8, 0x07'u8, 0xdf'u8, 0xc5'u8, 0xfc'u8, 0xeb'u8, 0x66'u8, 0xeb'u8, 0xef'u8, 0x54'u8, 0x1d'u8, 0x14'u8, 0x3b'u8, 0x00'u8, 0xa3'u8, 0xb6'u8, 0x76'u8, 0xf3'u8, 0xc0'u8, 0xfb'u8, 0xf4'u8, 0xc5'u8])) - checkPermutation(2909, 3920, 726, Eth2Digest(data: [0x5e'u8, 0xab'u8, 0xf2'u8, 0x89'u8, 0xfd'u8, 0xcf'u8, 0xe0'u8, 0xa3'u8, 0xab'u8, 0xa3'u8, 0x3a'u8, 0x18'u8, 0x5f'u8, 0xb1'u8, 0xa4'u8, 0xae'u8, 0x2f'u8, 0x2b'u8, 0x6f'u8, 0x78'u8, 0xda'u8, 0xf6'u8, 0x1f'u8, 0x5d'u8, 0x35'u8, 0x69'u8, 0x71'u8, 0xe0'u8, 0xcb'u8, 0x27'u8, 0x02'u8, 0x07'u8])) - checkPermutation(1943, 1959, 1292, Eth2Digest(data: [0xca'u8, 0x86'u8, 0x32'u8, 0x2d'u8, 0xb5'u8, 0x69'u8, 0x27'u8, 0xd7'u8, 0x27'u8, 0x10'u8, 0x1e'u8, 0x31'u8, 0xc9'u8, 0x3f'u8, 0x61'u8, 0x6f'u8, 0x74'u8, 0x63'u8, 0x17'u8, 0xd2'u8, 0x9a'u8, 0xa1'u8, 0x0d'u8, 0x88'u8, 0xf3'u8, 0x71'u8, 0x59'u8, 0x29'u8, 0x63'u8, 0xde'u8, 0x92'u8, 0xaa'u8])) - checkPermutation(1647, 2094, 1805, Eth2Digest(data: [0x3c'u8, 0xfe'u8, 0x27'u8, 0x42'u8, 0x30'u8, 0xa1'u8, 0x12'u8, 0xbc'u8, 0x68'u8, 0x61'u8, 0x48'u8, 0x82'u8, 0x64'u8, 0x53'u8, 0x39'u8, 0xfd'u8, 0xa2'u8, 0xf1'u8, 0x34'u8, 0x50'u8, 0x1a'u8, 0x04'u8, 0x20'u8, 0x79'u8, 0xd6'u8, 0x20'u8, 0xec'u8, 0x65'u8, 0xcf'u8, 0x8d'u8, 0x3f'u8, 0xa6'u8])) - checkPermutation(1012, 1877, 216, Eth2Digest(data: [0x7b'u8, 0x5f'u8, 0xf8'u8, 0xa8'u8, 0x48'u8, 0xaf'u8, 0x32'u8, 0xd8'u8, 0x5c'u8, 0x6d'u8, 0x37'u8, 0xc2'u8, 0x6e'u8, 0x61'u8, 0xa5'u8, 0x7e'u8, 0x96'u8, 0x78'u8, 0x0f'u8, 0xce'u8, 0xbc'u8, 0x35'u8, 0x0a'u8, 0xd1'u8, 0x84'u8, 0x5e'u8, 0x83'u8, 0xfe'u8, 0x5e'u8, 0x46'u8, 0x79'u8, 0xac'u8])) - checkPermutation(35, 2081, 1458, Eth2Digest(data: [0x40'u8, 0x69'u8, 0x1a'u8, 0xa3'u8, 0x1a'u8, 0x49'u8, 0xc2'u8, 0x39'u8, 0x1e'u8, 0x02'u8, 0x5e'u8, 0xc2'u8, 0x72'u8, 0xc8'u8, 0x12'u8, 0x51'u8, 0x0c'u8, 0xb0'u8, 0x7c'u8, 0x05'u8, 0x5f'u8, 0x62'u8, 0x01'u8, 0xe8'u8, 0x44'u8, 0x79'u8, 0x49'u8, 0x93'u8, 0x26'u8, 0x33'u8, 0x06'u8, 0x28'u8])) - checkPermutation(1136, 2189, 1579, Eth2Digest(data: [0x31'u8, 0xa0'u8, 0xde'u8, 0xb2'u8, 0xc8'u8, 0xc5'u8, 0xf8'u8, 0x09'u8, 0xf4'u8, 0x13'u8, 0xb7'u8, 0xa3'u8, 0x6e'u8, 0xc6'u8, 0x80'u8, 0xee'u8, 0x8b'u8, 0x19'u8, 0xbb'u8, 0xb9'u8, 0xa3'u8, 0x9c'u8, 0x4e'u8, 0x20'u8, 0x73'u8, 0x26'u8, 0x15'u8, 0x58'u8, 0x64'u8, 0xbc'u8, 0x8b'u8, 0xe5'u8])) - checkPermutation(1775, 3434, 707, Eth2Digest(data: [0x92'u8, 0xf3'u8, 0x0d'u8, 0x85'u8, 0x56'u8, 0x38'u8, 0x2b'u8, 0x72'u8, 0xa5'u8, 0x79'u8, 0x7d'u8, 0xb8'u8, 0x11'u8, 0x48'u8, 0x6e'u8, 0x7a'u8, 0x21'u8, 0x3e'u8, 0x01'u8, 0x45'u8, 0xd6'u8, 0xc9'u8, 0x46'u8, 0xe5'u8, 0x12'u8, 0x1a'u8, 0xa6'u8, 0xa8'u8, 0xf7'u8, 0x61'u8, 0xd1'u8, 0x64'u8])) - checkPermutation(1109, 2010, 433, Eth2Digest(data: [0x09'u8, 0x3f'u8, 0xb9'u8, 0x76'u8, 0xf2'u8, 0x49'u8, 0x73'u8, 0x61'u8, 0x89'u8, 0x70'u8, 0x12'u8, 0xdf'u8, 0xa6'u8, 0xdc'u8, 0x01'u8, 0x90'u8, 0x09'u8, 0xed'u8, 0xa2'u8, 0xe4'u8, 0x8b'u8, 0xbe'u8, 0xb4'u8, 0xb7'u8, 0xc5'u8, 0x6d'u8, 0x4a'u8, 0xa5'u8, 0xda'u8, 0x7d'u8, 0x5f'u8, 0x87'u8])) - checkPermutation(359, 538, 115, Eth2Digest(data: [0xa7'u8, 0x9b'u8, 0x35'u8, 0xbe'u8, 0xac'u8, 0xbe'u8, 0x48'u8, 0xc6'u8, 0x62'u8, 0xd6'u8, 0x08'u8, 0x84'u8, 0xc7'u8, 0x04'u8, 0x04'u8, 0x00'u8, 0x24'u8, 0xc5'u8, 0x5a'u8, 0xb8'u8, 0x79'u8, 0xe5'u8, 0xf6'u8, 0x15'u8, 0x21'u8, 0x01'u8, 0x3c'u8, 0x5f'u8, 0x45'u8, 0xeb'u8, 0x3b'u8, 0x70'u8])) - checkPermutation(1259, 1473, 1351, Eth2Digest(data: [0x02'u8, 0xc5'u8, 0x3c'u8, 0x9c'u8, 0x6d'u8, 0xdf'u8, 0x25'u8, 0x97'u8, 0x16'u8, 0xff'u8, 0x02'u8, 0xe4'u8, 0x9a'u8, 0x29'u8, 0x4e'u8, 0xba'u8, 0x33'u8, 0xe4'u8, 0xad'u8, 0x25'u8, 0x5d'u8, 0x7e'u8, 0x90'u8, 0xdb'u8, 0xef'u8, 0xdb'u8, 0xc9'u8, 0x91'u8, 0xad'u8, 0xf6'u8, 0x03'u8, 0xe5'u8])) - checkPermutation(2087, 2634, 1497, Eth2Digest(data: [0xa5'u8, 0xa4'u8, 0xc5'u8, 0x7c'u8, 0x57'u8, 0x05'u8, 0xec'u8, 0x69'u8, 0x7a'u8, 0x74'u8, 0xe6'u8, 0xc7'u8, 0x16'u8, 0x11'u8, 0x91'u8, 0xb1'u8, 0x8f'u8, 0x58'u8, 0xca'u8, 0x88'u8, 0x2a'u8, 0x0f'u8, 0xcc'u8, 0x18'u8, 0xf6'u8, 0x8d'u8, 0xc3'u8, 0xb5'u8, 0x7a'u8, 0x1a'u8, 0xa5'u8, 0xb6'u8])) - checkPermutation(2069, 2511, 1837, Eth2Digest(data: [0xe7'u8, 0x05'u8, 0x1e'u8, 0xbc'u8, 0x07'u8, 0xf2'u8, 0xe7'u8, 0xb4'u8, 0xd4'u8, 0xb2'u8, 0x8f'u8, 0x48'u8, 0xd1'u8, 0xe4'u8, 0x2d'u8, 0x7b'u8, 0x9d'u8, 0xce'u8, 0xc3'u8, 0x1c'u8, 0x24'u8, 0x0c'u8, 0xa6'u8, 0xe1'u8, 0xa0'u8, 0xc0'u8, 0x61'u8, 0x39'u8, 0xcc'u8, 0xfc'u8, 0x4b'u8, 0x8f'u8])) - checkPermutation(1660, 3932, 3046, Eth2Digest(data: [0x86'u8, 0x87'u8, 0xc0'u8, 0x29'u8, 0xff'u8, 0xc4'u8, 0x43'u8, 0x87'u8, 0x95'u8, 0x27'u8, 0xa6'u8, 0x4c'u8, 0x31'u8, 0xb7'u8, 0xac'u8, 0xbb'u8, 0x38'u8, 0xab'u8, 0x6e'u8, 0x34'u8, 0x37'u8, 0x79'u8, 0xd0'u8, 0xb2'u8, 0xc6'u8, 0xe2'u8, 0x50'u8, 0x04'u8, 0x6f'u8, 0xdb'u8, 0x9d'u8, 0xe8'u8])) - checkPermutation(379, 646, 32, Eth2Digest(data: [0x17'u8, 0xe8'u8, 0x54'u8, 0xf4'u8, 0xe8'u8, 0x04'u8, 0x01'u8, 0x34'u8, 0x5e'u8, 0x13'u8, 0xf7'u8, 0x2a'u8, 0xf4'u8, 0x5b'u8, 0x22'u8, 0x1c'u8, 0x9f'u8, 0x7a'u8, 0x84'u8, 0x0f'u8, 0x6a'u8, 0x8c'u8, 0x13'u8, 0x28'u8, 0xdd'u8, 0xf9'u8, 0xc9'u8, 0xca'u8, 0x9a'u8, 0x08'u8, 0x83'u8, 0x79'u8])) - s.len == committees - sumCommittees(s, num_validators div committees) == - get_active_validator_indices(genState, GENESIS_EPOCH).len() + # TODO: Empty! + discard