def __init__( self, chaindb: BaseBeaconChainDB, fork_choice_context: LMDGHOSTContext = None ) -> None: super().__init__(chaindb) self._fork_choice_store = Store( chaindb, self.block_class, self.config, fork_choice_context or self._get_fork_choice_context(), )
def test_lmd_ghost_fork_choice_scoring( sample_beacon_block_params, chaindb_at_genesis, # see note below on how this is used fork_choice_scoring, forking_descriptor, forking_asymmetry, genesis_state, genesis_block, config, ): """ Given some blocks and some attestations, can we score them correctly? """ chain_db = chaindb_at_genesis root_block = chain_db.get_canonical_head(SignedBeaconBlock) some_epoch = 3 some_slot_offset = 10 state = genesis_state.mset( "slot", compute_start_slot_at_epoch(some_epoch, config.SLOTS_PER_EPOCH) + some_slot_offset, "current_justified_checkpoint", Checkpoint.create(epoch=some_epoch, root=root_block.message.hash_tree_root), ) assert some_epoch >= state.current_justified_checkpoint.epoch # NOTE: the attestations have to be aligned to the blocks which start from ``base_slot``. base_slot = compute_start_slot_at_epoch(some_epoch, config.SLOTS_PER_EPOCH) + 1 block_tree = _build_block_tree( sample_beacon_block_params, root_block, base_slot, forking_descriptor, forking_asymmetry, config, ) slot_count = len(forking_descriptor) committee_sampling_fraction = 1 committees_by_slot = tuple( _get_committees(state, base_slot + slot_offset, config, committee_sampling_fraction) for slot_offset in range(slot_count)) _attach_committees_to_block_tree(state, block_tree, committees_by_slot, config, forking_asymmetry) _attach_attestations_to_block_tree_with_committees(block_tree, config) context = Context.from_genesis(genesis_state, genesis_block) store = Store(chain_db, SignedBeaconBlock, config, context) score_index = _build_score_index_from_decorated_block_tree( block_tree, store, state, config) for block in _iter_block_tree_by_block(block_tree): # NOTE: we use the ``fork_choice_scoring`` fixture, it doesn't matter for this test chain_db.persist_block(block, SignedBeaconBlock, fork_choice_scoring) for block in _iter_block_tree_by_block(block_tree): score = store.scoring(block) expected_score = score_index[block.message.hash_tree_root] assert score == expected_score
def test_store_get_latest_attestation(genesis_state, genesis_block, config, collisions_from_another_epoch): """ Given some attestations across the various sources, can we find the latest ones for each validator? """ some_epoch = 3 state = genesis_state.set( "slot", compute_start_slot_at_epoch(some_epoch, config.SLOTS_PER_EPOCH)) some_time = (_compute_seconds_since_genesis_for_epoch(some_epoch, config) + state.genesis_time) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) previous_epoch_committee_count = _get_committee_count( state, previous_epoch, config) current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) current_epoch_committee_count = _get_committee_count( state, current_epoch, config) number_of_committee_samples = 4 assert number_of_committee_samples <= previous_epoch_committee_count assert number_of_committee_samples <= current_epoch_committee_count block_producer = _mk_block_at_slot(genesis_block) # prepare samples from previous epoch previous_epoch_attestations_by_index = _mk_attestations_for_epoch_by_count( number_of_committee_samples, previous_epoch, block_producer, state, config) previous_epoch_attestations = _extract_attestations_from_index_keying( previous_epoch_attestations_by_index.values()) # prepare samples from current epoch current_epoch_attestations_by_index = _mk_attestations_for_epoch_by_count( number_of_committee_samples, current_epoch, block_producer, state, config) current_epoch_attestations_by_index = keyfilter( lambda index: index not in previous_epoch_attestations_by_index, current_epoch_attestations_by_index, ) current_epoch_attestations = _extract_attestations_from_index_keying( current_epoch_attestations_by_index.values()) pool_attestations_by_index = _mk_attestations_for_epoch_by_count( number_of_committee_samples, current_epoch, block_producer, state, config) pool_attestations_by_index = keyfilter( lambda index: (index not in previous_epoch_attestations_by_index or index not in current_epoch_attestations_by_index), pool_attestations_by_index, ) pool_attestations = _extract_attestations_from_index_keying( pool_attestations_by_index.values()) all_attestations_by_index = ( previous_epoch_attestations_by_index, current_epoch_attestations_by_index, pool_attestations_by_index, ) if collisions_from_another_epoch: ( previous_epoch_attestations_by_index, current_epoch_attestations_by_index, pool_attestations_by_index, ) = _introduce_collisions(all_attestations_by_index, block_producer, state, config) previous_epoch_attestations = _extract_attestations_from_index_keying( previous_epoch_attestations_by_index.values()) current_epoch_attestations = _extract_attestations_from_index_keying( current_epoch_attestations_by_index.values()) pool_attestations = _extract_attestations_from_index_keying( pool_attestations_by_index.values()) # build expected results expected_index = merge_with( _keep_by_latest_slot, previous_epoch_attestations_by_index, current_epoch_attestations_by_index, pool_attestations_by_index, ) chain_db = None # not relevant for this test context = Context.from_genesis(genesis_state, genesis_block) context.time = some_time store = Store(chain_db, SignedBeaconBlock, config, context) for attestations in ( previous_epoch_attestations, current_epoch_attestations, pool_attestations, ): for attestation in attestations: # NOTE: we need to synchronize the context w/ chain data used to construct # attestations above; this synchronization takes advantage of some of the # internals of ``on_attestation`` to shortcut constructing the complete network # state needed to test the function of the ``Store``. block = block_producer(attestation.data.slot) context.blocks[block.message.hash_tree_root] = block context.block_states[block.message.hash_tree_root] = genesis_state store.on_attestation(attestation, validate_signature=False) # sanity check assert expected_index.keys() == store._context.latest_messages.keys() for validator_index in range(len(state.validators)): expected_attestation_data = expected_index.get(validator_index, None) target = expected_attestation_data.target expected_message = LatestMessage(epoch=target.epoch, root=target.root) stored_message = store._context.latest_messages.get( validator_index, None) assert expected_message == stored_message
def test_store_get_latest_attestation(genesis_state, empty_attestation_pool, config, collisions_from_another_epoch): """ Given some attestations across the various sources, can we find the latest ones for each validator? """ some_epoch = 3 state = genesis_state.copy( slot=compute_start_slot_of_epoch(some_epoch, config.SLOTS_PER_EPOCH), ) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) previous_epoch_committee_count = _get_committee_count( state, previous_epoch, config, ) current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) current_epoch_committee_count = _get_committee_count( state, current_epoch, config, ) next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) next_epoch_committee_count = _get_committee_count( state, next_epoch, config, ) number_of_committee_samples = 4 assert number_of_committee_samples <= previous_epoch_committee_count assert number_of_committee_samples <= current_epoch_committee_count assert number_of_committee_samples <= next_epoch_committee_count # prepare samples from previous epoch previous_epoch_attestations_by_index = _mk_attestations_for_epoch_by_count( number_of_committee_samples, previous_epoch, state, config, ) previous_epoch_attestations = _extract_attestations_from_index_keying( previous_epoch_attestations_by_index.values(), ) # prepare samples from current epoch current_epoch_attestations_by_index = _mk_attestations_for_epoch_by_count( number_of_committee_samples, current_epoch, state, config, ) current_epoch_attestations_by_index = keyfilter( lambda index: index not in previous_epoch_attestations_by_index, current_epoch_attestations_by_index, ) current_epoch_attestations = _extract_attestations_from_index_keying( current_epoch_attestations_by_index.values(), ) # prepare samples for pool, taking half from the current epoch and half from the next epoch pool_attestations_in_current_epoch_by_index = _mk_attestations_for_epoch_by_count( number_of_committee_samples // 2, current_epoch, state, config, ) pool_attestations_in_next_epoch_by_index = _mk_attestations_for_epoch_by_count( number_of_committee_samples // 2, next_epoch, state, config, ) pool_attestations_by_index = merge( pool_attestations_in_current_epoch_by_index, pool_attestations_in_next_epoch_by_index, ) pool_attestations_by_index = keyfilter( lambda index: ( index not in previous_epoch_attestations_by_index or index not in current_epoch_attestations_by_index ), pool_attestations_by_index, ) pool_attestations = _extract_attestations_from_index_keying( pool_attestations_by_index.values(), ) all_attestations_by_index = ( previous_epoch_attestations_by_index, current_epoch_attestations_by_index, pool_attestations_by_index, ) if collisions_from_another_epoch: ( previous_epoch_attestations_by_index, current_epoch_attestations_by_index, pool_attestations_by_index, ) = _introduce_collisions( all_attestations_by_index, state, config, ) previous_epoch_attestations = _extract_attestations_from_index_keying( previous_epoch_attestations_by_index.values(), ) current_epoch_attestations = _extract_attestations_from_index_keying( current_epoch_attestations_by_index.values(), ) pool_attestations = _extract_attestations_from_index_keying( pool_attestations_by_index.values(), ) # build expected results expected_index = merge_with( _keep_by_latest_slot, previous_epoch_attestations_by_index, current_epoch_attestations_by_index, pool_attestations_by_index, ) # ensure we get the expected results state = state.copy( previous_epoch_attestations=previous_epoch_attestations, current_epoch_attestations=current_epoch_attestations, ) pool = empty_attestation_pool for attestation in pool_attestations: pool.add(attestation) chain_db = None # not relevant for this test store = Store(chain_db, state, pool, BeaconBlock, config) # sanity check assert expected_index.keys() == store._attestation_index.keys() for validator_index in range(len(state.validators)): expected_attestation_data = expected_index.get(validator_index, None) stored_attestation_data = store._get_latest_attestation(validator_index) assert expected_attestation_data == stored_attestation_data
def __init__(self, chain_db: BaseBeaconChainDB) -> None: self.chain_db = chain_db self._fork_choice_store = Store(chain_db, self.block_class, self.config, self._get_fork_choice_context()) self.fork_choice_scoring = LMDGHOSTScoring(self._fork_choice_store)
class SerenityStateMachine(BeaconStateMachine): # fork name fork = "serenity" # type: str config = SERENITY_CONFIG # classes block_class = SerenityBeaconBlock # type: Type[BaseBeaconBlock] signed_block_class = SerenitySignedBeaconBlock state_class = SerenityBeaconState # type: Type[BeaconState] state_transition_class = SerenityStateTransition # type: Type[BaseStateTransition] fork_choice_scoring_class = LMDGHOSTScoring # type: Type[BaseForkChoiceScoring] def __init__( self, chaindb: BaseBeaconChainDB, fork_choice_context: LMDGHOSTContext = None ) -> None: super().__init__(chaindb) self._fork_choice_store = Store( chaindb, self.block_class, self.config, fork_choice_context or self._get_fork_choice_context(), ) self.fork_choice_scoring = LMDGHOSTScoring(self._fork_choice_store) def _get_fork_choice_context(self) -> LMDGHOSTContext: try: fork_choice_context_data = self.chaindb.get_fork_choice_context_data_for( self.fork ) except MissingForkChoiceContext: # NOTE: ``MissingForkChoiceContext`` here implies that we are missing the # fork choice context for this fork, which happens to be the genesis fork. # In this situation (possibly uniquely), we want to build a new # fork choice context from the genesis data. genesis_root = self.chaindb.get_genesis_block_root() genesis_block = self.chaindb.get_block_by_root( genesis_root, self.block_class ) genesis_state = self.chaindb.get_state_by_root( genesis_block.message.state_root, self.state_class ) return LMDGHOSTContext.from_genesis(genesis_state, genesis_block) else: return LMDGHOSTContext.from_bytes(fork_choice_context_data) # methods def _get_justified_head_state(self) -> BeaconState: justified_head = self.chaindb.get_justified_head(self.block_class) return self.chaindb.get_state_by_root( justified_head.state_root, self.state_class ) def get_fork_choice_scoring(self) -> BaseForkChoiceScoring: return self.fork_choice_scoring def on_tick(self, time: Timestamp) -> None: self._fork_choice_store.on_tick(time) def on_block(self, block: BaseBeaconBlock) -> None: self._fork_choice_store.on_block(block) def on_attestation(self, attestation: Attestation) -> None: self._fork_choice_store.on_attestation(attestation)