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_registry_updates(state: BeaconState, config: Eth2Config) -> BeaconState: new_validators = tuple( _process_activation_eligibility_or_ejections(state, validator, config) for validator in state.validators ) delayed_activation_exit_epoch = get_delayed_activation_exit_epoch( state.finalized_epoch, config.ACTIVATION_EXIT_DELAY, ) activation_queue = sorted([ index for index, validator in enumerate(new_validators) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_epoch >= delayed_activation_exit_epoch ], key=lambda index: new_validators[index].activation_eligibility_epoch) for index in activation_queue[:get_churn_limit(state, config)]: new_validators = update_tuple_item_with_fn( new_validators, index, _update_validator_activation_epoch(state, config), ) return state.copy( validators=new_validators, )
def process_registry_updates(state: BeaconState, config: Eth2Config) -> BeaconState: new_validators = tuple( _process_activation_eligibility_or_ejections(state, validator, config) for validator in state.validators ) activation_exit_epoch = compute_activation_exit_epoch( state.finalized_checkpoint.epoch, config.MAX_SEED_LOOKAHEAD ) activation_queue = sorted( ( index for index, validator in enumerate(new_validators) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_epoch >= activation_exit_epoch ), key=lambda index: new_validators[index].activation_eligibility_epoch, ) for index in activation_queue[: get_validator_churn_limit(state, config)]: new_validators = update_tuple_item_with_fn( new_validators, index, _update_validator_activation_epoch(state, config) ) return state.copy(validators=new_validators)
def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> BeaconState: return state.copy(balances=update_tuple_item_with_fn( state.balances, index, lambda balance, *_: Gwei(0) if delta > balance else Gwei(balance - delta), ))
def slash_validator(state: BeaconState, index: ValidatorIndex, config: Eth2Config, whistleblower_index: ValidatorIndex = None) -> BeaconState: """ Slash the validator with index ``index``. Exit the validator, penalize the validator, and reward the whistleblower. """ # NOTE: remove in phase 1 assert whistleblower_index is None slots_per_epoch = config.SLOTS_PER_EPOCH current_epoch = state.current_epoch(slots_per_epoch) state = initiate_validator_exit(state, index, config) state = state.update_validator_with_fn( index, _set_validator_slashed, current_epoch, config.EPOCHS_PER_SLASHINGS_VECTOR, ) slashed_balance = state.validators[index].effective_balance slashed_epoch = current_epoch % config.EPOCHS_PER_SLASHINGS_VECTOR state = state.copy(slashings=update_tuple_item_with_fn( state.slashings, slashed_epoch, lambda balance, slashed_balance: Gwei(balance + slashed_balance), slashed_balance, )) state = decrease_balance( state, index, slashed_balance // config.MIN_SLASHING_PENALTY_QUOTIENT) proposer_index = get_beacon_proposer_index(state, CommitteeConfig(config)) if whistleblower_index is None: whistleblower_index = proposer_index whistleblower_reward = Gwei(slashed_balance // config.WHISTLEBLOWER_REWARD_QUOTIENT) proposer_reward = Gwei(whistleblower_reward // config.PROPOSER_REWARD_QUOTIENT) state = increase_balance(state, proposer_index, proposer_reward) state = increase_balance( state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward), ) return state
def update_validator_with_fn( self, validator_index: ValidatorIndex, fn: Callable[[Validator, Any], Validator], *args: Any, ) -> "BeaconState": """ Replace ``self.validators[validator_index]`` with the result of calling ``fn`` on the existing ``validator``. Any auxillary args passed in ``args`` are provided to ``fn`` along with the ``validator``. """ if validator_index >= len(self.validators) or validator_index < 0: raise IndexError("Incorrect validator index") return self.copy(validators=update_tuple_item_with_fn( self.validators, validator_index, fn, *args))
def _update_effective_balances(state: BeaconState, config: Eth2Config) -> Tuple[Validator, ...]: half_increment = config.EFFECTIVE_BALANCE_INCREMENT // 2 new_validators = state.validators for index, validator in enumerate(state.validators): balance = state.balances[index] if balance < validator.effective_balance or ( validator.effective_balance + 3 * half_increment < balance): new_effective_balance = min( balance - balance % config.EFFECTIVE_BALANCE_INCREMENT, config.MAX_EFFECTIVE_BALANCE, ) new_validators = update_tuple_item_with_fn( new_validators, index, lambda v, new_balance: v.copy(effective_balance=new_balance, ), new_effective_balance, ) return new_validators
def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> BeaconState: return state.copy(balances=update_tuple_item_with_fn( state.balances, index, lambda balance, *_: Gwei(balance + delta)))
def get_attestation_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))) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH) total_balance = get_total_active_balance(state, config) eligible_validator_indices = tuple( ValidatorIndex(index) for index, v in enumerate(state.validators) if v.is_active(previous_epoch) or ( v.slashed and previous_epoch + 1 < v.withdrawable_epoch)) matching_source_attestations = get_matching_source_attestations( state, previous_epoch, config) matching_target_attestations = get_matching_target_attestations( state, previous_epoch, config) matching_head_attestations = get_matching_head_attestations( state, previous_epoch, config) increment = config.EFFECTIVE_BALANCE_INCREMENT total_balance_in_increment = total_balance // increment for attestations in ( matching_source_attestations, matching_target_attestations, matching_head_attestations, ): unslashed_attesting_indices = get_unslashed_attesting_indices( state, attestations, config) attesting_balance = get_total_balance(state, unslashed_attesting_indices, config) attesting_balance_in_increment = attesting_balance // increment for index in eligible_validator_indices: if index in unslashed_attesting_indices: if is_in_inactivity_leak(state, config): reward = get_base_reward(state, index, config) else: reward = Gwei((get_base_reward(state, index, config) * attesting_balance_in_increment) // total_balance_in_increment) rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, reward) else: penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, get_base_reward(state, index, config), ) for index in get_unslashed_attesting_indices(state, matching_source_attestations, config): attestation = min( (a for a in matching_source_attestations if index in get_attesting_indices(state, a.data, a.aggregation_bits, config)), key=lambda a: a.inclusion_delay, ) proposer_reward = get_proposer_reward(state, index, config) rewards = update_tuple_item_with_fn( rewards, attestation.proposer_index, lambda balance, delta: balance + delta, proposer_reward, ) base_reward = get_base_reward(state, index, config) max_attester_reward = base_reward - proposer_reward rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, (max_attester_reward // attestation.inclusion_delay), ) if is_in_inactivity_leak(state, config): matching_target_attesting_indices = get_unslashed_attesting_indices( state, matching_target_attestations, config) for index in eligible_validator_indices: base_reward = get_base_reward(state, index, config) penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, BASE_REWARDS_PER_EPOCH * base_reward - get_proposer_reward(state, index, config), ) if index not in matching_target_attesting_indices: effective_balance = state.validators[index].effective_balance penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, effective_balance * get_finality_delay(state, config.SLOTS_PER_EPOCH) // config.INACTIVITY_PENALTY_QUOTIENT, ) return ( tuple(Gwei(reward) for reward in rewards), tuple(Gwei(penalty) for penalty in penalties), )
def get_attestation_deltas(state: BeaconState, config: Eth2Config) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: committee_config = CommitteeConfig(config) rewards = tuple( 0 for _ in range(len(state.validators)) ) penalties = tuple( 0 for _ in range(len(state.validators)) ) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) total_balance = get_total_active_balance(state, config) eligible_validator_indices = tuple( ValidatorIndex(index) for index, v in enumerate(state.validators) if v.is_active(previous_epoch) or ( v.slashed and previous_epoch + 1 < v.withdrawable_epoch ) ) matching_source_attestations = get_matching_source_attestations( state, previous_epoch, config, ) matching_target_attestations = get_matching_target_attestations( state, previous_epoch, config, ) matching_head_attestations = get_matching_head_attestations( state, previous_epoch, config, ) for attestations in ( matching_source_attestations, matching_target_attestations, matching_head_attestations ): unslashed_attesting_indices = get_unslashed_attesting_indices( state, attestations, committee_config, ) attesting_balance = get_total_balance(state, unslashed_attesting_indices) for index in eligible_validator_indices: if index in unslashed_attesting_indices: rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, get_base_reward( state, index, config, ) * attesting_balance // total_balance, ) else: penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, get_base_reward( state, index, config, ), ) for index in get_unslashed_attesting_indices( state, matching_source_attestations, committee_config, ): attestation = min( ( a for a in matching_source_attestations if index in get_attesting_indices( state, a.data, a.aggregation_bitfield, committee_config, ) ), key=lambda a: a.inclusion_delay, ) base_reward = get_base_reward(state, index, config) proposer_reward = base_reward // config.PROPOSER_REWARD_QUOTIENT rewards = update_tuple_item_with_fn( rewards, attestation.proposer_index, lambda balance, delta: balance + delta, proposer_reward, ) max_attester_reward = base_reward - proposer_reward rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, ( max_attester_reward * config.MIN_ATTESTATION_INCLUSION_DELAY // attestation.inclusion_delay ) ) finality_delay = previous_epoch - state.finalized_epoch if finality_delay > config.MIN_EPOCHS_TO_INACTIVITY_PENALTY: matching_target_attesting_indices = get_unslashed_attesting_indices( state, matching_target_attestations, committee_config, ) for index in eligible_validator_indices: penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, BASE_REWARDS_PER_EPOCH * get_base_reward( state, index, config, ), ) if index not in matching_target_attesting_indices: effective_balance = state.validators[index].effective_balance penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, effective_balance * finality_delay // config.INACTIVITY_PENALTY_QUOTIENT, ) return tuple( Gwei(reward) for reward in rewards ), tuple( Gwei(penalty) for penalty in penalties )