Esempio n. 1
0
 def _set_branch_node(
         self,
         keypath,
         node_type,
         left_child,
         right_child,
         value,
         if_delete_subtrie=False):
     # Which child node to update? Depends on first bit in keypath
     if keypath[:1] == BYTE_0:
         new_left_child = self._set(left_child, keypath[1:], value, if_delete_subtrie)
         new_right_child = right_child
     else:
         new_right_child = self._set(right_child, keypath[1:], value, if_delete_subtrie)
         new_left_child = left_child
     # Compress branch node into kv node
     if new_left_child == BLANK_HASH or new_right_child == BLANK_HASH:
         subnodetype, sub_left_child, sub_right_child = parse_node(
             self.db[
                 new_left_child
                 if new_left_child != BLANK_HASH
                 else new_right_child]
         )
         first_bit = BYTE_1 if new_right_child != BLANK_HASH else BYTE_0
         # Compress (k1, (k2, NODE)) -> (k1 + k2, NODE)
         if subnodetype == KV_TYPE:
             return self._hash_and_save(
                 encode_kv_node(first_bit + sub_left_child, sub_right_child)
             )
         # kv node pointing to a branch node
         elif subnodetype in (BRANCH_TYPE, LEAF_TYPE):
             return self._hash_and_save(
                 encode_kv_node(
                     first_bit,
                     new_left_child
                     if new_left_child != BLANK_HASH
                     else new_right_child
                 )
             )
     else:
         return self._hash_and_save(encode_branch_node(new_left_child, new_right_child))
Esempio n. 2
0
    def _set(self, node_hash, keypath, value, if_delete_subtrie=False):
        """
        If if_delete_subtrie is set to True, what it will do is that it take in a keypath
        and traverse til the end of keypath, then delete the whole subtrie of that node.

        Note: keypath should be in binary array format, i.e., encoded by encode_to_bin()
        """
        # Empty trie
        if node_hash == BLANK_HASH:
            if value:
                return self._hash_and_save(
                    encode_kv_node(
                        keypath, self._hash_and_save(encode_leaf_node(value))))
            else:
                return BLANK_HASH
        nodetype, left_child, right_child = parse_node(self.db[node_hash])
        # Node is a leaf node
        if nodetype == LEAF_TYPE:
            # Keypath must match, there should be no remaining keypath
            if keypath:
                raise NodeOverrideError(
                    "Fail to set the value because the prefix of it's key"
                    " is the same as existing key")
            if if_delete_subtrie:
                return BLANK_HASH
            return self._hash_and_save(
                encode_leaf_node(value)) if value else BLANK_HASH
        # node is a key-value node
        elif nodetype == KV_TYPE:
            # Keypath too short
            if not keypath:
                if if_delete_subtrie:
                    return BLANK_HASH
                else:
                    raise NodeOverrideError(
                        "Fail to set the value because it's key"
                        " is the prefix of other existing key")
            return self._set_kv_node(keypath, node_hash, nodetype, left_child,
                                     right_child, value, if_delete_subtrie)
        # node is a branch node
        elif nodetype == BRANCH_TYPE:
            # Keypath too short
            if not keypath:
                if if_delete_subtrie:
                    return BLANK_HASH
                else:
                    raise NodeOverrideError(
                        "Fail to set the value because it's key"
                        " is the prefix of other existing key")
            return self._set_branch_node(keypath, nodetype, left_child,
                                         right_child, value, if_delete_subtrie)
        raise Exception("Invariant: This shouldn't ever happen")
Esempio n. 3
0
 def _set_kv_node(self,
                  keypath,
                  node_hash,
                  node_type,
                  left_child,
                  right_child,
                  value,
                  if_delete_subtrie=False):
     # Keypath prefixes match
     if if_delete_subtrie:
         if len(keypath) < len(
                 left_child) and keypath == left_child[:len(keypath)]:
             return BLANK_HASH
     if keypath[:len(left_child)] == left_child:
         # Recurse into child
         subnode_hash = self._set(
             right_child,
             keypath[len(left_child):],
             value,
             if_delete_subtrie,
         )
         # If child is empty
         if subnode_hash == BLANK_HASH:
             return BLANK_HASH
         subnodetype, sub_left_child, sub_right_child = parse_node(
             self.db[subnode_hash])
         # If the child is a key-value node, compress together the keypaths
         # into one node
         if subnodetype == KV_TYPE:
             return self._hash_and_save(
                 encode_kv_node(left_child + sub_left_child,
                                sub_right_child))
         else:
             return self._hash_and_save(
                 encode_kv_node(left_child, subnode_hash))
     # Keypath prefixes don't match. Here we will be converting a key-value node
     # of the form (k, CHILD) into a structure of one of the following forms:
     # 1.    (k[:-1], (NEWCHILD, CHILD))
     # 2.    (k[:-1], ((k2, NEWCHILD), CHILD))
     # 3.    (k1, ((k2, CHILD), NEWCHILD))
     # 4.    (k1, ((k2, CHILD), (k2', NEWCHILD))
     # 5.    (CHILD, NEWCHILD)
     # 6.    ((k[1:], CHILD), (k', NEWCHILD))
     # 7.    ((k[1:], CHILD), NEWCHILD)
     # 8.    (CHILD, (k[1:], NEWCHILD))
     else:
         common_prefix_len = get_common_prefix_length(
             left_child, keypath[:len(left_child)])
         # New key-value pair can not contain empty value
         # Or one can not delete non-exist subtrie
         if not value or if_delete_subtrie:
             return node_hash
         # valnode: the child node that has the new value we are adding
         # Case 1: keypath prefixes almost match, so we are in case (1), (2), (5), (6)
         if len(keypath) == common_prefix_len + 1:
             valnode = self._hash_and_save(encode_leaf_node(value))
         # Case 2: keypath prefixes mismatch in the middle, so we need to break
         # the keypath in half. We are in case (3), (4), (7), (8)
         else:
             valnode = self._hash_and_save(
                 encode_kv_node(
                     keypath[common_prefix_len + 1:],
                     self._hash_and_save(encode_leaf_node(value)),
                 ))
         # oldnode: the child node the has the old child value
         # Case 1: (1), (3), (5), (6)
         if len(left_child) == common_prefix_len + 1:
             oldnode = right_child
         # (2), (4), (6), (8)
         else:
             oldnode = self._hash_and_save(
                 encode_kv_node(left_child[common_prefix_len + 1:],
                                right_child))
         # Create the new branch node (because the key paths diverge, there has to
         # be some "first bit" at which they diverge, so there must be a branch
         # node somewhere)
         if keypath[common_prefix_len:common_prefix_len + 1] == BYTE_1:
             newsub = self._hash_and_save(
                 encode_branch_node(oldnode, valnode))
         else:
             newsub = self._hash_and_save(
                 encode_branch_node(valnode, oldnode))
         # Case 1: keypath prefixes match in the first bit, so we still need
         # a kv node at the top
         # (1) (2) (3) (4)
         if common_prefix_len:
             return self._hash_and_save(
                 encode_kv_node(left_child[:common_prefix_len], newsub))
         # Case 2: keypath prefixes diverge in the first bit, so we replace the
         # kv node with a branch node
         # (5) (6) (7) (8)
         else:
             return newsub
Esempio n. 4
0
def test_encode_binary_trie_kv_node(keypath, node, expected_output):
    if expected_output:
        assert expected_output == encode_kv_node(keypath, node)
    else:
        with pytest.raises(ValidationError):
            encode_kv_node(keypath, node)