def test_hexary_trie_missing_traversal_node_with_traverse_from(): db = {} trie = HexaryTrie(db, prune=True) key1 = to_bytes(0x0123) trie.set( key1, b'use a value long enough that it must be hashed according to trie spec' ) key2 = to_bytes(0x1234) trie.set(key2, b'val2') # delete first child of the root root_node = trie.root_node first_child_hash = root_node.raw[0] del db[first_child_hash] # Get exception with relevant info about lookup nibbles with pytest.raises(MissingTraversalNode) as exc_info: trie.traverse_from(root_node, (0, 1, 2, 3)) exception = exc_info.value assert exception.nibbles_traversed == (0, ) assert encode_hex(first_child_hash) in str(exception) # Other keys are still traversable node = trie.traverse((1, )) assert node.value == b'val2' assert node.sub_segments == ()
def traverse_via_cache(parent_prefix, parent_node, child_extension): if parent_node is None: # Can't traverse_from to the root node node = traversal_trie.traverse(()) elif not len(child_extension): assert False, "For all but the root node, the child extension must not be empty" else: logging_db = KeyAccessLogger(db) single_access_trie = HexaryTrie(logging_db) node = single_access_trie.traverse_from(parent_node, child_extension) # Traversing from parent to child should touch at most one node (the child) # It might touch 0 nodes, if the child was embedded inside the parent assert len(logging_db.read_keys) in {0, 1} # Validate that traversal from the root gives you the same result: slow_node = traversal_trie.traverse(parent_prefix + child_extension) assert node == slow_node if node.value: found_values.add(node.value) for new_child in node.sub_segments: # traverse into children traverse_via_cache(parent_prefix + child_extension, node, new_child)
def test_traverse_from_partial_path(trie_items, traverse_key, path_to_node, sub_segments, node_val): """ What happens when you try to traverse_from() into an extension or leaf node """ db = {} trie = HexaryTrie(db) for key, val in trie_items: trie[key] = val root = trie.root_node with pytest.raises(TraversedPartialPath) as excinfo: trie.traverse_from(root, traverse_key) exc = excinfo.value assert exc.nibbles_traversed == path_to_node assert exc.node.sub_segments == sub_segments assert exc.node.value == node_val
def test_traverse_non_matching_leaf(): trie = HexaryTrie({}) EMPTY_NODE = trie.root_node trie[b'\xFFleaf-at-root'] = b'some-value' final_root = trie.root_node # Traversing partway into the leaf raises the TraversedPartialPath exception with pytest.raises(TraversedPartialPath): trie.traverse((0xf, )) with pytest.raises(TraversedPartialPath): trie.traverse_from(final_root, (0xf, )) # But traversing to any *non*-matching nibble should return a blank node, because no # children reside underneath that nibble. Returning the leaf with a mismatched nibble # would be a bug. for nibble in range(0xf): # Note that we do not want to look at the 0xf nibble, because that's the one that # should raise the exception above assert trie.traverse((nibble, )) == EMPTY_NODE assert trie.traverse_from(final_root, (nibble, )) == EMPTY_NODE