def test_get_committee_assignment_no_assignment(genesis_state, genesis_epoch, slots_per_epoch, config): state = genesis_state validator_index = 1 current_epoch = state.current_epoch(slots_per_epoch) validator = state.validators[validator_index].copy( exit_epoch=genesis_epoch) state = state.update_validator(validator_index, validator) assert not validator.is_active(current_epoch) with pytest.raises(NoCommitteeAssignment): get_committee_assignment(state, config, current_epoch, validator_index)
def test_get_committee_assignment_no_assignment(genesis_state, genesis_epoch, slots_per_epoch, config): state = genesis_state validator_index = 1 current_epoch = state.current_epoch(slots_per_epoch) validator = state.validators[validator_index].set("exit_epoch", genesis_epoch) state = state.transform(["validators", validator_index], validator) assert not validator.is_active(current_epoch) with pytest.raises(NoCommitteeAssignment): get_committee_assignment(state, config, current_epoch, validator_index)
def get_attestation(self, public_key: BLSPubkey, slot: Slot, committee_index: CommitteeIndex) -> Attestation: current_tick = self.clock.compute_current_tick() state = advance_state_to_slot(self.chain, current_tick.slot) block = self.chain.get_block_by_slot(slot) if not block: # try to find earlier block, assuming skipped slots block = self.chain.get_canonical_head() # sanity check the assumption in this leg of the conditional assert block.slot < slot else: block = block.message target_checkpoint = _get_target_checkpoint(state, block.hash_tree_root, self.eth2_config) data = AttestationData.create( slot=slot, index=committee_index, beacon_block_root=block.hash_tree_root, source=state.current_justified_checkpoint, target=target_checkpoint, ) validator_index = state.get_validator_index_for_public_key(public_key) epoch = compute_epoch_at_slot(slot, self.eth2_config.SLOTS_PER_EPOCH) committee_assignment = get_committee_assignment( state, self.eth2_config, epoch, validator_index) committee = committee_assignment.committee committee_validator_index = committee.index(validator_index) aggregation_bits = Bitfield( tuple(i == committee_validator_index for i in range(len(committee)))) return Attestation.create(aggregation_bits=aggregation_bits, data=data)
def get_validator_duties(self, public_keys: Collection[BLSPubkey], epoch: Epoch) -> Iterable[ValidatorDuty]: if epoch < GENESIS_EPOCH: return () current_tick = self.clock.compute_current_tick() state = advance_state_to_slot(self.chain, current_tick.slot) for public_key in public_keys: validator_index = state.get_validator_index_for_public_key( public_key) try: committee_assignment = get_committee_assignment( state, self.eth2_config, epoch, validator_index) except NoCommitteeAssignment: continue if is_proposer(state, validator_index, self.eth2_config): # TODO (ralexstokes) clean this up! if state.slot != 0: block_proposal_slot = state.slot else: block_proposal_slot = Slot((1 << 64) - 1) else: # NOTE: temporary sentinel value for "no slot" # The API has since been updated w/ much better ergonomics block_proposal_slot = Slot((1 << 64) - 1) yield ValidatorDuty( public_key, committee_assignment.slot, committee_assignment.committee_index, block_proposal_slot, )
def _get_local_current_epoch_assignment( self, validator_index: ValidatorIndex, epoch: Epoch ) -> CommitteeAssignment: """ Return the validator's epoch assignment at the given epoch. Note that ``epoch`` <= next_epoch. """ is_new_local_validator = ( validator_index not in self.local_validator_epoch_assignment ) should_update = is_new_local_validator or ( not is_new_local_validator and (epoch > self.local_validator_epoch_assignment[validator_index][0]) ) if should_update: state_machine = self.chain.get_state_machine() state = self.chain.get_head_state() self.local_validator_epoch_assignment[validator_index] = ( epoch, get_committee_assignment( state, state_machine.config, epoch, validator_index ), ) return self.local_validator_epoch_assignment[validator_index][1]
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 test_get_committee_assignment( genesis_state, slots_per_epoch, max_committees_per_slot, config, validator_count, state_epoch, epoch, ): state_slot = compute_start_slot_at_epoch(state_epoch, slots_per_epoch) state = genesis_state.set("slot", state_slot) committee_validator_count = [0 for _ in range(max_committees_per_slot)] slots = [] epoch_start_slot = compute_start_slot_at_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 committee_validator_count[assignment.committee_index] += 1 slots.append(assignment.slot) assert sum(committee_validator_count) == validator_count
def _get_this_epoch_assignment(self, validator_index: ValidatorIndex, this_epoch: Epoch) -> CommitteeAssignment: # update `this_epoch_assignment` if it's outdated if this_epoch > self.this_epoch_assignment[validator_index][0]: state_machine = self.chain.get_state_machine() state = self.chain.get_head_state() self.this_epoch_assignment[validator_index] = ( this_epoch, get_committee_assignment( state, state_machine.config, this_epoch, validator_index, )) return self.this_epoch_assignment[validator_index][1]
def _get_slot_with_validator_selected(candidate_indices, state, config): epoch = state.current_epoch(config.SLOTS_PER_EPOCH) for index in candidate_indices: try: committee, shard, slot, is_proposer = get_committee_assignment( state, config, epoch, index, ) if is_proposer: return slot, index except NoCommitteeAssignment: continue raise Exception( "Check the parameters of the genesis state; the above code should return" " some proposer if the set of ``candidate_indices`` is big enough.")