def test_get_active_validator_indices(sample_validator_record_params): current_epoch = 1 # 3 validators are ACTIVE validators = [ Validator.create(**sample_validator_record_params).mset( "activation_epoch", 0, "exit_epoch", FAR_FUTURE_EPOCH) for i in range(3) ] active_validator_indices = get_active_validator_indices( validators, current_epoch) assert len(active_validator_indices) == 3 validators[0] = validators[0].set( "activation_epoch", current_epoch + 1 # activation_epoch > current_epoch ) active_validator_indices = get_active_validator_indices( validators, current_epoch) assert len(active_validator_indices) == 2 validators[1] = validators[1].set( "exit_epoch", current_epoch # current_epoch == exit_epoch ) active_validator_indices = get_active_validator_indices( validators, current_epoch) assert len(active_validator_indices) == 1
def test_get_active_validator_indices(sample_validator_record_params): current_epoch = 1 # 3 validators are ACTIVE validators = [ Validator( **sample_validator_record_params, ).copy( activation_epoch=0, exit_epoch=FAR_FUTURE_EPOCH, ) for i in range(3) ] active_validator_indices = get_active_validator_indices(validators, current_epoch) assert len(active_validator_indices) == 3 validators[0] = validators[0].copy( activation_epoch=current_epoch + 1, # activation_epoch > current_epoch ) active_validator_indices = get_active_validator_indices(validators, current_epoch) assert len(active_validator_indices) == 2 validators[1] = validators[1].copy( exit_epoch=current_epoch, # current_epoch == exit_epoch ) active_validator_indices = get_active_validator_indices(validators, current_epoch) assert len(active_validator_indices) == 1
def test_get_active_validator_indices(sample_validator_record_params): current_epoch = 1 # 3 validators are ACTIVE validators = tuple( Validator.create(**sample_validator_record_params).mset( "activation_epoch", 0, "exit_epoch", FAR_FUTURE_EPOCH ) for i in range(3) ) active_validator_indices = get_active_validator_indices(validators, current_epoch) assert len(active_validator_indices) == 3 # activation_epoch > current_epoch two_active_vals = update_tuple_item( validators, 0, validators[0].set("activation_epoch", current_epoch + 1) ) active_validator_indices = get_active_validator_indices( two_active_vals, current_epoch ) assert len(active_validator_indices) == 2 # current_epoch == exit_epoch one_active_val = update_tuple_item( two_active_vals, 1, validators[1].set("exit_epoch", current_epoch) ) active_validator_indices = get_active_validator_indices( one_active_val, current_epoch ) assert len(active_validator_indices) == 1
def _is_epoch_justifiable(state: BeaconState, attestations: Sequence[PendingAttestationRecord], epoch: Epoch, config: Eth2Config) -> bool: """ Determine if epoch boundary attesting balance is greater than 2/3 of total_balance for the given ``epoch``. """ active_validator_indices = get_active_validator_indices( state.validator_registry, epoch, ) if not active_validator_indices: return False total_balance = get_total_balance( state.validator_balances, active_validator_indices, config.MAX_DEPOSIT_AMOUNT, ) attesting_balance = get_epoch_boundary_attesting_balance( state, attestations, epoch, config) return 3 * attesting_balance >= 2 * total_balance
def test_update_latest_index_roots(genesis_state, config, state_slot, epoch_length, latest_index_roots_length): state = genesis_state.copy( slot=state_slot, ) result_state = _update_latest_index_roots(state, config) # TODO: chanege to hash_tree_root index_root = hash_eth2( b''.join( [ index.to_bytes(32, 'big') for index in get_active_validator_indices( state.validator_registry, # TODO: change to `per-epoch` version state.slot, ) ] ) ) assert result_state.latest_index_roots[ state.next_epoch(epoch_length) % latest_index_roots_length ] == index_root
def _calculate_first_committee_at_slot(state: BeaconState, slot: Slot, config: CommitteeConfig) -> Tuple[ValidatorIndex, ...]: slots_per_epoch = config.SLOTS_PER_EPOCH shard_count = config.SHARD_COUNT target_committee_size = config.TARGET_COMMITTEE_SIZE current_epoch = state.current_epoch(slots_per_epoch) active_validator_indices = get_active_validator_indices(state.validators, current_epoch) committees_per_slot = get_committees_per_slot( len(active_validator_indices), shard_count, slots_per_epoch, target_committee_size, ) offset = committees_per_slot * (slot % slots_per_epoch) shard = ( get_epoch_start_shard(state, current_epoch, config) + offset ) % shard_count return get_crosslink_committee( state, current_epoch, shard, config, )
def test_update_latest_active_index_roots(genesis_state, committee_config, state_slot, slots_per_epoch, latest_active_index_roots_length, activation_exit_delay): state = genesis_state.copy( slot=state_slot, ) result_state = _update_latest_active_index_roots(state, committee_config) # TODO: chanege to hash_tree_root index_root = hash_eth2( b''.join( [ index.to_bytes(32, 'little') for index in get_active_validator_indices( state.validator_registry, # TODO: change to `per-epoch` version slot_to_epoch(state.slot, slots_per_epoch), ) ] ) ) target_epoch = state.next_epoch(slots_per_epoch) + activation_exit_delay assert result_state.latest_active_index_roots[ target_epoch % latest_active_index_roots_length ] == index_root
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_compact_committees_root(state: BeaconState, epoch: Epoch, config: CommitteeConfig) -> Hash32: shard_count = config.SHARD_COUNT committees = (CompactCommittee(), ) * shard_count start_shard = get_start_shard(state, epoch, config) active_validator_indices = get_active_validator_indices( state.validators, epoch, ) committee_count = get_committee_count( len(active_validator_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) for committee_number in range(committee_count): shard = Shard((start_shard + committee_number) % shard_count) compact_committee = _compute_compact_committee_for_shard_in_epoch( state, epoch, shard, config, ) committees = update_tuple_item( committees, shard, compact_committee, ) return ssz.get_hash_tree_root(committees, sedes=ssz.sedes.Vector( CompactCommittee, shard_count))
def get_beacon_committee( state: BeaconState, slot: Slot, index: CommitteeIndex, config: CommitteeConfig) -> Tuple[ValidatorIndex, ...]: epoch = compute_epoch_at_slot(slot, config.SLOTS_PER_EPOCH) committees_per_slot = get_committee_count_at_slot( state, slot, config.MAX_COMMITTEES_PER_SLOT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) active_validator_indices = get_active_validator_indices( state.validators, epoch) domain_type = signature_domain_to_domain_type( SignatureDomain.DOMAIN_BEACON_ATTESTER) return _compute_committee( indices=active_validator_indices, seed=get_seed(state, epoch, domain_type, config), index=(slot % config.SLOTS_PER_EPOCH) * committees_per_slot + index, count=committees_per_slot * config.SLOTS_PER_EPOCH, shuffle_round_count=config.SHUFFLE_ROUND_COUNT, )
def _find_latest_attestation_targets( state: BeaconState, store: Store, config: Eth2Config) -> Iterable[AttestationTarget]: epoch = compute_epoch_at_slot(state.slot, config.SLOTS_PER_EPOCH) active_validators = get_active_validator_indices(state.validators, epoch) return filter( second, map(_find_latest_attestation_target(store), active_validators))
def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard, config: CommitteeConfig) -> Iterable[ValidatorIndex]: target_shard = ( shard + config.SHARD_COUNT - get_epoch_start_shard(state, epoch, config) ) % config.SHARD_COUNT active_validator_indices = get_active_validator_indices( state.validators, epoch, ) return _compute_committee( indices=active_validator_indices, seed=generate_seed(state, epoch, config), index=target_shard, count=get_epoch_committee_count( len(active_validator_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ), shuffle_round_count=config.SHUFFLE_ROUND_COUNT, )
def _update_latest_index_roots(state: BeaconState, committee_config: CommitteeConfig) -> BeaconState: """ Return the BeaconState with updated `latest_index_roots`. """ next_epoch = state.next_epoch(committee_config.EPOCH_LENGTH) # TODO: chanege to hash_tree_root active_validator_indices = get_active_validator_indices( state.validator_registry, EpochNumber(next_epoch + committee_config.ENTRY_EXIT_DELAY), ) index_root = hash_eth2( b''.join( [ index.to_bytes(32, 'big') for index in active_validator_indices ] ) ) latest_index_roots = update_tuple_item( state.latest_index_roots, ( (next_epoch + committee_config.ENTRY_EXIT_DELAY) % committee_config.LATEST_INDEX_ROOTS_LENGTH ), index_root, ) return state.copy( latest_index_roots=latest_index_roots, )
def test_calculate_first_committee_at_slot(genesis_state, config): state = genesis_state slots_per_epoch = config.SLOTS_PER_EPOCH shard_count = config.SHARD_COUNT target_committee_size = config.TARGET_COMMITTEE_SIZE current_epoch = state.current_epoch(slots_per_epoch) active_validator_indices = get_active_validator_indices(state.validators, current_epoch) committees_per_slot = get_committees_per_slot( len(active_validator_indices), shard_count, slots_per_epoch, target_committee_size, ) assert state.slot % config.SLOTS_PER_EPOCH == 0 for slot in range(state.slot, state.slot + config.SLOTS_PER_EPOCH): offset = committees_per_slot * (slot % slots_per_epoch) shard = ( get_epoch_start_shard(state, current_epoch, config) + offset ) % shard_count committee = get_crosslink_committee( state, current_epoch, shard, config, ) assert committee == _calculate_first_committee_at_slot(state, slot, CommitteeConfig(config))
def get_attestation_data_slot(state: BeaconState, data: AttestationData, config: Eth2Config) -> Slot: active_validator_indices = get_active_validator_indices( state.validators, data.target.epoch, ) committee_count = get_committee_count( len(active_validator_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) offset = ( data.crosslink.shard + config.SHARD_COUNT - get_start_shard( state, data.target.epoch, CommitteeConfig(config), ) ) % config.SHARD_COUNT committees_per_slot = committee_count // config.SLOTS_PER_EPOCH return compute_start_slot_of_epoch( data.target.epoch, config.SLOTS_PER_EPOCH, ) + offset // committees_per_slot
def _get_committee_count(state, epoch, config): active_validators = get_active_validator_indices(state.validators, epoch) return get_committee_count( len(active_validators), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, )
def get_crosslink_deltas( state: BeaconState, config: Eth2Config) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: rewards = tuple(0 for _ in range(len(state.validators))) penalties = tuple(0 for _ in range(len(state.validators))) epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) active_validators_indices = get_active_validator_indices( state.validators, epoch) epoch_committee_count = get_committee_count( len(active_validators_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) epoch_start_shard = get_start_shard( state, epoch, CommitteeConfig(config), ) for shard_offset in range(epoch_committee_count): shard = Shard((epoch_start_shard + shard_offset) % config.SHARD_COUNT) crosslink_committee = set( get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), )) _, attesting_indices = get_winning_crosslink_and_attesting_indices( state=state, epoch=epoch, shard=shard, config=config, ) total_attesting_balance = get_total_balance( state, attesting_indices, ) total_committee_balance = get_total_balance( state, crosslink_committee, ) for index in crosslink_committee: base_reward = get_base_reward(state, index, config) if index in attesting_indices: rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, base_reward * total_attesting_balance // total_committee_balance) else: penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, base_reward, ) return tuple(Gwei(reward) for reward in rewards), tuple( Gwei(penalty) for penalty in penalties)
def process_crosslinks(state: BeaconState, config: Eth2Config) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) new_current_crosslinks = state.current_crosslinks for epoch in (previous_epoch, current_epoch): active_validators_indices = get_active_validator_indices( state.validators, epoch) epoch_committee_count = get_committee_count( len(active_validators_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) epoch_start_shard = get_start_shard( state, epoch, CommitteeConfig(config), ) for shard_offset in range(epoch_committee_count): shard = Shard( (epoch_start_shard + shard_offset) % config.SHARD_COUNT) crosslink_committee = set( get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), )) if not crosslink_committee: # empty crosslink committee this epoch continue winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices( state=state, epoch=epoch, shard=shard, config=config, ) threshold_met = _is_threshold_met_against_committee( state, attesting_indices, crosslink_committee, ) if threshold_met: new_current_crosslinks = update_tuple_item( new_current_crosslinks, shard, winning_crosslink, ) return state.copy( previous_crosslinks=state.current_crosslinks, current_crosslinks=new_current_crosslinks, )
def is_valid_genesis_state(state: BeaconState, config: Eth2Config) -> bool: if state.genesis_time < config.MIN_GENESIS_TIME: return False validator_count = len( get_active_validator_indices(state.validators, config.GENESIS_EPOCH)) if validator_count < config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: return False return True
def test_get_active_validator_indices(sample_validator_record_params): current_slot = 1 # 3 validators are ACTIVE validators = [ ValidatorRecord(**sample_validator_record_params, ).copy( activation_slot=0, exit_slot=FAR_FUTURE_SLOT, ) for i in range(3) ] active_validator_indices = get_active_validator_indices( validators, current_slot) assert len(active_validator_indices) == 3 validators[0] = validators[0].copy( activation_slot=current_slot + 1, # activation_slot > current_slot ) active_validator_indices = get_active_validator_indices( validators, current_slot) assert len(active_validator_indices) == 2
def get_validator_churn_limit(state: BeaconState, config: Eth2Config) -> int: slots_per_epoch = config.SLOTS_PER_EPOCH min_per_epoch_churn_limit = config.MIN_PER_EPOCH_CHURN_LIMIT churn_limit_quotient = config.CHURN_LIMIT_QUOTIENT current_epoch = state.current_epoch(slots_per_epoch) active_validator_indices = get_active_validator_indices( state.validators, current_epoch) return max(min_per_epoch_churn_limit, len(active_validator_indices) // churn_limit_quotient)
def _mk_attestation_inputs_in_epoch(epoch, state, config): active_validators_indices = get_active_validator_indices(state.validators, epoch) epoch_committee_count = get_committee_count( len(active_validators_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) epoch_start_shard = get_start_shard( state, epoch, CommitteeConfig(config), ) for shard_offset in random.sample(range(epoch_committee_count), epoch_committee_count): shard = Shard((epoch_start_shard + shard_offset) % config.SHARD_COUNT) committee = get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), ) if not committee: # empty crosslink committee this epoch continue attestation_data = AttestationData( target=Checkpoint( epoch=epoch, ), crosslink=Crosslink( shard=shard, ), ) committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for index in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, index) for index in committee: yield ( index, ( get_attestation_data_slot( state, attestation_data, config, ), ( aggregation_bits, attestation_data, ), ), )
def get_latest_attesting_balance(self, root: SigningRoot) -> Gwei: state = self._get_checkpoint_state_for( self._context.justified_checkpoint) active_indices = get_active_validator_indices( state.validators, state.current_epoch(self.slots_per_epoch)) return Gwei( sum( _effective_balance_for_validator(state, i) for i in active_indices if (i in self._context.latest_messages and self.get_ancestor_root( self._latest_message_for_index(i).root, self._get_block_by_root(root).slot, ) == root)))
def get_next_epoch_committee_count(state: 'BeaconState', shard_count: int, slots_per_epoch: int, target_committee_size: int) -> int: next_active_validators = get_active_validator_indices( state.validator_registry, state.current_shuffling_epoch + 1, ) return get_epoch_committee_count( active_validator_count=len(next_active_validators), shard_count=shard_count, slots_per_epoch=slots_per_epoch, target_committee_size=target_committee_size, )
def _compute_next_active_index_roots(state: BeaconState, config: Eth2Config) -> Tuple[Hash32, ...]: next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) index_root_position = (next_epoch + config.ACTIVATION_EXIT_DELAY ) % config.EPOCHS_PER_HISTORICAL_VECTOR validator_indices_for_new_active_index_root = get_active_validator_indices( state.validators, Epoch(next_epoch + config.ACTIVATION_EXIT_DELAY)) new_active_index_root = ssz.get_hash_tree_root( validator_indices_for_new_active_index_root, ssz.sedes.List(ssz.uint64, config.VALIDATOR_REGISTRY_LIMIT), ) return update_tuple_item(state.active_index_roots, index_root_position, new_active_index_root)
def get_previous_epoch_committee_count(state: 'BeaconState', shard_count: int, epoch_length: int, target_committee_size: int) -> int: previous_active_validators = get_active_validator_indices( state.validator_registry, state.previous_calculation_epoch, ) return get_epoch_committee_count( active_validator_count=len(previous_active_validators), shard_count=shard_count, epoch_length=epoch_length, target_committee_size=target_committee_size, )
def get_committee_assignment( state: BeaconState, config: Eth2Config, epoch: Epoch, validator_index: ValidatorIndex, ) -> CommitteeAssignment: """ Return the ``CommitteeAssignment`` in the ``epoch`` for ``validator_index``. ``CommitteeAssignment.committee`` is the tuple array of validators in the committee ``CommitteeAssignment.shard`` is the shard to which the committee is assigned ``CommitteeAssignment.slot`` is the slot at which the committee is assigned ``CommitteeAssignment.is_proposer`` is a bool signalling if the validator is expected to propose a beacon block at the assigned slot. """ next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) if epoch > next_epoch: raise ValidationError( f"Epoch for committee assignment ({epoch}) must not be after next epoch {next_epoch}." ) active_validators = get_active_validator_indices(state.validators, epoch) committees_per_slot = ( get_committee_count( len(active_validators), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) // config.SLOTS_PER_EPOCH ) epoch_start_slot = compute_start_slot_of_epoch(epoch, config.SLOTS_PER_EPOCH) epoch_start_shard = get_start_shard(state, epoch, CommitteeConfig(config)) for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH): offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH) slot_start_shard = (epoch_start_shard + offset) % config.SHARD_COUNT for i in range(committees_per_slot): shard = Shard((slot_start_shard + i) % config.SHARD_COUNT) committee = get_crosslink_committee( state, epoch, shard, CommitteeConfig(config) ) if validator_index in committee: is_proposer = validator_index == get_beacon_proposer_index( state.copy(slot=slot), CommitteeConfig(config) ) return CommitteeAssignment( committee, Shard(shard), Slot(slot), is_proposer ) raise NoCommitteeAssignment
def _current_previous_epochs_justifiable( state: BeaconState, current_epoch: Epoch, previous_epoch: Epoch, config: BeaconConfig) -> Tuple[bool, bool]: """ Determine if epoch boundary attesting balance is greater than 2/3 of current_total_balance for current and previous epochs. """ current_epoch_active_validator_indices = get_active_validator_indices( state.validator_registry, current_epoch, ) previous_epoch_active_validator_indices = get_active_validator_indices( state.validator_registry, previous_epoch, ) current_total_balance = get_total_balance( state.validator_balances, current_epoch_active_validator_indices, config.MAX_DEPOSIT_AMOUNT, ) previous_total_balance = get_total_balance( state.validator_balances, previous_epoch_active_validator_indices, config.MAX_DEPOSIT_AMOUNT, ) (previous_epoch_boundary_attesting_balance, current_epoch_boundary_attesting_balance ) = get_epoch_boundary_attesting_balances(current_epoch, previous_epoch, state, config) previous_epoch_justifiable = (3 * previous_epoch_boundary_attesting_balance >= 2 * previous_total_balance) current_epoch_justifiable = (3 * current_epoch_boundary_attesting_balance >= 2 * current_total_balance) return current_epoch_justifiable, previous_epoch_justifiable
def genesis_state_with_active_index_roots(state: BeaconState, config: Eth2Config) -> BeaconState: active_validator_indices = get_active_validator_indices( state.validators, config.GENESIS_EPOCH, ) genesis_active_index_root = ssz.hash_tree_root( active_validator_indices, ssz.sedes.List(ssz.uint64), ) active_index_roots = ( (genesis_active_index_root,) * config.EPOCHS_PER_HISTORICAL_VECTOR ) return state.copy( active_index_roots=active_index_roots, )
def get_shard_delta(state: BeaconState, epoch: Epoch, config: CommitteeConfig) -> int: shard_count = config.SHARD_COUNT slots_per_epoch = config.SLOTS_PER_EPOCH active_validator_indices = get_active_validator_indices(state.validators, epoch) return min( get_committee_count( len(active_validator_indices), shard_count, slots_per_epoch, config.TARGET_COMMITTEE_SIZE, ), shard_count - shard_count // slots_per_epoch, )