def sample_beacon_state_params( config, genesis_slot, genesis_epoch, sample_fork_params, sample_eth1_data_params, sample_block_header_params, sample_crosslink_record_params, ): return { # Versioning "genesis_time": 0, "slot": genesis_slot + 100, "fork": Fork(**sample_fork_params), # History "latest_block_header": BeaconBlockHeader(**sample_block_header_params), "block_roots": (ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, "state_roots": (ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, "historical_roots": (), # Eth1 "eth1_data": Eth1Data(**sample_eth1_data_params), "eth1_data_votes": (), "eth1_deposit_index": 0, # Registry "validators": (), "balances": (), # Shuffling "start_shard": 1, "randao_mixes": (ZERO_HASH32, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, "active_index_roots": (ZERO_HASH32, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, "compact_committees_roots": (ZERO_HASH32, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, # Slashings "slashings": (0, ) * config.EPOCHS_PER_SLASHINGS_VECTOR, # Attestations "previous_epoch_attestations": (), "current_epoch_attestations": (), # Crosslinks "previous_crosslinks": ((Crosslink(**sample_crosslink_record_params), ) * config.SHARD_COUNT), "current_crosslinks": ((Crosslink(**sample_crosslink_record_params), ) * config.SHARD_COUNT), # Justification "justification_bits": (False, ) * JUSTIFICATION_BITS_LENGTH, "previous_justified_checkpoint": Checkpoint(epoch=0, root=b"\x99" * 32), "current_justified_checkpoint": Checkpoint(epoch=0, root=b"\x55" * 32), # Finality "finalized_checkpoint": Checkpoint(epoch=0, root=b"\x33" * 32), }
def sample_beacon_state_params(config, genesis_slot, genesis_epoch, sample_fork_params, sample_eth1_data_params, sample_block_header_params, sample_crosslink_record_params): return { # Versioning 'genesis_time': 0, 'slot': genesis_slot + 100, 'fork': Fork(**sample_fork_params), # History 'latest_block_header': BeaconBlockHeader(**sample_block_header_params), 'block_roots': (ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, 'state_roots': (ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, 'historical_roots': (), # Eth1 'eth1_data': Eth1Data(**sample_eth1_data_params), 'eth1_data_votes': (), 'eth1_deposit_index': 0, # Registry 'validators': (), 'balances': (), # Shuffling 'start_shard': 1, 'randao_mixes': (ZERO_HASH32, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, 'active_index_roots': (ZERO_HASH32, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, # Slashings 'slashed_balances': (0, ) * config.EPOCHS_PER_SLASHED_BALANCES_VECTOR, # Attestations 'previous_epoch_attestations': (), 'current_epoch_attestations': (), # Crosslinks 'previous_crosslinks': ((Crosslink(**sample_crosslink_record_params), ) * config.SHARD_COUNT), 'current_crosslinks': ((Crosslink(**sample_crosslink_record_params), ) * config.SHARD_COUNT), # Justification 'previous_justified_epoch': 0, 'previous_justified_root': b'\x99' * 32, 'current_justified_epoch': 0, 'current_justified_root': b'\x55' * 32, 'justification_bitfield': 0, # Finality 'finalized_epoch': 0, 'finalized_root': b'\x33' * 32, }
def test_find_winning_crosslink_and_attesting_indices_from_candidates( genesis_state, number_of_candidates, config): some_epoch = config.GENESIS_EPOCH + 20 some_shard = 3 state = genesis_state.copy( slot=compute_start_slot_of_epoch(some_epoch, config.SLOTS_PER_EPOCH), start_shard=some_shard, current_crosslinks=tuple( Crosslink(shard=i, data_root=(i).to_bytes(32, "little")) for i in range(config.SHARD_COUNT)), ) full_committee = get_crosslink_committee(state, some_epoch, some_shard, CommitteeConfig(config)) # break the committees up into different subsets to simulate different # attestations for the same crosslink committees = tuple( random_sample( len(full_committee) // number_of_candidates, full_committee) for _ in range(number_of_candidates)) seen = set() filtered_committees = tuple() for committee in committees: deduplicated_committee = tuple() for index in committee: if index in seen: pass else: seen.add(index) deduplicated_committee += (index, ) filtered_committees += (deduplicated_committee, ) candidates = tuple( mk_pending_attestation_from_committee( state.current_crosslinks[some_shard], len(full_committee), some_shard, target_epoch=some_epoch, ) for committee in filtered_committees) if number_of_candidates == 0: expected_result = (Crosslink(), set()) else: expected_result = (candidates[0].data.crosslink, set(sorted(full_committee))) result = _find_winning_crosslink_and_attesting_indices_from_candidates( state, candidates, config) assert result == expected_result
def _find_winning_crosslink_and_attesting_indices_from_candidates( state: BeaconState, candidate_attestations: Sequence[PendingAttestation], config: Eth2Config) -> Tuple[Crosslink, Tuple[ValidatorIndex, ...]]: if not candidate_attestations: return (Crosslink(), tuple()) attestations_by_crosslink = groupby( lambda a: a.data.crosslink, candidate_attestations, ) winning_crosslink, winning_attestations = max( attestations_by_crosslink.items(), key=lambda pair: _score_crosslink( state, pair[0], # crosslink pair[1], # attestations config, ), ) return ( winning_crosslink, get_unslashed_attesting_indices( state, winning_attestations, CommitteeConfig(config), ), )
def test_get_attesting_indices(genesis_state, config): state = genesis_state.copy( slot=get_epoch_start_slot(3, config.SLOTS_PER_EPOCH)) target_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) target_shard = (state.start_shard + 3) % config.SHARD_COUNT some_committee = get_crosslink_committee( state, target_epoch, target_shard, CommitteeConfig(config), ) data = AttestationData( target_epoch=target_epoch, crosslink=Crosslink(shard=target_shard, ), ) some_subset_count = random.randint(1, len(some_committee) // 2) some_subset = random.sample(some_committee, some_subset_count) bitfield = get_empty_bitfield(len(some_committee)) for i, index in enumerate(some_committee): if index in some_subset: bitfield = set_voted(bitfield, i) indices = get_attesting_indices( state, data, bitfield, CommitteeConfig(config), ) assert set(indices) == set(some_subset) assert len(indices) == len(some_subset)
async def test_send_multiple_attestations(request, event_loop): alice, msg_buffer = await get_command_setup(request, event_loop) attestations = tuple( Attestation( aggregation_bitfield=b"\x00\x00\x00", data=AttestationData( slot=0, beacon_block_root=ZERO_HASH32, source_epoch=SERENITY_CONFIG.GENESIS_EPOCH, target_root=ZERO_HASH32, source_root=ZERO_HASH32, shard=shard, previous_crosslink=Crosslink(SERENITY_CONFIG.GENESIS_EPOCH, ZERO_HASH32), crosslink_data_root=ZERO_HASH32, ), custody_bitfield=b"\x00\x00\x00", ) for shard in range(10)) alice.sub_proto.send_attestation_records(attestations) message = await msg_buffer.msg_queue.get() assert isinstance(message.command, Attestations) assert message.payload == tuple( ssz.encode(attestation) for attestation in attestations)
def validate_attestation_previous_crosslink_or_root(attestation_data: AttestationData, state_latest_crosslink: Crosslink, slots_per_epoch: int) -> None: """ Validate that either the attestation ``previous_crosslink`` or ``crosslink_data_root`` field of ``attestation_data`` is the provided ``latest_crosslink``. Raise ``ValidationError`` if it's invalid. """ attestation_creating_crosslink = Crosslink( epoch=slot_to_epoch(attestation_data.slot, slots_per_epoch), crosslink_data_root=attestation_data.crosslink_data_root, ) acceptable_crosslink_data = { # Case 1: Latest crosslink matches the one in the state attestation_data.previous_crosslink, # Case 2: State has already been updated, state's latest crosslink matches the crosslink # the attestation is trying to create attestation_creating_crosslink, } if state_latest_crosslink not in acceptable_crosslink_data: raise ValidationError( f"State's latests crosslink ({state_latest_crosslink}) doesn't match " " case 1: the `attestation_data.previous_crosslink` " f"({attestation_data.previous_crosslink.root}) or " "`case 2: the crosslink the attestation is trying to create " f"({attestation_creating_crosslink})" )
def test_validate_crosslink(genesis_state, mutator, is_valid, config): some_shard = 3 parent = genesis_state.current_crosslinks[some_shard] target_epoch = config.GENESIS_EPOCH + 1 valid_crosslink = Crosslink( shard=some_shard, parent_root=parent.hash_tree_root, start_epoch=parent.end_epoch, end_epoch=target_epoch, data_root=ZERO_HASH32, ) candidate_crosslink = mutator(valid_crosslink) if is_valid: _validate_crosslink( candidate_crosslink, target_epoch, parent, config.MAX_EPOCHS_PER_CROSSLINK, ) else: with pytest.raises(ValidationError): _validate_crosslink( candidate_crosslink, target_epoch, parent, config.MAX_EPOCHS_PER_CROSSLINK, )
def test_get_unslashed_attesting_indices(genesis_state, config): state = genesis_state.copy( slot=compute_start_slot_of_epoch(3, config.SLOTS_PER_EPOCH)) target_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) target_shard = (state.start_shard + 3) % config.SHARD_COUNT some_committee = get_crosslink_committee(state, target_epoch, target_shard, CommitteeConfig(config)) data = AttestationData(target=Checkpoint(epoch=target_epoch), crosslink=Crosslink(shard=target_shard)) some_subset_count = random.randrange(1, len(some_committee) // 2) some_subset = random.sample(some_committee, some_subset_count) bitfield = get_empty_bitfield(len(some_committee)) for i, index in enumerate(some_committee): if index in some_subset: if random.choice([True, False]): state = state.update_validator_with_fn( index, lambda v, *_: v.copy(slashed=True)) bitfield = set_voted(bitfield, i) some_subset = tuple( filter(lambda index: not state.validators[index].slashed, some_subset)) indices = get_unslashed_attesting_indices( state, (PendingAttestation(data=data, aggregation_bits=bitfield), ), CommitteeConfig(config), ) assert set(indices) == set(some_subset) assert len(indices) == len(some_subset)
def sample_attestation_data_params(sample_crosslink_record_params): return { "beacon_block_root": b"\x11" * 32, "source": Checkpoint(epoch=11, root=b"\x22" * 32), "target": Checkpoint(epoch=12, root=b"\x33" * 32), "crosslink": Crosslink(**sample_crosslink_record_params), }
def test_get_matching_head_attestations(genesis_state, config): some_epoch = config.GENESIS_EPOCH + 20 some_slot = ( compute_start_slot_of_epoch(some_epoch, config.SLOTS_PER_EPOCH) + config.SLOTS_PER_EPOCH // 4) some_target_root = b"\x33" * 32 target_attestations = tuple((PendingAttestation(data=AttestationData( beacon_block_root=some_target_root, target=Checkpoint(epoch=some_epoch - 1), crosslink=Crosslink(shard=i), )) for i in range(3))) current_epoch_attestations = target_attestations + tuple( (PendingAttestation(data=AttestationData( beacon_block_root=b"\x44" * 32, target=Checkpoint(epoch=some_epoch - 1), )) for _ in range(3))) state = genesis_state.copy( slot=some_slot - 1, block_roots=tuple(some_target_root for _ in range(config.SLOTS_PER_HISTORICAL_ROOT)), current_epoch_attestations=current_epoch_attestations, ) attestations = get_matching_head_attestations(state, some_epoch, config) assert attestations == target_attestations
def create_mock_signed_attestations_at_slot( state: BeaconState, config: Eth2Config, state_machine: BaseBeaconStateMachine, attestation_slot: Slot, beacon_block_root: Hash32, keymap: Dict[BLSPubkey, int], voted_attesters_ratio: float = 1.0) -> Iterable[Attestation]: """ Create the mocking attestations of the given ``attestation_slot`` slot with ``keymap``. """ crosslink_committees_at_slot = get_crosslink_committees_at_slot( state, attestation_slot, config, ) # Get `target_root` target_root = _get_target_root(state, config, beacon_block_root) target_epoch = compute_epoch_of_slot( state.slot, config.SLOTS_PER_EPOCH, ) for crosslink_committee in crosslink_committees_at_slot: committee, shard = crosslink_committee parent_crosslink = state.current_crosslinks[shard] attestation_data = AttestationData( beacon_block_root=beacon_block_root, source=Checkpoint( epoch=state.current_justified_checkpoint.epoch, root=state.current_justified_checkpoint.root, ), target=Checkpoint( root=target_root, epoch=target_epoch, ), crosslink=Crosslink( shard=shard, parent_root=parent_crosslink.hash_tree_root, start_epoch=parent_crosslink.end_epoch, end_epoch=min( target_epoch, parent_crosslink.end_epoch + config.MAX_EPOCHS_PER_CROSSLINK), )) num_voted_attesters = int(len(committee) * voted_attesters_ratio) yield _create_mock_signed_attestation( state, attestation_data, attestation_slot, committee, num_voted_attesters, keymap, config.SLOTS_PER_EPOCH, )
def test_num_crosslink_records(expected, sample_crosslink_record_params, filled_beacon_state): crosslinks = [ Crosslink(**sample_crosslink_record_params) for i in range(expected) ] state = filled_beacon_state.copy(latest_crosslinks=crosslinks, ) assert state.num_crosslinks == expected
def test_validate_attestation_data(genesis_state, sample_attestation_data_params, attestation_source_epoch, attestation_target_epoch, current_epoch, previous_justified_epoch, current_justified_epoch, slots_per_epoch, config, is_valid): state = genesis_state.copy( slot=compute_start_slot_of_epoch(current_epoch, slots_per_epoch) + 5, previous_justified_checkpoint=Checkpoint( epoch=previous_justified_epoch, ), current_justified_checkpoint=Checkpoint( epoch=current_justified_epoch, ), ) start_shard = get_start_shard( state, current_epoch, CommitteeConfig(config), ) if attestation_target_epoch == current_epoch: crosslinks = state.current_crosslinks else: crosslinks = state.previous_crosslinks parent_crosslink = crosslinks[start_shard] attestation_data = AttestationData(**sample_attestation_data_params).copy( source=Checkpoint( epoch=attestation_source_epoch, ), target=Checkpoint( epoch=attestation_target_epoch, ), crosslink=Crosslink( start_epoch=parent_crosslink.end_epoch, end_epoch=attestation_target_epoch, parent_root=parent_crosslink.hash_tree_root, shard=start_shard, ), ) if is_valid: _validate_attestation_data( state, attestation_data, config, ) else: with pytest.raises(ValidationError): _validate_attestation_data( state, attestation_data, config, )
def test_process_attestations(genesis_state, genesis_block, sample_beacon_block_params, sample_beacon_block_body_params, config, keymap, fixture_sm_class, chaindb, empty_attestation_pool, success): attestation_slot = 0 current_slot = attestation_slot + config.MIN_ATTESTATION_INCLUSION_DELAY state = genesis_state.copy(slot=current_slot, ) attestations = create_mock_signed_attestations_at_slot( state=state, config=config, state_machine=fixture_sm_class( chaindb, empty_attestation_pool, genesis_block.slot, ), attestation_slot=attestation_slot, beacon_block_root=genesis_block.signing_root, keymap=keymap, voted_attesters_ratio=1.0, ) assert len(attestations) > 0 if not success: # create invalid attestation by shard # i.e. wrong parent invalid_attestation_data = attestations[-1].data.copy( crosslink=attestations[-1].data.crosslink.copy( parent_root=Crosslink(shard=333, ).root, )) invalid_attestation = attestations[-1].copy( data=invalid_attestation_data, ) attestations = attestations[:-1] + (invalid_attestation, ) block_body = BeaconBlockBody(**sample_beacon_block_body_params).copy( attestations=attestations, ) block = SerenityBeaconBlock(**sample_beacon_block_params).copy( slot=current_slot, body=block_body, ) if success: new_state = process_attestations( state, block, config, ) assert len(new_state.current_epoch_attestations) == len(attestations) else: with pytest.raises(ValidationError): process_attestations( state, block, config, )
def sample_attestation_data_params(sample_crosslink_record_params): return { 'beacon_block_root': b'\x11' * 32, 'source_epoch': 11, 'source_root': b'\x22' * 32, 'target_epoch': 12, 'target_root': b'\x33' * 32, 'crosslink': Crosslink(**sample_crosslink_record_params), }
def _mk_attestation_inputs_in_epoch(epoch, state, config): 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 random.sample(range(epoch_committee_count), epoch_committee_count): shard = Shard((epoch_start_shard + shard_offset) % config.SHARD_COUNT) committee = get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), ) if not committee: # empty crosslink committee this epoch continue attestation_data = AttestationData( target=Checkpoint( epoch=epoch, ), crosslink=Crosslink( shard=shard, ), ) committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for index in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, index) for index in committee: yield ( index, ( get_attestation_data_slot( state, attestation_data, config, ), ( aggregation_bits, attestation_data, ), ), )
def create_signed_attestation_at_slot(state: BeaconState, config: Eth2Config, state_machine: BaseBeaconStateMachine, attestation_slot: Slot, beacon_block_root: Hash32, validator_privkeys: Dict[ValidatorIndex, int], committee: Tuple[ValidatorIndex, ...], shard: Shard) -> Attestation: """ Create the attestations of the given ``attestation_slot`` slot with ``validator_privkeys``. """ state_transition = state_machine.state_transition state = state_transition.apply_state_transition( state, future_slot=attestation_slot, ) target_epoch = compute_epoch_of_slot( attestation_slot, config.SLOTS_PER_EPOCH, ) target_root = _get_target_root(state, config, beacon_block_root) parent_crosslink = state.current_crosslinks[shard] attestation_data = AttestationData( beacon_block_root=beacon_block_root, source=Checkpoint( epoch=state.current_justified_checkpoint.epoch, root=state.current_justified_checkpoint.root, ), target=Checkpoint( root=target_root, epoch=target_epoch, ), crosslink=Crosslink( shard=shard, parent_root=parent_crosslink.hash_tree_root, start_epoch=parent_crosslink.end_epoch, end_epoch=target_epoch, )) return _create_mock_signed_attestation( state, attestation_data, attestation_slot, committee, len(committee), keymapper(lambda index: state.validators[index].pubkey, validator_privkeys), config.SLOTS_PER_EPOCH, )
def sample_attestation_data_params(sample_crosslink_record_params): return { 'beacon_block_root': b'\x11' * 32, 'source': Checkpoint( epoch=11, root=b'\x22' * 32, ), 'target': Checkpoint( epoch=12, root=b'\x33' * 32, ), 'crosslink': Crosslink(**sample_crosslink_record_params), }
async def test_send_single_attestation(request, event_loop): alice, msg_buffer = await get_command_setup(request, event_loop) attestation = Attestation( aggregation_bits=b"\x00\x00\x00", data=AttestationData(crosslink=Crosslink(shard=1, )), custody_bits=b"\x00\x00\x00", ) alice.sub_proto.send_attestation_records((attestation, )) message = await msg_buffer.msg_queue.get() assert isinstance(message.command, Attestations) assert message.payload["encoded_attestations"] == ( ssz.encode(attestation), )
def _mk_attestation_for_block_with_committee(block, committee, shard, config): committee_count = len(committee) aggregation_bitfield = bitfield.get_empty_bitfield(committee_count) for index in range(committee_count): aggregation_bitfield = bitfield.set_voted(aggregation_bitfield, index) attestation = Attestation( aggregation_bitfield=aggregation_bitfield, data=AttestationData( beacon_block_root=block.signing_root, target_epoch=slot_to_epoch(block.slot, config.SLOTS_PER_EPOCH), crosslink=Crosslink(shard=shard, ), ), ) return attestation
async def test_send_multiple_attestations(request, event_loop): alice, msg_buffer = await get_command_setup(request, event_loop) attestations = tuple( Attestation( aggregation_bits=b"\x00\x00\x00", data=AttestationData(crosslink=Crosslink(shard=shard, )), custody_bits=b"\x00\x00\x00", ) for shard in range(10)) alice.sub_proto.send_attestation_records(attestations) message = await msg_buffer.msg_queue.get() assert isinstance(message.command, Attestations) assert message.payload["encoded_attestations"] == tuple( ssz.encode(attestation) for attestation in attestations)
def _find_collision(state, config, index, epoch): """ Given a target epoch, make the attestation expected for the validator w/ the given index. """ active_validators = get_active_validator_indices(state.validators, epoch) committees_per_slot = get_committee_count( len(active_validators), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) // config.SLOTS_PER_EPOCH epoch_start_slot = compute_start_slot_of_epoch( epoch, config.SLOTS_PER_EPOCH, ) epoch_start_shard = get_start_shard(state, epoch, CommitteeConfig(config)) for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH): offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH) slot_start_shard = (epoch_start_shard + offset) % config.SHARD_COUNT for i in range(committees_per_slot): shard = Shard((slot_start_shard + i) % config.SHARD_COUNT) committee = get_crosslink_committee(state, epoch, shard, CommitteeConfig(config)) if index in committee: # TODO(ralexstokes) refactor w/ tools/builder attestation_data = AttestationData( target=Checkpoint( epoch=epoch, ), crosslink=Crosslink( shard=shard, ), ) committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for i in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, i) return { index: ( slot, (aggregation_bits, attestation_data) ) for index in committee } else: raise Exception("should have found a duplicate validator")
def genesis_state(filled_beacon_state, activated_genesis_validators, genesis_balances, slots_per_epoch, target_committee_size, genesis_epoch, shard_count, slots_per_historical_root, latest_slashed_exit_length, latest_randao_mixes_length): return filled_beacon_state.copy( validator_registry=activated_genesis_validators, validator_balances=genesis_balances, latest_block_roots=tuple(ZERO_HASH32 for _ in range(slots_per_historical_root)), latest_slashed_balances=(0, ) * latest_slashed_exit_length, latest_crosslinks=tuple( Crosslink( epoch=genesis_epoch, crosslink_data_root=ZERO_HASH32, ) for _ in range(shard_count)), latest_randao_mixes=tuple(ZERO_HASH32 for _ in range(latest_randao_mixes_length)), )
def mock_attestation(): return Attestation( aggregation_bitfield=b'\x12' * 16, data=AttestationData( slot=XIAO_LONG_BAO_CONFIG.GENESIS_SLOT + 1, beacon_block_root=ZERO_HASH32, source_epoch=XIAO_LONG_BAO_CONFIG.GENESIS_EPOCH, source_root=ZERO_HASH32, target_root=ZERO_HASH32, shard=0, previous_crosslink=Crosslink( epoch=XIAO_LONG_BAO_CONFIG.GENESIS_EPOCH, crosslink_data_root=ZERO_HASH32, ), crosslink_data_root=ZERO_HASH32, ), custody_bitfield=b'\x34' * 16, aggregate_signature=b'\x56' * 96, )
def _mk_pending_attestation( bitfield: Bitfield = default_bitfield, target_root: Hash32 = ZERO_HASH32, target_epoch: Epoch = default_epoch, shard: Shard = default_shard, start_epoch: Epoch = default_epoch, parent_root: Hash32 = ZERO_HASH32, data_root: Hash32 = ZERO_HASH32) -> PendingAttestation: return PendingAttestation( aggregation_bitfield=bitfield, data=AttestationData(target_epoch=target_epoch, target_root=target_root, crosslink=Crosslink( shard=shard, parent_root=parent_root, start_epoch=start_epoch, end_epoch=target_epoch, data_root=data_root, )), )
def _mk_pending_attestation( bitfield: Bitfield = default_bitfield, target_root: SigningRoot = ZERO_SIGNING_ROOT, target_epoch: Epoch = default_epoch, shard: Shard = default_shard, start_epoch: Epoch = default_epoch, parent_root: Hash32 = ZERO_HASH32, data_root: Hash32 = ZERO_HASH32, ) -> PendingAttestation: return PendingAttestation( aggregation_bits=bitfield, data=AttestationData( target=Checkpoint(epoch=target_epoch, root=target_root), crosslink=Crosslink( shard=shard, parent_root=parent_root, start_epoch=start_epoch, end_epoch=target_epoch, data_root=data_root, ), ), )
def test_process_rewards_and_penalties_for_crosslinks( genesis_state, config, slots_per_epoch, target_committee_size, shard_count, current_slot, num_attesting_validators, max_effective_balance, min_attestation_inclusion_delay, sample_attestation_data_params, sample_pending_attestation_record_params): state = genesis_state.copy(slot=current_slot, ) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) prev_epoch_start_slot = compute_start_slot_of_epoch( previous_epoch, slots_per_epoch) prev_epoch_crosslink_committees = [ get_crosslink_committees_at_slot( state, slot, CommitteeConfig(config), )[0] for slot in range(prev_epoch_start_slot, prev_epoch_start_slot + slots_per_epoch) ] # Record which validators attest during each slot for reward collation. each_slot_attestion_validators_list = [] epoch_start_shard = get_start_shard( state, previous_epoch, CommitteeConfig(config), ) shard_delta = get_shard_delta( state, previous_epoch, CommitteeConfig(config), ) a = epoch_start_shard b = epoch_start_shard + shard_delta if a > b: valid_shards_for_epoch = range(b, a) else: valid_shards_for_epoch = range(a, b) indices_to_check = set() previous_epoch_attestations = [] for committee, shard in prev_epoch_crosslink_committees: if shard not in valid_shards_for_epoch: continue for index in committee: indices_to_check.add(index) # Randomly sample `num_attesting_validators` validators # from the committee to attest in this slot. crosslink_attesting_validators = random.sample( committee, num_attesting_validators, ) each_slot_attestion_validators_list.append( crosslink_attesting_validators) participants_bitfield = get_empty_bitfield(len(committee)) for index in crosslink_attesting_validators: participants_bitfield = set_voted(participants_bitfield, committee.index(index)) previous_epoch_attestations.append( PendingAttestation( **sample_pending_attestation_record_params).copy( aggregation_bits=participants_bitfield, data=AttestationData( **sample_attestation_data_params).copy( target=Checkpoint(epoch=previous_epoch, ), crosslink=Crosslink( shard=shard, parent_root=Crosslink().hash_tree_root, ), ), )) state = state.copy( previous_epoch_attestations=tuple(previous_epoch_attestations), ) rewards_received, penalties_received = get_crosslink_deltas( state, config, ) expected_rewards_received = { index: 0 for index in range(len(state.validators)) } validator_balance = max_effective_balance for i in range(slots_per_epoch): crosslink_committee, shard = prev_epoch_crosslink_committees[i] if shard not in valid_shards_for_epoch: continue attesting_validators = each_slot_attestion_validators_list[i] total_attesting_balance = len(attesting_validators) * validator_balance total_committee_balance = len(crosslink_committee) * validator_balance for index in crosslink_committee: if index in attesting_validators: reward = get_base_reward( state=state, index=index, config=config, ) * total_attesting_balance // total_committee_balance expected_rewards_received[index] += reward else: penalty = get_base_reward( state=state, index=index, config=config, ) expected_rewards_received[index] -= penalty # Check the rewards/penalties match for index in range(len(state.validators)): if index not in indices_to_check: continue assert (rewards_received[index] - penalties_received[index] == expected_rewards_received[index])
def test_get_attestation_deltas(genesis_state, config, slots_per_epoch, target_committee_size, shard_count, min_attestation_inclusion_delay, inactivity_penalty_quotient, finalized_epoch, current_slot, sample_pending_attestation_record_params, sample_attestation_data_params): state = genesis_state.copy(slot=current_slot, finalized_checkpoint=Checkpoint( epoch=finalized_epoch, )) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) epoch_start_shard = get_start_shard( state, previous_epoch, CommitteeConfig(config), ) shard_delta = get_shard_delta( state, previous_epoch, CommitteeConfig(config), ) a = epoch_start_shard b = epoch_start_shard + shard_delta if a > b: valid_shards_for_epoch = range(b, a) else: valid_shards_for_epoch = range(a, b) indices_to_check = set() prev_epoch_start_slot = compute_start_slot_of_epoch( previous_epoch, slots_per_epoch) prev_epoch_attestations = tuple() for slot in range(prev_epoch_start_slot, prev_epoch_start_slot + slots_per_epoch): committee, shard = get_crosslink_committees_at_slot( state, slot, CommitteeConfig(config), )[0] if not committee: continue if shard not in valid_shards_for_epoch: continue participants_bitfield = get_empty_bitfield(len(committee)) for i, index in enumerate(committee): indices_to_check.add(index) participants_bitfield = set_voted(participants_bitfield, i) prev_epoch_attestations += (PendingAttestation( **sample_pending_attestation_record_params).copy( aggregation_bits=participants_bitfield, inclusion_delay=min_attestation_inclusion_delay, proposer_index=get_beacon_proposer_index( state.copy(slot=slot, ), CommitteeConfig(config), ), data=AttestationData(**sample_attestation_data_params).copy( crosslink=Crosslink(shard=shard, ), target=Checkpoint( epoch=previous_epoch, root=get_block_root( state, previous_epoch, config.SLOTS_PER_EPOCH, config.SLOTS_PER_HISTORICAL_ROOT, ), ), beacon_block_root=get_block_root_at_slot( state, slot, config.SLOTS_PER_HISTORICAL_ROOT, ), ), ), ) state = state.copy(previous_epoch_attestations=prev_epoch_attestations, ) rewards_received, penalties_received = get_attestation_deltas( state, config, ) # everyone attested, no penalties assert (sum(penalties_received) == 0) the_reward = rewards_received[0] # everyone performed the same, equal rewards assert (sum(rewards_received) // len(rewards_received) == the_reward)
def test_process_crosslinks(genesis_state, config, success_in_previous_epoch, success_in_current_epoch): shard_count = config.SHARD_COUNT current_slot = config.SLOTS_PER_EPOCH * 5 - 1 current_epoch = compute_epoch_of_slot(current_slot, config.SLOTS_PER_EPOCH) assert current_epoch - 4 >= 0 previous_crosslinks = tuple( Crosslink( shard=i, start_epoch=current_epoch - 4, end_epoch=current_epoch - 3, ) for i in range(shard_count)) parent_crosslinks = tuple( Crosslink( shard=i, parent_root=previous_crosslinks[i].hash_tree_root, start_epoch=current_epoch - 2, end_epoch=current_epoch - 1, ) for i in range(shard_count)) new_crosslinks = tuple( Crosslink( shard=i, parent_root=parent_crosslinks[i].hash_tree_root, start_epoch=current_epoch - 1, end_epoch=current_epoch, ) for i in range(shard_count)) # generate expected state for correct crosslink generation state = genesis_state.copy( slot=current_slot, previous_crosslinks=previous_crosslinks, current_crosslinks=parent_crosslinks, ) previous_epoch = current_epoch - 1 expected_success_shards = set() previous_epoch_attestations = tuple( mk_all_pending_attestations_with_some_participation_in_epoch( state, previous_epoch, config, 0.7 if success_in_previous_epoch else 0, )) if success_in_previous_epoch: for a in previous_epoch_attestations: expected_success_shards.add(a.data.crosslink.shard) current_epoch_attestations = tuple( mk_all_pending_attestations_with_some_participation_in_epoch( state, current_epoch, config, 0.7 if success_in_current_epoch else 0, )) if success_in_current_epoch: for a in current_epoch_attestations: expected_success_shards.add(a.data.crosslink.shard) state = state.copy( previous_epoch_attestations=previous_epoch_attestations, current_epoch_attestations=current_epoch_attestations, ) post_state = process_crosslinks(state, config) assert post_state.previous_crosslinks == state.current_crosslinks for shard in range(shard_count): crosslink = post_state.current_crosslinks[shard] if shard in expected_success_shards: if success_in_current_epoch: expected_crosslink = new_crosslinks[shard] else: expected_crosslink = parent_crosslinks[shard] assert crosslink == expected_crosslink else: # no change assert crosslink == state.current_crosslinks[shard]