def test_get_current_and_previous_epoch_attestations(random, sample_state, genesis_epoch, epoch_length, sample_attestation_data_params, sample_attestation_params): num_previous_epoch_attestation, num_current_epoch_attestation = random.sample( range(epoch_length), 2, ) previous_epoch_attestion_slots = random.sample( range(epoch_length), num_previous_epoch_attestation, ) current_epoch_attestion_slots = random.sample( range(epoch_length, epoch_length * 2), num_current_epoch_attestation, ) previous_epoch_attestations = [] for slot in previous_epoch_attestion_slots: previous_epoch_attestations.append( Attestation(**sample_attestation_params).copy( data=AttestationData(**sample_attestation_data_params).copy( slot=slot, ), ) ) current_epoch_attestations = [] for slot in current_epoch_attestion_slots: current_epoch_attestations.append( Attestation(**sample_attestation_params).copy( data=AttestationData(**sample_attestation_data_params).copy( slot=slot, ), ) ) state = sample_state.copy( slot=(epoch_length * 2 - 1), latest_attestations=(previous_epoch_attestations + current_epoch_attestations), ) assert set(previous_epoch_attestations) == set( get_previous_epoch_attestations(state, epoch_length, genesis_epoch)) assert set(current_epoch_attestations) == set( get_current_epoch_attestations(state, epoch_length))
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 _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