Ejemplo n.º 1
0
    def _iter(self, node, key):
        node_type = get_node_type(node)
        if node_type == NODE_TYPE_BLANK:
            return None
        elif node_type == NODE_TYPE_LEAF:
            descend_key = extract_key(node)
            if descend_key > key:
                return descend_key
            return None
        elif node_type == NODE_TYPE_BRANCH:
            scan_range = range(16)
            if len(key):
                sub_node = self.trie.get_node(node[key[0]])
                nibbles = self._iter(sub_node, key[1:])
                if nibbles is not None:
                    return (key[0],) + nibbles
                scan_range = range(key[0] + 1, 16)

            for i in scan_range:
                sub_node = self.trie.get_node(node[i])
                nibbles = self._get_next(sub_node)
                if nibbles is not None:
                    return (i,) + nibbles
            return None
        elif node_type == NODE_TYPE_EXTENSION:
            descend_key = extract_key(node)
            sub_node = self.trie.get_node(node[1])
            sub_key = key[len(descend_key):]
            if key_starts_with(key, descend_key):
                nibbles = self._iter(sub_node, sub_key)
                if nibbles is not None:
                    return descend_key + nibbles
            return None
Ejemplo n.º 2
0
    def _get_proof(self, node, trie_key, proven_len=0, last_proof=tuple()):
        updated_proof = last_proof + (node, )
        unproven_key = trie_key[proven_len:]

        node_type = get_node_type(node)
        if node_type == NODE_TYPE_BLANK:
            return last_proof
        elif node_type == NODE_TYPE_LEAF:
            return updated_proof
        elif node_type == NODE_TYPE_EXTENSION:
            current_key = extract_key(node)
            if key_starts_with(unproven_key, current_key):
                next_node = self.get_node(node[1])
                new_proven_len = proven_len + len(current_key)
                return self._get_proof(next_node, trie_key, new_proven_len,
                                       updated_proof)
            else:
                return updated_proof
        elif node_type == NODE_TYPE_BRANCH:
            if not unproven_key:
                return updated_proof
            next_node = self.get_node(node[unproven_key[0]])
            new_proven_len = proven_len + 1
            return self._get_proof(next_node, trie_key, new_proven_len,
                                   updated_proof)
        else:
            raise Exception("Invariant: This shouldn't ever happen")
Ejemplo n.º 3
0
    def _set_kv_node(self, node, trie_key, value):
        current_key = extract_key(node)
        common_prefix, current_key_remainder, trie_key_remainder = consume_common_prefix(
            current_key,
            trie_key,
        )
        is_extension = is_extension_node(node)

        if not current_key_remainder and not trie_key_remainder:
            if is_leaf_node(node):
                return [node[0], value]
            else:
                sub_node = self._get_node(node[1])
                # TODO: this needs to cleanup old storage.
                new_node = self._set(sub_node, trie_key_remainder, value)
        elif not current_key_remainder:
            if is_extension:
                sub_node = self._get_node(node[1])
                # TODO: this needs to cleanup old storage.
                new_node = self._set(sub_node, trie_key_remainder, value)
            else:
                subnode_position = trie_key_remainder[0]
                subnode_key = compute_leaf_key(trie_key_remainder[1:])
                sub_node = [subnode_key, value]

                new_node = [BLANK_NODE] * 16 + [node[1]]
                new_node[subnode_position] = self._persist_node(sub_node)
        else:
            new_node = [BLANK_NODE] * 17

            if len(current_key_remainder) == 1 and is_extension:
                new_node[current_key_remainder[0]] = node[1]
            else:
                if is_extension:
                    compute_key_fn = compute_extension_key
                else:
                    compute_key_fn = compute_leaf_key

                new_node[current_key_remainder[0]] = self._persist_node([
                    compute_key_fn(current_key_remainder[1:]),
                    node[1],
                ])

            if trie_key_remainder:
                new_node[trie_key_remainder[0]] = self._persist_node([
                    compute_leaf_key(trie_key_remainder[1:]),
                    value,
                ])
            else:
                new_node[-1] = value

        if common_prefix:
            new_node_key = self._persist_node(new_node)
            return [compute_extension_key(common_prefix), new_node_key]
        else:
            return new_node
