def test_get_committee_assignment( genesis_state, slots_per_epoch, shard_count, config, validator_count, state_epoch, epoch, fixture_sm_class, ): state_slot = compute_start_slot_of_epoch(state_epoch, slots_per_epoch) state = genesis_state.copy(slot=state_slot) proposer_count = 0 shard_validator_count = [0 for _ in range(shard_count)] slots = [] epoch_start_slot = compute_start_slot_of_epoch(epoch, slots_per_epoch) for validator_index in range(validator_count): assignment = get_committee_assignment(state, config, epoch, validator_index) assert assignment.slot >= epoch_start_slot assert assignment.slot < epoch_start_slot + slots_per_epoch if assignment.is_proposer: proposer_count += 1 shard_validator_count[assignment.shard] += 1 slots.append(assignment.slot) assert proposer_count == slots_per_epoch assert sum(shard_validator_count) == validator_count
def create_mock_slashable_attestation( state: BeaconState, config: Eth2Config, keymap: Dict[BLSPubkey, int], attestation_slot: Slot, ) -> IndexedAttestation: """ Create an `IndexedAttestation` that is signed by one attester. """ attester_index = ValidatorIndex(0) committee = (attester_index, ) shard = Shard(0) # Use genesis block root as `beacon_block_root`, only for tests. beacon_block_root = get_block_root_at_slot( state, attestation_slot, config.SLOTS_PER_HISTORICAL_ROOT) # Get `target_root` target_root = _get_target_root(state, config, beacon_block_root) # Get `source_root` source_root = get_block_root_at_slot( state, compute_start_slot_of_epoch(state.current_justified_checkpoint.epoch, config.SLOTS_PER_EPOCH), config.SLOTS_PER_HISTORICAL_ROOT, ) previous_crosslink = state.current_crosslinks[shard] attestation_data = AttestationData( beacon_block_root=beacon_block_root, source=Checkpoint(epoch=state.current_justified_checkpoint.epoch, root=source_root), target=Checkpoint( epoch=compute_epoch_of_slot(attestation_slot, config.SLOTS_PER_EPOCH), root=target_root, ), crosslink=previous_crosslink, ) message_hash = _get_mock_message(attestation_data) attesting_indices = _get_mock_attesting_indices(committee, num_voted_attesters=1) signature = sign_transaction( message_hash=message_hash, privkey=keymap[state.validators[attesting_indices[0]].pubkey], state=state, slot=attestation_slot, signature_domain=SignatureDomain.DOMAIN_ATTESTATION, slots_per_epoch=config.SLOTS_PER_EPOCH, ) validator_indices = tuple(committee[i] for i in attesting_indices) return IndexedAttestation( custody_bit_0_indices=validator_indices, custody_bit_1_indices=tuple(), data=attestation_data, signature=signature, )
def get_attestation_data_slot(state: BeaconState, data: AttestationData, config: Eth2Config) -> Slot: active_validator_indices = get_active_validator_indices( state.validators, data.target.epoch, ) committee_count = get_committee_count( len(active_validator_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) offset = ( data.crosslink.shard + config.SHARD_COUNT - get_start_shard( state, data.target.epoch, CommitteeConfig(config), ) ) % config.SHARD_COUNT committees_per_slot = committee_count // config.SLOTS_PER_EPOCH return compute_start_slot_of_epoch( data.target.epoch, config.SLOTS_PER_EPOCH, ) + offset // committees_per_slot
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 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 test_get_matching_source_attestations(genesis_state, current_epoch, target_epoch, success, config): state = genesis_state.copy( slot=compute_start_slot_of_epoch(current_epoch, config.SLOTS_PER_EPOCH), current_epoch_attestations=tuple( PendingAttestation(data=AttestationData( beacon_block_root=current_epoch.to_bytes(32, "little")))), previous_epoch_attestations=tuple( PendingAttestation(data=AttestationData( beacon_block_root=(current_epoch - 1).to_bytes(32, "little")))), ) if success: attestations = get_matching_source_attestations( state, target_epoch, config) else: with pytest.raises(InvalidEpochError): get_matching_source_attestations(state, target_epoch, config) return if current_epoch == target_epoch: assert attestations == state.current_epoch_attestations else: assert attestations == state.previous_epoch_attestations
def create_mock_attester_slashing_is_surround_vote( state: BeaconState, config: Eth2Config, keymap: Dict[BLSPubkey, int], attestation_epoch: Epoch, ) -> AttesterSlashing: # target_epoch_2 < target_epoch_1 attestation_slot_2 = compute_start_slot_of_epoch(attestation_epoch, config.SLOTS_PER_EPOCH) attestation_slot_1 = Slot(attestation_slot_2 + config.SLOTS_PER_EPOCH) slashable_attestation_1 = create_mock_slashable_attestation( state.copy(slot=attestation_slot_1, current_justified_epoch=config.GENESIS_EPOCH), config, keymap, attestation_slot_1, ) slashable_attestation_2 = create_mock_slashable_attestation( state.copy( slot=attestation_slot_1, current_justified_epoch=config.GENESIS_EPOCH + 1, # source_epoch_1 < source_epoch_2 ), config, keymap, attestation_slot_2, ) return AttesterSlashing(attestation_1=slashable_attestation_1, attestation_2=slashable_attestation_2)
def test_validate_validator_minimum_lifespan( genesis_state, keymap, current_epoch, activation_epoch, slots_per_epoch, persistent_committee_period, success, ): state = genesis_state.copy( slot=compute_start_slot_of_epoch(current_epoch, slots_per_epoch)) validator_index = 0 validator = state.validators[validator_index].copy( activation_epoch=activation_epoch) state = state.update_validator(validator_index, validator) if success: _validate_validator_minimum_lifespan( validator, state.current_epoch(slots_per_epoch), persistent_committee_period) else: with pytest.raises(ValidationError): _validate_validator_minimum_lifespan( validator, state.current_epoch(slots_per_epoch), persistent_committee_period, )
def test_validate_eligible_exit_epoch( genesis_state, keymap, current_epoch, voluntary_exit_epoch, slots_per_epoch, config, success, ): state = genesis_state.copy( slot=compute_start_slot_of_epoch(current_epoch, slots_per_epoch)) validator_index = 0 voluntary_exit = create_mock_voluntary_exit( state, config, keymap, validator_index, exit_epoch=voluntary_exit_epoch) if success: _validate_eligible_exit_epoch(voluntary_exit.epoch, state.current_epoch(slots_per_epoch)) else: with pytest.raises(ValidationError): _validate_eligible_exit_epoch(voluntary_exit.epoch, state.current_epoch(slots_per_epoch))
async def _validate_hello_req(self, hello_other_side: HelloRequest) -> None: state_machine = self.chain.get_state_machine() state = self.chain.get_head_state() config = state_machine.config if hello_other_side.fork_version != state.fork.current_version: raise ValidationError( "`fork_version` mismatches: " f"hello_other_side.fork_version={hello_other_side.fork_version}, " f"state.fork.current_version={state.fork.current_version}") # Can not validate the checkpoint with `finalized_epoch` higher than ours if hello_other_side.finalized_epoch > state.finalized_checkpoint.epoch: return # Get the finalized root at `hello_other_side.finalized_epoch` # Edge case where nothing is finalized yet if (hello_other_side.finalized_epoch == 0 and hello_other_side.finalized_root == ZERO_SIGNING_ROOT): return finalized_epoch_start_slot = compute_start_slot_of_epoch( hello_other_side.finalized_epoch, config.SLOTS_PER_EPOCH, ) finalized_root = self.chain.get_canonical_block_root( finalized_epoch_start_slot) if hello_other_side.finalized_root != finalized_root: raise ValidationError( "`finalized_root` mismatches: " f"hello_other_side.finalized_root={hello_other_side.finalized_root}, " f"hello_other_side.finalized_epoch={hello_other_side.finalized_epoch}, " f"our `finalized_root` at the same `finalized_epoch`={finalized_root}" )
async def validate_peer_status(chain: BaseBeaconChain, peer_status: Status) -> None: state_machine = chain.get_state_machine() state = chain.get_head_state() config = state_machine.config if peer_status.head_fork_version != state.fork.current_version: raise IrrelevantNetwork( "`fork_version` mismatches: " f"peer_status.head_fork_version={peer_status.head_fork_version}, " f"state.fork.current_version={state.fork.current_version}") # Can not validate the checkpoint with `finalized_epoch` higher than ours if peer_status.finalized_epoch > state.finalized_checkpoint.epoch: return # Edge case where nothing is finalized yet if (peer_status.finalized_epoch == 0 and peer_status.finalized_root == ZERO_SIGNING_ROOT): return finalized_epoch_start_slot = compute_start_slot_of_epoch( peer_status.finalized_epoch, config.SLOTS_PER_EPOCH, ) finalized_root = chain.get_canonical_block_root(finalized_epoch_start_slot) if peer_status.finalized_root != finalized_root: raise IrrelevantNetwork( "`finalized_root` mismatches: " f"peer_status.finalized_root={peer_status.finalized_root}, " f"peer_status.finalized_epoch={peer_status.finalized_epoch}, " f"our `finalized_root` at the same `finalized_epoch`={finalized_root}" )
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_validate_voluntary_exit(genesis_state, keymap, slots_per_epoch, persistent_committee_period, config): state = genesis_state.copy(slot=compute_start_slot_of_epoch( config.GENESIS_EPOCH + persistent_committee_period, slots_per_epoch)) validator_index = 0 valid_voluntary_exit = create_mock_voluntary_exit(state, config, keymap, validator_index) validate_voluntary_exit(state, valid_voluntary_exit, slots_per_epoch, persistent_committee_period)
def validate_start_slot(chain: BaseBeaconChain, start_slot: Slot) -> None: config = chain.get_state_machine().config state = chain.get_head_state() finalized_epoch_start_slot = compute_start_slot_of_epoch( epoch=state.finalized_checkpoint.epoch, slots_per_epoch=config.SLOTS_PER_EPOCH, ) if start_slot < finalized_epoch_start_slot: raise ValidationError( f"`start_slot`({start_slot}) lower than our" f" latest finalized slot({finalized_epoch_start_slot})")
def _get_target_root(state: BeaconState, config: Eth2Config, beacon_block_root: Hash32) -> Hash32: epoch = compute_epoch_of_slot(state.slot, config.SLOTS_PER_EPOCH) epoch_start_slot = compute_start_slot_of_epoch(epoch, config.SLOTS_PER_EPOCH) if epoch_start_slot == state.slot: return beacon_block_root else: return get_block_root(state, epoch, config.SLOTS_PER_EPOCH, config.SLOTS_PER_HISTORICAL_ROOT)
def test_get_start_shard(genesis_state, current_epoch, target_epoch, expected_epoch_start_shard, config): state = genesis_state.copy(slot=compute_start_slot_of_epoch( current_epoch, config.SLOTS_PER_EPOCH), ) if expected_epoch_start_shard is None: with pytest.raises(ValidationError): get_start_shard(state, target_epoch, CommitteeConfig(config)) else: epoch_start_shard = get_start_shard(state, target_epoch, CommitteeConfig(config)) assert epoch_start_shard == expected_epoch_start_shard
def test_get_seed(genesis_state, committee_config, slots_per_epoch, min_seed_lookahead, activation_exit_delay, epochs_per_historical_vector): def mock_get_randao_mix(state, epoch, epochs_per_historical_vector): return hash_eth2( state.hash_tree_root + epoch.to_bytes(32, byteorder='little') + epochs_per_historical_vector.to_bytes(32, byteorder='little') ) def mock_get_active_index_root(state, epoch, epochs_per_historical_vector): return hash_eth2( state.hash_tree_root + epoch.to_bytes(32, byteorder='little') + slots_per_epoch.to_bytes(32, byteorder='little') + epochs_per_historical_vector.to_bytes(32, byteorder='little') ) state = genesis_state epoch = 1 state = state.copy( slot=compute_start_slot_of_epoch(epoch, committee_config.SLOTS_PER_EPOCH), ) epoch_as_bytes = epoch.to_bytes(32, 'little') seed = _get_seed( state=state, epoch=epoch, randao_provider=mock_get_randao_mix, active_index_root_provider=mock_get_active_index_root, epoch_provider=lambda *_: epoch_as_bytes, committee_config=committee_config, ) assert seed == hash_eth2( mock_get_randao_mix( state=state, epoch=(epoch + epochs_per_historical_vector - min_seed_lookahead - 1), epochs_per_historical_vector=epochs_per_historical_vector, ) + mock_get_active_index_root( state=state, epoch=epoch, epochs_per_historical_vector=epochs_per_historical_vector, ) + epoch_as_bytes )
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 get_committee_assignment( state: BeaconState, config: Eth2Config, epoch: Epoch, validator_index: ValidatorIndex, ) -> CommitteeAssignment: """ Return the ``CommitteeAssignment`` in the ``epoch`` for ``validator_index``. ``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. """ next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) if epoch > next_epoch: raise ValidationError( f"Epoch for committee assignment ({epoch}) must not be after next epoch {next_epoch}." ) 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 validator_index in committee: is_proposer = validator_index == get_beacon_proposer_index( state.copy(slot=slot), CommitteeConfig(config) ) return CommitteeAssignment( committee, Shard(shard), Slot(slot), is_proposer ) raise NoCommitteeAssignment
def test_determine_slashing_penalty(genesis_state, config, slots_per_epoch, current_epoch, epochs_per_slashings_vector, total_penalties, total_balance, expected_penalty): state = genesis_state.copy(slot=compute_start_slot_of_epoch( current_epoch, slots_per_epoch), ) # if the size of the v-set changes then update the parameters above assert len(state.validators) == 10 validator_index = 0 penalty = _determine_slashing_penalty( total_penalties, total_balance, state.validators[validator_index].effective_balance, config.EFFECTIVE_BALANCE_INCREMENT, ) assert penalty == expected_penalty
def test_randao_processing(sample_beacon_block_params, sample_beacon_block_body_params, sample_beacon_state_params, keymap, config): proposer_pubkey, proposer_privkey = first(keymap.items()) state = SerenityBeaconState(**sample_beacon_state_params).copy( validators=tuple( create_mock_validator(proposer_pubkey, config) for _ in range(config.TARGET_COMMITTEE_SIZE) ), balances=(config.MAX_EFFECTIVE_BALANCE,) * config.TARGET_COMMITTEE_SIZE, randao_mixes=tuple( ZERO_HASH32 for _ in range(config.EPOCHS_PER_HISTORICAL_VECTOR) ), ) epoch = state.current_epoch(config.SLOTS_PER_EPOCH) slot = compute_start_slot_of_epoch(epoch, config.SLOTS_PER_EPOCH) randao_reveal = _generate_randao_reveal( privkey=proposer_privkey, slot=slot, state=state, config=config, ) block_body = BeaconBlockBody(**sample_beacon_block_body_params).copy( randao_reveal=randao_reveal, ) block = SerenityBeaconBlock(**sample_beacon_block_params).copy( body=block_body, ) new_state = process_randao(state, block, config) updated_index = epoch % config.EPOCHS_PER_HISTORICAL_VECTOR original_mixes = state.randao_mixes updated_mixes = new_state.randao_mixes assert all( updated == original if index != updated_index else updated != original for index, (updated, original) in enumerate(zip(updated_mixes, original_mixes)) )
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 create_mock_attester_slashing_is_double_vote( state: BeaconState, config: Eth2Config, keymap: Dict[BLSPubkey, int], attestation_epoch: Epoch, ) -> AttesterSlashing: attestation_slot_1 = compute_start_slot_of_epoch(attestation_epoch, config.SLOTS_PER_EPOCH) attestation_slot_2 = Slot(attestation_slot_1 + 1) slashable_attestation_1 = create_mock_slashable_attestation( state, config, keymap, attestation_slot_1) slashable_attestation_2 = create_mock_slashable_attestation( state, config, keymap, attestation_slot_2) return AttesterSlashing(attestation_1=slashable_attestation_1, attestation_2=slashable_attestation_2)
def test_process_slashings(genesis_state, config, current_epoch, slashings, slots_per_epoch, epochs_per_slashings_vector, expected_penalty): state = genesis_state.copy( slot=compute_start_slot_of_epoch(current_epoch, slots_per_epoch), slashings=slashings, ) slashing_validator_index = 0 validator = state.validators[slashing_validator_index].copy( slashed=True, withdrawable_epoch=current_epoch + epochs_per_slashings_vector // 2) state = state.update_validator(slashing_validator_index, validator) result_state = process_slashings(state, config) penalty = (state.balances[slashing_validator_index] - result_state.balances[slashing_validator_index]) assert penalty == expected_penalty
def create_mock_voluntary_exit( state: BeaconState, config: Eth2Config, keymap: Dict[BLSPubkey, int], validator_index: ValidatorIndex, exit_epoch: Epoch = None, ) -> VoluntaryExit: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) target_epoch = current_epoch if exit_epoch is None else exit_epoch voluntary_exit = VoluntaryExit(epoch=target_epoch, validator_index=validator_index) return voluntary_exit.copy(signature=sign_transaction( message_hash=voluntary_exit.signing_root, privkey=keymap[state.validators[validator_index].pubkey], state=state, slot=compute_start_slot_of_epoch(target_epoch, config.SLOTS_PER_EPOCH), signature_domain=SignatureDomain.DOMAIN_VOLUNTARY_EXIT, slots_per_epoch=config.SLOTS_PER_EPOCH, ))
def test_randao_reveal_validation(is_valid, epoch, expected_epoch, proposer_key_index, expected_proposer_key_index, privkeys, pubkeys, sample_fork_params, genesis_state, config): state = genesis_state.copy( slot=compute_start_slot_of_epoch(epoch, config.SLOTS_PER_EPOCH), ) message_hash = epoch.to_bytes(32, byteorder="little") slots_per_epoch = config.SLOTS_PER_EPOCH domain = get_domain( state, SignatureDomain.DOMAIN_RANDAO, slots_per_epoch, ) proposer_privkey = privkeys[proposer_key_index] randao_reveal = bls.sign( message_hash=message_hash, privkey=proposer_privkey, domain=domain, ) try: validate_randao_reveal( state=state, proposer_index=expected_proposer_key_index, epoch=expected_epoch, randao_reveal=randao_reveal, slots_per_epoch=slots_per_epoch, ) except ValidationError: if is_valid: raise else: if not is_valid: pytest.fail("Did not raise")
def test_get_matching_target_attestations(genesis_state, config): some_epoch = config.GENESIS_EPOCH + 20 some_slot = compute_start_slot_of_epoch(some_epoch, config.SLOTS_PER_EPOCH) some_target_root = b"\x33" * 32 target_attestations = tuple((PendingAttestation(data=AttestationData( target=Checkpoint(root=some_target_root))) for _ in range(3))) current_epoch_attestations = target_attestations + tuple( (PendingAttestation(data=AttestationData(target=Checkpoint( root=b"\x44" * 32))) for _ in range(3))) state = genesis_state.copy( slot=some_slot + 1, block_roots=update_tuple_item( genesis_state.block_roots, some_slot % config.SLOTS_PER_HISTORICAL_ROOT, some_target_root, ), current_epoch_attestations=current_epoch_attestations, ) attestations = get_matching_target_attestations(state, some_epoch, config) assert attestations == target_attestations
def test_get_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: 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)
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)