download: verify that bad blocks or hashes are caught by the download process

This commit is contained in:
Brian Warner 2007-04-16 16:30:21 -07:00
parent 5b3e923093
commit 42179e5ae2
2 changed files with 90 additions and 7 deletions

View File

@ -102,7 +102,7 @@ class ValidatedBucket:
bh[0] = self._share_hash bh[0] = self._share_hash
self.block_hash_tree.set_hashes(bh, {blocknum: blockhash}) self.block_hash_tree.set_hashes(bh, {blocknum: blockhash})
except (hashutil.BadHashError, hashutil.NotEnoughHashesError): except (hashtree.BadHashError, hashtree.NotEnoughHashesError):
# log.WEIRD: indicates undetected disk/network error, or more # log.WEIRD: indicates undetected disk/network error, or more
# likely a programming error # likely a programming error
log.msg("hash failure in shnum=%d on %s" % (self.sharenum, log.msg("hash failure in shnum=%d on %s" % (self.sharenum,

View File

@ -2,6 +2,7 @@
from twisted.trial import unittest from twisted.trial import unittest
from twisted.internet import defer from twisted.internet import defer
from twisted.python.failure import Failure
from foolscap import eventual from foolscap import eventual
from allmydata import encode, download from allmydata import encode, download
from allmydata.uri import pack_uri from allmydata.uri import pack_uri
@ -40,7 +41,9 @@ class FakeStorageServer:
return (set(), dict([(shnum, FakeBucketWriter(),) for shnum in sharenums]),) return (set(), dict([(shnum, FakeBucketWriter(),) for shnum in sharenums]),)
class FakeBucketWriter: class FakeBucketWriter:
def __init__(self): # these are used for both reading and writing
def __init__(self, mode="good"):
self.mode = mode
self.blocks = {} self.blocks = {}
self.block_hashes = None self.block_hashes = None
self.share_hashes = None self.share_hashes = None
@ -71,14 +74,26 @@ class FakeBucketWriter:
assert not self.closed assert not self.closed
self.closed = True self.closed = True
def flip_bit(self, good):
return good[:-1] + chr(ord(good[-1]) ^ 0x01)
def get_block(self, blocknum): def get_block(self, blocknum):
assert isinstance(blocknum, int) assert isinstance(blocknum, int)
if self.mode == "bad block":
return self.flip_bit(self.blocks[blocknum])
return self.blocks[blocknum] return self.blocks[blocknum]
def get_block_hashes(self): def get_block_hashes(self):
if self.mode == "bad blockhash":
hashes = self.block_hashes[:]
hashes[1] = self.flip_bit(hashes[1])
return hashes
return self.block_hashes return self.block_hashes
def get_share_hashes(self): def get_share_hashes(self):
if self.mode == "bad sharehash":
hashes = self.share_hashes[:]
hashes[1] = (hashes[1][0], self.flip_bit(hashes[1][1]))
return hashes
return self.share_hashes return self.share_hashes
@ -122,7 +137,7 @@ class Encode(unittest.TestCase):
return d return d
class Roundtrip(unittest.TestCase): class Roundtrip(unittest.TestCase):
def send_and_recover(self, NUM_SHARES, NUM_PEERS, NUM_SEGMENTS=4): def send_and_recover(self, NUM_SHARES, NUM_SEGMENTS=4, bucket_modes={}):
e = encode.Encoder() e = encode.Encoder()
data = "happy happy joy joy" * 4 data = "happy happy joy joy" * 4
e.setup(StringIO(data)) e.setup(StringIO(data))
@ -135,10 +150,9 @@ class Roundtrip(unittest.TestCase):
shareholders = {} shareholders = {}
all_shareholders = [] all_shareholders = []
all_peers = [] all_peers = []
for i in range(NUM_PEERS):
all_peers.append(FakeBucketWriter())
for shnum in range(NUM_SHARES): for shnum in range(NUM_SHARES):
peer = all_peers[shnum % NUM_PEERS] mode = bucket_modes.get(shnum, "good")
peer = FakeBucketWriter(mode)
shareholders[shnum] = peer shareholders[shnum] = peer
all_shareholders.append(peer) all_shareholders.append(peer)
e.set_shareholders(shareholders) e.set_shareholders(shareholders)
@ -171,5 +185,74 @@ class Roundtrip(unittest.TestCase):
return d return d
def test_one_share_per_peer(self): def test_one_share_per_peer(self):
return self.send_and_recover(100, 100) return self.send_and_recover(100)
def test_bad_blocks(self):
# the first 74 servers have bad blocks, which will be caught by the
# blockhashes
modemap = dict([(i, "bad block")
for i in range(74)]
+ [(i, "good")
for i in range(74, 100)])
return self.send_and_recover(100, bucket_modes=modemap)
def test_bad_blocks_failure(self):
# the first 76 servers have bad blocks, which will be caught by the
# blockhashes, and the download will fail
modemap = dict([(i, "bad block")
for i in range(76)]
+ [(i, "good")
for i in range(76, 100)])
d = self.send_and_recover(100, bucket_modes=modemap)
def _done(res):
self.failUnless(isinstance(res, Failure))
self.failUnless(res.check(download.NotEnoughPeersError))
d.addBoth(_done)
return d
def test_bad_blockhashes(self):
# the first 74 servers have bad block hashes, so the blockhash tree
# will not validate
modemap = dict([(i, "bad blockhash")
for i in range(74)]
+ [(i, "good")
for i in range(74, 100)])
return self.send_and_recover(100, bucket_modes=modemap)
def test_bad_blockhashes_failure(self):
# the first 76 servers have bad block hashes, so the blockhash tree
# will not validate, and the download will fail
modemap = dict([(i, "bad blockhash")
for i in range(76)]
+ [(i, "good")
for i in range(76, 100)])
d = self.send_and_recover(100, bucket_modes=modemap)
def _done(res):
self.failUnless(isinstance(res, Failure))
self.failUnless(res.check(download.NotEnoughPeersError))
d.addBoth(_done)
return d
def test_bad_sharehashes(self):
# the first 74 servers have bad block hashes, so the sharehash tree
# will not validate
modemap = dict([(i, "bad sharehash")
for i in range(74)]
+ [(i, "good")
for i in range(74, 100)])
return self.send_and_recover(100, bucket_modes=modemap)
def test_bad_sharehashes_failure(self):
# the first 76 servers have bad block hashes, so the sharehash tree
# will not validate, and the download will fail
modemap = dict([(i, "bad sharehash")
for i in range(76)]
+ [(i, "good")
for i in range(76, 100)])
d = self.send_and_recover(100, bucket_modes=modemap)
def _done(res):
self.failUnless(isinstance(res, Failure))
self.failUnless(res.check(download.NotEnoughPeersError))
d.addBoth(_done)
return d