def test_encoded(): for case in VECTORS["encode"]: input_len = case["input_len"] input_bytes = generate_input.input_bytes(input_len) output_len = case["output_len"] expected_bao_hash = case["bao_hash"] encoded_blake2b = case["encoded_blake2b"] corruptions = case["corruptions"] # First make sure the encoded output is what it's supposed to be. encoded = bao_encode(input_bytes) assert output_len == len(encoded) assert encoded_blake2b == blake2b(encoded) # Test hashing the encoded bytes. encoded_hash = bao_hash_encoded(encoded) assert expected_bao_hash == encoded_hash # Now test decoding. output = bao_decode(expected_bao_hash, encoded) assert input_bytes == output # Make sure decoding with the wrong hash fails. wrong_hash = "0" * len(encoded_hash) assert_decode_failure(bao_decode, wrong_hash, encoded) # Make sure each of the corruption points causes decoding to fail. for c in corruptions: corrupted = bytearray(encoded) corrupted[c] ^= 1 assert_decode_failure(bao_decode, encoded_hash, corrupted)
def slices(): ret = [] for case in seeks(): size = case["input_len"] offsets = case["seek_offsets"] b = input_bytes(size) encoded = bao.bao_encode(b) slices = [] for offset in offsets: slice_bytes = io.BytesIO() slice_len = 2 * CHUNK_SIZE bao.bao_slice(io.BytesIO(encoded), slice_bytes, offset, slice_len) slice_hash = blake2b_hash(slice_bytes.getbuffer()) fields = [ ("start", offset), ("len", slice_len), ("output_len", len(slice_bytes.getbuffer())), ("output_blake2b", slice_hash), ("corruptions", slice_corruption_points(size, offset, slice_len)), ] slices.append(OrderedDict(fields)) fields = [ ("input_len", size), ("bao_hash", bao.bao_hash(io.BytesIO(b)).hex()), ("slices", slices), ] ret.append(OrderedDict(fields)) return ret
def test_hashes(): for case in VECTORS["hash"]: input_len = case["input_len"] input_bytes = generate_input.input_bytes(input_len) expected_hash = case["bao_hash"] computed_hash = bao_hash(input_bytes) assert expected_hash == computed_hash
def hashes(): ret = [] for size in SIZES: b = input_bytes(size) h = bao.bao_hash(io.BytesIO(b)) fields = [("input_len", size), ("bao_hash", h.hex())] ret.append(OrderedDict(fields)) return ret
def encoded(): ret = [] for size in SIZES: b = input_bytes(size) encoded = bao.bao_encode(b) fields = [ ("input_len", size), ("output_len", len(encoded)), ("bao_hash", bao.bao_hash(io.BytesIO(b)).hex()), ("encoded_blake2b", blake2b_hash(encoded)), ("corruptions", encode_corruption_points(size)), ] ret.append(OrderedDict(fields)) return ret
def test_slices(): for case in VECTORS["slice"]: input_len = case["input_len"] input_bytes = generate_input.input_bytes(input_len) expected_bao_hash = case["bao_hash"] slices = case["slices"] encoded, hash_ = bao_encode(input_bytes) outboard, hash_outboard = bao_encode_outboard(input_bytes) assert expected_bao_hash == hash_ assert expected_bao_hash == hash_outboard for slice_case in slices: slice_start = slice_case["start"] slice_len = slice_case["len"] output_len = slice_case["output_len"] output_blake3 = slice_case["output_blake3"] corruptions = slice_case["corruptions"] # Make sure the slice output is what it should be. slice_bytes = bao_slice(encoded, slice_start, slice_len) assert output_len == len(slice_bytes) assert output_blake3 == blake3(slice_bytes) # Make sure slicing an outboard tree is the same. outboard_slice_bytes = bao_slice_outboard(input_bytes, outboard, slice_start, slice_len) assert slice_bytes == outboard_slice_bytes # Test decoding the slice, and compare it to the input. Note that # slicing a byte array in Python allows indices past the end of the # array, and sort of silently caps them. input_slice = input_bytes[slice_start:][:slice_len] output = bao_decode_slice(slice_bytes, hash_, slice_start, slice_len) assert input_slice == output # Make sure decoding with the wrong hash fails. wrong_hash = "0" * len(hash_) assert_decode_failure(bao_decode_slice, slice_bytes, wrong_hash, slice_start, slice_len) # Make sure each of the slice corruption points causes decoding to # fail. for c in corruptions: corrupted = bytearray(slice_bytes) corrupted[c] ^= 1 assert_decode_failure(bao_decode_slice, corrupted, hash_, slice_start, slice_len)
def test_outboard(): for case in VECTORS["outboard"]: input_len = case["input_len"] input_bytes = generate_input.input_bytes(input_len) output_len = case["output_len"] expected_bao_hash = case["bao_hash"] encoded_blake2b = case["encoded_blake2b"] outboard_corruptions = case["outboard_corruptions"] input_corruptions = case["input_corruptions"] # First make sure the encoded output is what it's supposed to be. outboard = bao_encode_outboard(input_bytes) assert output_len == len(outboard) assert encoded_blake2b == blake2b(outboard) # Test `bao hash --outboard`. bao_hash_encoded = bao_hash_outboard(input_bytes, outboard) assert expected_bao_hash == bao_hash_encoded # Now test decoding. output = bao_decode_outboard(expected_bao_hash, input_bytes, outboard) assert input_bytes == output # Make sure decoding with the wrong hash fails. wrong_hash = "0" * len(expected_bao_hash) assert_decode_failure(bao_decode_outboard, wrong_hash, input_bytes, outboard) # Make sure each of the outboard corruption points causes decoding to # fail. for c in outboard_corruptions: corrupted = bytearray(outboard) corrupted[c] ^= 1 assert_decode_failure(bao_decode_outboard, expected_bao_hash, input_bytes, corrupted) # Make sure each of the input corruption points causes decoding to # fail. for c in input_corruptions: corrupted = bytearray(input_bytes) corrupted[c] ^= 1 assert_decode_failure(bao_decode_outboard, expected_bao_hash, corrupted, outboard)
def outboard(): ret = [] for size in SIZES: b = input_bytes(size) encoded = bao.bao_encode(b, outboard=True) input_corruptions = [] corruption = 0 while corruption < size: input_corruptions.append(corruption) corruption += CHUNK_SIZE fields = [ ("input_len", size), ("output_len", len(encoded)), ("bao_hash", bao.bao_hash(io.BytesIO(b)).hex()), ("encoded_blake2b", blake2b_hash(encoded)), ("outboard_corruptions", encode_corruption_points(size, outboard=True)), ("input_corruptions", input_corruptions), ] ret.append(OrderedDict(fields)) return ret
cmd = " ".join(["bao.py"] + list(args)) if should_fail: assert output.returncode != 0, "`{}` should've failed".format(cmd) else: assert output.returncode == 0, "`{}` failed".format(cmd) return output.stdout def test_hash_cli(): # CLI tests just use the final (largest) test vector in each set, to avoid # shelling out hundreds of times. There's no need to exhaustively test the # implementation via the CLI, because it's tested on its own above. # Instead, we just need to verify once that it's hooked up properly. case = VECTORS["hash"][-1] input_len = case["input_len"] input_bytes = generate_input.input_bytes(input_len) expected_hash = case["bao_hash"] computed_hash = bao_cli("hash", input=input_bytes).decode().strip() assert expected_hash == computed_hash def blake2b(b): return hashlib.blake2b(b, digest_size=16).hexdigest() def assert_decode_failure(f, *args): try: f(*args) except AssertionError: pass