Exemplo n.º 1
0
    def parse(cls, data: Dict[Any, Any]) -> "HashedEntry":
        """ Creates a HashedEntry object from the provided dict. """
        if not isinstance(data, dict) or not is_field_hash(data, "hash"):
            raise MalformedListProofError.parse_error(str(data))

        key = ProofListKey.parse(data)
        return HashedEntry(key, Hash(bytes.fromhex(data["hash"])))
Exemplo n.º 2
0
    def hash_node(left: Hash, right: Hash) -> Hash:
        """ Convenience method to obtain a hashed value of the merkle tree node. """

        data = struct.pack(
            "<B", Hasher.HashTag.LIST_BRANCH_NODE) + left.value + right.value

        return Hash.hash_data(data)
Exemplo n.º 3
0
    def hash_single_entry_map(path: bytes, child_hash: Hash) -> Hash:
        """
        Hash of the map with single entry.
        ``` text
        h = sha-256( HashTag::MapBranchNode || <key> || <child_hash> )
        ```
        """
        data = struct.pack("<B", Hasher.HashTag.MAP_BRANCH_NODE) + path + child_hash.value

        return Hash.hash_data(data)
Exemplo n.º 4
0
    def hash_map_branch(branch_node: bytes) -> Hash:
        """
        Hash of the map branch node.
        ```text
        h = sha-256( HashTag::MapBranchNode || <left_key> || <right_key> || <left_hash> || <right_hash> )
        ```
        """
        data = struct.pack("<B", Hasher.HashTag.MAP_BRANCH_NODE) + branch_node

        return Hash.hash_data(data)
Exemplo n.º 5
0
    def hash_map_node(root: Hash) -> Hash:
        """
        Hash of the map object.
        ```text
        h = sha-256( HashTag::MapNode || merkle_root )
        ```
        """
        data = struct.pack("<B", Hasher.HashTag.MAP_NODE) + root.value

        return Hash.hash_data(data)
Exemplo n.º 6
0
    def hash_list_node(length: int, merkle_root: Hash) -> Hash:
        """
        Hash of the list object.
        ```text
        h = sha-256( HashTag::ListNode || len as u64 || merkle_root )
        ```
        """
        data = struct.pack("<BQ", Hasher.HashTag.LIST_NODE, length) + merkle_root.value

        return Hash.hash_data(data)
Exemplo n.º 7
0
    def parse(cls, data: Dict[Any, Any]) -> "HashedEntry":
        """ Creates a HashedEntry object from the provided dict. """
        if not isinstance(data, dict) or not is_field_hash(data, "hash"):
            err = MalformedListProofError.parse_error(str(data))
            logger.warning(
                "Could not parse `hash` from dict, which is required for HashedEntry object creation. %s",
                str(err))
            raise err

        key = ProofListKey.parse(data)
        return HashedEntry(key, Hash(bytes.fromhex(data["hash"])))
Exemplo n.º 8
0
    def parse(data: Dict[str, str]) -> "_MapProofEntry":
        """ Parses MapProofEntry from the provided dict. """

        if not isinstance(data.get("path"), str) or not is_field_hash(data, "hash"):
            raise MalformedMapProofError.malformed_entry(data)

        path_bits = data["path"]
        path = ProofPath.parse(path_bits)

        data_hash = to_bytes(data["hash"])
        if data_hash is None:
            raise MalformedMapProofError.malformed_entry(data)

        return _MapProofEntry(path, Hash(data_hash))
Exemplo n.º 9
0
    def test_hash(self) -> None:
        """Tests the Hash class."""
        raw_hash = bytes([0xAB for _ in range(_HASH_BYTES_LEN)])
        hash_obj = Hash(raw_hash)

        self.assertTrue(isinstance(hash_obj, _FixedByteArray))

        self.assertEqual(hash_obj.value, raw_hash)
        self.assertEqual(hash_obj.hex(), raw_hash.hex())
        self.assertEqual(Hash.hash_data(bytes()),
                         Hash(crypto_hash_sha256(bytes())))
        self.assertEqual(Hash.hash_data(bytes([1, 2])),
                         Hash(crypto_hash_sha256(bytes([1, 2]))))
Exemplo n.º 10
0
 def child_hash(self, kind: str) -> Hash:
     """Returns a stored child hash for the specified kind ("left" or "right")."""
     return Hash(bytes(self.raw[self._hash_slice(kind)]))
