def test_chaindb_get_score(chaindb): genesis = BlockHeader(difficulty=1, block_number=0, gas_limit=0) chaindb.persist_header(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 == 1 assert chaindb.get_score(genesis.hash) == 1 block1 = BlockHeader( difficulty=10, block_number=1, gas_limit=0, parent_hash=genesis.hash, timestamp=genesis.timestamp + 1, ) chaindb.persist_header(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 == 11 assert chaindb.get_score(block1.hash) == 11
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 HeaderNotFound("No header with hash {0} found".format( encode_hex(block_hash))) return rlp.decode(encoded_score, sedes=rlp.sedes.big_endian_int)
def test_chaindb_persist_block(chaindb, block): block = block.copy(header=set_empty_root(chaindb, block.header)) 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)
def _persist_header_chain( cls, db: BaseDB, headers: Iterable[BlockHeader] ) -> Tuple[Tuple[BlockHeader, ...], Tuple[BlockHeader, ...]]: try: first_header = first(headers) except StopIteration: return tuple(), tuple() else: for parent, child in sliding_window(2, headers): if parent.hash != child.parent_hash: raise ValidationError( "Non-contiguous chain. Expected {} to have {} as parent but was {}" .format( encode_hex(child.hash), encode_hex(parent.hash), encode_hex(child.parent_hash), )) is_genesis = first_header.parent_hash == GENESIS_PARENT_HASH if not is_genesis and not cls._header_exists( db, first_header.parent_hash): raise ParentNotFound( "Cannot persist block header ({}) with unknown parent ({})" .format(encode_hex(first_header.hash), encode_hex(first_header.parent_hash))) score = 0 if is_genesis else cls._get_score( db, first_header.parent_hash) for header in headers: db.set( header.hash, rlp.encode(header), ) score += header.difficulty db.set( SchemaV1.make_block_hash_to_score_lookup_key(header.hash), rlp.encode(score, sedes=rlp.sedes.big_endian_int), ) try: previous_canonical_head = cls._get_canonical_head(db).hash head_score = cls._get_score(db, previous_canonical_head) except CanonicalHeadNotFound: (new_canonical_headers, old_canonical_headers) = cls._set_as_canonical_chain_head( db, header.hash) else: if score > head_score: (new_canonical_headers, old_canonical_headers) = cls._set_as_canonical_chain_head( db, header.hash) else: new_canonical_headers = tuple() old_canonical_headers = tuple() return new_canonical_headers, old_canonical_headers
def _set_hash_scores_to_db(cls, db: DatabaseAPI, header: BlockHeaderAPI, score: int) -> int: new_score = score + header.difficulty db.set( SchemaV1.make_block_hash_to_score_lookup_key(header.hash), rlp.encode(new_score, sedes=rlp.sedes.big_endian_int), ) return new_score
def test_chaindb_persist_header(chaindb, header): with pytest.raises(HeaderNotFound): chaindb.get_block_header_by_hash(header.hash) number_to_hash_key = SchemaV1.make_block_hash_to_score_lookup_key(header.hash) assert not chaindb.exists(number_to_hash_key) chaindb.persist_header(header) assert chaindb.get_block_header_by_hash(header.hash) == header assert chaindb.exists(number_to_hash_key)
def persist_header( self, header: BlockHeader ) -> Tuple[Tuple[BlockHeader, ...], Tuple[BlockHeader, ...]]: """ :returns: iterable of headers newly on the canonical chain """ if header.parent_hash != GENESIS_PARENT_HASH: try: self.get_block_header_by_hash(header.parent_hash) except HeaderNotFound: raise ParentNotFound( "Cannot persist block header ({}) with unknown parent ({})" .format(encode_hex(header.hash), encode_hex(header.parent_hash))) self.db.set( header.hash, rlp.encode(header), ) if header.parent_hash == GENESIS_PARENT_HASH: score = header.difficulty else: score = self.get_score(header.parent_hash) + header.difficulty self.db.set( SchemaV1.make_block_hash_to_score_lookup_key(header.hash), rlp.encode(score, sedes=rlp.sedes.big_endian_int), ) try: head_score = self.get_score(self.get_canonical_head().hash) except CanonicalHeadNotFound: new_canonical_headers, old_canonical_headers = self._set_as_canonical_chain_head( header.hash, ) else: if score > head_score: new_canonical_headers, old_canonical_headers = self._set_as_canonical_chain_head( header.hash) else: new_canonical_headers = tuple() old_canonical_headers = tuple() return new_canonical_headers, old_canonical_headers
def persist_header_chain( self, headers: Iterable[BlockHeader] ) -> Tuple[Tuple[BlockHeader, ...], Tuple[BlockHeader, ...]]: """ Return two iterable of headers, the first containing the new canonical headers, the second containing the old canonical headers """ try: first_header = first(headers) except StopIteration: return tuple(), tuple() else: for parent, child in sliding_window(2, headers): if parent.hash != child.parent_hash: raise ValidationError( "Non-contiguous chain. Expected {} to have {} as parent but was {}" .format( encode_hex(child.hash), encode_hex(parent.hash), encode_hex(child.parent_hash), )) is_genesis = first_header.parent_hash == GENESIS_PARENT_HASH if not is_genesis and not self.header_exists( first_header.parent_hash): raise ParentNotFound( "Cannot persist block header ({}) with unknown parent ({})" .format(encode_hex(first_header.hash), encode_hex(first_header.parent_hash))) score = 0 if is_genesis else self.get_score( first_header.parent_hash) for header in headers: self.db.set( header.hash, rlp.encode(header), ) score += header.difficulty self.db.set( SchemaV1.make_block_hash_to_score_lookup_key(header.hash), rlp.encode(score, sedes=rlp.sedes.big_endian_int), ) try: head_score = self.get_score(self.get_canonical_head().hash) except CanonicalHeadNotFound: (new_canonical_headers, old_canonical_headers) = self._set_as_canonical_chain_head( header.hash) else: if score > head_score: (new_canonical_headers, old_canonical_headers) = self._set_as_canonical_chain_head( header.hash) else: new_canonical_headers = tuple() old_canonical_headers = tuple() return new_canonical_headers, old_canonical_headers