def _normalize_branch_node(self, node): """ A branch node which is left with only a single non-blank item should be turned into either a leaf or extension node. """ iter_node = iter(node) if any(iter_node) and any(iter_node): return node if node[16]: return [compute_leaf_key([]), node[16]] sub_node_idx, sub_node_hash = next( (idx, v) for idx, v in enumerate(node[:16]) if v) sub_node = self.get_node(sub_node_hash) sub_node_type = get_node_type(sub_node) self._prune_node(sub_node) if sub_node_type in {NODE_TYPE_LEAF, NODE_TYPE_EXTENSION}: new_subnode_key = encode_nibbles( tuple( itertools.chain( [sub_node_idx], decode_nibbles(sub_node[0]), ))) return [new_subnode_key, sub_node[1]] elif sub_node_type == NODE_TYPE_BRANCH: subnode_hash = self._persist_node(sub_node) return [encode_nibbles([sub_node_idx]), subnode_hash] else: raise Exception("Invariant: this code block should be unreachable")
def deserialize(cls, encoded: bytes) -> 'HexaryTrieFog': serial_prefix = b'HexaryTrieFog:' if not encoded.startswith(serial_prefix): raise ValueError(f"Cannot deserialize this into HexaryTrieFog object: {encoded!r}") else: encoded_list = encoded[len(serial_prefix):] prefix_list = ast.literal_eval(encoded_list.decode()) deserialized_prefixes = SortedSet( # decode nibbles from compressed bytes value, and validate each value in range(16) Nibbles(decode_nibbles(prefix)) for prefix in prefix_list ) return cls._new_trie_fog(deserialized_prefixes)
def get_node_type(node): if node == BLANK_NODE: return NODE_TYPE_BLANK elif len(node) == 2: key, _ = node nibbles = decode_nibbles(key) if is_nibbles_terminated(nibbles): return NODE_TYPE_LEAF else: return NODE_TYPE_EXTENSION elif len(node) == 17: return NODE_TYPE_BRANCH else: raise InvalidNode("Unable to determine node type")
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")