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
def test_get_winning_root(random, monkeypatch, target_committee_size, block_root_1_participants, block_root_2_participants, config, committee_config, n_validators_state, sample_attestation_data_params, sample_attestation_params): shard = 1 committee = tuple([i for i in range(target_committee_size)]) from eth2.beacon import committee_helpers def mock_get_crosslink_committees_at_slot(state, slot, committee_config, registry_change=False): return (( committee, shard, ), ) monkeypatch.setattr(committee_helpers, 'get_crosslink_committees_at_slot', mock_get_crosslink_committees_at_slot) competing_block_roots = [ hash_eth2(bytearray(random.getrandbits(8) for _ in range(10))), hash_eth2(bytearray(random.getrandbits(8) for _ in range(10))) ] # Generate bitfield of each participants set root_1_participants_bitfield = get_aggregation_bitfield( block_root_1_participants, target_committee_size, ) root_2_participants_bitfield = get_aggregation_bitfield( block_root_2_participants, target_committee_size, ) # `attestions` contains attestation to different block root by different set of participants attestations = ( # Attestation to `crosslink_data_root_1` by `attestation_participants_1` Attestation(**sample_attestation_params).copy( aggregation_bitfield=root_1_participants_bitfield, data=AttestationData(**sample_attestation_data_params).copy( shard=shard, crosslink_data_root=competing_block_roots[0], ), ), # Attestation to `crosslink_data_root_2` by `attestation_participants_2` Attestation(**sample_attestation_params).copy( aggregation_bitfield=root_2_participants_bitfield, data=AttestationData(**sample_attestation_data_params).copy( shard=shard, crosslink_data_root=competing_block_roots[1], ), ), ) try: winning_root, attesting_balance = get_winning_root( state=n_validators_state, shard=shard, attestations=attestations, max_deposit_amount=config.MAX_DEPOSIT_AMOUNT, committee_config=committee_config, ) attesting_validators_indices = get_attester_indices_from_attesttion( state=n_validators_state, attestations=(a for a in attestations if a.data.shard == shard and a.data.crosslink_data_root == winning_root), committee_config=committee_config, ) total_attesting_balance = get_total_balance( n_validators_state.validator_balances, attesting_validators_indices, config.MAX_DEPOSIT_AMOUNT, ) assert attesting_balance == total_attesting_balance except NoWinningRootError: assert len(block_root_1_participants) == 0 and len( block_root_2_participants) == 0 else: if len(block_root_1_participants) == len(block_root_2_participants): if competing_block_roots[0] < competing_block_roots[1]: assert winning_root == competing_block_roots[0] else: assert winning_root == competing_block_roots[1] elif len(block_root_1_participants) < len(block_root_2_participants): assert winning_root == competing_block_roots[1] else: assert winning_root == competing_block_roots[0]
def test_get_winning_root( random, monkeypatch, target_committee_size, block_root_1_participants, block_root_2_participants, config, committee_config, n_validators_state, sample_attestation_data_params, sample_attestation_params): shard = 1 committee = tuple([i for i in range(target_committee_size)]) from eth2.beacon import committee_helpers def mock_get_crosslink_committees_at_slot(state, slot, committee_config): return ( (committee, shard,), ) monkeypatch.setattr( committee_helpers, 'get_crosslink_committees_at_slot', mock_get_crosslink_committees_at_slot ) competing_block_roots = [ hash_eth2(bytearray(random.getrandbits(8) for _ in range(10))), hash_eth2(bytearray(random.getrandbits(8) for _ in range(10))) ] # Generate bitfield of each participants set root_1_participants_bitfield = get_empty_bitfield(target_committee_size) root_2_participants_bitfield = get_empty_bitfield(target_committee_size) for i in block_root_1_participants: root_1_participants_bitfield = set_voted(root_1_participants_bitfield, i) for i in block_root_2_participants: root_2_participants_bitfield = set_voted(root_2_participants_bitfield, i) # `attestions` contains attestation to different block root by different set of participants attestations = [ # Attestation to `shard_block_root_1` by `attestation_participants_1` Attestation(**sample_attestation_params).copy( data=AttestationData(**sample_attestation_data_params).copy( shard=shard, shard_block_root=competing_block_roots[0], ), aggregation_bitfield=root_1_participants_bitfield ), # Attestation to `shard_block_root_2` by `attestation_participants_2` Attestation(**sample_attestation_params).copy( data=AttestationData(**sample_attestation_data_params).copy( shard=shard, shard_block_root=competing_block_roots[1], ), aggregation_bitfield=root_2_participants_bitfield ), ] try: winning_root, attesting_balance = get_winning_root( state=n_validators_state, shard=shard, attestations=attestations, max_deposit_amount=config.MAX_DEPOSIT_AMOUNT, committee_config=committee_config, ) attesting_validators_indices = get_attesting_validator_indices( state=n_validators_state, attestations=attestations, shard=shard, shard_block_root=winning_root, committee_config=committee_config, ) total_attesting_balance = sum( get_effective_balance( n_validators_state.validator_balances, i, config.MAX_DEPOSIT_AMOUNT ) for i in attesting_validators_indices ) assert attesting_balance == total_attesting_balance except NoWinningRootError: assert len(block_root_1_participants) == 0 and len(block_root_2_participants) == 0 else: if len(block_root_1_participants) == len(block_root_2_participants): root_1_as_int = big_endian_to_int(competing_block_roots[0]) root_2_as_int = big_endian_to_int(competing_block_roots[1]) if root_1_as_int < root_2_as_int: assert winning_root == competing_block_roots[0] else: assert winning_root == competing_block_roots[1] elif len(block_root_1_participants) < len(block_root_2_participants): assert winning_root == competing_block_roots[1] else: assert winning_root == competing_block_roots[0]