Ejemplo n.º 4
0
def traverse_node(db, path, nodehash, start: int):
    node_rlp, node = get_node(db, nodehash)
    node_type = get_node_type(node)
    if node_type == NODE_TYPE_BRANCH:
        yield Node('branch', node_rlp, path)
        yield from traverse_branch(db, path, node, start)
    elif node_type == NODE_TYPE_LEAF:
        rest = extract_key(node)
        # TODO: also traverse the state root?
        yield Node('leaf', node_rlp, path + rest)
    elif node_type == NODE_TYPE_EXTENSION:
        # TODO: decide whether to yield this node, does it still match {start}
        # TODO: test that we're building this path correctly
        rest = extract_key(node)
        full_path = path + rest
        yield Node('extension', node_rlp, full_path)
        yield from traverse_node(db, full_path, node[1], start[1:])
    else:
        raise Exception(f"don't know how to handle type {node_type}")
Ejemplo n.º 5
0
    def _traverse_from(self, node: RawHexaryNode,
                       trie_key) -> Tuple[RawHexaryNode, Nibbles]:
        """
        Traverse down the trie from the given node, using the trie_key to navigate.

        At each node, consume a prefix from the key, and navigate to its child. Repeat with that
        child node and so on, until:
        - there is no key remaining, or
        - the child node is a blank node, or
        - the child node is a leaf node

        :return: (the deepest child node, the unconsumed suffix of the key)
        :raises MissingTraversalNode: if a node body is missing from the database
        """
        remaining_key = trie_key
        while remaining_key:
            node_type = get_node_type(node)

            if node_type == NODE_TYPE_BLANK:
                return BLANK_NODE, (
                )  # type: ignore # mypy thinks BLANK_NODE != b''
            elif node_type == NODE_TYPE_LEAF:
                leaf_key = extract_key(node)
                if key_starts_with(leaf_key, remaining_key):
                    return node, remaining_key
                else:
                    # The trie key and leaf node key branch away from each other, so there
                    # is no node at the specified key.
                    return BLANK_NODE, (
                    )  # type: ignore # mypy thinks BLANK_NODE != b''
            elif node_type == NODE_TYPE_EXTENSION:
                try:
                    next_node_pointer, remaining_key = self._traverse_extension(
                        node, remaining_key)
                except _PartialTraversal:
                    # could only descend part-way into an extension node
                    return node, remaining_key
            elif node_type == NODE_TYPE_BRANCH:
                next_node_pointer = node[remaining_key[0]]
                remaining_key = remaining_key[1:]
            else:
                raise Exception("Invariant: This shouldn't ever happen")

            try:
                node = self.get_node(next_node_pointer)
            except KeyError as exc:
                used_key = trie_key[:len(trie_key) - len(remaining_key)]

                raise MissingTraversalNode(exc.args[0], used_key)

        # navigated down the full key
        return node, Nibbles(())
Ejemplo n.º 6
0
 def _get_next(self, node):
     node_type = get_node_type(node)
     if node_type == NODE_TYPE_BLANK:
         return None
     elif node_type == NODE_TYPE_LEAF:
         curr_key = extract_key(node)
         return curr_key
     elif node_type == NODE_TYPE_EXTENSION:
         curr_key = extract_key(node)
         sub_node = self.trie.get_node(node[1])
         return curr_key + self._get_next(sub_node)
     elif node_type == NODE_TYPE_BRANCH:
         if node[16]:
             return (16,)
         for i in range(16):
             sub_node = self.trie.get_node(node[i])
             nibbles = self._get_next(sub_node)
             if nibbles is not None:
                 return (i,) + nibbles
         raise Exception("Invariant: this means we have an empty branch node")
     else:
         raise Exception("Invariant: unknown node type {0}".format(node))
