def _mk_attestation_inputs_in_epoch(epoch, block_producer, state, config): for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): if not committee: # empty committee this slot continue if slot >= state.slot: # do not make attestations in the future break block = block_producer(slot) root = block.message.hash_tree_root attestation_data = AttestationData.create( slot=slot, index=committee_index, target=Checkpoint.create(epoch=epoch, root=root), beacon_block_root=root, ) committee_size = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_size) for index in range(committee_size): aggregation_bits = bitfield.set_voted(aggregation_bits, index) for index in committee: yield ( index, (attestation_data.slot, (aggregation_bits, attestation_data)), )
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.committee_index`` is the index to which the committee is assigned ``CommitteeAssignment.slot`` is the slot at which the committee is assigned """ 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}." ) for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, CommitteeConfig(config) ): if validator_index in committee: return CommitteeAssignment( committee, CommitteeIndex(committee_index), Slot(slot) ) raise NoCommitteeAssignment
def _find_collision(state, config, validator_index, epoch, block_producer): """ Given a target epoch, make the attestation expected for the validator w/ the given ``validator_index``. """ for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): if slot >= state.slot: # do not make attestations in the future return {} if validator_index in committee: # TODO(ralexstokes) refactor w/ tools/builder block = block_producer(slot) root = block.message.hash_tree_root attestation_data = AttestationData.create( slot=slot, index=committee_index, target=Checkpoint.create(epoch=epoch, root=root), beacon_block_root=root, ) 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 test_aggregate_votes(validator_count, privkeys, genesis_state, config): config = CommitteeConfig(config) state = genesis_state epoch = compute_epoch_at_slot(state.slot, config.SLOTS_PER_EPOCH) sum_aggregator_count = 0 for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): assert config.TARGET_COMMITTEE_SIZE == len(committee) aggregator_count = 0 for index in range(validator_count): if index in committee: signature = slot_signature(genesis_state, slot, privkeys[index], config) attester_is_aggregator = is_aggregator(state, slot, committee_index, signature, config) if attester_is_aggregator: aggregator_count += 1 assert aggregator_count > 0 sum_aggregator_count += aggregator_count # The average aggregator count per slot should be around # `TARGET_AGGREGATORS_PER_COMMITTEE`. average_aggregator_count = sum_aggregator_count / config.SLOTS_PER_EPOCH assert (TARGET_AGGREGATORS_PER_COMMITTEE - 3 < average_aggregator_count < TARGET_AGGREGATORS_PER_COMMITTEE + 3)
def _mk_attestation_inputs_in_epoch(epoch, block_producer, state, config): for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config ): if not committee: # empty committee this slot continue block = block_producer(slot) root = block.signing_root attestation_data = AttestationData( slot=slot, index=committee_index, target=Checkpoint(epoch=epoch, root=root), beacon_block_root=root, ) committee_size = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_size) for index in range(committee_size): aggregation_bits = bitfield.set_voted(aggregation_bits, index) for index in committee: yield ( index, (attestation_data.slot, (aggregation_bits, attestation_data)), )
def _mk_some_pending_attestations_with_some_participation_in_epoch( state: BeaconState, epoch: Epoch, config: Eth2Config, participation_ratio: float) -> Iterable[PendingAttestation]: block_root = get_block_root(state, epoch, config.SLOTS_PER_EPOCH, config.SLOTS_PER_HISTORICAL_ROOT) for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): if not committee: continue committee_size = len(committee) participants_count = math.ceil(participation_ratio * committee_size) if not participants_count: return tuple() yield mk_pending_attestation_from_committee( committee_size, target_epoch=epoch, target_root=block_root, slot=Slot(slot), committee_index=committee_index, )
def _find_collision(state, config, validator_index, epoch): """ Given a target epoch, make the attestation expected for the validator w/ the given ``validator_index``. """ for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): if validator_index in committee: # TODO(ralexstokes) refactor w/ tools/builder attestation_data = AttestationData(slot=slot, index=committee_index, target=Checkpoint(epoch=epoch)) 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 test_get_attestation_deltas( genesis_state, config, slots_per_epoch, target_committee_size, max_committees_per_slot, min_attestation_inclusion_delay, inactivity_penalty_quotient, finalized_epoch, current_slot, sample_pending_attestation_record_params, sample_attestation_data_params, ): state = genesis_state.mset( "slot", current_slot, "finalized_checkpoint", Checkpoint.create(epoch=finalized_epoch), ) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) has_inactivity_penalty = (previous_epoch - finalized_epoch > config.MIN_EPOCHS_TO_INACTIVITY_PENALTY) indices_to_check = set() prev_epoch_attestations = tuple() for committee, committee_index, slot in iterate_committees_at_epoch( state, previous_epoch, config): 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.create( **sample_pending_attestation_record_params).mset( "aggregation_bits", participants_bitfield, "inclusion_delay", min_attestation_inclusion_delay, "proposer_index", get_beacon_proposer_index(state.set("slot", slot), CommitteeConfig(config)), "data", AttestationData.create(**sample_attestation_data_params).mset( "slot", slot, "index", committee_index, "beacon_block_root", get_block_root_at_slot(state, slot, config.SLOTS_PER_HISTORICAL_ROOT), "target", Checkpoint.create( epoch=previous_epoch, root=get_block_root( state, previous_epoch, config.SLOTS_PER_EPOCH, config.SLOTS_PER_HISTORICAL_ROOT, ), ), ), ), ) state = state.set("previous_epoch_attestations", prev_epoch_attestations) rewards_received, penalties_received = get_attestation_deltas( state, config) if has_inactivity_penalty: assert sum(penalties_received) > 0 else: assert sum(penalties_received) == 0 assert all(reward > 0 for reward in rewards_received)