Ejemplo n.º 1
0
    def batch_update(self, subtree, lca):
        """ Step 3/3 of batch insertion.

            Modifies the MHT to reflect the changes made during the previous
            two steps.

            Arguments:
            subtree - the MHT representing the tree to be placed at the least
                      common ancestor of the insertion range.
            lca - the root of the subtree of the original MHT being modified
        """

        if lca.parent:
            # LCA isn't the root, so make sure the new subtree has the
            # right parent
            subtree.root.parent = lca.parent

            # Set the LCA's parent child to the new subtree instead of
            # the LCA
            if lca.parent.left is lca:
                lca.parent.left = subtree.root
            elif lca.parent.right is lca:
                lca.parent.right = subtree.root
        else:
            # if the lca has no parent, it was the root of the tree
            self.root = subtree.root

        MHTNode.update_parents(subtree.root.parent)

        self.elems = dict(self.elems.items() + subtree.elems.items())
        self.sorted_elems = self.root.subtree_elems()
Ejemplo n.º 2
0
    def test_node(self):
        lchild = MHTNode.leaf(5)
        rchild = MHTNode.leaf(6)

        for i in range(0, self.num_iters):
            elem = random.randint(0, 1000000)
            hval = sha256(bytes(elem)).digest()

            n = MHTNode.node(hval, lchild, rchild)

            self.assertEqual(n.hval, hval,
                'Wrong hval written to node')
            self.assertEqual(n.left, lchild, 'Wrong left child in node')
            self.assertEqual(n.right, rchild, 'Wrong right child in node')
            self.assertEqual(n.elem, None, 'Nodes should contain no element')
Ejemplo n.º 3
0
    def _gestalt_batch_insert(self, left, right, new_elems):
        """ Run all three phases of batch update in a row. Note that this is
            mostly for testing/comparison purposes: the three phases are
            separated for a reason! In particular, this function
            is NOT safe for concurrent access.

            Arguments:
            left, right - the left and right bounds of the range to be inserted
                          into
            new_elems - the list of new elements to be inserted
        """

        lefti = bisect.bisect_left(self.sorted_elems, left)
        righti = bisect.bisect_right(self.sorted_elems, right)

        ## can't currently handle the case where there are no elements in the
        ## given range
        assert lefti < righti

        lca = MHTNode.least_common_ancestor(
            self.elems[self.sorted_elems[lefti]],
            self.elems[self.sorted_elems[righti-1]])

        range_elems = lca.subtree_elems()
        
        for elem in new_elems:
            MHT.batch_list_insert(elem, range_elems)

        subtree = MHT.batch_node_insert(range_elems)

        self.batch_update(subtree, lca)
Ejemplo n.º 4
0
    def test_lca(self):
        elems = [random.randint(0, 100000000) for i in range(0, self.size)]
        elems.sort()
        mht = MHT.new(elems)

        for i in range(0, self.num_iters):
            leaf1 = mht.elems[random.choice(mht.sorted_elems)]
            leaf2 = mht.elems[random.choice(mht.sorted_elems)]

            while leaf2 is leaf1:
                leaf2 = mht.elems[random.choice(mht.sorted_elems)]

            lca = MHTNode.least_common_ancestor(leaf1, leaf2)

            current = leaf1
            parents1 = []

            while current is not lca:
                self.assertTrue(current)
                parents1.append(current)
                current = current.parent
            
            current = leaf2

            while current is not lca:
                self.assertTrue(current)
                self.assertTrue(current not in parents1)
                current = current.parent
Ejemplo n.º 5
0
    def test_leaf(self):
        for i in range(0, self.num_iters):
            elem = random.randint(0, 1000000)
            l = MHTNode.leaf(elem)

            self.assertEqual(l.elem, elem,
                'leaf element is not what was expected')
            self.assertEqual(l.parent, None, 'new nodes should have no parent')
            self.assertEqual(l.left, None, 'leaves have no left child')
            self.assertEqual(l.right, None, 'leaves have no right child')

            hval = sha256(bytes(elem)).digest()
            l2 = MHTNode.leaf(elem, hval=hval)

            self.assertEqual(l.hval, hval,
                'Wrong hval being written to leaf')
