def process_validator_registry(state: BeaconState, config: BeaconConfig) -> BeaconState: state = state.copy( previous_calculation_epoch=state.current_calculation_epoch, previous_epoch_start_shard=state.current_epoch_start_shard, previous_epoch_seed=state.current_epoch_seed, ) need_to_update, num_shards_in_committees = _check_if_update_validator_registry(state, config) if need_to_update: state = update_validator_registry(state) # Update step-by-step since updated `state.current_calculation_epoch` # is used to calculate other value). Follow the spec tightly now. state = state.copy( current_calculation_epoch=state.next_epoch(config.EPOCH_LENGTH), ) state = state.copy( current_epoch_start_shard=( state.current_epoch_start_shard + num_shards_in_committees ) % config.SHARD_COUNT, ) # The `helpers.generate_seed` function is only present to provide an entry point # for mocking this out in tests. current_epoch_seed = helpers.generate_seed( state=state, epoch=state.current_calculation_epoch, epoch_length=config.EPOCH_LENGTH, seed_lookahead=config.SEED_LOOKAHEAD, entry_exit_delay=config.ENTRY_EXIT_DELAY, latest_index_roots_length=config.LATEST_INDEX_ROOTS_LENGTH, latest_randao_mixes_length=config.LATEST_RANDAO_MIXES_LENGTH, ) state = state.copy( current_epoch_seed=current_epoch_seed, ) else: epochs_since_last_registry_change = ( state.current_epoch(config.EPOCH_LENGTH) - state.validator_registry_update_epoch ) if is_power_of_two(epochs_since_last_registry_change): # Update step-by-step since updated `state.current_calculation_epoch` # is used to calculate other value). Follow the spec tightly now. state = state.copy( current_calculation_epoch=state.next_epoch(config.EPOCH_LENGTH), ) # The `helpers.generate_seed` function is only present to provide an entry point # for mocking this out in tests. current_epoch_seed = helpers.generate_seed( state=state, epoch=state.current_calculation_epoch, epoch_length=config.EPOCH_LENGTH, seed_lookahead=config.SEED_LOOKAHEAD, entry_exit_delay=config.ENTRY_EXIT_DELAY, latest_index_roots_length=config.LATEST_INDEX_ROOTS_LENGTH, latest_randao_mixes_length=config.LATEST_RANDAO_MIXES_LENGTH, ) state = state.copy( current_epoch_seed=current_epoch_seed, ) else: pass return state
def _settle_penality_to_validator_and_whistleblower( *, state: BeaconState, validator_index: ValidatorIndex, latest_penalized_exit_length: int, whistleblower_reward_quotient: int, max_deposit_amount: Gwei, committee_config: CommitteeConfig) -> BeaconState: """ Apply penality/reward to validator and whistleblower and update the meta data More intuitive pseudo-code: current_epoch_penalization_index = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH state.latest_penalized_balances[current_epoch_penalization_index] += ( get_effective_balance(state, index) ) whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward validator.penalized_epoch = slot_to_epoch(state.slot) """ epoch_length = committee_config.EPOCH_LENGTH # Update `state.latest_penalized_balances` current_epoch_penalization_index = state.current_epoch( epoch_length) % latest_penalized_exit_length effective_balance = get_effective_balance( state.validator_balances, validator_index, max_deposit_amount, ) penalized_exit_balance = ( state.latest_penalized_balances[current_epoch_penalization_index] + effective_balance) latest_penalized_balances = update_tuple_item( tuple_data=state.latest_penalized_balances, index=current_epoch_penalization_index, new_value=penalized_exit_balance, ) state = state.copy(latest_penalized_balances=latest_penalized_balances, ) # Update whistleblower's balance whistleblower_reward = (effective_balance // whistleblower_reward_quotient) whistleblower_index = get_beacon_proposer_index( state, state.slot, committee_config, ) state = state.update_validator_balance( whistleblower_index, state.validator_balances[whistleblower_index] + whistleblower_reward, ) # Update validator's balance and `penalized_epoch` field validator = state.validator_registry[validator_index] validator = validator.copy( penalized_epoch=state.current_epoch(epoch_length), ) state = state.update_validator( validator_index, validator, state.validator_balances[validator_index] - whistleblower_reward, ) return state
def get_total_active_balance(state: BeaconState, config: Eth2Config) -> Gwei: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) active_validator_indices = get_active_validator_indices( state.validators, current_epoch) return get_total_balance(state, set(active_validator_indices))
def process_attestations(state: BeaconState, block: BaseBeaconBlock, config: Eth2Config) -> BeaconState: """ Implements 'per-block-processing.operations.attestations' portion of Phase 0 spec: https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1 Validate the ``attestations`` contained within the ``block`` in the context of ``state``. If any invalid, throw ``ValidationError``. Otherwise, append a ``PendingAttestationRecords`` for each to ``previous_epoch_attestations`` or ``current_epoch_attestations``. Return resulting ``state``. """ if len(block.body.attestations) > config.MAX_ATTESTATIONS: raise ValidationError( f"The block ({block}) has too many attestations:\n" f"\tFound {len(block.body.attestations)} attestations, " f"maximum: {config.MAX_ATTESTATIONS}" ) for attestation in block.body.attestations: validate_attestation( state, attestation, config.MIN_ATTESTATION_INCLUSION_DELAY, config.SLOTS_PER_HISTORICAL_ROOT, CommitteeConfig(config), ) # update attestations previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) new_previous_epoch_pending_attestations = [] new_current_epoch_pending_attestations = [] for attestation in block.body.attestations: if slot_to_epoch(attestation.data.slot, config.SLOTS_PER_EPOCH) == current_epoch: new_current_epoch_pending_attestations.append( PendingAttestationRecord( data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot, ) ) elif slot_to_epoch(attestation.data.slot, config.SLOTS_PER_EPOCH) == previous_epoch: new_previous_epoch_pending_attestations.append( PendingAttestationRecord( data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot, ) ) state = state.copy( previous_epoch_attestations=( state.previous_epoch_attestations + tuple(new_previous_epoch_pending_attestations) ), current_epoch_attestations=( state.current_epoch_attestations + tuple(new_current_epoch_pending_attestations) ), ) return state
def _compute_next_start_shard(state: BeaconState, config: Eth2Config) -> Shard: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) return ( state.start_shard + get_shard_delta(state, current_epoch, CommitteeConfig(config)) ) % config.SHARD_COUNT
def update_validator_registry(state: BeaconState, config: BeaconConfig) -> BeaconState: """ Update validator registry. """ current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) # The active validators active_validator_indices = get_active_validator_indices( state.validator_registry, current_epoch) # The total effective balance of active validators total_balance = get_total_balance( state.validator_balances, active_validator_indices, config.MAX_DEPOSIT_AMOUNT, ) # The maximum balance churn in Gwei (for deposits and exits separately) max_balance_churn = max( config.MAX_DEPOSIT_AMOUNT, total_balance // (2 * config.MAX_BALANCE_CHURN_QUOTIENT)) # Activate validators within the allowable balance churn # linter didn't like a bare lambda state = _churn_validators( state=state, config=config, check_should_churn_fn=lambda state, index: _is_ready_to_activate( state, index, max_deposit_amount=config.MAX_DEPOSIT_AMOUNT, ), churn_fn=lambda state, index: activate_validator( state, index, is_genesis=False, genesis_epoch=config.GENESIS_EPOCH, slots_per_epoch=config.SLOTS_PER_EPOCH, activation_exit_delay=config.ACTIVATION_EXIT_DELAY, ), max_balance_churn=max_balance_churn, ) # Exit validators within the allowable balance churn # linter didn't like a bare lambda state = _churn_validators( state=state, config=config, check_should_churn_fn=lambda state, index: _is_ready_to_exit( state, index), churn_fn=lambda state, index: exit_validator( state, index, slots_per_epoch=config.SLOTS_PER_EPOCH, activation_exit_delay=config.ACTIVATION_EXIT_DELAY, ), max_balance_churn=max_balance_churn, ) state = state.copy(validator_registry_update_epoch=current_epoch, ) return state
def _process_rewards_and_penalties_for_crosslinks( state: BeaconState, config: BeaconConfig, previous_epoch_attestations: Iterable[PendingAttestationRecord], effective_balances: Dict[ValidatorIndex, Gwei], base_rewards: Dict[ValidatorIndex, Gwei], old_rewards_received: Dict[ValidatorIndex, SignedGwei] ) -> Dict[ValidatorIndex, SignedGwei]: rewards_received = old_rewards_received.copy() previous_epoch_start_slot = get_epoch_start_slot( state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH), config.SLOTS_PER_EPOCH, ) current_epoch_start_slot = get_epoch_start_slot( state.current_epoch(config.SLOTS_PER_EPOCH), config.SLOTS_PER_EPOCH, ) # Also need current epoch attestations to compute the winning root. current_epoch_attestations = get_current_epoch_attestations( state, config.SLOTS_PER_EPOCH) for slot in range(previous_epoch_start_slot, current_epoch_start_slot): crosslink_committees_at_slot = get_crosslink_committees_at_slot( state, slot, CommitteeConfig(config), ) for crosslink_committee, shard in crosslink_committees_at_slot: filtered_attestations = _filter_attestations_by_shard( previous_epoch_attestations + current_epoch_attestations, shard, ) try: winning_root, total_attesting_balance = get_winning_root( state=state, shard=shard, attestations=filtered_attestations, max_deposit_amount=config.MAX_DEPOSIT_AMOUNT, committee_config=CommitteeConfig(config), ) except NoWinningRootError: # No winning shard block root found for this shard. # Hence no one is counted as attesting validator. attesting_validator_indices: Iterable[ValidatorIndex] = set() else: attesting_validator_indices = get_attester_indices_from_attesttion( state=state, attestations=(a for a in filtered_attestations if a.data.shard == shard and a.data.crosslink_data_root == winning_root), committee_config=CommitteeConfig(config), ) total_balance = get_total_balance_from_effective_balances( effective_balances, crosslink_committee, ) for index in attesting_validator_indices: reward = base_rewards[ index] * total_attesting_balance // total_balance rewards_received[index] = SignedGwei(rewards_received[index] + reward) for index in set(crosslink_committee).difference( attesting_validator_indices): penalty = base_rewards[index] rewards_received[index] = SignedGwei(rewards_received[index] - penalty) return rewards_received
def process_crosslinks(state: BeaconState, config: BeaconConfig) -> BeaconState: """ Implement 'per-epoch-processing.crosslinks' portion of Phase 0 spec: https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslinks For each shard from the past two epochs, find the shard block root that has been attested to by the most stake. If enough(>= 2/3 total stake) attesting stake, update the crosslink record of that shard. Return resulting ``state`` """ latest_crosslinks = state.latest_crosslinks previous_epoch_attestations = get_previous_epoch_attestations( state, config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH, ) current_epoch_attestations = get_current_epoch_attestations( state, config.SLOTS_PER_EPOCH) previous_epoch_start_slot = get_epoch_start_slot( state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH), config.SLOTS_PER_EPOCH, ) next_epoch_start_slot = get_epoch_start_slot( state.next_epoch(config.SLOTS_PER_EPOCH), config.SLOTS_PER_EPOCH, ) for slot in range(previous_epoch_start_slot, next_epoch_start_slot): crosslink_committees_at_slot = get_crosslink_committees_at_slot( state, slot, CommitteeConfig(config), ) for crosslink_committee, shard in crosslink_committees_at_slot: try: winning_root, total_attesting_balance = get_winning_root( state=state, shard=shard, # Use `_filter_attestations_by_shard` to filter out attestations # not attesting to this shard so we don't need to going over # irrelevent attestations over and over again. attestations=_filter_attestations_by_shard( previous_epoch_attestations + current_epoch_attestations, shard, ), max_deposit_amount=config.MAX_DEPOSIT_AMOUNT, committee_config=CommitteeConfig(config), ) except NoWinningRootError: # No winning shard block root found for this shard. pass else: total_balance = get_total_balance( state.validator_balances, crosslink_committee, config.MAX_DEPOSIT_AMOUNT, ) if 3 * total_attesting_balance >= 2 * total_balance: latest_crosslinks = update_tuple_item( latest_crosslinks, shard, CrosslinkRecord( epoch=state.current_epoch(config.SLOTS_PER_EPOCH), crosslink_data_root=winning_root, ), ) else: # Don't update the crosslink of this shard pass state = state.copy(latest_crosslinks=latest_crosslinks, ) return state
def _compute_inactivity_leak_deltas( state: BeaconState, config: Eth2Config, previous_epoch_active_validator_indices: Sequence[ValidatorIndex], previous_epoch_attester_indices: Sequence[ValidatorIndex], previous_epoch_boundary_attester_indices: Sequence[ValidatorIndex], previous_epoch_head_attester_indices: Sequence[ValidatorIndex], inclusion_infos: Dict[ValidatorIndex, InclusionInfo], effective_balances: Dict[ValidatorIndex, Gwei], base_rewards: Dict[ValidatorIndex, Gwei], epochs_since_finality: int ) -> Tuple[Dict[ValidatorIndex, Gwei], Dict[ValidatorIndex, Gwei]]: # noqa: E501 inactivity_penalties = { ValidatorIndex(index): get_inactivity_penalty( base_reward=base_rewards[ValidatorIndex(index)], effective_balance=effective_balances[ValidatorIndex(index)], epochs_since_finality=epochs_since_finality, inactivity_penalty_quotient=config.INACTIVITY_PENALTY_QUOTIENT, ) for index in range(len(state.validator_registry)) } rewards_received = { ValidatorIndex(index): Gwei(0) for index in range(len(state.validator_registry)) } penalties_received = rewards_received.copy() for index in previous_epoch_active_validator_indices: if index not in previous_epoch_attester_indices: penalties_received = _update_rewards_or_penalies( index, inactivity_penalties[index], penalties_received, ) else: # If a validator did attest, apply a small penalty # for getting attestations included late rewards_received = _update_rewards_or_penalies( index, (base_rewards[index] // config.MIN_ATTESTATION_INCLUSION_DELAY // inclusion_infos[index].inclusion_distance), rewards_received, ) penalties_received = _update_rewards_or_penalies( index, base_rewards[index], penalties_received, ) if index not in previous_epoch_boundary_attester_indices: penalties_received = _update_rewards_or_penalies( index, inactivity_penalties[index], penalties_received, ) if index not in previous_epoch_head_attester_indices: penalties_received = _update_rewards_or_penalies( index, base_rewards[index], penalties_received, ) # Penalize slashed-but-inactive validators as though they were active but offline current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) for i in range(len(state.validator_registry)): eligible = ( i not in previous_epoch_active_validator_indices and state.validator_registry[ValidatorIndex(i)].slashed and current_epoch < state.validator_registry[i].withdrawable_epoch) if eligible: penalties_received = _update_rewards_or_penalies( ValidatorIndex(i), 2 * inactivity_penalties[ValidatorIndex(i)] + base_rewards[ValidatorIndex(i)], penalties_received, ) return (rewards_received, penalties_received)
def process_justification(state: BeaconState, config: Eth2Config) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH) current_epoch_justifiable = _is_epoch_justifiable( state, state.current_epoch_attestations, current_epoch, config, ) previous_epoch_justifiable = _is_epoch_justifiable( state, state.previous_epoch_attestations, previous_epoch, config, ) _justification_bitfield = state.justification_bitfield << 1 if previous_epoch_justifiable and current_epoch_justifiable: justification_bitfield = _justification_bitfield | 3 elif previous_epoch_justifiable: justification_bitfield = _justification_bitfield | 2 elif current_epoch_justifiable: justification_bitfield = _justification_bitfield | 1 else: justification_bitfield = _justification_bitfield if current_epoch_justifiable: new_justified_epoch = current_epoch elif previous_epoch_justifiable: new_justified_epoch = previous_epoch else: new_justified_epoch = state.current_justified_epoch new_finalized_epoch, _ = _get_finalized_epoch( justification_bitfield, state.previous_justified_epoch, state.current_justified_epoch, state.finalized_epoch, previous_epoch, ) # Update state state = state.copy( previous_justified_epoch=state.current_justified_epoch, previous_justified_root=state.current_justified_root, justification_bitfield=justification_bitfield, ) if new_justified_epoch != state.current_justified_epoch: state = state.copy( current_justified_epoch=new_justified_epoch, current_justified_root=get_block_root( state, get_epoch_start_slot(new_justified_epoch, config.SLOTS_PER_EPOCH), config.SLOTS_PER_HISTORICAL_ROOT, ), ) if new_finalized_epoch != state.finalized_epoch: state = state.copy(finalized_epoch=new_finalized_epoch, finalized_root=get_block_root( state, get_epoch_start_slot(new_finalized_epoch, config.SLOTS_PER_EPOCH), config.SLOTS_PER_HISTORICAL_ROOT, )) return state