def validate_proposer_slashing_epoch(proposer_slashing: ProposerSlashing, slots_per_epoch: int) -> None: epoch_1 = compute_epoch_of_slot(proposer_slashing.header_1.slot, slots_per_epoch) epoch_2 = compute_epoch_of_slot(proposer_slashing.header_2.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})")
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_of_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 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 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, ) shard = Shard(0) # 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_of_epoch(state.current_justified_checkpoint.epoch, config.SLOTS_PER_EPOCH), config.SLOTS_PER_HISTORICAL_ROOT, ) previous_crosslink = state.current_crosslinks[shard] attestation_data = AttestationData( beacon_block_root=beacon_block_root, source=Checkpoint(epoch=state.current_justified_checkpoint.epoch, root=source_root), target=Checkpoint( epoch=compute_epoch_of_slot(attestation_slot, config.SLOTS_PER_EPOCH), root=target_root, ), crosslink=previous_crosslink, ) message_hash = _get_mock_message(attestation_data) 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_ATTESTATION, slots_per_epoch=config.SLOTS_PER_EPOCH, ) validator_indices = tuple(committee[i] for i in attesting_indices) return IndexedAttestation( custody_bit_0_indices=validator_indices, custody_bit_1_indices=tuple(), data=attestation_data, signature=signature, )
def _find_latest_attestation_targets( state: BeaconState, store: Store, config: Eth2Config) -> Iterable[AttestationTarget]: epoch = compute_epoch_of_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_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_of_slot(slot, self.genesis_config.SLOTS_PER_EPOCH) except JustifiedHeadNotFound: return self.genesis_config.GENESIS_EPOCH
def create_mock_signed_attestations_at_slot( state: BeaconState, config: Eth2Config, state_machine: BaseBeaconStateMachine, attestation_slot: Slot, beacon_block_root: Hash32, keymap: Dict[BLSPubkey, int], voted_attesters_ratio: float = 1.0) -> Iterable[Attestation]: """ Create the mocking attestations of the given ``attestation_slot`` slot with ``keymap``. """ crosslink_committees_at_slot = get_crosslink_committees_at_slot( state, attestation_slot, config, ) # Get `target_root` target_root = _get_target_root(state, config, beacon_block_root) target_epoch = compute_epoch_of_slot( state.slot, config.SLOTS_PER_EPOCH, ) for crosslink_committee in crosslink_committees_at_slot: committee, shard = crosslink_committee parent_crosslink = state.current_crosslinks[shard] attestation_data = AttestationData( 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, ), crosslink=Crosslink( shard=shard, parent_root=parent_crosslink.hash_tree_root, start_epoch=parent_crosslink.end_epoch, end_epoch=min( target_epoch, parent_crosslink.end_epoch + config.MAX_EPOCHS_PER_CROSSLINK), )) num_voted_attesters = int(len(committee) * voted_attesters_ratio) yield _create_mock_signed_attestation( state, attestation_data, attestation_slot, committee, num_voted_attesters, keymap, config.SLOTS_PER_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_of_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_of_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: Hash32) -> Hash32: epoch = compute_epoch_of_slot(state.slot, config.SLOTS_PER_EPOCH) epoch_start_slot = compute_start_slot_of_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 generate_config_by_dict(dict_config: Dict[str, Any]) -> Eth2Config: filtered_keys = ("DOMAIN_", "EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS") return Eth2Config(**assoc( keyfilter(lambda name: all(key not in name for key in filtered_keys), dict_config), "GENESIS_EPOCH", compute_epoch_of_slot(dict_config["GENESIS_SLOT"], dict_config["SLOTS_PER_EPOCH"]), ))
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( state, future_slot=attestation_slot, ) target_epoch = compute_epoch_of_slot( attestation_slot, config.SLOTS_PER_EPOCH, ) target_root = _get_target_root(state, config, beacon_block_root) parent_crosslink = state.current_crosslinks[shard] attestation_data = AttestationData( 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, ), crosslink=Crosslink( shard=shard, parent_root=parent_crosslink.hash_tree_root, start_epoch=parent_crosslink.end_epoch, end_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, )
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( # Align with debug log below bold_green("Head epoch=%s slot=%s state_root=%s"), state.current_epoch(self.slots_per_epoch), head.slot, encode_hex(head.state_root), ) self.logger.debug( bold_green("Justified epoch=%s root=%s (current)"), state.current_justified_checkpoint.epoch, encode_hex(state.current_justified_checkpoint.root), ) self.logger.debug( bold_green("Justified epoch=%s root=%s (previous)"), state.previous_justified_checkpoint.epoch, encode_hex(state.previous_justified_checkpoint.root), ) self.logger.debug( bold_green("Finalized epoch=%s root=%s"), state.finalized_checkpoint.epoch, encode_hex(state.finalized_checkpoint.root), ) self.logger.debug( bold_green("current_epoch_attestations %s"), state.current_epoch_attestations, ) self.logger.debug( bold_green("previous_epoch_attestations %s"), state.previous_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_of_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
def generate_config_by_dict(dict_config: Dict[str, Any]) -> Eth2Config: config_without_domains = keyfilter(lambda name: "DOMAIN_" not in name, dict_config) config_without_phase_1 = keyfilter( lambda name: "EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS" not in name, config_without_domains, ) return Eth2Config(**assoc( config_without_phase_1, "GENESIS_EPOCH", compute_epoch_of_slot( dict_config['GENESIS_SLOT'], dict_config['SLOTS_PER_EPOCH'], )))
def _mk_attestation_for_block_with_committee(block, committee, shard, config): 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) attestation = Attestation( aggregation_bits=aggregation_bits, data=AttestationData( beacon_block_root=block.signing_root, target=Checkpoint(epoch=compute_epoch_of_slot( block.slot, config.SLOTS_PER_EPOCH)), crosslink=Crosslink(shard=shard), ), ) return attestation
def test_update_active_index_roots(genesis_state, config, state_slot, slots_per_epoch, epochs_per_historical_vector, activation_exit_delay): state = genesis_state.copy(slot=state_slot, ) result = _compute_next_active_index_roots(state, config) index_root = ssz.get_hash_tree_root( get_active_validator_indices( state.validators, compute_epoch_of_slot(state.slot, slots_per_epoch), ), ssz.sedes.List(ssz.uint64, config.VALIDATOR_REGISTRY_LIMIT), ) target_epoch = state.next_epoch(slots_per_epoch) + activation_exit_delay assert result[target_epoch % epochs_per_historical_vector] == index_root
def validate_block_header_signature( state: BeaconState, header: BeaconBlockHeader, pubkey: BLSPubkey, slots_per_epoch: int, ) -> None: try: bls.validate( pubkey=pubkey, message_hash=header.signing_root, signature=header.signature, domain=get_domain( state, SignatureDomain.DOMAIN_BEACON_PROPOSER, slots_per_epoch, compute_epoch_of_slot(header.slot, slots_per_epoch), ), ) except SignatureError as error: raise ValidationError("Header signature is invalid:", error)
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_of_slot(slot, config.SLOTS_PER_EPOCH) message_hash = ssz.get_hash_tree_root(epoch, sedes=ssz.sedes.uint64) randao_reveal = sign_transaction( message_hash=message_hash, privkey=privkey, state=state, slot=slot, signature_domain=SignatureDomain.DOMAIN_RANDAO, slots_per_epoch=config.SLOTS_PER_EPOCH, ) return randao_reveal
def _introduce_collisions(all_attestations_by_index, state, config): """ Find some attestations for later epochs for the validators that are current 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_of_slot(src_slot, config.SLOTS_PER_EPOCH) dst_epoch = src_epoch + 1 collision = _find_collision(state, config, index=src_index, epoch=dst_epoch) collisions += (merge(dst, collision),) return collisions
def get_crosslink_committees_at_slot( state: BeaconState, slot: Slot, config: Eth2Config ) -> Tuple[Tuple[Tuple[ValidatorIndex, ...], Shard], ...]: epoch = compute_epoch_of_slot(slot, config.SLOTS_PER_EPOCH) active_validators = get_active_validator_indices(state.validators, epoch) committees_per_slot = (get_committee_count( len(active_validators), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) // config.SLOTS_PER_EPOCH) results = [] offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH) slot_start_shard = Shard( (get_start_shard(state, epoch, CommitteeConfig(config)) + offset) % config.SHARD_COUNT) for i in range(committees_per_slot): shard = (slot_start_shard + i) % config.SHARD_COUNT committee = get_crosslink_committee(state, epoch, shard, CommitteeConfig(config)) results.append((committee, Shard(shard))) return tuple(results)
def current_epoch(self, slots_per_epoch: int) -> Epoch: return compute_epoch_of_slot(self.slot, slots_per_epoch)
def test_process_crosslinks(genesis_state, config, success_in_previous_epoch, success_in_current_epoch): shard_count = config.SHARD_COUNT current_slot = config.SLOTS_PER_EPOCH * 5 - 1 current_epoch = compute_epoch_of_slot(current_slot, config.SLOTS_PER_EPOCH) assert current_epoch - 4 >= 0 previous_crosslinks = tuple( Crosslink( shard=i, start_epoch=current_epoch - 4, end_epoch=current_epoch - 3, ) for i in range(shard_count)) parent_crosslinks = tuple( Crosslink( shard=i, parent_root=previous_crosslinks[i].hash_tree_root, start_epoch=current_epoch - 2, end_epoch=current_epoch - 1, ) for i in range(shard_count)) new_crosslinks = tuple( Crosslink( shard=i, parent_root=parent_crosslinks[i].hash_tree_root, start_epoch=current_epoch - 1, end_epoch=current_epoch, ) for i in range(shard_count)) # generate expected state for correct crosslink generation state = genesis_state.copy( slot=current_slot, previous_crosslinks=previous_crosslinks, current_crosslinks=parent_crosslinks, ) previous_epoch = current_epoch - 1 expected_success_shards = set() previous_epoch_attestations = tuple( mk_all_pending_attestations_with_some_participation_in_epoch( state, previous_epoch, config, 0.7 if success_in_previous_epoch else 0, )) if success_in_previous_epoch: for a in previous_epoch_attestations: expected_success_shards.add(a.data.crosslink.shard) current_epoch_attestations = tuple( mk_all_pending_attestations_with_some_participation_in_epoch( state, current_epoch, config, 0.7 if success_in_current_epoch else 0, )) if success_in_current_epoch: for a in current_epoch_attestations: expected_success_shards.add(a.data.crosslink.shard) state = state.copy( previous_epoch_attestations=previous_epoch_attestations, current_epoch_attestations=current_epoch_attestations, ) post_state = process_crosslinks(state, config) assert post_state.previous_crosslinks == state.current_crosslinks for shard in range(shard_count): crosslink = post_state.current_crosslinks[shard] if shard in expected_success_shards: if success_in_current_epoch: expected_crosslink = new_crosslinks[shard] else: expected_crosslink = parent_crosslinks[shard] assert crosslink == expected_crosslink else: # no change assert crosslink == state.current_crosslinks[shard]
def test_demo(base_db, validator_count, keymap, pubkeys, fork_choice_scoring): bls.use_noop_backend() slots_per_epoch = 8 config = SERENITY_CONFIG._replace( SLOTS_PER_EPOCH=slots_per_epoch, GENESIS_EPOCH=compute_epoch_of_slot(SERENITY_CONFIG.GENESIS_SLOT, slots_per_epoch), TARGET_COMMITTEE_SIZE=3, SHARD_COUNT=2, MIN_ATTESTATION_INCLUSION_DELAY=2, ) override_lengths(config) fixture_sm_class = SerenityStateMachine.configure( __name__="SerenityStateMachineForTesting", config=config) genesis_slot = config.GENESIS_SLOT genesis_epoch = config.GENESIS_EPOCH chaindb = BeaconChainDB(base_db, config) attestation_pool = AttestationPool() genesis_state, genesis_block = create_mock_genesis( pubkeys=pubkeys[:validator_count], config=config, keymap=keymap, genesis_block_class=SerenityBeaconBlock, ) for i in range(validator_count): assert genesis_state.validators[i].is_active(genesis_slot) chaindb.persist_block(genesis_block, SerenityBeaconBlock, fork_choice_scoring) chaindb.persist_state(genesis_state) state = genesis_state block = genesis_block chain_length = 3 * config.SLOTS_PER_EPOCH blocks = (block, ) attestations_map = {} # Dict[Slot, Sequence[Attestation]] for current_slot in range(genesis_slot + 1, genesis_slot + chain_length + 1): if current_slot > genesis_slot + config.MIN_ATTESTATION_INCLUSION_DELAY: attestations = attestations_map[ current_slot - config.MIN_ATTESTATION_INCLUSION_DELAY] else: attestations = () block = create_mock_block( state=state, config=config, state_machine=fixture_sm_class(chaindb, attestation_pool), block_class=SerenityBeaconBlock, parent_block=block, keymap=keymap, slot=current_slot, attestations=attestations, ) # Get state machine instance sm = fixture_sm_class(chaindb, attestation_pool) state, _ = sm.import_block(block, state) chaindb.persist_state(state) chaindb.persist_block(block, SerenityBeaconBlock, fork_choice_scoring) blocks += (block, ) # Mock attestations attestation_slot = current_slot attestations = create_mock_signed_attestations_at_slot( state=state, config=config, state_machine=fixture_sm_class(chaindb, attestation_pool), attestation_slot=attestation_slot, beacon_block_root=block.signing_root, keymap=keymap, voted_attesters_ratio=1.0, ) attestations_map[attestation_slot] = attestations assert state.slot == chain_length + genesis_slot # Justification assertions assert state.current_justified_checkpoint.epoch == genesis_epoch assert state.finalized_checkpoint.epoch == genesis_epoch
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, ) 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 attestations = attestations + (attestation, ) self.logger.debug("Brodcasting attestations %s", attestations) await self.p2p_node.broadcast_attestations(attestations) return attestations