def _determine_new_justified_epoch_and_bitfield(state: BeaconState, config: Eth2Config) -> Tuple[Epoch, int]: genesis_epoch = config.GENESIS_EPOCH previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, genesis_epoch) current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch_justifiable = _is_epoch_justifiable( state, previous_epoch, config, ) current_epoch_justifiable = _is_epoch_justifiable( state, current_epoch, config, ) ( new_current_justified_epoch, justification_bitfield, ) = _determine_updated_justifications( previous_epoch_justifiable, previous_epoch, current_epoch_justifiable, current_epoch, state.current_justified_epoch, state.justification_bitfield << 1, ) return ( new_current_justified_epoch, justification_bitfield, )
def _determine_new_justified_epoch_and_bitfield( state: BeaconState, config: Eth2Config) -> Tuple[Epoch, Bitfield]: genesis_epoch = config.GENESIS_EPOCH previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, genesis_epoch) current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch_justifiable = _is_epoch_justifiable(state, previous_epoch, config) current_epoch_justifiable = _is_epoch_justifiable(state, current_epoch, config) ( new_current_justified_epoch, justification_bits, ) = _determine_updated_justifications( previous_epoch_justifiable, previous_epoch, current_epoch_justifiable, current_epoch, state.current_justified_checkpoint.epoch, (False, ) + state.justification_bits[:-1], ) return (new_current_justified_epoch, justification_bits)
def _validate_attestation_data( state: BeaconState, data: AttestationData, config: Eth2Config ) -> None: slots_per_epoch = config.SLOTS_PER_EPOCH current_epoch = state.current_epoch(slots_per_epoch) previous_epoch = state.previous_epoch(slots_per_epoch) attestation_slot = data.slot if data.target.epoch == current_epoch: expected_checkpoint = state.current_justified_checkpoint else: expected_checkpoint = state.previous_justified_checkpoint _validate_eligible_committee_index( state, attestation_slot, data.index, config.MAX_COMMITTEES_PER_SLOT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) _validate_eligible_target_epoch(data.target.epoch, current_epoch, previous_epoch) _validate_slot_matches_target_epoch( data.target.epoch, attestation_slot, slots_per_epoch ) validate_attestation_slot( attestation_slot, state.slot, slots_per_epoch, config.MIN_ATTESTATION_INCLUSION_DELAY, ) _validate_checkpoint(data.source, expected_checkpoint)
def _validate_attestation_data(state: BeaconState, data: AttestationData, config: Eth2Config) -> None: slots_per_epoch = config.SLOTS_PER_EPOCH current_epoch = state.current_epoch(slots_per_epoch) previous_epoch = state.previous_epoch(slots_per_epoch, config.GENESIS_EPOCH) attestation_slot = get_attestation_data_slot(state, data, config) if data.target.epoch == current_epoch: expected_checkpoint = state.current_justified_checkpoint parent_crosslink = state.current_crosslinks[data.crosslink.shard] else: expected_checkpoint = state.previous_justified_checkpoint parent_crosslink = state.previous_crosslinks[data.crosslink.shard] _validate_eligible_shard_number(data.crosslink.shard, config.SHARD_COUNT) _validate_eligible_target_epoch(data.target.epoch, current_epoch, previous_epoch) validate_attestation_slot( attestation_slot, state.slot, slots_per_epoch, config.MIN_ATTESTATION_INCLUSION_DELAY, ) _validate_checkpoint(data.source, expected_checkpoint) _validate_crosslink( data.crosslink, data.target.epoch, parent_crosslink, config.MAX_EPOCHS_PER_CROSSLINK, )
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 get_matching_source_attestations( state: BeaconState, epoch: Epoch, config: Eth2Config) -> Tuple[PendingAttestation, ...]: if epoch == state.current_epoch(config.SLOTS_PER_EPOCH): return state.current_epoch_attestations elif epoch == state.previous_epoch(config.SLOTS_PER_EPOCH): return state.previous_epoch_attestations else: raise InvalidEpochError
def _process_rewards_and_penalties_for_crosslinks( state: BeaconState, config: Eth2Config, effective_balances: Dict[ValidatorIndex, Gwei], base_rewards: Dict[ValidatorIndex, Gwei] ) -> Tuple[Dict[ValidatorIndex, Gwei], Dict[ValidatorIndex, Gwei]]: # noqa: E501 previous_epoch_start_slot = get_epoch_start_slot( state.previous_epoch(config.SLOTS_PER_EPOCH), config.SLOTS_PER_EPOCH, ) current_epoch_start_slot = get_epoch_start_slot( state.current_epoch(config.SLOTS_PER_EPOCH), config.SLOTS_PER_EPOCH, ) rewards_received = { ValidatorIndex(index): Gwei(0) for index in range(len(state.validator_registry)) } penalties_received = rewards_received.copy() 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: winning_root, attesting_validator_indices = get_winning_root_and_participants( state=state, shard=shard, effective_balances=effective_balances, committee_config=CommitteeConfig(config), ) total_attesting_balance = get_total_balance( state.validator_balances, attesting_validator_indices, config.MAX_DEPOSIT_AMOUNT, ) total_balance = get_total_balance_from_effective_balances( effective_balances, crosslink_committee, ) for index in attesting_validator_indices: rewards_received = _update_rewards_or_penalies( index, base_rewards[index] * total_attesting_balance // total_balance, rewards_received, ) for index in set(crosslink_committee).difference( attesting_validator_indices): penalties_received = _update_rewards_or_penalies( index, base_rewards[index], penalties_received, ) return (rewards_received, penalties_received)
def get_committee_assignment( state: BeaconState, config: BeaconConfig, epoch: Epoch, validator_index: ValidatorIndex, registry_change: bool = False) -> CommitteeAssignment: """ Return the ``CommitteeAssignment`` in the ``epoch`` for ``validator_index`` and ``registry_change``. ``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. """ current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) next_epoch = Epoch(current_epoch + 1) validate_epoch_within_previous_and_next(epoch, previous_epoch, next_epoch) epoch_start_slot = get_epoch_start_slot(epoch, config.SLOTS_PER_EPOCH) committee_config = CommitteeConfig(config) for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH): crosslink_committees = get_crosslink_committees_at_slot( state, slot, committee_config, registry_change=registry_change, ) selected_committees = [ committee for committee in crosslink_committees if validator_index in committee[0] ] if len(selected_committees) > 0: validators = selected_committees[0][0] shard = selected_committees[0][1] is_proposer = validator_index == get_beacon_proposer_index( state, Slot(slot), committee_config, registry_change=registry_change, ) return CommitteeAssignment(validators, shard, Slot(slot), is_proposer) raise NoCommitteeAssignment
def process_justification(state: BeaconState, config: BeaconConfig) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) current_epoch_justifiable, previous_epoch_justifiable = _current_previous_epochs_justifiable( state, current_epoch, 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.justified_epoch finalized_epoch, _ = _get_finalized_epoch( justification_bitfield, state.previous_justified_epoch, state.justified_epoch, state.finalized_epoch, previous_epoch, ) return state.copy( previous_justified_epoch=state.justified_epoch, justified_epoch=new_justified_epoch, justification_bitfield=justification_bitfield, finalized_epoch=finalized_epoch, )
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
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.EPOCH_LENGTH, config.GENESIS_EPOCH, ) current_epoch_attestations = get_current_epoch_attestations(state, config.EPOCH_LENGTH) prev_epoch_start_slot = get_epoch_start_slot( state.previous_epoch(config.EPOCH_LENGTH, config.GENESIS_EPOCH), config.EPOCH_LENGTH, ) next_epoch_start_slot = get_epoch_start_slot( state.next_epoch(config.EPOCH_LENGTH), config.EPOCH_LENGTH, ) for slot in range(prev_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 = sum( get_effective_balance(state.validator_balances, i, config.MAX_DEPOSIT_AMOUNT) for i in crosslink_committee ) if 3 * total_attesting_balance >= 2 * total_balance: latest_crosslinks = update_tuple_item( latest_crosslinks, shard, CrosslinkRecord( epoch=state.current_epoch(config.EPOCH_LENGTH), shard_block_root=winning_root, ), ) else: # Don't update the crosslink of this shard pass state = state.copy( latest_crosslinks=latest_crosslinks, ) return state
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_finality_delay(state: BeaconState, slots_per_epoch: int) -> int: return state.previous_epoch( slots_per_epoch) - state.finalized_checkpoint.epoch
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 )
def process_rewards_and_penalties(state: BeaconState, config: Eth2Config) -> 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))) 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 = state.previous_epoch_attestations previous_epoch_attester_indices = get_attester_indices_from_attestations( 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 = { ValidatorIndex(index): get_effective_balance( state.validator_balances, ValidatorIndex(index), config.MAX_DEPOSIT_AMOUNT, ) for index in range(len(state.validator_registry)) } # Compute base reward of each previous epoch active validator for later use base_rewards = { ValidatorIndex(index): get_base_reward( state=state, index=ValidatorIndex(index), base_reward_quotient=config.BASE_REWARD_QUOTIENT, previous_total_balance=previous_total_balance, max_deposit_amount=config.MAX_DEPOSIT_AMOUNT, ) for index in range(len(state.validator_registry)) } # 1. Process rewards and penalties for justification and finalization finality_rewards, finality_penalties = _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, ) # 2. Process rewards and penalties for crosslinks crosslinks_rewards, crosslinks_penalties = _process_rewards_and_penalties_for_crosslinks( state, config, effective_balances, base_rewards, ) # Apply the overall rewards/penalties for index in range(len(state.validator_registry)): state = state.update_validator_balance( ValidatorIndex(index), # Prevent validator balance under flow max( (state.validator_balances[index] + finality_rewards[index] + crosslinks_rewards[index] - finality_penalties[index] - crosslinks_penalties[index]), 0, ), ) return state
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 process_crosslinks(state: BeaconState, config: Eth2Config) -> 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 effective_balances = { ValidatorIndex(index): get_effective_balance( state.validator_balances, ValidatorIndex(index), config.MAX_DEPOSIT_AMOUNT, ) for index in range(len(state.validator_registry)) } previous_epoch_start_slot = get_epoch_start_slot( state.previous_epoch(config.SLOTS_PER_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: winning_root, attesting_validator_indices = get_winning_root_and_participants( state=state, shard=shard, effective_balances=effective_balances, committee_config=CommitteeConfig(config), ) if len(attesting_validator_indices) > 0: total_attesting_balance = get_total_balance( state.validator_balances, attesting_validator_indices, config.MAX_DEPOSIT_AMOUNT, ) 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=slot_to_epoch(Slot(slot), config.SLOTS_PER_EPOCH), crosslink_data_root=winning_root, ), ) state = state.copy(latest_crosslinks=latest_crosslinks, ) return state
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
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_rewards_and_penalties_for_finality( state: BeaconState, config: BeaconConfig, previous_epoch_active_validator_indices: Set[ValidatorIndex], previous_total_balance: Gwei, previous_epoch_attestations: Sequence[Attestation], previous_epoch_attester_indices: Set[ValidatorIndex], inclusion_infos: Dict[ValidatorIndex, InclusionInfo], effective_balances: Dict[ValidatorIndex, Gwei], base_rewards: Dict[ValidatorIndex, Gwei], old_rewards_received: Dict[ValidatorIndex, SignedGwei] ) -> Dict[ValidatorIndex, SignedGwei]: previous_epoch_boundary_attestations = ( a for a in previous_epoch_attestations if a.data.epoch_boundary_root == get_block_root( state, get_epoch_start_slot( state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH), config.SLOTS_PER_EPOCH, ), config.LATEST_BLOCK_ROOTS_LENGTH, )) previous_epoch_boundary_attester_indices = get_attester_indices_from_attesttion( state=state, attestations=previous_epoch_boundary_attestations, committee_config=CommitteeConfig(config), ) previous_epoch_head_attestations = get_previous_epoch_head_attestations( state, config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH, config.LATEST_BLOCK_ROOTS_LENGTH, ) previous_epoch_head_attester_indices = get_attester_indices_from_attesttion( state=state, attestations=previous_epoch_head_attestations, committee_config=CommitteeConfig(config), ) rewards_received = {index: Gwei(0) for index in old_rewards_received} penalties_received = rewards_received.copy() epochs_since_finality = state.next_epoch( config.SLOTS_PER_EPOCH) - state.finalized_epoch if epochs_since_finality <= 4: # 1.1 Expected FFG source: previous_epoch_attesting_balance = get_total_balance_from_effective_balances( effective_balances, previous_epoch_attester_indices, ) # Reward validators in `previous_epoch_attester_indices` # # Punish active validators not in `previous_epoch_attester_indices` excluded_active_validators_indices = previous_epoch_active_validator_indices.difference( previous_epoch_attester_indices, ) rewards = { index: Gwei(base_rewards[index] * previous_epoch_attesting_balance // previous_total_balance) for index in previous_epoch_attester_indices } penalties = { index: base_rewards[index] for index in excluded_active_validators_indices } rewards_received, penalties_received = _apply_rewards_and_penalties( RewardSettlementContext( rewards=rewards, indices_to_reward=previous_epoch_attester_indices, penalties=penalties, indices_to_penalize=excluded_active_validators_indices, rewards_received=rewards_received, penalties_received=penalties_received, ), ) # 1.2 Expected FFG target: previous_epoch_boundary_attesting_balance = get_total_balance_from_effective_balances( effective_balances, previous_epoch_boundary_attester_indices, ) # Reward validators in `previous_epoch_boundary_attester_indices` # Punish active validators not in `previous_epoch_boundary_attester_indices` excluded_active_validators_indices = previous_epoch_active_validator_indices.difference( previous_epoch_boundary_attester_indices, ) rewards = { index: Gwei(base_rewards[index] * previous_epoch_boundary_attesting_balance // previous_total_balance) for index in previous_epoch_boundary_attester_indices } penalties = { index: base_rewards[index] for index in excluded_active_validators_indices } rewards_received, penalties_received = _apply_rewards_and_penalties( RewardSettlementContext( rewards=rewards, indices_to_reward=previous_epoch_boundary_attester_indices, penalties=penalties, indices_to_penalize=excluded_active_validators_indices, rewards_received=rewards_received, penalties_received=penalties_received, ), ) # 1.3 Expected beacon chain head: previous_epoch_head_attesting_balance = get_total_balance_from_effective_balances( effective_balances, previous_epoch_head_attester_indices, ) # Reward validators in `previous_epoch_head_attester_indices` # Punish active validators not in `previous_epoch_head_attester_indices` excluded_active_validators_indices = previous_epoch_active_validator_indices.difference( previous_epoch_head_attester_indices, ) rewards = { index: Gwei(base_rewards[index] * previous_epoch_head_attesting_balance // previous_total_balance) for index in previous_epoch_head_attester_indices } penalties = { index: base_rewards[index] for index in excluded_active_validators_indices } rewards_received, penalties_received = _apply_rewards_and_penalties( RewardSettlementContext( rewards=rewards, indices_to_reward=previous_epoch_head_attester_indices, penalties=penalties, indices_to_penalize=excluded_active_validators_indices, rewards_received=rewards_received, penalties_received=penalties_received, ), ) # 1.4 Inclusion distance: # Reward validators in `previous_epoch_attester_indices` rewards = { index: Gwei(base_rewards[index] * config.MIN_ATTESTATION_INCLUSION_DELAY // inclusion_infos[index].inclusion_distance) for index in previous_epoch_attester_indices } rewards_received, penalties_received = _apply_rewards_and_penalties( RewardSettlementContext( rewards=rewards, indices_to_reward=previous_epoch_attester_indices, rewards_received=rewards_received, penalties_received=penalties_received, ), ) # epochs_since_finality > 4 else: # Punish active validators not in `previous_epoch_attester_indices` excluded_active_validators_indices = previous_epoch_active_validator_indices.difference( previous_epoch_attester_indices, ) inactivity_penalties = { index: base_rewards[index] + (effective_balances[index] * epochs_since_finality // config.INACTIVITY_PENALTY_QUOTIENT // 2) for index in excluded_active_validators_indices } rewards_received, penalties_received = _apply_rewards_and_penalties( RewardSettlementContext( penalties=inactivity_penalties, indices_to_penalize=excluded_active_validators_indices, rewards_received=rewards_received, penalties_received=penalties_received, ), ) # Punish active validators not in `previous_epoch_boundary_attester_indices` excluded_active_validators_indices = previous_epoch_active_validator_indices.difference( previous_epoch_boundary_attester_indices, ) inactivity_penalties = { index: base_rewards[index] + (effective_balances[index] * epochs_since_finality // config.INACTIVITY_PENALTY_QUOTIENT // 2) for index in excluded_active_validators_indices } rewards_received, penalties_received = _apply_rewards_and_penalties( RewardSettlementContext( penalties=inactivity_penalties, indices_to_penalize=excluded_active_validators_indices, rewards_received=rewards_received, penalties_received=penalties_received, ), ) # Punish active validators not in `previous_epoch_head_attester_indices` excluded_active_validators_indices = previous_epoch_active_validator_indices.difference( previous_epoch_head_attester_indices, ) penalties = { index: base_rewards[index] for index in excluded_active_validators_indices } rewards_received, penalties_received = _apply_rewards_and_penalties( RewardSettlementContext( penalties=penalties, indices_to_penalize=excluded_active_validators_indices, rewards_received=rewards_received, penalties_received=penalties_received, ), ) # Punish penalized active validators penalties = { index: 3 * base_rewards[index] + 2 * (effective_balances[index] * epochs_since_finality // config.INACTIVITY_PENALTY_QUOTIENT // 2) for index in previous_epoch_active_validator_indices if state.validator_registry[index].slashed is True } rewards_received, penalties_received = _apply_rewards_and_penalties( RewardSettlementContext( penalties=penalties, indices_to_penalize={index for index in penalties}, rewards_received=rewards_received, penalties_received=penalties_received, ), ) # Punish validators in `previous_epoch_attester_indices` penalties = { index: Gwei(base_rewards[index] - (base_rewards[index] * config.MIN_ATTESTATION_INCLUSION_DELAY // inclusion_infos[index].inclusion_distance)) for index in previous_epoch_attester_indices } rewards_received, penalties_received = _apply_rewards_and_penalties( RewardSettlementContext( penalties=penalties, indices_to_penalize=previous_epoch_attester_indices, rewards_received=rewards_received, penalties_received=penalties_received, ), ) historical_rewards_received = old_rewards_received.copy() for index in rewards_received: historical_rewards_received = _update_rewards_or_penalies( index, rewards_received[index] - penalties_received[index], historical_rewards_received, ) return historical_rewards_received