def validate_proposer_slashing_epoch(proposer_slashing: ProposerSlashing, slots_per_epoch: int) -> None: epoch_1 = compute_epoch_at_slot( proposer_slashing.signed_header_1.message.slot, slots_per_epoch) epoch_2 = compute_epoch_at_slot( proposer_slashing.signed_header_2.message.slot, slots_per_epoch) if epoch_1 != epoch_2: raise ValidationError( f"Epoch of proposer_slashing.proposal_1 ({epoch_1}) !=" f" epoch of proposer_slashing.proposal_2 ({epoch_2})")
def compute_slots_since_epoch_start(slot: Slot, slots_per_epoch: int) -> Slot: return Slot( slot - compute_start_slot_at_epoch( compute_epoch_at_slot(slot, slots_per_epoch), slots_per_epoch ) )
def _get_highest_justified_epoch(self, db: DatabaseAPI) -> Epoch: try: justified_head_root = self._get_justified_head_root(db) slot = self.get_slot_by_root(justified_head_root) return compute_epoch_at_slot(slot, self.genesis_config.SLOTS_PER_EPOCH) except JustifiedHeadNotFound: return self.genesis_config.GENESIS_EPOCH
def get_beacon_committee( state: BeaconState, slot: Slot, index: CommitteeIndex, config: CommitteeConfig) -> Tuple[ValidatorIndex, ...]: epoch = compute_epoch_at_slot(slot, config.SLOTS_PER_EPOCH) committees_per_slot = get_committee_count_at_slot( state, slot, config.MAX_COMMITTEES_PER_SLOT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) active_validator_indices = get_active_validator_indices( state.validators, epoch) domain_type = signature_domain_to_domain_type( SignatureDomain.DOMAIN_BEACON_ATTESTER) return _compute_committee( indices=active_validator_indices, seed=get_seed(state, epoch, domain_type, config), index=(slot % config.SLOTS_PER_EPOCH) * committees_per_slot + index, count=committees_per_slot * config.SLOTS_PER_EPOCH, shuffle_round_count=config.SHUFFLE_ROUND_COUNT, )
def generate_config_by_dict(dict_config: Dict[str, Any]) -> Eth2Config: filtered_keys = ( "DOMAIN_", "ETH1_FOLLOW_DISTANCE", "TARGET_AGGREGATORS_PER_COMMITTEE", "RANDOM_SUBNETS_PER_VALIDATOR", "EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION", # Phase 1 "MAX_EPOCHS_PER_CROSSLINK", "EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS", "EPOCHS_PER_CUSTODY_PERIOD", "CUSTODY_PERIOD_TO_RANDAO_PADDING", "SHARD_SLOTS_PER_BEACON_SLOT", "EPOCHS_PER_SHARD_PERIOD", "PHASE_1_FORK_EPOCH", "PHASE_1_FORK_SLOT", ) return Eth2Config(**assoc( keyfilter(lambda name: all(key not in name for key in filtered_keys), dict_config), "GENESIS_EPOCH", compute_epoch_at_slot(dict_config["GENESIS_SLOT"], dict_config["SLOTS_PER_EPOCH"]), ))
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 = alice.chain.get_head_state() epoch = compute_epoch_at_slot(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 attestation.data.slot == assignment.slot assert attestation.data.beacon_block_root == head.signing_root assert attestation.data.index == assignment.committee_index # 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 _introduce_collisions(all_attestations_by_index, block_producer, state, config): """ Find some attestations for later epochs for the validators that are currently attesting in each source of attestation. """ collisions = (all_attestations_by_index[0], ) for src, dst in sliding_window(2, all_attestations_by_index): if not src: # src can be empty at low validator count collisions += (dst, ) continue src_index = random.choice(list(src.keys())) src_val = src[src_index] src_slot, _ = src_val src_epoch = compute_epoch_at_slot(src_slot, config.SLOTS_PER_EPOCH) dst_epoch = src_epoch + 1 collision = _find_collision( state, config, validator_index=src_index, epoch=dst_epoch, block_producer=block_producer, ) collisions += (merge(dst, collision), ) return collisions
def __init__( self, finalized_block_node: BlockNode[BaseBeaconBlock], justified_checkpoint: Checkpoint, justified_state: BeaconState, config: Eth2Config, block_sink: BlockSink, ) -> None: self._config = config finalized_checkpoint = Checkpoint.create( epoch=compute_epoch_at_slot(finalized_block_node.slot, config.SLOTS_PER_EPOCH), root=finalized_block_node.root, ) self._impl = ProtoArrayForkChoice( finalized_block_node, finalized_checkpoint, justified_checkpoint, block_sink, config, ) self._justified = justified_checkpoint self._finalized = finalized_checkpoint self.update_justified(justified_state)
def _find_latest_attestation_targets( state: BeaconState, store: Store, config: Eth2Config) -> Iterable[AttestationTarget]: epoch = compute_epoch_at_slot(state.slot, config.SLOTS_PER_EPOCH) active_validators = get_active_validator_indices(state.validators, epoch) return filter( second, map(_find_latest_attestation_target(store), active_validators))
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 _load_checkpoints(self) -> None: justified = self._chain_db.get_justified_head(BeaconBlock) finalized = self._chain_db.get_finalized_head(BeaconBlock) state_machine = self.get_state_machine(self._current_head.slot) config = state_machine.config self._justified_checkpoint = Checkpoint.create( epoch=compute_epoch_at_slot(justified.slot, config.SLOTS_PER_EPOCH), root=justified.hash_tree_root, ) self._finalized_checkpoint = Checkpoint.create( epoch=compute_epoch_at_slot(finalized.slot, config.SLOTS_PER_EPOCH), root=finalized.hash_tree_root, )
def _validate_slot_matches_target_epoch(target_epoch: Epoch, attestation_slot: Slot, slots_per_epoch: int) -> None: epoch = compute_epoch_at_slot(attestation_slot, slots_per_epoch) if target_epoch != epoch: raise ValidationError( f"Attestation at slot {attestation_slot} (epoch {epoch}) must be in" f" the same epoch with it's target epoch (epoch {target_epoch})")
async def handle_first_tick(self, slot: Slot) -> None: head = self.chain.get_canonical_head() state_machine = self.chain.get_state_machine() state = self.chain.get_head_state() self.logger.debug( bold_green( "status at slot %s in epoch %s: state_root %s, finalized_checkpoint %s" ), state.slot, state.current_epoch(self.slots_per_epoch), humanize_hash(head.message.state_root), state.finalized_checkpoint, ) self.logger.debug( ("status at slot %s in epoch %s:" " previous_justified_checkpoint %s, current_justified_checkpoint %s" ), state.slot, state.current_epoch(self.slots_per_epoch), state.previous_justified_checkpoint, state.current_justified_checkpoint, ) self.logger.debug( ("status at slot %s in epoch %s:" " previous_epoch_attestations %s, current_epoch_attestations %s"), state.slot, state.current_epoch(self.slots_per_epoch), state.previous_epoch_attestations, state.current_epoch_attestations, ) # To see if a validator is assigned to propose during the slot, the beacon state must # be in the epoch in question. At the epoch boundaries, the validator must run an # epoch transition into the epoch to successfully check the proposal assignment of the # first slot. temp_state = state_machine.state_transition.apply_state_transition( state, future_slot=slot, ) proposer_index = get_beacon_proposer_index( temp_state, CommitteeConfig(state_machine.config), ) # `latest_proposed_epoch` is used to prevent validator from erraneously proposing twice # in the same epoch due to service crashing. epoch = compute_epoch_at_slot(slot, self.slots_per_epoch) if proposer_index in self.validator_privkeys: has_proposed = epoch <= self.latest_proposed_epoch[proposer_index] if not has_proposed: await self.propose_block( proposer_index=proposer_index, slot=slot, state=state, state_machine=state_machine, head_block=head, ) self.latest_proposed_epoch[proposer_index] = epoch
async def test_validator_get_committee_assigment(event_loop, event_bus): alice_indices = [7] alice = await get_validator(event_loop=event_loop, event_bus=event_bus, indices=alice_indices) state_machine = alice.chain.get_state_machine() state = alice.chain.get_head_state() epoch = compute_epoch_at_slot(state.slot, state_machine.config.SLOTS_PER_EPOCH) assert alice.this_epoch_assignment[alice_indices[0]][0] == -1 alice._get_this_epoch_assignment(alice_indices[0], epoch) assert alice.this_epoch_assignment[alice_indices[0]][0] == epoch
def sign_transaction(*, message_hash: Hash32, privkey: int, state: BeaconState, slot: Slot, signature_domain: SignatureDomain, slots_per_epoch: int) -> BLSSignature: domain = get_domain( state, signature_domain, slots_per_epoch, message_epoch=compute_epoch_at_slot(slot, slots_per_epoch), ) return bls.sign(message_hash=message_hash, privkey=privkey, domain=domain)
def _get_target_root(state: BeaconState, config: Eth2Config, beacon_block_root: Root) -> Root: epoch = compute_epoch_at_slot(state.slot, config.SLOTS_PER_EPOCH) epoch_start_slot = compute_start_slot_at_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 get_committee_count_at_slot( state: BeaconState, slot: Slot, max_committees_per_slot: int, slots_per_epoch: int, target_committee_size: int, ) -> int: epoch = compute_epoch_at_slot(slot, slots_per_epoch) return get_committee_count_per_slot_at_epoch(state, epoch, max_committees_per_slot, slots_per_epoch, target_committee_size)
def get_block_signature(state: BeaconState, block: BeaconBlock, private_key: int, slots_per_epoch: int) -> BLSSignature: epoch = compute_epoch_at_slot(block.slot, slots_per_epoch) domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_PROPOSER, slots_per_epoch, message_epoch=epoch, ) signing_root = compute_signing_root(block, domain) return bls.sign(private_key, signing_root)
def get_slot_signature(state: BeaconState, slot: Slot, privkey: int, config: Eth2Config) -> BLSSignature: """ Sign on ``slot`` and return the signature. """ domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_ATTESTER, config.SLOTS_PER_EPOCH, message_epoch=compute_epoch_at_slot(slot, config.SLOTS_PER_EPOCH), ) return bls.sign(get_hash_tree_root(slot, sedes=uint64), privkey, domain)
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 validate_aggregator_proof( state: BeaconState, aggregate_and_proof: AggregateAndProof, config: Eth2Config ) -> None: slot = aggregate_and_proof.aggregate.data.slot pubkey = state.validators[aggregate_and_proof.aggregator_index].pubkey domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_ATTESTER, config.SLOTS_PER_EPOCH, message_epoch=compute_epoch_at_slot(slot, config.SLOTS_PER_EPOCH), ) signing_root = compute_signing_root(SerializableUint64(slot), domain) bls.validate(signing_root, aggregate_and_proof.selection_proof, pubkey)
def get_slot_signature( state: BeaconState, slot: Slot, privkey: int, config: Eth2Config ) -> BLSSignature: """ Sign on ``slot`` and return the signature. """ domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_ATTESTER, config.SLOTS_PER_EPOCH, message_epoch=compute_epoch_at_slot(slot, config.SLOTS_PER_EPOCH), ) signing_root = compute_signing_root(SerializableUint64(slot), domain) return bls.sign(privkey, signing_root)
def _get_attesting_assignments_at_slot( self, slot: Slot) -> Set[CommitteeAssignment]: """ Return the set of ``CommitteeAssignment``s of the given ``slot`` """ epoch = compute_epoch_at_slot(slot, self.slots_per_epoch) validator_assignments = self._get_local_current_epoch_assignments( epoch) committee_assignments = set(validator_assignments.values()) committee_assignments_at_slot = set( filter( lambda committee_assignment: committee_assignment.slot == slot, committee_assignments)) return committee_assignments_at_slot
def __init__( self, finalized_block: BlockNode[T], finalized: Checkpoint, justified: Checkpoint, block_sink: BlockSink, config: Eth2Config, ): finalized_epoch = compute_epoch_at_slot(finalized_block.slot, config.SLOTS_PER_EPOCH) assert finalized_epoch == finalized.epoch self.proto_array = ProtoArray(justified.epoch, finalized_block, block_sink, config) self.balances = [] self.votes = []
def _read_state_randao_mixes(self, state_root: Root, EPOCHS_PER_HISTORICAL_VECTOR: int, SLOTS_PER_EPOCH: int) -> Iterable[Root]: """ Reconstructs the ``randao_mixes`` at a given state root. """ state_slot = self._read_state_slot(state_root) state_epoch = compute_epoch_at_slot(state_slot, SLOTS_PER_EPOCH) finalized_slot = self.get_finalized_head(BeaconBlock).slot non_finalized_state_roots = dict( enumerate( self.get_state_parents(state_root, state_slot - finalized_slot), finalized_slot, )) # create a list of epochs that corresponds to each mix in ``state.randao_mixes`` epochs = [ Epoch(n) for n in range(state_epoch - EPOCHS_PER_HISTORICAL_VECTOR + 1, state_epoch + 1) ] offset = EPOCHS_PER_HISTORICAL_VECTOR - epochs[ 0] % EPOCHS_PER_HISTORICAL_VECTOR epochs = epochs[offset:] + epochs[:offset] genesis_root = self._read_state_root_at_slot(Slot(0)) genesis_randao_mix = Root( Hash32(self.db[SchemaV1.state_root_to_randao_mix(genesis_root)])) for epoch in epochs: if epoch < 0: yield genesis_randao_mix elif epoch == state_epoch: # yield the randao mix at the particular slot key = SchemaV1.state_root_to_randao_mix(state_root) yield Root(Hash32(self.db[key])) else: # yield the randao mix at the last slot in the epoch slot = Slot((epoch + 1) * SLOTS_PER_EPOCH - 1) if slot in non_finalized_state_roots: root = non_finalized_state_roots[slot] else: root = self._read_state_root_at_slot(slot) key = SchemaV1.state_root_to_randao_mix(root) yield Root(Hash32(self.db[key]))
async def handle_first_tick(self, slot: Slot) -> None: head = self.chain.get_canonical_head() state_machine = self.chain.get_state_machine() state = self.chain.get_head_state() self.logger.debug( bold_green( "status at slot %s in epoch %s: state_root %s, finalized_checkpoint %s" ), state.slot, state.current_epoch(self.slots_per_epoch), humanize_hash(head.state_root), state.finalized_checkpoint, ) self.logger.debug( ("status at slot %s in epoch %s:" " previous_justified_checkpoint %s, current_justified_checkpoint %s" ), state.slot, state.current_epoch(self.slots_per_epoch), state.previous_justified_checkpoint, state.current_justified_checkpoint, ) self.logger.debug( ("status at slot %s in epoch %s:" " previous_epoch_attestations %s, current_epoch_attestations %s"), state.slot, state.current_epoch(self.slots_per_epoch), state.previous_epoch_attestations, state.current_epoch_attestations, ) proposer_index = _get_proposer_index( state.copy(slot=slot, ), state_machine.config, ) # `latest_proposed_epoch` is used to prevent validator from erraneously proposing twice # in the same epoch due to service crashing. epoch = compute_epoch_at_slot(slot, self.slots_per_epoch) if proposer_index in self.validator_privkeys: has_proposed = epoch <= self.latest_proposed_epoch[proposer_index] if not has_proposed: await self.propose_block( proposer_index=proposer_index, slot=slot, state=state, state_machine=state_machine, head_block=head, ) self.latest_proposed_epoch[proposer_index] = epoch
async def test_validator_aggregate(event_loop, event_bus, monkeypatch): num_validators = 50 alice_indices = [i for i in range(num_validators)] alice = await get_validator( event_loop=event_loop, event_bus=event_bus, monkeypatch=monkeypatch, indices=alice_indices, num_validators=num_validators, ) alice.skip_block( slot=alice.chain.get_canonical_head().slot + 100, state=alice.chain.get_head_state(), state_machine=alice.chain.get_state_machine(), ) state_machine = alice.chain.get_state_machine() state = alice.chain.get_head_state() head = alice.chain.get_canonical_head() epoch = compute_epoch_at_slot(state.slot, state_machine.config.SLOTS_PER_EPOCH) assignment = alice._get_local_current_epoch_assignment( alice_indices[0], epoch) attested_attsetation = await alice.attest(assignment.slot) assert len(attested_attsetation) >= 1 aggregate_and_proofs = await alice.aggregate(assignment.slot) assert len(aggregate_and_proofs) >= 1 for aggregate_and_proof in aggregate_and_proofs: attestation = aggregate_and_proof.aggregate assert attestation.data.slot == assignment.slot assert attestation.data.beacon_block_root == head.signing_root assert attestation.data.index == assignment.committee_index # 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 create_signed_attestation_at_slot( state: BeaconState, config: Eth2Config, state_machine: BaseBeaconStateMachine, attestation_slot: Slot, beacon_block_root: SigningRoot, validator_privkeys: Dict[ValidatorIndex, int], committee: Tuple[ValidatorIndex, ...], committee_index: CommitteeIndex, attesting_indices: Sequence[CommitteeValidatorIndex], ) -> 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( state, future_slot=attestation_slot) target_epoch = compute_epoch_at_slot(attestation_slot, config.SLOTS_PER_EPOCH) target_root = _get_target_root(state, config, beacon_block_root) attestation_data = AttestationData( slot=attestation_slot, index=committee_index, beacon_block_root=beacon_block_root, source=Checkpoint( epoch=state.current_justified_checkpoint.epoch, root=state.current_justified_checkpoint.root, ), target=Checkpoint(root=target_root, epoch=target_epoch), ) return _create_mock_signed_attestation( state, attestation_data, attestation_slot, committee, len(committee), keymapper(lambda index: state.validators[index].pubkey, validator_privkeys), config.SLOTS_PER_EPOCH, is_for_simulation=False, attesting_indices=attesting_indices, )
def generate_randao_reveal(privkey: int, slot: Slot, state: BeaconState, config: Eth2Config) -> BLSSignature: """ Return the RANDAO reveal for the validator represented by ``privkey``. The current implementation requires a validator to provide the BLS signature over the SSZ-serialized epoch in which they are proposing a block. """ epoch = compute_epoch_at_slot(slot, config.SLOTS_PER_EPOCH) randao_reveal = sign_transaction( object=SerializableUint64(epoch), privkey=privkey, state=state, slot=slot, signature_domain=SignatureDomain.DOMAIN_RANDAO, slots_per_epoch=config.SLOTS_PER_EPOCH, ) return randao_reveal
def validate_block_header_signature( state: BeaconState, header: SignedBeaconBlockHeader, pubkey: BLSPubkey, slots_per_epoch: int, ) -> None: domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_PROPOSER, slots_per_epoch, compute_epoch_at_slot(header.message.slot, slots_per_epoch), ) signing_root = compute_signing_root(header.message, domain) try: bls.validate(signing_root, header.signature, pubkey) except SignatureError as error: raise ValidationError("Header signature is invalid:", error)