Exemplo n.º 11
0
def collect(entries: List[_MapProofEntry]) -> Hash:
    """
    Computes the root hash of the Merkle Patricia tree backing the specified entries
    in the map view.
    The tree is not restored in full; instead, we add the paths to
    the tree in their lexicographic order (i.e., according to the `PartialOrd` implementation
    of `ProofPath`) and keep track of the rightmost nodes (the right contour) of the tree.
    It is easy to see that adding paths in the lexicographic order means that only
    the nodes in the right contour may be updated on each step. Further, on each step
    zero or more nodes are evicted from the contour, and a single new node is
    added to it.
    `entries` are assumed to be sorted by the path in the increasing order.
    """

    def common_prefix(left: ProofPath, right: ProofPath) -> ProofPath:
        return left.prefix(left.common_prefix_len(right))

    def hash_branch(left_child: _MapProofEntry, right_child: _MapProofEntry) -> Hash:
        branch = BranchNode()
        branch.set_child("left", left_child.path, left_child.hash)
        branch.set_child("right", right_child.path, right_child.hash)

        return branch.object_hash()

    def fold(contour: List[_MapProofEntry], last_prefix: ProofPath) -> Optional[ProofPath]:
        last_entry = contour.pop()
        penultimate_entry = contour.pop()

        contour.append(_MapProofEntry(path=last_prefix, data_hash=hash_branch(penultimate_entry, last_entry)))

        if len(contour) > 1:
            penultimate_entry = contour[len(contour) - 2]
            return common_prefix(penultimate_entry.path, last_prefix)

        return None

    if not entries:
        return Hash(Hasher.DEFAULT_HASH)

    if len(entries) == 1:
        if not entries[0].path.is_leaf():
            raise MalformedMapProofError.non_terminal_node(entries[0].path)

        return Hasher.hash_single_entry_map(entries[0].path.as_bytes(), entries[0].hash)

    # There is more than 1 entry.

    # Contour of entries to be folded into the result hash:
    contour: List[_MapProofEntry] = []

    # Initical contour state:
    first_entry, second_entry = entries[0], entries[1]
    last_prefix = common_prefix(first_entry.path, second_entry.path)
    contour = [first_entry, second_entry]

    # Process the rest of the entries:
    for entry in entries[2:]:
        new_prefix = common_prefix(contour[-1].path, entry.path)

        # Fold contour from the latest added entry to the beginning.
        # At each iteration take two latest entries and attempt to fold them into one new entry:
        while len(contour) > 1 and len(new_prefix) < len(last_prefix):
            prefix = fold(contour, last_prefix)
            if prefix is not None:
                last_prefix = prefix

        contour.append(entry)
        last_prefix = new_prefix

    # All entries are processed. Fold the contour into the final hash:
    while len(contour) > 1:
        prefix = fold(contour, last_prefix)
        if prefix:
            last_prefix = prefix

    return contour[0].hash
Exemplo n.º 12
0
    def hash_leaf(val: bytes) -> Hash:
        """ Convenience method to obtain a hashed value of the merkle tree leaf. """

        data = struct.pack("<B", Hasher.HashTag.BLOB) + val

        return Hash.hash_data(data)
Exemplo n.º 13
0
    def hash_raw_data(data: bytes) -> Hash:
        """ SHA256 hash of the provided data. """

        return Hash.hash_data(data)
Exemplo n.º 14
0
def run() -> None:
    """This example creates a wallet in the Cryptocurrency service, retrieves
    proofs for the wallet and verifies them.
    For the example to work, be sure to have `exonum-cryptocurrency-advanced`
    service instance with name `XNM` deployed."""
    client = ExonumClient(hostname="127.0.0.1",
                          public_api_port=8080,
                          private_api_port=8081)

    with client.protobuf_loader() as loader:
        # Load and compile proto files:
        loader.load_main_proto_files()
        loader.load_service_proto_files(RUST_RUNTIME_ID,
                                        CRYPTOCURRENCY_ARTIFACT_NAME)

        instance_id = get_cryptocurrency_instance_id(client)

        cryptocurrency_message_generator = MessageGenerator(
            instance_id, CRYPTOCURRENCY_ARTIFACT_NAME)

        alice_keypair = create_wallet(client, cryptocurrency_message_generator,
                                      "Alice")

        wallet_info_response = client.get_service(
            CRYPTOCURRENCY_INSTANCE_NAME, "v1/wallets/info?pub_key={}".format(
                alice_keypair.public_key.hex()))
        ensure_status_code(wallet_info_response)
        wallet_info = wallet_info_response.json()

        # `MapProof` to the whole Exonum state hash:
        proof_to_table = wallet_info["wallet_proof"]["to_table"]
        # Expected hash of the proof to the table is a state hash of the block:
        expected_to_table_hash_raw = wallet_info["block_proof"]["block"][
            "state_hash"]
        expected_to_table_hash = Hash(
            bytes.fromhex(expected_to_table_hash_raw))

        # Verify the proof to the table:
        verify_proof_to_table(proof_to_table, expected_to_table_hash)

        # `MapProof` to the wallet as a part of the Cryptocurrency schema:
        proof_to_wallet = wallet_info["wallet_proof"]["to_wallet"]
        # Expected hash of the proof to the wallet is the value stored in the
        # proof to the table:
        expected_to_wallet_hash_raw = wallet_info["wallet_proof"]["to_table"][
            "entries"][0]["value"]
        expected_to_wallet_hash = Hash(
            bytes.fromhex(expected_to_wallet_hash_raw))

        # Verify the proof to the wallet:
        verify_proof_to_wallet(proof_to_wallet, expected_to_wallet_hash)

        # `ListProof` for the transactions associtated with the wallet:
        proof_wallet_history = wallet_info["wallet_history"]["proof"]
        # Expected hash for the wallet history is the hash stored in the proof
        # to the wallet:
        expected_history_hash_raw = wallet_info["wallet_proof"]["to_wallet"][
            "entries"][0]["value"]["history_hash"]
        expected_history_hash = Hash(bytes(expected_history_hash_raw["data"]))

        # Verify the proof for the wallet history:
        verify_wallet_history_proof(proof_wallet_history,
                                    expected_history_hash)
Exemplo n.º 15
0
def _parse_hash(hex_data: str) -> Hash:
    return Hash(_to_bytes(hex_data))