Ejemplo n.º 6
0
    def _batch_single_insert(self, elem, i, new_elems):
        """ Insertion of a single element into a Merkle hash tree without
            a corresponding VO, as part of a batch insert.
            NB: this function does *not* guarantee anything about the
            balancedness of the resulting tree.

            Arguments:
            self - the MHT to be modified
            elem - the element to be inserted
            i - the index at which to insert elem
            new_elems - the new list of elements

            Returns:
            i - the index at which elem was inserted into the list

            Assumes:
            - There are at least two 'boundary' elements in the MHT
            - One boundary element is less than everything else that will
              be inserted (including elem), and the other is greater than
              everything else (including elem).

            Optimization Opportunities:
            - We might be able to get away with waiting to update the tree
              until the end of the batch insert, then doing it all at once;
              it seems like there's a lot of unnecessary work being done here.
        """

        if i-1 < 0 or i+1 > len(self.sorted_elems):
            raise MHTInsertionException(
                'Element to be inserted %d does not occur within range of MHT (from %d to %d)\nGiven index %d in a %d-element list' %(elem, self.sorted_elems[0], self.sorted_elems[-1], i, len(self.sorted_elems)))

        new_leaf = MHTNode.leaf(elem)

        self.sorted_elems = new_elems
        self.elems[elem] = new_leaf

        # Sibling choice needs to be deterministic, so we choose the
        # left sibling
        sibling_leaf = self.elems[self.sorted_elems[i-1]]

        MHTNode.insert(new_leaf, sibling_leaf)
Ejemplo n.º 7
0
    def test_find_boundary(self):
        elems = [0, 2, 4, 6, 8, 10, 12, 14, 16]

        lower, li, ri, upper = MHTNode.find_boundary(elems, 5, 11)

        self.assertEqual(lower, 4, 'Invalid lower boundary returned')
        self.assertEqual(elems[li], 6,
            'Invalid first element of range returned')
        self.assertEqual(upper, 12,
            'Invalid upper boundary returned: %d' %upper)
        self.assertEqual(elems[ri], 10,
            'Invalid first element of range returned')

        lower, li, ri, upper = MHTNode.find_boundary(elems, 6, 10)

        self.assertEqual(lower, 4, 'Invalid lower boundary returned')
        self.assertEqual(elems[li], 6,
            'Invalid first element of range returned')
        self.assertEqual(upper, 12, 'Invalid upper boundary returned')
        self.assertEqual(elems[ri], 10,
            'Invalid first element of range returned')
Ejemplo n.º 8
0
    def new(elems):
        """ Assumes elems is sorted, nonempty, and contains no
            repeated elements.
        """
        sorted_elems = elems

        work = [MHTNode.leaf(elem) for elem in elems]
        elem_dict = dict(zip(elems, work))

        next_level = []

        # I am going out of my way not to implement this recursively,
        # because python
        while work:
            for i in range(0, len(work), 2):

                if i+1 >= len(work):
                    next_level.append(work[i])
                else:
                    left = work[i]
                    right = work[i+1]

                    new_hash = MHTUtils.merge_hashes(left.hval, right.hval)
                    new_node = MHTNode.node(new_hash, left, right)

                    left.parent = new_node
                    right.parent = new_node

                    next_level.append(new_node)

            if len(next_level) == 1:
                return MHT(elems=elem_dict, sorted_elems=elems,
                           root=next_level[0])

            work = next_level
            next_level = []
Ejemplo n.º 9
0
    def range_query(self, lower, upper):
        """ Return a verification object for all elements between lower
            and upper (inclusive) in the tree.

            Arguments:
            lower - lower bound for the range query
            upper - upper bound for the range query

            Returns:
            Verification object for the range (never None)

            Assume the MHT contains at least two elements and that the
            least & greatest elements stored are known to the client.
        """

        lbound, li, ri, rbound = MHTNode.find_boundary(self.sorted_elems,
                                                       lower,
                                                       upper)
        
        vo_elems = self.sorted_elems[li:ri+1]

        return VO.new(lbound, vo_elems, rbound, self.root)