Switch hash functions, prevent underflow & verify that shuffling works (#149)
* prevent balance underflow, per spec implementation note * verify that permutation shuffing works: add YAML dependency to be used for ingesting test cases; switch from blake2 to keccak256 to match EF test cases; remove inefficient naive-spec-version of shuffling algorithm now that protolambda's can be tested directly
This commit is contained in:
parent
68a39a21be
commit
81d5becc7b
|
@ -22,7 +22,8 @@ requires "nim >= 0.19.0",
|
|||
"serialization",
|
||||
"json_serialization",
|
||||
"json_rpc",
|
||||
"chronos"
|
||||
"chronos",
|
||||
"yaml"
|
||||
|
||||
### Helper functions
|
||||
proc test(name: string, defaultLang = "c") =
|
||||
|
|
|
@ -121,6 +121,10 @@ func exit_validator*(state: var BeaconState,
|
|||
|
||||
validator.exit_epoch = get_entry_exit_effect_epoch(get_current_epoch(state))
|
||||
|
||||
func reduce_balance*(balance: var uint64, amount: uint64) =
|
||||
# Not in spec, but useful to avoid underflow.
|
||||
balance -= min(amount, balance)
|
||||
|
||||
func slash_validator*(state: var BeaconState, index: ValidatorIndex) =
|
||||
## Slash the validator with index ``index``.
|
||||
## Note that this function mutates ``state``.
|
||||
|
@ -140,7 +144,7 @@ func slash_validator*(state: var BeaconState, index: ValidatorIndex) =
|
|||
WHISTLEBLOWER_REWARD_QUOTIENT
|
||||
|
||||
state.validator_balances[whistleblower_index] += whistleblower_reward
|
||||
state.validator_balances[index] -= whistleblower_reward
|
||||
reduce_balance(state.validator_balances[index], whistleblower_reward)
|
||||
validator.slashed_epoch = get_current_epoch(state)
|
||||
|
||||
# Spec bug in v0.3.0, fixed since: it has LATEST_PENALIZED_EXIT_LENGTH
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
# `eth2hash`, and it outputs a `Eth2Digest`. Easy to sed :)
|
||||
|
||||
import
|
||||
nimcrypto/[blake2, hash], eth/common/eth_types_json_serialization,
|
||||
nimcrypto/[keccak, hash], eth/common/eth_types_json_serialization,
|
||||
hashes
|
||||
|
||||
export
|
||||
|
@ -29,7 +29,7 @@ export
|
|||
|
||||
type
|
||||
Eth2Digest* = MDigest[32 * 8] ## `hash32` from spec
|
||||
Eth2Hash* = blake2_512 ## Context for hash function
|
||||
Eth2Hash* = keccak256 ## Context for hash function
|
||||
|
||||
func shortLog*(x: Eth2Digest): string =
|
||||
($x)[0..7]
|
||||
|
|
|
@ -12,73 +12,11 @@ import
|
|||
../ssz,
|
||||
./crypto, ./datatypes, ./digest, ./helpers
|
||||
|
||||
# TODO remove once there are test vectors to check with directly
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#get_permuted_index
|
||||
func get_permuted_index_spec(index: uint64, list_size: uint64, seed: Eth2Digest): uint64 =
|
||||
## Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1`
|
||||
## with ``seed`` as entropy.
|
||||
##
|
||||
## Utilizes 'swap or not' shuffling found in
|
||||
## https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf
|
||||
## See the 'generalized domain' algorithm on page 3.
|
||||
result = index
|
||||
var pivot_buffer: array[(32+1), byte]
|
||||
var source_buffer: array[(32+1+4), byte]
|
||||
|
||||
for round in 0 ..< SHUFFLE_ROUND_COUNT:
|
||||
pivot_buffer[0..31] = seed.data
|
||||
let round_bytes1 = int_to_bytes1(round)[0]
|
||||
pivot_buffer[32] = round_bytes1
|
||||
|
||||
let
|
||||
pivot = bytes_to_int(eth2hash(pivot_buffer).data[0..7]) mod list_size
|
||||
flip = (pivot - result) mod list_size
|
||||
position = max(result, flip)
|
||||
|
||||
## Tradeoff between slicing (if reusing one larger buffer) and additional
|
||||
## copies here of seed and `int_to_bytes1(round)`.
|
||||
source_buffer[0..31] = seed.data
|
||||
source_buffer[32] = round_bytes1
|
||||
source_buffer[33..36] = int_to_bytes4(position div 256)
|
||||
|
||||
let
|
||||
source = eth2hash(source_buffer).data
|
||||
byte_value = source[(position mod 256) div 8]
|
||||
bit = (byte_value shr (position mod 8)) mod 2
|
||||
|
||||
if bit != 0:
|
||||
result = flip
|
||||
|
||||
# TODO remove once there are test vectors to check with directly
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#get_shuffling
|
||||
func get_shuffling_spec*(seed: Eth2Digest, validators: openArray[Validator],
|
||||
epoch: Epoch): seq[seq[ValidatorIndex]] =
|
||||
## Shuffles ``validators`` into crosslink committees seeded by ``seed`` and
|
||||
## ``slot``.
|
||||
## Returns a list of ``SLOTS_PER_EPOCH * committees_per_slot`` committees where
|
||||
## each committee is itself a list of validator indices.
|
||||
|
||||
let
|
||||
active_validator_indices = get_active_validator_indices(validators, epoch)
|
||||
|
||||
committees_per_epoch = get_epoch_committee_count(
|
||||
len(active_validator_indices)).int
|
||||
|
||||
shuffled_active_validator_indices = mapIt(
|
||||
active_validator_indices,
|
||||
active_validator_indices[get_permuted_index_spec(
|
||||
it, len(active_validator_indices).uint64, seed).int])
|
||||
|
||||
# Split the shuffled list into committees_per_epoch pieces
|
||||
result = split(shuffled_active_validator_indices, committees_per_epoch)
|
||||
assert result.len() == committees_per_epoch # what split should do..
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#get_shuffling
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#get_permuted_index
|
||||
func get_shuffling*(seed: Eth2Digest,
|
||||
validators: openArray[Validator],
|
||||
epoch: Epoch
|
||||
): seq[seq[ValidatorIndex]] =
|
||||
func get_shuffled_seq*(seed: Eth2Digest,
|
||||
validators: openArray[Validator],
|
||||
): seq[ValidatorIndex] =
|
||||
## Via https://github.com/protolambda/eth2-shuffle/blob/master/shuffle.go
|
||||
## Shuffles ``validators`` into crosslink committees seeded by ``seed`` and
|
||||
## ``slot``.
|
||||
|
@ -87,11 +25,7 @@ func get_shuffling*(seed: Eth2Digest,
|
|||
##
|
||||
## Invert the inner/outer loops from the spec, essentially. Most useful
|
||||
## hash result re-use occurs within a round.
|
||||
let
|
||||
active_validator_indices = get_active_validator_indices(validators, epoch)
|
||||
list_size = active_validator_indices.len.uint64
|
||||
committees_per_epoch = get_epoch_committee_count(
|
||||
len(active_validator_indices)).int
|
||||
let list_size = validators.len.uint64
|
||||
var
|
||||
# Share these buffers.
|
||||
pivot_buffer: array[(32+1), byte]
|
||||
|
@ -116,7 +50,7 @@ func get_shuffling*(seed: Eth2Digest,
|
|||
## Only need to run, per round, position div 256 hashes, so precalculate
|
||||
## them. This consumes memory, but for low-memory devices, it's possible
|
||||
## to mitigate by some light LRU caching and similar.
|
||||
for reduced_position in 0 ..< list_size.int div 256:
|
||||
for reduced_position in 0 ..< sources.len:
|
||||
source_buffer[33..36] = int_to_bytes4(reduced_position.uint64)
|
||||
sources[reduced_position] = eth2hash(source_buffer)
|
||||
|
||||
|
@ -124,9 +58,9 @@ func get_shuffling*(seed: Eth2Digest,
|
|||
## efficiency gains exist in caching and re-using data.
|
||||
for index in 0 ..< list_size.int:
|
||||
let
|
||||
cur_idx_permutated = shuffled_active_validator_indices[index]
|
||||
flip = (pivot - cur_idx_permutated.uint64) mod list_size
|
||||
position = max(cur_idx_permutated, flip.int)
|
||||
cur_idx_permuted = shuffled_active_validator_indices[index]
|
||||
flip = ((list_size + pivot) - cur_idx_permuted.uint64) mod list_size
|
||||
position = max(cur_idx_permuted, flip.int)
|
||||
|
||||
let
|
||||
source = sources[position div 256].data
|
||||
|
@ -136,8 +70,26 @@ func get_shuffling*(seed: Eth2Digest,
|
|||
if bit != 0:
|
||||
shuffled_active_validator_indices[index] = flip.ValidatorIndex
|
||||
|
||||
result = shuffled_active_validator_indices
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#get_shuffling
|
||||
func get_shuffling*(seed: Eth2Digest,
|
||||
validators: openArray[Validator],
|
||||
epoch: Epoch
|
||||
): seq[seq[ValidatorIndex]] =
|
||||
## This function is factored to facilitate testing with
|
||||
## https://github.com/ethereum/eth2.0-test-generators/tree/master/permutated_index
|
||||
## test vectors, which the split of get_shuffling obfuscates.
|
||||
|
||||
let
|
||||
active_validator_indices = get_active_validator_indices(validators, epoch)
|
||||
committees_per_epoch = get_epoch_committee_count(
|
||||
len(active_validator_indices)).int
|
||||
|
||||
# Split the shuffled list into committees_per_epoch pieces
|
||||
result = split(shuffled_active_validator_indices, committees_per_epoch)
|
||||
result = split(
|
||||
get_shuffled_seq(seed, validators),
|
||||
committees_per_epoch)
|
||||
assert result.len() == committees_per_epoch # what split should do..
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#get_previous_epoch_committee_count
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import
|
||||
endians, typetraits, options, algorithm,
|
||||
eth/common, nimcrypto/blake2,
|
||||
eth/common, nimcrypto/keccak,
|
||||
./spec/[crypto, datatypes, digest]
|
||||
|
||||
# ################### Helper functions ###################################
|
||||
|
|
|
@ -125,7 +125,7 @@ func slashValidator(state: var BeaconState, index: ValidatorIndex) =
|
|||
whistleblower_reward = get_effective_balance(state, index) div
|
||||
WHISTLEBLOWER_REWARD_QUOTIENT
|
||||
state.validator_balances[whistleblower_index] += whistleblower_reward
|
||||
state.validator_balances[index] -= whistleblower_reward
|
||||
reduce_balance(state.validator_balances[index], whistleblower_reward)
|
||||
validator.slashed_epoch = get_current_epoch(state)
|
||||
|
||||
# v0.3.0 spec bug, fixed later, involving renamed constants. Use v0.3.0 name.
|
||||
|
@ -397,8 +397,9 @@ proc processTransfers(state: var BeaconState, blck: BeaconBlock,
|
|||
notice "Transfer: incorrect signature"
|
||||
return false
|
||||
|
||||
state.validator_balances[
|
||||
transfer.from_field.int] -= transfer.amount + transfer.fee
|
||||
reduce_balance(
|
||||
state.validator_balances[transfer.from_field.int],
|
||||
transfer.amount + transfer.fee)
|
||||
state.validator_balances[transfer.to.int] += transfer.amount
|
||||
state.validator_balances[
|
||||
get_beacon_proposer_index(state, state.slot)] += transfer.fee
|
||||
|
@ -543,7 +544,7 @@ func process_slashings(state: var BeaconState) =
|
|||
min(total_penalties * 3, total_balance) div total_balance,
|
||||
get_effective_balance(state, index.ValidatorIndex) div
|
||||
MIN_PENALTY_QUOTIENT)
|
||||
state.validator_balances[index] -= penalty
|
||||
reduce_balance(state.validator_balances[index], penalty)
|
||||
|
||||
func process_exit_queue(state: var BeaconState) =
|
||||
## Process the exit queue.
|
||||
|
@ -804,8 +805,8 @@ func processEpoch(state: var BeaconState) =
|
|||
|
||||
for v in active_validator_indices:
|
||||
if v notin attesters:
|
||||
# TODO underflows?
|
||||
statePtr.validator_balances[v] -= base_reward(statePtr[], v)
|
||||
reduce_balance(
|
||||
statePtr.validator_balances[v], base_reward(statePtr[], v))
|
||||
|
||||
if epochs_since_finality <= 4'u64:
|
||||
# Case 1: epochs_since_finality <= 4
|
||||
|
@ -833,24 +834,28 @@ func processEpoch(state: var BeaconState) =
|
|||
else:
|
||||
# Case 2: epochs_since_finality > 4
|
||||
for index in active_validator_indices:
|
||||
# TODO underflows?
|
||||
if index notin previous_epoch_attester_indices:
|
||||
state.validator_balances[index] -=
|
||||
inactivity_penalty(state, index, epochs_since_finality)
|
||||
reduce_balance(
|
||||
state.validator_balances[index],
|
||||
inactivity_penalty(state, index, epochs_since_finality))
|
||||
if index notin previous_epoch_boundary_attester_indices:
|
||||
state.validator_balances[index] -=
|
||||
inactivity_penalty(state, index, epochs_since_finality)
|
||||
reduce_balance(
|
||||
state.validator_balances[index],
|
||||
inactivity_penalty(state, index, epochs_since_finality))
|
||||
if index notin previous_epoch_head_attester_indices:
|
||||
state.validator_balances[index] -= base_reward(state, index)
|
||||
reduce_balance(
|
||||
state.validator_balances[index], base_reward(state, index))
|
||||
if state.validator_registry[index].slashed_epoch <= current_epoch:
|
||||
state.validator_balances[index] -=
|
||||
reduce_balance(
|
||||
state.validator_balances[index],
|
||||
2'u64 * inactivity_penalty(
|
||||
state, index, epochs_since_finality) + base_reward(state, index)
|
||||
state, index, epochs_since_finality) + base_reward(state, index))
|
||||
if index in previous_epoch_attester_indices:
|
||||
state.validator_balances[index] -=
|
||||
reduce_balance(
|
||||
state.validator_balances[index],
|
||||
base_reward(state, index) -
|
||||
base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY div
|
||||
inclusion_distance(state, index)
|
||||
inclusion_distance(state, index))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestation-inclusion
|
||||
block:
|
||||
|
@ -872,8 +877,8 @@ func processEpoch(state: var BeaconState) =
|
|||
total_attesting_balance(crosslink_committee) div
|
||||
get_total_balance(state, crosslink_committee.committee)
|
||||
else:
|
||||
# TODO underflows?
|
||||
state.validator_balances[index] -= base_reward(state, index)
|
||||
reduce_balance(
|
||||
state.validator_balances[index], base_reward(state, index))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#ejections
|
||||
process_ejections(state)
|
||||
|
|
|
@ -16,11 +16,19 @@ func sumCommittees(v: openArray[seq[ValidatorIndex]], reqCommitteeLen: int): int
|
|||
assert x.len == reqCommitteeLen
|
||||
inc result, x.len
|
||||
|
||||
func checkPermutation(index: int, list_size: uint64,
|
||||
permuted_index: int, seed: Eth2Digest): bool =
|
||||
let
|
||||
validators = repeat(
|
||||
Validator(exit_epoch: FAR_FUTURE_EPOCH), list_size)
|
||||
s = get_shuffled_seq(seed, validators)
|
||||
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 "Smoke validator shuffling":
|
||||
test "Validator shuffling":
|
||||
let
|
||||
num_validators = 32*1024
|
||||
validators = repeat(
|
||||
|
@ -28,13 +36,50 @@ suite "Validators":
|
|||
exit_epoch: FAR_FUTURE_EPOCH
|
||||
), num_validators)
|
||||
s = get_shuffling(Eth2Digest(), validators, 0)
|
||||
#s_spec = get_shuffling_spec(Eth2Digest(), validators, 0)
|
||||
#s = get_shuffling_spec(Eth2Digest(), validators, 0)
|
||||
committees = get_epoch_committee_count(len(validators)).int
|
||||
check:
|
||||
## Enable checking equivalence of spec and optimized versions.
|
||||
## TODO enable checking against YAML test vectors
|
||||
#s == s_spec
|
||||
# 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
|
||||
# 32k validators: SLOTS_PER_EPOCH slots * committee_count_per_slot =
|
||||
# get_epoch_committee_count committees.
|
||||
|
|
Loading…
Reference in New Issue