def _validate_attestation_data(state: BeaconState, data: AttestationData, config: Eth2Config) -> None: slots_per_epoch = config.SLOTS_PER_EPOCH current_epoch = state.current_epoch(slots_per_epoch) previous_epoch = state.previous_epoch(slots_per_epoch, config.GENESIS_EPOCH) attestation_slot = get_attestation_data_slot(state, data, config) if data.target.epoch == current_epoch: expected_checkpoint = state.current_justified_checkpoint parent_crosslink = state.current_crosslinks[data.crosslink.shard] else: expected_checkpoint = state.previous_justified_checkpoint parent_crosslink = state.previous_crosslinks[data.crosslink.shard] _validate_eligible_shard_number(data.crosslink.shard, config.SHARD_COUNT) _validate_eligible_target_epoch(data.target.epoch, current_epoch, previous_epoch) validate_attestation_slot( attestation_slot, state.slot, slots_per_epoch, config.MIN_ATTESTATION_INCLUSION_DELAY, ) _validate_checkpoint(data.source, expected_checkpoint) _validate_crosslink( data.crosslink, data.target.epoch, parent_crosslink, config.MAX_EPOCHS_PER_CROSSLINK, )
async def test_validator_attest(event_loop, event_bus, monkeypatch): alice_indices = [i for i in range(8)] alice = await get_validator(event_loop=event_loop, event_bus=event_bus, indices=alice_indices) head = alice.chain.get_canonical_head() state_machine = alice.chain.get_state_machine() state = state_machine.state epoch = slot_to_epoch(state.slot, state_machine.config.SLOTS_PER_EPOCH) assignment = alice._get_this_epoch_assignment(alice_indices[0], epoch) attestations = await alice.attest(assignment.slot) assert len(attestations) == 1 attestation = attestations[0] assert get_attestation_data_slot( state, attestation.data, state_machine.config, ) == assignment.slot assert attestation.data.beacon_block_root == head.signing_root assert attestation.data.crosslink.shard == assignment.shard # Advance the state and validate the attestation config = state_machine.config future_state = state_machine.state_transition.apply_state_transition( state, future_slot=assignment.slot + config.MIN_ATTESTATION_INCLUSION_DELAY, ) validate_attestation( future_state, attestation, config, )
def _find_attestation_targets(self): result = {} for _, attestation in self._attestation_pool: target_slot = get_attestation_data_slot(self._state, attestation.data, self._config) for validator_index in _iter_attestation_by_validator_index( self._state, attestation, self._config): if validator_index in result: existing = result[validator_index] existing_slot = get_attestation_data_slot( self._state, existing.data, self._config) if existing_slot > target_slot: continue result[validator_index] = attestation return result
def beacon_attestation_validator(msg_forwarder: ID, msg: rpc_pb2.Message) -> bool: try: attestation = ssz.decode(msg.data, sedes=Attestation) except (TypeError, ssz.DeserializationError) as error: # Not correctly encoded logger.debug( bold_red("Failed to validate attestation=%s, error=%s"), attestation, str(error), ) return False state_machine = chain.get_state_machine() config = state_machine.config state = chain.get_head_state() # Check that beacon blocks attested to by the attestation are validated try: chain.get_block_by_root(attestation.data.beacon_block_root) except BlockNotFound: logger.debug( bold_red( "Failed to validate attestation=%s, attested block=%s is not validated yet" ), attestation, encode_hex(attestation.data.beacon_block_root), ) return False # Fast forward to state in future slot in order to pass # attestation.data.slot validity check attestation_data_slot = get_attestation_data_slot( state, attestation.data, config, ) future_state = state_machine.state_transition.apply_state_transition( state, future_slot=Slot(attestation_data_slot + config.MIN_ATTESTATION_INCLUSION_DELAY), ) try: validate_attestation( future_state, attestation, config, ) except ValidationError as error: logger.debug( bold_red("Failed to validate attestation=%s, error=%s"), attestation, str(error), ) return False return True
def get_matching_head_attestations( state: BeaconState, epoch: Epoch, config: Eth2Config) -> Iterable[PendingAttestation]: for a in get_matching_source_attestations(state, epoch, config): beacon_block_root = get_block_root_at_slot( state, get_attestation_data_slot(state, a.data, config), config.SLOTS_PER_HISTORICAL_ROOT, ) if a.data.beacon_block_root == beacon_block_root: yield a
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 _mk_pre_index_from_attestation( self, state: BeaconState, attestation: AttestationLike) -> Iterable[PreIndex]: attestation_data = attestation.data slot = get_attestation_data_slot(state, attestation_data, self._config) return ({ index: (slot, attestation_data) } for index in get_attesting_indices( state, attestation.data, attestation.aggregation_bits, CommitteeConfig(self._config), ))
def get_ready_attestations(self) -> Iterable[Attestation]: config = self.chain.get_state_machine().config state = self.chain.get_head_state() for attestation in self.attestation_pool.get_all(): data = attestation.data attestation_slot = get_attestation_data_slot(state, data, config) try: validate_attestation_slot( attestation_slot, state.slot, config.SLOTS_PER_EPOCH, config.MIN_ATTESTATION_INCLUSION_DELAY, ) except ValidationError: continue else: yield attestation
def process_attestations(state: BeaconState, block: BaseBeaconBlock, config: Eth2Config) -> BeaconState: if len(block.body.attestations) > config.MAX_ATTESTATIONS: raise ValidationError( f"The block has too many attestations:\n" f"\tFound {len(block.body.attestations)} attestations, " f"maximum: {config.MAX_ATTESTATIONS}") current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) new_current_epoch_attestations: Tuple[PendingAttestation, ...] = tuple() new_previous_epoch_attestations: Tuple[PendingAttestation, ...] = tuple() for attestation in block.body.attestations: validate_attestation( state, attestation, config, ) attestation_slot = get_attestation_data_slot( state, attestation.data, config, ) proposer_index = get_beacon_proposer_index( state, CommitteeConfig(config), ) pending_attestation = PendingAttestation( aggregation_bits=attestation.aggregation_bits, data=attestation.data, inclusion_delay=state.slot - attestation_slot, proposer_index=proposer_index, ) if attestation.data.target.epoch == current_epoch: new_current_epoch_attestations += (pending_attestation, ) else: new_previous_epoch_attestations += (pending_attestation, ) return state.copy( current_epoch_attestations=(state.current_epoch_attestations + new_current_epoch_attestations), previous_epoch_attestations=(state.previous_epoch_attestations + new_previous_epoch_attestations), )
def get_ready_attestations(self) -> Iterable[Attestation]: state_machine = self.chain.get_state_machine() config = state_machine.config state = state_machine.state for attestation in self.attestation_pool.get_all(): data = attestation.data attestation_slot = get_attestation_data_slot(state, data, config) try: validate_attestation_slot( attestation_slot, state.slot, config.SLOTS_PER_EPOCH, config.MIN_ATTESTATION_INCLUSION_DELAY, ) except ValidationError: # TODO: Should clean up attestations with invalid slot because # they are no longer available for inclusion into block. continue else: yield attestation