def get_shuffling(*, seed: Hash32, validators: Sequence['ValidatorRecord'], epoch: EpochNumber, epoch_length: int, target_committee_size: int, shard_count: int) -> Tuple[Iterable[ValidatorIndex], ...]: """ Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. Return a list of ``committee_per_epoch`` committees where each committee is itself a list of validator indices. If ``get_shuffling(seed, validators, epoch)`` returns some value ``x`` for some ``epoch <= get_current_epoch(state) + ENTRY_EXIT_DELAY``, it should return the same value ``x`` for the same ``seed`` and ``epoch`` and possible future modifications of ``validators`` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. """ active_validator_indices = get_active_validator_indices(validators, epoch) committees_per_epoch = get_epoch_committee_count( len(active_validator_indices), shard_count, epoch_length, target_committee_size, ) # Shuffle shuffled_active_validator_indices = shuffle(active_validator_indices, seed) # Split the shuffled list into committees_per_epoch pieces return tuple( split( shuffled_active_validator_indices, committees_per_epoch, ))
def get_shuffling(*, seed: Hash32, validators: Sequence['ValidatorRecord'], epoch: Epoch, committee_config: CommitteeConfig) -> Tuple[Sequence[ValidatorIndex], ...]: """ Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. Return a list of ``committee_per_epoch`` committees where each committee is itself a list of validator indices. If ``get_shuffling(seed, validators, epoch)`` returns some value ``x`` for some ``epoch <= get_current_epoch(state) + ACTIVATION_EXIT_DELAY``, it should return the same value ``x`` for the same ``seed`` and ``epoch`` and possible future modifications 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( len(active_validator_indices), shard_count, slots_per_epoch, target_committee_size, ) # Shuffle shuffled_active_validator_indices = shuffle( active_validator_indices, seed, shuffle_round_count=shuffle_round_count, ) # Split the shuffled list into committees_per_epoch pieces return tuple( split( shuffled_active_validator_indices, committees_per_epoch, ) )
def get_shuffling(*, seed: Hash32, validators: Sequence['ValidatorRecord'], slot: SlotNumber, epoch_length: int, target_committee_size: int, shard_count: int) -> Tuple[Iterable[ValidatorIndex], ...]: """ Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``slot``. Return a list of ``EPOCH_LENGTH * committees_per_slot`` committees where each committee is itself a list of validator indices. If ``get_shuffling(seed, validators, slot)`` returns some value ``x``, it should return the same value ``x`` for the same seed and slot and possible future modifications of validators forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. """ # Normalizes slot to start of epoch boundary slot = SlotNumber(slot - slot % epoch_length) active_validator_indices = get_active_validator_indices(validators, slot) committees_per_slot = get_committee_count_per_slot( len(active_validator_indices), shard_count, epoch_length, target_committee_size, ) # Shuffle seed = bitwise_xor(seed, Hash32(slot.to_bytes(32, byteorder="big"))) shuffled_active_validator_indices = shuffle(active_validator_indices, seed) # Split the shuffled list into epoch_length * committees_per_slot pieces return tuple( split( shuffled_active_validator_indices, committees_per_slot * epoch_length, ))
def get_shuffling(*, seed: Hash32, validators: Sequence['ValidatorRecord'], crosslinking_start_shard: ShardNumber, slot: SlotNumber, epoch_length: int, target_committee_size: int, shard_count: int) -> Iterable[Tuple[ShardCommittee]]: """ Return shuffled ``shard_committee_for_slots`` (``[[ShardCommittee]]``) of the given active ``validators`` using ``seed`` as entropy. Two-dimensional: The first layer is ``slot`` number ``shard_committee_for_slots[slot] -> [ShardCommittee]`` The second layer is ``shard_indices`` number ``shard_committee_for_slots[slot][shard_indices] -> ShardCommittee`` Example: validators: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] After shuffling: [6, 0, 2, 12, 14, 8, 10, 4, 9, 1, 5, 13, 15, 7, 3, 11] Split by slot: [ [6, 0, 2, 12, 14], [8, 10, 4, 9, 1], [5, 13, 15, 7, 3, 11] ] Split by shard: [ [6, 0], [2, 12, 14], [8, 10], [4, 9, 1], [5, 13, 15] ,[7, 3, 11] ] Fill to output: [ # slot 0 [ ShardCommittee(shard_id=0, committee=[6, 0]), ShardCommittee(shard_id=1, committee=[2, 12, 14]), ], # slot 1 [ ShardCommittee(shard_id=2, committee=[8, 10]), ShardCommittee(shard_id=3, committee=[4, 9, 1]), ], # slot 2 [ ShardCommittee(shard_id=4, committee=[5, 13, 15]), ShardCommittee(shard_id=5, committee=[7, 3, 11]), ], ] """ active_validators = get_active_validator_indices(validators, slot) active_validators_size = len(active_validators) committees_per_slot = clamp( 1, shard_count // epoch_length, active_validators_size // epoch_length // target_committee_size, ) # Shuffle with seed shuffled_active_validator_indices = shuffle(active_validators, seed) # Split the shuffled list into epoch_length pieces validators_per_slot = split(shuffled_active_validator_indices, epoch_length) for index, slot_indices in enumerate(validators_per_slot): # Split the shuffled list into committees_per_slot pieces shard_indices = split(slot_indices, committees_per_slot) start_shard = crosslinking_start_shard + index * committees_per_slot yield _get_shards_committees_for_shard_indices( shard_indices, start_shard, active_validators_size, shard_count, )