Ejemplo n.º 7
0
    def _get_kv_node(self, node, trie_key):
        current_key = extract_key(node)
        node_type = get_node_type(node)

        if node_type == NODE_TYPE_LEAF:
            if trie_key == current_key:
                return node[1]
            else:
                return BLANK_NODE
        elif node_type == NODE_TYPE_EXTENSION:
            if key_starts_with(trie_key, current_key):
                sub_node = self.get_node(node[1])
                return self._get(sub_node, trie_key[len(current_key):])
            else:
                return BLANK_NODE
        else:
            raise Exception("Invariant: unreachable code path")
Ejemplo n.º 8
0
    def _traverse_extension(self, node, trie_key):
        current_key = extract_key(node)

        common_prefix, current_key_remainder, trie_key_remainder = consume_common_prefix(
            current_key,
            trie_key,
        )

        if len(current_key_remainder) == 0:
            # The full extension node's key was consumed
            return node[1], trie_key_remainder
        elif len(trie_key_remainder) == 0:
            # The trie key was consumed before reaching the end of the extension node's key
            raise _PartialTraversal
        else:
            # The trie key and extension node key branch away from each other, so there
            # is no node at the specified key.
            return BLANK_NODE, ()
Ejemplo n.º 9
0
    def _delete_kv_node(self, node, trie_key):
        current_key = extract_key(node)

        if not key_starts_with(trie_key, current_key):
            # key not present?....
            return node

        node_type = get_node_type(node)

        if node_type == NODE_TYPE_LEAF:
            if trie_key == current_key:
                return BLANK_NODE
            else:
                return node

        sub_node_key = trie_key[len(current_key):]
        sub_node = self.get_node(node[1])

        new_sub_node = self._delete(sub_node, sub_node_key)
        encoded_new_sub_node = self._persist_node(new_sub_node)

        if encoded_new_sub_node == node[1]:
            return node

        if new_sub_node == BLANK_NODE:
            return BLANK_NODE

        new_sub_node_type = get_node_type(new_sub_node)
        if new_sub_node_type in {NODE_TYPE_LEAF, NODE_TYPE_EXTENSION}:
            self._prune_node(new_sub_node)

            new_key = current_key + decode_nibbles(new_sub_node[0])
            return [encode_nibbles(new_key), new_sub_node[1]]

        if new_sub_node_type == NODE_TYPE_BRANCH:
            return [encode_nibbles(current_key), encoded_new_sub_node]

        raise Exception("Invariant, this code path should not be reachable")
Ejemplo n.º 10
0
    def _get(self, root_hash, trie_key):
        node, remaining_key = self._traverse(root_hash, trie_key)

        node_type = get_node_type(node)

        if node_type == NODE_TYPE_BLANK:
            return BLANK_NODE
        elif node_type == NODE_TYPE_LEAF:
            if remaining_key == extract_key(node):
                return node[1]
            else:
                # Any remaining key that isn't an exact match for the leaf node must
                #   be pointing to a value that doesn't exist.
                return BLANK_NODE
        elif node_type == NODE_TYPE_EXTENSION:
            if len(remaining_key) > 0:
                # Any remaining key should have traversed down into the extension's child.
                #   (or returned a blank node if the key didn't match the extension)
                raise ValidationError(
                    "Traverse should never return an extension node with remaining key, "
                    f"but returned node {node!r} with remaining key {remaining_key}."
                )
            else:
                return BLANK_NODE
        elif node_type == NODE_TYPE_BRANCH:
            if len(remaining_key) > 0:
                # Any remaining key should have traversed down into the branch's child, even
                #   if the branch had an empty child, which would then return a BLANK_NODE.
                raise ValidationError(
                    "Traverse should never return a non-empty branch node with remaining key, "
                    f"but returned node {node!r} with remaining key {remaining_key}."
                )
            else:
                return node[-1]
        else:
            raise Exception("Invariant: This shouldn't ever happen")