Exemple #1
0
    def _persist_header_chain(
            cls,
            db: DatabaseAPI,
            headers: Iterable[BlockHeaderAPI],
            genesis_parent_hash: Hash32,
    ) -> Tuple[Tuple[BlockHeaderAPI, ...], Tuple[BlockHeaderAPI, ...]]:
        headers_iterator = iter(headers)

        try:
            first_header = first(headers_iterator)
        except StopIteration:
            return tuple(), tuple()

        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(
                f"Cannot persist block header ({encode_hex(first_header.hash)}) "
                f"with unknown parent ({encode_hex(first_header.parent_hash)})"
            )

        if is_genesis:
            score = 0
        else:
            score = cls._get_score(db, first_header.parent_hash)

        curr_chain_head = first_header
        db.set(
            curr_chain_head.hash,
            rlp.encode(curr_chain_head),
        )
        score = cls._set_hash_scores_to_db(db, curr_chain_head, score)

        orig_headers_seq = concat([(first_header,), headers_iterator])
        for parent, child in sliding_window(2, orig_headers_seq):
            if parent.hash != child.parent_hash:
                raise ValidationError(
                    f"Non-contiguous chain. Expected {encode_hex(child.hash)} "
                    f"to have {encode_hex(parent.hash)} as parent "
                    f"but was {encode_hex(child.parent_hash)}"
                )

            curr_chain_head = child
            db.set(
                curr_chain_head.hash,
                rlp.encode(curr_chain_head),
            )

            score = cls._set_hash_scores_to_db(db, curr_chain_head, score)

        try:
            previous_canonical_head = cls._get_canonical_head_hash(db)
            head_score = cls._get_score(db, previous_canonical_head)
        except CanonicalHeadNotFound:
            return cls._set_as_canonical_chain_head(db, curr_chain_head, genesis_parent_hash)

        if score > head_score:
            return cls._set_as_canonical_chain_head(db, curr_chain_head, genesis_parent_hash)

        return tuple(), tuple()
Exemple #2
0
 def _remove_transaction_from_canonical_chain(
         db: DatabaseAPI, transaction_hash: Hash32) -> None:
     """
     Removes the transaction specified by the given hash from the canonical
     chain.
     """
     db.delete(
         SchemaV1.make_transaction_hash_to_block_lookup_key(
             transaction_hash))
Exemple #3
0
    def test_database_api_delete(self, db: DatabaseAPI) -> None:
        db[b'key-1'] = b'value-1'

        assert b'key-1' in db

        db.delete(b'key-1')

        assert not db.exists(b'key-1')
        assert b'key-1' not in db
Exemple #4
0
 def _add_block_number_to_hash_lookup(db: DatabaseAPI, header: BlockHeaderAPI) -> None:
     """
     Sets a record in the database to allow looking up this header by its
     block number.
     """
     block_number_to_hash_key = SchemaV1.make_block_number_to_hash_lookup_key(
         header.block_number
     )
     db.set(
         block_number_to_hash_key,
         rlp.encode(header.hash, sedes=rlp.sedes.binary),
     )
Exemple #5
0
 def _persist_checkpoint_header(
         cls,
         db: DatabaseAPI,
         header: BlockHeaderAPI,
         score: int
 ) -> None:
     db.set(
         header.hash,
         rlp.encode(header),
     )
     previous_score = score - header.difficulty
     cls._set_hash_scores_to_db(db, header, previous_score)
     cls._set_as_canonical_chain_head(db, header, header.parent_hash)
Exemple #6
0
    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
Exemple #7
0
 def _add_transaction_to_canonical_chain(db: DatabaseAPI,
                                         transaction_hash: Hash32,
                                         block_header: BlockHeaderAPI,
                                         index: int) -> None:
     """
     :param bytes transaction_hash: the hash of the transaction to add the lookup for
     :param block_header: The header of the block with the txn that is in the canonical chain
     :param int index: the position of the transaction in the block
     - add lookup from transaction hash to the block number and index that the body is stored at
     - remove transaction hash to body lookup in the pending pool
     """
     transaction_key = TransactionKey(block_header.block_number, index)
     db.set(
         SchemaV1.make_transaction_hash_to_block_lookup_key(
             transaction_hash),
         rlp.encode(transaction_key),
     )
Exemple #8
0
    def _set_as_canonical_chain_head(
        cls,
        db: DatabaseAPI,
        header: BlockHeaderAPI,
        genesis_parent_hash: Hash32,
    ) -> Tuple[Tuple[BlockHeaderAPI, ...], Tuple[BlockHeaderAPI, ...]]:
        """
        Sets the canonical chain HEAD to the block header as specified by the
        given block hash.

        :return: a tuple of the headers that are newly in the canonical chain, and the headers that
            are no longer in the canonical chain
        """
        try:
            current_canonical_head = cls._get_canonical_head_hash(db)
        except CanonicalHeadNotFound:
            current_canonical_head = None

        new_canonical_headers: Tuple[BlockHeaderAPI, ...]
        old_canonical_headers: Tuple[BlockHeaderAPI, ...]

        if current_canonical_head and header.parent_hash == current_canonical_head:
            # the calls to _find_new_ancestors and _decanonicalize_old_headers are
            # relatively expensive, it's better to skip them in this case, where we're
            # extending the canonical chain by a header
            new_canonical_headers = (header,)
            old_canonical_headers = ()
        else:
            new_canonical_headers = cast(
                Tuple[BlockHeaderAPI, ...],
                tuple(reversed(cls._find_new_ancestors(db, header, genesis_parent_hash)))
            )
            old_canonical_headers = cls._decanonicalize_old_headers(
                db, new_canonical_headers
            )

        for h in new_canonical_headers:
            cls._add_block_number_to_hash_lookup(db, h)

        db.set(SchemaV1.make_canonical_head_hash_lookup_key(), header.hash)

        return new_canonical_headers, old_canonical_headers
Exemple #9
0
 def _persist_uncles(db: DatabaseAPI,
                     uncles: Tuple[BlockHeaderAPI]) -> Hash32:
     uncles_hash = keccak(rlp.encode(uncles))
     db.set(uncles_hash,
            rlp.encode(uncles, sedes=rlp.sedes.CountableList(BlockHeader)))
     return uncles_hash
Exemple #10
0
 def test_database_api_delete_missing_key(self, db: DatabaseAPI) -> None:
     assert b'key-1' not in db
     db.delete(b'key-1')
Exemple #11
0
 def test_database_api_get(self, db: DatabaseAPI) -> None:
     db[b'key-1'] = b'value-1'
     assert db.get(b'key-1') == b'value-1'
Exemple #12
0
    def test_database_api_exists(self, db: DatabaseAPI) -> None:
        assert db.exists(b'key-1') is False

        db[b'key-1'] = b'value-1'

        assert db.exists(b'key-1') is True
Exemple #13
0
 def test_database_api_item_setter(self, db: DatabaseAPI) -> None:
     db.set(b'key-1', b'value-1')
     assert db[b'key-1'] == b'value-1'
     db.set(b'key-1', b'value-2')
     assert db[b'key-1'] == b'value-2'
Exemple #14
0
 def test_database_api_get_missing_key(self, db: DatabaseAPI) -> None:
     assert b'key-1' not in db
     assert db.get(b'key-1') is None