diff --git a/eth2/beacon/_utils/random.py b/eth2/beacon/_utils/random.py index 55208701da..b39736ca21 100644 --- a/eth2/beacon/_utils/random.py +++ b/eth2/beacon/_utils/random.py @@ -29,9 +29,10 @@ def get_permuted_index(index: int, list_size: int, seed: Hash32, - shuffle_round_count: int=90) -> int: + shuffle_round_count: int) -> int: """ - Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. + 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 @@ -48,20 +49,21 @@ def get_permuted_index(index: int, f"`MAX_LIST_SIZE` ({MAX_LIST_SIZE}" ) + new_index = index for round in range(shuffle_round_count): pivot = int.from_bytes( hash_eth2(seed + round.to_bytes(1, 'little'))[0:8], 'little', ) % list_size - flip = (pivot - index) % list_size - hash_pos = max(index, flip) + flip = (pivot - new_index) % list_size + hash_pos = max(new_index, flip) h = hash_eth2(seed + round.to_bytes(1, 'little') + (hash_pos // 256).to_bytes(4, 'little')) byte = h[(hash_pos % 256) // 8] bit = (byte >> (hash_pos % 8)) % 2 - index = flip if bit else index + new_index = flip if bit else new_index - return index + return new_index @to_tuple diff --git a/eth2/beacon/committee_helpers.py b/eth2/beacon/committee_helpers.py index 2e6b0cd127..a42570908b 100644 --- a/eth2/beacon/committee_helpers.py +++ b/eth2/beacon/committee_helpers.py @@ -72,10 +72,7 @@ def get_shuffling(*, seed: Hash32, validators: Sequence['ValidatorRecord'], epoch: Epoch, - slots_per_epoch: int, - target_committee_size: int, - shard_count: int, - shuffle_round_count: int) -> Tuple[Iterable[ValidatorIndex], ...]: + committee_config: CommitteeConfig) -> Tuple[Iterable[ValidatorIndex], ...]: """ Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. Return a list of ``committee_per_epoch`` committees where each @@ -87,6 +84,11 @@ def get_shuffling(*, of ``validators`` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. """ + slots_per_epoch = committee_config.SLOTS_PER_EPOCH + target_committee_size = committee_config.TARGET_COMMITTEE_SIZE + shard_count = committee_config.SHARD_COUNT + shuffle_round_count = committee_config.SHUFFLE_ROUND_COUNT + active_validator_indices = get_active_validator_indices(validators, epoch) committees_per_epoch = get_epoch_committee_count( @@ -287,8 +289,6 @@ def get_crosslink_committees_at_slot( genesis_epoch = committee_config.GENESIS_EPOCH shard_count = committee_config.SHARD_COUNT slots_per_epoch = committee_config.SLOTS_PER_EPOCH - target_committee_size = committee_config.TARGET_COMMITTEE_SIZE - shuffle_round_count = committee_config.SHUFFLE_ROUND_COUNT epoch = slot_to_epoch(slot, slots_per_epoch) current_epoch = state.current_epoch(slots_per_epoch) @@ -330,10 +330,7 @@ def get_crosslink_committees_at_slot( seed=shuffling_context.seed, validators=state.validator_registry, epoch=shuffling_context.shuffling_epoch, - slots_per_epoch=slots_per_epoch, - target_committee_size=target_committee_size, - shard_count=shard_count, - shuffle_round_count=shuffle_round_count, + committee_config=committee_config, ) offset = slot % slots_per_epoch committees_per_slot = shuffling_context.committees_per_epoch // slots_per_epoch diff --git a/tests/eth2/beacon/_utils/test_random.py b/tests/eth2/beacon/_utils/test_random.py index 4a0b4a9c11..21329d6065 100644 --- a/tests/eth2/beacon/_utils/test_random.py +++ b/tests/eth2/beacon/_utils/test_random.py @@ -14,7 +14,7 @@ def slow_shuffle(items, seed, shuffle_round_count): length = len(items) return tuple( [ - items[get_permuted_index(i, length, seed, shuffle_round_count=shuffle_round_count)] + items[get_permuted_index(i, length, seed, shuffle_round_count)] for i in range(length) ] ) @@ -44,6 +44,6 @@ def test_shuffle_consistent(values, seed, shuffle_round_count): assert shuffle(values, seed, shuffle_round_count) == expect -def test_get_permuted_index_invalid(): +def test_get_permuted_index_invalid(shuffle_round_count): with pytest.raises(ValidationError): - get_permuted_index(2, 2, b'\x12' * 32) + get_permuted_index(2, 2, b'\x12' * 32, shuffle_round_count) diff --git a/tests/eth2/beacon/test_committee_helpers.py b/tests/eth2/beacon/test_committee_helpers.py index 7e61162d40..5bc1c52653 100644 --- a/tests/eth2/beacon/test_committee_helpers.py +++ b/tests/eth2/beacon/test_committee_helpers.py @@ -150,17 +150,13 @@ def test_get_next_epoch_committee_count(n_validators_state, def test_get_shuffling_is_complete(activated_genesis_validators, slots_per_epoch, target_committee_size, - shuffle_round_count, - shard_count, + committee_config, epoch): shuffling = get_shuffling( seed=b'\x35' * 32, validators=activated_genesis_validators, epoch=epoch, - slots_per_epoch=slots_per_epoch, - target_committee_size=target_committee_size, - shard_count=shard_count, - shuffle_round_count=shuffle_round_count, + committee_config=committee_config, ) assert len(shuffling) == slots_per_epoch @@ -344,7 +340,6 @@ def test_get_crosslink_committees_at_slot( slots_per_epoch, target_committee_size, shard_count, - shuffle_round_count, genesis_epoch, committee_config, registry_change, @@ -448,10 +443,7 @@ def mock_generate_seed(state, seed=seed, validators=state.validator_registry, epoch=shuffling_epoch, - slots_per_epoch=slots_per_epoch, - target_committee_size=target_committee_size, - shard_count=shard_count, - shuffle_round_count=shuffle_round_count, + committee_config=committee_config, ) assert shuffling[committees_per_slot * offset] == crosslink_committees_at_slot[0][0]