def get_base_reward(*, state: 'BeaconState', index: ValidatorIndex, base_reward_quotient: int, previous_total_balance: Gwei, max_deposit_amount: Gwei) -> Gwei: if previous_total_balance == 0: return Gwei(0) adjusted_quotient = (integer_squareroot(previous_total_balance) // base_reward_quotient) return Gwei( get_effective_balance( state.validator_balances, index, max_deposit_amount, ) // adjusted_quotient // 5)
async def _get_eth1_vote( self, slot: Slot, state: BeaconState, state_machine: BaseBeaconStateMachine) -> Eth1Data: slots_per_eth1_voting_period = state_machine.config.SLOTS_PER_ETH1_VOTING_PERIOD seconds_per_slot = state_machine.config.SECONDS_PER_SLOT eth1_follow_distance = ETH1_FOLLOW_DISTANCE eth1_voting_period_start_timestamp = ( self.genesis_time + (slot - slot % slots_per_eth1_voting_period) * seconds_per_slot) new_eth1_data = await self._request_eth1_data( Timestamp(eth1_voting_period_start_timestamp), eth1_follow_distance, 2 * eth1_follow_distance, ) # Default is the `Eth1Data` at `ETH1_FOLLOW_DISTANCE` default_eth1_data = new_eth1_data[0] # Compute `previous_eth1_distance` which is the distance between current block and # `state.eth1_data`. resp: GetDistanceResponse = await self.event_bus.request( GetDistanceRequest( block_hash=self.starting_eth1_block_hash, eth1_voting_period_start_timestamp=Timestamp( eth1_voting_period_start_timestamp), )) if resp.error is not None: return default_eth1_data previous_eth1_distance = resp.distance # Request all eth1 data within `previous_eth1_distance` all_eth1_data: Tuple[Eth1Data, ...] = () # Copy overlapped eth1 data from `new_eth1_data` if 2 * eth1_follow_distance >= previous_eth1_distance: all_eth1_data = new_eth1_data[:(previous_eth1_distance - eth1_follow_distance)] else: all_eth1_data = new_eth1_data[:] all_eth1_data += await self._request_eth1_data( Timestamp(eth1_voting_period_start_timestamp), 2 * eth1_follow_distance, previous_eth1_distance, ) # Filter out invalid votes voting_period_int_sqroot = integer_squareroot( slots_per_eth1_voting_period) period_tail = slot % slots_per_eth1_voting_period >= voting_period_int_sqroot if period_tail: votes_to_consider = all_eth1_data else: votes_to_consider = new_eth1_data valid_votes: Tuple[Eth1Data, ...] = tuple(vote for vote in state.eth1_data_votes if vote in votes_to_consider) # Vote with most count wins. Otherwise vote for defaute eth1 data. win_vote = max( valid_votes, key=lambda v: ( valid_votes.count(v), -all_eth1_data.index(v), ), # Tiebreak by smallest distance default=default_eth1_data, ) return win_vote
def test_integer_squareroot_edge_cases(value): with pytest.raises(ValueError): integer_squareroot(value)
def test_integer_squareroot_success(value, expected): actual = integer_squareroot(value) assert actual == expected
def test_integer_squareroot_correct(value): result = integer_squareroot(value) assert (result + 1) ** 2 > value assert result ** 2 <= value
def get_base_reward(state: BeaconState, index: ValidatorIndex, config: Eth2Config) -> Gwei: total_balance = get_total_active_balance(state, config) effective_balance = state.validators[index].effective_balance return Gwei(effective_balance * config.BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH)
def process_rewards_and_penalties(state: BeaconState, config: BeaconConfig) -> BeaconState: # Compute previous epoch active validator indices and the total balance they account for # for later use. previous_epoch_active_validator_indices = set( get_active_validator_indices( state.validator_registry, state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH))) previous_total_balance: Gwei = get_total_balance( state.validator_balances, tuple(previous_epoch_active_validator_indices), config.MAX_DEPOSIT_AMOUNT, ) # Compute previous epoch attester indices and the total balance they account for # for later use. previous_epoch_attestations = get_previous_epoch_attestations( state, config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH, ) previous_epoch_attester_indices = get_attester_indices_from_attesttion( state=state, attestations=previous_epoch_attestations, committee_config=CommitteeConfig(config), ) # Compute inclusion slot/distance of previous attestations for later use. inclusion_infos = get_inclusion_infos( state=state, attestations=previous_epoch_attestations, committee_config=CommitteeConfig(config), ) # Compute effective balance of each previous epoch active validator for later use effective_balances = { index: get_effective_balance( state.validator_balances, index, config.MAX_DEPOSIT_AMOUNT, ) for index in previous_epoch_active_validator_indices } # Compute base reward of each previous epoch active validator for later use _base_reward_quotient = (integer_squareroot(previous_total_balance) // config.BASE_REWARD_QUOTIENT) base_rewards = { index: get_base_reward( state=state, index=index, base_reward_quotient=_base_reward_quotient, max_deposit_amount=config.MAX_DEPOSIT_AMOUNT, ) for index in previous_epoch_active_validator_indices } # Initialize the reward (validator) received map rewards_received = { index: SignedGwei(0) for index in previous_epoch_active_validator_indices } # 1. Process rewards and penalties for justification and finalization rewards_received = pipe( rewards_received, _process_rewards_and_penalties_for_finality( state, config, previous_epoch_active_validator_indices, previous_total_balance, previous_epoch_attestations, previous_epoch_attester_indices, inclusion_infos, effective_balances, base_rewards, ), _process_rewards_and_penalties_for_attestation_inclusion( state, config, previous_epoch_attester_indices, inclusion_infos, base_rewards, ), _process_rewards_and_penalties_for_crosslinks( state, config, previous_epoch_attestations, effective_balances, base_rewards, )) # Apply the overall rewards/penalties for index in previous_epoch_active_validator_indices: state = state.update_validator_balance( index, # Prevent validator balance under flow max(state.validator_balances[index] + rewards_received[index], 0), ) return state