def iterate_committees_at_slot( state: BeaconState, slot: Slot, committees_per_slot: int, config: Eth2Config ) -> Iterable[Tuple[Tuple[ValidatorIndex, ...], CommitteeIndex, Slot]]: """ Iterate ``committee``, ``committee_index``, ``slot`` of the given ``slot``. """ for committee_index in range(committees_per_slot): committee = get_beacon_committee(state, slot, CommitteeIndex(committee_index), config) yield committee, CommitteeIndex(committee_index), slot
def __init__( self, chain: BaseBeaconChain, p2p_node: Node, validator_privkeys: Dict[ValidatorIndex, int], event_bus: EndpointAPI, get_ready_attestations_fn: GetReadyAttestationsFn, get_aggregatable_attestations_fn: GetAggregatableAttestationsFn, import_attestation_fn: ImportAttestationFn, token: CancelToken = None) -> None: super().__init__(token) self.chain = chain self.p2p_node = p2p_node self.validator_privkeys = validator_privkeys self.event_bus = event_bus config = self.chain.get_state_machine().config self.slots_per_epoch = config.SLOTS_PER_EPOCH # TODO: `latest_proposed_epoch` and `latest_attested_epoch` should be written # into/read from validator's own db. self.latest_proposed_epoch = {} self.latest_attested_epoch = {} self.local_validator_epoch_assignment = {} for validator_index in validator_privkeys: self.latest_proposed_epoch[validator_index] = Epoch(-1) self.latest_attested_epoch[validator_index] = Epoch(-1) self.local_validator_epoch_assignment[validator_index] = ( Epoch(-1), CommitteeAssignment((), CommitteeIndex(-1), Slot(-1)), ) self.get_ready_attestations: GetReadyAttestationsFn = get_ready_attestations_fn self.get_aggregatable_attestations: GetAggregatableAttestationsFn = get_aggregatable_attestations_fn # noqa: E501 self.import_attestation: ImportAttestationFn = import_attestation_fn
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
async def _get_attestation(context: Context, request: Request) -> Response: if not isinstance(request, dict): return {} public_key = BLSPubkey(decode_hex(request["validator_pubkey"])) slot = Slot(int(request["slot"])) committee_index = CommitteeIndex(int(request["committee_index"])) attestation = context.get_attestation(public_key, slot, committee_index) return to_formatted_dict(attestation)
async def _get_attestation(self, request: web.Request) -> web.Response: # _public_key = BLSPubkey(decode_hex(request.query["validator_pubkey"])) slot = Slot(int(request.query["slot"])) committee_index = CommitteeIndex(int(request.query["committee_index"])) attestation = Attestation.create( aggregation_bits=Bitfield([True, False, False]), data=AttestationData.create(index=committee_index, slot=slot), ) return web.json_response(to_formatted_dict(attestation))
def create_mock_signed_attestations_at_slot( state: BeaconState, config: Eth2Config, state_machine: BaseBeaconStateMachine, attestation_slot: Slot, beacon_block_root: Root, keymap: Dict[BLSPubkey, int], voted_attesters_ratio: float = 1.0, ) -> Iterable[Attestation]: """ Create the mocking attestations of the given ``attestation_slot`` slot with ``keymap``. """ if voted_attesters_ratio == 0: return () committees_per_slot = get_committee_count_at_slot( state, attestation_slot, config.MAX_COMMITTEES_PER_SLOT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) # Get `target_root` target_root = _get_target_root(state, config, beacon_block_root) target_epoch = compute_epoch_at_slot(state.slot, config.SLOTS_PER_EPOCH) for committee, committee_index, _ in iterate_committees_at_slot( state, attestation_slot, committees_per_slot, config): attestation_data = AttestationData.create( slot=attestation_slot, index=CommitteeIndex(committee_index), beacon_block_root=beacon_block_root, source=Checkpoint.create( epoch=state.current_justified_checkpoint.epoch, root=state.current_justified_checkpoint.root, ), target=Checkpoint.create(root=target_root, epoch=target_epoch), ) num_voted_attesters = max(int(len(committee) * voted_attesters_ratio), 1) yield _create_mock_signed_attestation( state, attestation_data, attestation_slot, committee, num_voted_attesters, keymap, config.SLOTS_PER_EPOCH, )
def _get_mock_attesting_indices( committee: Sequence[ValidatorIndex], num_voted_attesters: int) -> Tuple[CommitteeIndex, ...]: """ Get voting indices of the given ``committee``. """ committee_size = len(committee) assert num_voted_attesters <= committee_size attesting_indices = tuple( CommitteeIndex(i) for i in random.sample(range(committee_size), num_voted_attesters)) return tuple(sorted(attesting_indices))
def _get_mock_message_and_attesting_indices( attestation_data: AttestationData, committee: Sequence[ValidatorIndex], num_voted_attesters: int) -> Tuple[Hash32, Tuple[CommitteeIndex, ...]]: """ Get ``message_hash`` and voting indices of the given ``committee``. """ message_hash = AttestationDataAndCustodyBit( data=attestation_data, custody_bit=False).hash_tree_root committee_size = len(committee) assert num_voted_attesters <= committee_size attesting_indices = tuple( CommitteeIndex(i) for i in random.sample(range(committee_size), num_voted_attesters)) return message_hash, tuple(sorted(attesting_indices))
def duty_fetcher(current_tick, _public_keys, target_epoch, _slots_per_epoch, _seconds_per_slot): duties = () if target_epoch < 0: return duties some_slot = current_tick.slot if current_tick.epoch >= 0: block_proposal_duty = BlockProposalDuty( public_key, Tick(0, some_slot, target_epoch, 0), current_tick) duties += (block_proposal_duty, ) attestation_duty = AttestationDuty( public_key, Tick(0, some_slot + slots_per_epoch, target_epoch, 1), current_tick, CommitteeIndex(22), ) duties += (attestation_duty, ) return duties
def _parse_attestation_duty( duty_data: Dict[str, Any], validator_public_key: BLSPubkey, current_tick: Tick, target_epoch: Epoch, genesis_time: int, seconds_per_slot: int, ticks_per_slot: int, ) -> AttestationDuty: target_tick = Tick.computing_t_from( Slot(duty_data["attestation_slot"]), target_epoch, AttestationDuty.tick_count, genesis_time, seconds_per_slot, ticks_per_slot, ) return AttestationDuty( validator_public_key=validator_public_key, tick_for_execution=target_tick, discovered_at_tick=current_tick, committee_index=CommitteeIndex(duty_data["committee_index"]), )
def _fetch_some_random_duties( current_tick: Tick, public_keys: Collection[BLSPubkey], target_epoch: Epoch, slots_per_epoch: Slot, seconds_per_slot: int, ) -> Tuple[Duty, ...]: if not public_keys: return () if target_epoch < 0: return () some_slot = Slot( random.randint(0, slots_per_epoch) + target_epoch * slots_per_epoch) execution_time = seconds_per_slot * (some_slot - current_tick.slot) + current_tick.t is_attestation = bool(random.getrandbits(1)) # NOTE: use of ``tuple`` here is satisfy ``mypy``. some_validator = random.choice(tuple(public_keys)) if is_attestation: committee_index = random.randint(0, 64) some_tick_count = 1 attestation_duty = AttestationDuty( some_validator, Tick(execution_time, some_slot, target_epoch, some_tick_count), current_tick, CommitteeIndex(committee_index), ) return (attestation_duty, ) else: some_tick_count = 0 block_proposal_duty = BlockProposalDuty( some_validator, Tick(execution_time, some_slot, target_epoch, some_tick_count), current_tick, ) return (block_proposal_duty, )
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, ) # 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_at_epoch(state.current_justified_checkpoint.epoch, config.SLOTS_PER_EPOCH), config.SLOTS_PER_HISTORICAL_ROOT, ) committees_per_slot = get_committee_count_at_slot( state, Slot(attestation_slot), config.MAX_COMMITTEES_PER_SLOT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) # Use the first committee assert committees_per_slot > 0 committee_index = CommitteeIndex(0) attestation_data = AttestationData.create( slot=attestation_slot, index=committee_index, beacon_block_root=beacon_block_root, source=Checkpoint.create( epoch=state.current_justified_checkpoint.epoch, root=source_root), target=Checkpoint.create( epoch=compute_epoch_at_slot(attestation_slot, config.SLOTS_PER_EPOCH), root=target_root, ), ) message_hash = attestation_data.hash_tree_root 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_BEACON_ATTESTER, slots_per_epoch=config.SLOTS_PER_EPOCH, ) validator_indices = tuple(committee[i] for i in attesting_indices) return IndexedAttestation.create(attesting_indices=validator_indices, data=attestation_data, signature=signature)
async def attest(self, slot: Slot) -> Tuple[Attestation, ...]: attestations: Tuple[Attestation, ...] = () head = self.chain.get_canonical_head() state_machine = self.chain.get_state_machine() state = self.chain.get_head_state() epoch = compute_epoch_of_slot(slot, self.slots_per_epoch) validator_assignments = { validator_index: self._get_this_epoch_assignment( validator_index, epoch, ) for validator_index in self.validator_privkeys } attesting_validators = self._get_attesting_validator_and_shard( validator_assignments, slot, epoch, ) if len(attesting_validators) == 0: return () # Sort the attesting validators by shard sorted_attesting_validators = sorted( attesting_validators, key=itemgetter(1), ) # Group the attesting validators by shard attesting_validators_groups = groupby( sorted_attesting_validators, key=itemgetter(1), ) for shard, group in attesting_validators_groups: # Get the validator_index -> privkey map of the attesting validators attesting_validator_privkeys = { attesting_data[0]: self.validator_privkeys[attesting_data[0]] for attesting_data in group } attesting_validators_indices = tuple( attesting_validator_privkeys.keys()) # Get one of the attesting validator's assignment in order to get the committee info assignment = self._get_this_epoch_assignment( attesting_validators_indices[0], epoch, ) attestation = create_signed_attestation_at_slot( state, state_machine.config, state_machine, slot, head.signing_root, attesting_validator_privkeys, assignment.committee, shard, tuple( CommitteeIndex(assignment.committee.index(index)) for index in attesting_validators_indices), ) self.logger.debug( bold_green("Validators=%s attest to block=%s attestation=%s"), attesting_validators_indices, head, attestation, ) for validator_index in attesting_validators_indices: self.latest_attested_epoch[validator_index] = epoch self.logger.debug("Broadcasting attestation %s", attestation) await self.p2p_node.broadcast_attestation(attestation) attestations = attestations + (attestation, ) # TODO: Aggregate attestations return attestations
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_without_block( state, attestation_slot, ) # Get `target_root` target_root = _get_target_root(state, config, beacon_block_root) previous_crosslink = state.latest_crosslinks[shard] attestation_data = AttestationData( slot=attestation_slot, beacon_block_root=beacon_block_root, source_epoch=state.current_justified_epoch, source_root=state.current_justified_root, target_root=target_root, shard=shard, previous_crosslink=previous_crosslink, crosslink_data_root=ZERO_HASH32, ) message_hash = AttestationDataAndCustodyBit(data=attestation_data, custody_bit=False).root signatures = [ sign_transaction( message_hash=message_hash, privkey=privkey, fork=state.fork, slot=attestation_data.slot, signature_domain=SignatureDomain.DOMAIN_ATTESTATION, slots_per_epoch=config.SLOTS_PER_EPOCH, ) for _, privkey in validator_privkeys.items() ] voting_committee_indices = [ CommitteeIndex(committee.index(validator_index)) for validator_index in validator_privkeys ] # aggregate signatures and construct participant bitfield aggregation_bitfield, aggregate_signature = aggregate_votes( bitfield=get_empty_bitfield(len(committee)), sigs=(), voting_sigs=signatures, voting_committee_indices=voting_committee_indices, ) # create attestation from attestation_data, particpipant_bitfield, and signature return Attestation( aggregation_bitfield=aggregation_bitfield, data=attestation_data, custody_bitfield=Bitfield(get_empty_bitfield( len(aggregation_bitfield))), aggregate_signature=aggregate_signature, )