def test_chaindb_get_score(chaindb, sample_beacon_block_params): genesis = BeaconBlock(**sample_beacon_block_params).copy( parent_root=GENESIS_PARENT_HASH, slot=0, ) chaindb.persist_block(genesis) genesis_score_key = SchemaV1.make_block_root_to_score_lookup_key( genesis.root) genesis_score = rlp.decode(chaindb.db.get(genesis_score_key), sedes=rlp.sedes.big_endian_int) assert genesis_score == 0 assert chaindb.get_score(genesis.root) == 0 block1 = BeaconBlock(**sample_beacon_block_params).copy( parent_root=genesis.root, slot=1, ) chaindb.persist_block(block1) block1_score_key = SchemaV1.make_block_root_to_score_lookup_key( block1.root) block1_score = rlp.decode(chaindb.db.get(block1_score_key), sedes=rlp.sedes.big_endian_int) assert block1_score == 1 assert chaindb.get_score(block1.root) == 1
def test_chaindb_get_score(chaindb, sample_beacon_block_params): genesis = BaseBeaconBlock(**sample_beacon_block_params).copy( ancestor_hashes=(GENESIS_PARENT_HASH, ), slot=0, ) chaindb.persist_block(genesis) genesis_score_key = SchemaV1.make_block_hash_to_score_lookup_key( genesis.hash) genesis_score = rlp.decode(chaindb.db.get(genesis_score_key), sedes=rlp.sedes.big_endian_int) assert genesis_score == 0 assert chaindb.get_score(genesis.hash) == 0 block1 = BaseBeaconBlock(**sample_beacon_block_params).copy( ancestor_hashes=(genesis.hash, ), slot=1, ) chaindb.persist_block(block1) block1_score_key = SchemaV1.make_block_hash_to_score_lookup_key( block1.hash) block1_score = rlp.decode(chaindb.db.get(block1_score_key), sedes=rlp.sedes.big_endian_int) assert block1_score == 1 assert chaindb.get_score(block1.hash) == 1
def _get_canonical_head(cls, db: BaseDB) -> BaseBeaconBlock: try: canonical_head_hash = db[ SchemaV1.make_canonical_head_hash_lookup_key()] except KeyError: raise CanonicalHeadNotFound("No canonical head set for this chain") return cls._get_block_by_hash(db, canonical_head_hash)
def _persist_block_chain( cls, db: BaseDB, blocks: Iterable[BaseBeaconBlock], block_class: Type[BaseBeaconBlock] ) -> Tuple[Tuple[BaseBeaconBlock, ...], Tuple[BaseBeaconBlock, ...]]: try: first_block = first(blocks) except StopIteration: return tuple(), tuple() else: for parent, child in sliding_window(2, blocks): if parent.root != child.parent_root: raise ValidationError( "Non-contiguous chain. Expected {} to have {} as parent but was {}" .format( encode_hex(child.root), encode_hex(parent.root), encode_hex(child.parent_root), )) is_genesis = first_block.parent_root == GENESIS_PARENT_HASH if not is_genesis and not cls._block_exists( db, first_block.parent_root): raise ParentNotFound( "Cannot persist block ({}) with unknown parent ({})". format(encode_hex(first_block.root), encode_hex(first_block.parent_root))) if is_genesis: score = 0 else: score = cls._get_score(db, first_block.parent_root) for block in blocks: db.set( block.root, rlp.encode(block), ) # TODO: It's a stub before we implement fork choice rule score = block.slot db.set( SchemaV1.make_block_root_to_score_lookup_key(block.root), rlp.encode(score, sedes=rlp.sedes.big_endian_int), ) try: previous_canonical_head = cls._get_canonical_head(db, block_class).root head_score = cls._get_score(db, previous_canonical_head) except CanonicalHeadNotFound: return cls._set_as_canonical_chain_head(db, block.root, block_class) if score > head_score: return cls._set_as_canonical_chain_head(db, block.root, block_class) else: return tuple(), tuple()
def _get_score(db: BaseDB, block_hash: Hash32) -> int: try: encoded_score = db[SchemaV1.make_block_hash_to_score_lookup_key( block_hash)] except KeyError: raise BlockNotFound("No block with hash {0} found".format( encode_hex(block_hash))) return rlp.decode(encoded_score, sedes=rlp.sedes.big_endian_int)
def _set_block_scores_to_db(cls, db: BaseDB, block: BaseBeaconBlock) -> None: # TODO: It's a stub before we implement fork choice rule score = block.slot db.set( SchemaV1.make_block_root_to_score_lookup_key(block.root), rlp.encode(score, sedes=rlp.sedes.big_endian_int), )
def _get_canonical_head( cls, db: BaseDB, block_class: Type[BaseBeaconBlock]) -> BaseBeaconBlock: try: canonical_head_root = db[ SchemaV1.make_canonical_head_root_lookup_key()] except KeyError: raise CanonicalHeadNotFound("No canonical head set for this chain") return cls._get_block_by_root(db, Hash32(canonical_head_root), block_class)
def _set_deletatable_state( db: BaseDB, deletable_state_roots: Iterable[Hash32]) -> None: """ Set deletable_state_roots. """ lookup_key = SchemaV1.make_deletable_state_roots_lookup_key() db.set( lookup_key, rlp.encode(deletable_state_roots, sedes=CountableList(hash32)), )
def _get_canonical_block_hash(db: BaseDB, slot: int) -> Hash32: validate_slot(slot) slot_to_hash_key = SchemaV1.make_block_slot_to_hash_lookup_key(slot) try: encoded_key = db[slot_to_hash_key] except KeyError: raise BlockNotFound( "No canonical block for block slot #{0}".format(slot)) else: return rlp.decode(encoded_key, sedes=rlp.sedes.binary)
def test_chaindb_persist_block_and_slot_to_hash(chaindb, block): with pytest.raises(BlockNotFound): chaindb.get_block_by_hash(block.hash) slot_to_hash_key = SchemaV1.make_block_hash_to_score_lookup_key(block.hash) assert not chaindb.exists(slot_to_hash_key) chaindb.persist_block(block) assert chaindb.get_block_by_hash(block.hash) == block assert chaindb.exists(slot_to_hash_key)
def test_chaindb_persist_block_and_slot_to_root(chaindb, block): with pytest.raises(BlockNotFound): chaindb.get_block_by_root(block.root, block.__class__) slot_to_root_key = SchemaV1.make_block_root_to_score_lookup_key(block.root) assert not chaindb.exists(slot_to_root_key) chaindb.persist_block(block, block.__class__) assert chaindb.get_block_by_root(block.root, block.__class__) == block assert chaindb.exists(slot_to_root_key)
def _get_canonical_crystallized_state_root(db: BaseDB, slot: int) -> Hash32: validate_slot(slot) slot_to_hash_key = SchemaV1.make_slot_to_crystallized_state_lookup_key( slot) try: encoded_key = db[slot_to_hash_key] except KeyError: raise StateRootNotFound( "No canonical crystallized state for slot #{0}".format(slot)) else: return rlp.decode(encoded_key, sedes=rlp.sedes.binary)
def _add_block_slot_to_hash_lookup(db: BaseDB, block: BaseBeaconBlock) -> None: """ Set a record in the database to allow looking up this block by its block slot. """ block_slot_to_hash_key = SchemaV1.make_block_slot_to_hash_lookup_key( block.slot_number) db.set( block_slot_to_hash_key, rlp.encode(block.hash, sedes=rlp.sedes.binary), )
def _get_active_state_root_by_crystallized( db: BaseDB, crystallized_state_root: Hash32) -> Hash32: state_root_to_hash_key = SchemaV1.make_crystallized_to_active_state_root_lookup_key( crystallized_state_root) try: encoded_key = db[state_root_to_hash_key] except KeyError: raise StateRootNotFound( "No canonical active state for crystallized_state_root #{0}". format(state_root_to_hash_key)) else: return rlp.decode(encoded_key, sedes=rlp.sedes.binary)
def _add_crystallized_to_active_state_lookup( cls, db: BaseDB, active_state: ActiveState, crystallized_state_root: Hash32) -> None: """ Set a record in the database to allow looking up this block by its last state recalculation slot. """ slot_to_hash_key = SchemaV1.make_crystallized_to_active_state_root_lookup_key( crystallized_state_root, ) db.set( slot_to_hash_key, rlp.encode(active_state.hash, sedes=rlp.sedes.binary), )
def _get_deletable_state_roots(db: BaseDB) -> Tuple[Hash32]: """ Return deletable_state_roots. """ lookup_key = SchemaV1.make_deletable_state_roots_lookup_key() if not db.exists(lookup_key): db.set( lookup_key, rlp.encode((), sedes=CountableList(hash32)), ) deletable_state_roots = rlp.decode(db[lookup_key], sedes=CountableList(hash32)) return deletable_state_roots
def _set_as_canonical_chain_head( cls, db: BaseDB, block_hash: Hash32 ) -> Tuple[Tuple[BaseBeaconBlock, ...], Tuple[BaseBeaconBlock, ...]]: """ Set the canonical chain HEAD to the block as specified by the given block hash. :return: a tuple of the blocks that are newly in the canonical chain, and the blocks that are no longer in the canonical chain """ try: block = cls._get_block_by_hash(db, block_hash) except BlockNotFound: raise ValueError( "Cannot use unknown block hash as canonical head: {}".format( block_hash)) new_canonical_blocks = tuple( reversed(cls._find_new_ancestors(db, block))) old_canonical_blocks = [] for block in new_canonical_blocks: try: old_canonical_hash = cls._get_canonical_block_hash( db, block.slot_number) except BlockNotFound: # no old_canonical block, and no more possible break else: old_canonical_block = cls._get_block_by_hash( db, old_canonical_hash) old_canonical_blocks.append(old_canonical_block) for block in new_canonical_blocks: cls._add_block_slot_to_hash_lookup(db, block) db.set(SchemaV1.make_canonical_head_hash_lookup_key(), block.hash) return new_canonical_blocks, tuple(old_canonical_blocks)
def _add_slot_to_crystallized_state_lookup( cls, db: BaseDB, crystallized_state: CrystallizedState) -> None: """ Set a record in the database to allow looking up this block by its last state recalculation slot. If it's a fork, store the old state root in `deletable_state_roots`. """ slot_to_hash_key = SchemaV1.make_slot_to_crystallized_state_lookup_key( crystallized_state.last_state_recalc) if db.exists(slot_to_hash_key): deletable_state_roots = cls._get_deletable_state_roots(db) replaced_state_root = rlp.decode(db[slot_to_hash_key], sedes=rlp.sedes.binary) cls._set_deletatable_state( db, deletable_state_roots + (replaced_state_root, ), ) db.set( slot_to_hash_key, rlp.encode(crystallized_state.hash, sedes=rlp.sedes.binary), )
def test_chaindb_persist_block_and_block_to_root(chaindb, block): block_to_root_key = SchemaV1.make_block_root_to_score_lookup_key(block.root) assert not chaindb.exists(block_to_root_key) chaindb.persist_block(block, block.__class__) assert chaindb.exists(block_to_root_key)
def test_chaindb_add_block_number_to_hash_lookup(chaindb, block): block_slot_to_hash_key = SchemaV1.make_block_slot_to_hash_lookup_key( block.slot_number) assert not chaindb.exists(block_slot_to_hash_key) chaindb.persist_block(block) assert chaindb.exists(block_slot_to_hash_key)
def test_chaindb_add_block_number_to_root_lookup(chaindb, block): block_slot_to_root_key = SchemaV1.make_block_slot_to_root_lookup_key(block.slot) assert not chaindb.exists(block_slot_to_root_key) chaindb.persist_block(block, block.__class__) assert chaindb.exists(block_slot_to_root_key)
def test_chaindb_persist_block_and_block_to_hash(chaindb, block): block_to_hash_key = SchemaV1.make_block_hash_to_score_lookup_key( block.hash) assert not chaindb.exists(block_to_hash_key) chaindb.persist_block(block) assert chaindb.exists(block_to_hash_key)