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
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")
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
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}")
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(())
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))
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")
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, ()
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")
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")