2007-04-06 16:09:57 +00:00
|
|
|
# -*- test-case-name: allmydata.test.test_hashtree -*-
|
|
|
|
|
|
|
|
from twisted.trial import unittest
|
|
|
|
|
|
|
|
from allmydata.util.hashutil import tagged_hash
|
2007-04-12 20:13:25 +00:00
|
|
|
from allmydata import hashtree
|
2007-04-06 16:09:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
def make_tree(numleaves):
|
|
|
|
leaves = ["%d" % i for i in range(numleaves)]
|
|
|
|
leaf_hashes = [tagged_hash("tag", leaf) for leaf in leaves]
|
2007-04-12 20:13:25 +00:00
|
|
|
ht = hashtree.HashTree(leaf_hashes)
|
2007-04-06 16:09:57 +00:00
|
|
|
return ht
|
|
|
|
|
|
|
|
class Complete(unittest.TestCase):
|
|
|
|
def testCreate(self):
|
|
|
|
# try out various sizes
|
|
|
|
ht = make_tree(6)
|
|
|
|
ht = make_tree(8)
|
|
|
|
ht = make_tree(9)
|
|
|
|
root = ht[0]
|
|
|
|
self.failUnlessEqual(len(root), 32)
|
|
|
|
|
2007-04-12 20:07:40 +00:00
|
|
|
def testDump(self):
|
|
|
|
ht = make_tree(6)
|
|
|
|
expected = [(0,0),
|
|
|
|
(1,1), (3,2), (7,3), (8,3), (4,2), (9,3), (10,3),
|
|
|
|
(2,1), (5,2), (11,3), (12,3), (6,2), (13,3), (14,3),
|
|
|
|
]
|
|
|
|
self.failUnlessEqual(list(ht.depth_first()), expected)
|
|
|
|
d = "\n" + ht.dump()
|
|
|
|
#print d
|
|
|
|
self.failUnless("\n 0:" in d)
|
|
|
|
self.failUnless("\n 1:" in d)
|
|
|
|
self.failUnless("\n 3:" in d)
|
|
|
|
self.failUnless("\n 7:" in d)
|
|
|
|
self.failUnless("\n 8:" in d)
|
|
|
|
self.failUnless("\n 4:" in d)
|
|
|
|
|
2007-04-06 16:09:57 +00:00
|
|
|
class Incomplete(unittest.TestCase):
|
2007-04-12 20:07:40 +00:00
|
|
|
|
2007-04-06 16:09:57 +00:00
|
|
|
def testCheck(self):
|
|
|
|
# first create a complete hash tree
|
|
|
|
ht = make_tree(6)
|
|
|
|
# then create a corresponding incomplete tree
|
2007-04-12 20:13:25 +00:00
|
|
|
iht = hashtree.IncompleteHashTree(6)
|
2007-04-06 16:09:57 +00:00
|
|
|
|
|
|
|
# suppose we wanted to validate leaf[0]
|
|
|
|
# leaf[0] is the same as node[7]
|
2007-04-12 20:07:40 +00:00
|
|
|
self.failUnlessEqual(iht.needed_hashes(leaves=[0]), set([8, 4, 2, 0]))
|
|
|
|
self.failUnlessEqual(iht.needed_hashes(leaves=[1]), set([7, 4, 2, 0]))
|
|
|
|
iht.set_hashes({0: ht[0]}) # set the root
|
|
|
|
self.failUnlessEqual(iht.needed_hashes(leaves=[0]), set([8, 4, 2]))
|
|
|
|
self.failUnlessEqual(iht.needed_hashes(leaves=[1]), set([7, 4, 2]))
|
|
|
|
iht.set_hashes({5: ht[5]})
|
|
|
|
self.failUnlessEqual(iht.needed_hashes(leaves=[0]), set([8, 4, 2]))
|
|
|
|
self.failUnlessEqual(iht.needed_hashes(leaves=[1]), set([7, 4, 2]))
|
|
|
|
|
|
|
|
current_hashes = list(iht)
|
2007-04-06 16:09:57 +00:00
|
|
|
try:
|
|
|
|
# this should fail because there aren't enough hashes known
|
2007-04-12 20:07:40 +00:00
|
|
|
iht.set_hashes(leaves={0: tagged_hash("tag", "0")},
|
|
|
|
must_validate=True)
|
2007-04-12 20:13:25 +00:00
|
|
|
except hashtree.NotEnoughHashesError:
|
2007-04-06 16:09:57 +00:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.fail("didn't catch not enough hashes")
|
|
|
|
|
2007-04-12 20:07:40 +00:00
|
|
|
# and the set of hashes stored in the tree should still be the same
|
|
|
|
self.failUnlessEqual(list(iht), current_hashes)
|
|
|
|
|
2007-04-06 16:09:57 +00:00
|
|
|
# provide the missing hashes
|
2007-04-12 20:07:40 +00:00
|
|
|
iht.set_hashes({2: ht[2], 4: ht[4], 8: ht[8]})
|
|
|
|
self.failUnlessEqual(iht.needed_hashes(leaves=[0]), set())
|
2007-04-06 16:09:57 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
# this should fail because the hash is just plain wrong
|
2007-04-12 20:07:40 +00:00
|
|
|
iht.set_hashes(leaves={0: tagged_hash("bad tag", "0")})
|
2007-04-12 20:13:25 +00:00
|
|
|
except hashtree.BadHashError:
|
2007-04-06 16:09:57 +00:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.fail("didn't catch bad hash")
|
|
|
|
|
|
|
|
try:
|
|
|
|
# this should succeed
|
2007-04-12 20:07:40 +00:00
|
|
|
iht.set_hashes(leaves={0: tagged_hash("tag", "0")})
|
2007-04-12 20:13:25 +00:00
|
|
|
except hashtree.BadHashError, e:
|
2007-04-06 16:09:57 +00:00
|
|
|
self.fail("bad hash: %s" % e)
|
|
|
|
|
|
|
|
try:
|
|
|
|
# this should succeed too
|
2007-04-12 20:07:40 +00:00
|
|
|
iht.set_hashes(leaves={1: tagged_hash("tag", "1")})
|
2007-04-12 20:13:25 +00:00
|
|
|
except hashtree.BadHashError:
|
2007-04-06 16:09:57 +00:00
|
|
|
self.fail("bad hash")
|
|
|
|
|
|
|
|
# giving it a bad internal hash should also cause problems
|
2007-04-12 20:07:40 +00:00
|
|
|
iht.set_hashes({13: tagged_hash("bad tag", "x")})
|
2007-04-06 16:09:57 +00:00
|
|
|
try:
|
2007-04-12 20:07:40 +00:00
|
|
|
iht.set_hashes({14: tagged_hash("tag", "14")})
|
2007-04-12 20:13:25 +00:00
|
|
|
except hashtree.BadHashError:
|
2007-04-06 16:09:57 +00:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.fail("didn't catch bad hash")
|
|
|
|
# undo our damage
|
2007-04-12 20:07:40 +00:00
|
|
|
iht[13] = None
|
2007-04-06 16:09:57 +00:00
|
|
|
|
2007-04-12 20:07:40 +00:00
|
|
|
self.failUnlessEqual(iht.needed_hashes(leaves=[4]), set([12, 6]))
|
2007-04-06 16:09:57 +00:00
|
|
|
|
2007-04-12 20:07:40 +00:00
|
|
|
iht.set_hashes({6: ht[6], 12: ht[12]})
|
2007-04-06 16:09:57 +00:00
|
|
|
try:
|
|
|
|
# this should succeed
|
2007-04-12 20:07:40 +00:00
|
|
|
iht.set_hashes(leaves={4: tagged_hash("tag", "4")})
|
2007-04-12 20:13:25 +00:00
|
|
|
except hashtree.BadHashError, e:
|
2007-04-06 16:09:57 +00:00
|
|
|
self.fail("bad hash: %s" % e)
|
|
|
|
|