test_mozilla_merkle.py (4588B)
1 import codecs 2 import hashlib 3 import random 4 import unittest 5 6 import mozunit 7 from mozharness.mozilla.merkle import InclusionProof, MerkleTree 8 9 decode_hex = codecs.getdecoder("hex_codec") 10 encode_hex = codecs.getencoder("hex_codec") 11 12 # Pre-computed tree on 7 inputs 13 # 14 # ______F_____ 15 # / \ 16 # __D__ _E_ 17 # / \ / \ 18 # A B C | 19 # / \ / \ / \ | 20 # 0 1 2 3 4 5 6 21 hash_fn = hashlib.sha256 22 23 data = [ 24 decode_hex("fbc459361fc111024c6d1fd83d23a9ff")[0], 25 decode_hex("ae3a44925afec860451cd8658b3cadde")[0], 26 decode_hex("418903fe6ef29fc8cab93d778a7b018b")[0], 27 decode_hex("3d1c53c00b2e137af8c4c23a06388c6b")[0], 28 decode_hex("e656ebd8e2758bc72599e5896be357be")[0], 29 decode_hex("81aae91cf90be172eedd1c75c349bf9e")[0], 30 decode_hex("00c262edf8b0bc345aca769e8733e25e")[0], 31 ] 32 33 leaves = [ 34 decode_hex("5cb551f87797381a24a5359a986e2cef25b1f2113b387197fe48e8babc9ad5c7")[0], 35 decode_hex("9899dc0be00306bda2a8e69cec32525ca6244f132479bcf840d8c1bc8bdfbff2")[0], 36 decode_hex("fdd27d0393e32637b474efb9b3efad29568c3ec9b091fdda40fd57ec9196f06d")[0], 37 decode_hex("c87292a6c8528c2a0679b6c1eefb47e4dbac7840d23645d5b7cb47cf1a8d365f")[0], 38 decode_hex("2ff3bdac9bec3580b82da8a357746f15919414d9cbe517e2dd96910c9814c30c")[0], 39 decode_hex("883e318240eccc0e2effafebdb0fd4fd26d0996da1b01439566cb9babef8725f")[0], 40 decode_hex("bb13dfb7b202a95f241ea1715c8549dc048d9936ec747028002f7c795de72fcf")[0], 41 ] 42 43 nodeA = decode_hex("06447a7baa079cb0b4b6119d0f575bec508915403fdc30923eba982b63759805")[ 44 0 45 ] 46 nodeB = decode_hex("3db98027c655ead4fe897bef3a4b361839a337941a9e624b475580c9d4e882ee")[ 47 0 48 ] 49 nodeC = decode_hex("17524f8b0169b2745c67846925d55449ae80a8022ef8189dcf4cbb0ec7fcc470")[ 50 0 51 ] 52 nodeD = decode_hex("380d0dc6fd7d4f37859a12dbfc7171b3cce29ab0688c6cffd2b15f3e0b21af49")[ 53 0 54 ] 55 nodeE = decode_hex("3a9c2886a5179a6e1948876034f99d52a8f393f47a09887adee6d1b4a5c5fbd6")[ 56 0 57 ] 58 nodeF = decode_hex("d1a0d3947db4ae8305f2ac32985957e02659b2ea3c10da52a48d2526e9af3bbc")[ 59 0 60 ] 61 62 proofs = [ 63 [leaves[1], nodeB, nodeE], 64 [leaves[0], nodeB, nodeE], 65 [leaves[3], nodeA, nodeE], 66 [leaves[2], nodeA, nodeE], 67 [leaves[5], leaves[6], nodeD], 68 [leaves[4], leaves[6], nodeD], 69 [nodeC, nodeD], 70 ] 71 72 known_proof5 = decode_hex( 73 "020000" 74 + "0000000000000007" 75 + "0000000000000005" 76 + "0063" 77 + "20" 78 + encode_hex(leaves[4])[0].decode() 79 + "20" 80 + encode_hex(leaves[6])[0].decode() 81 + "20" 82 + encode_hex(nodeD)[0].decode() 83 )[0] 84 85 86 class TestMerkleTree(unittest.TestCase): 87 def testPreComputed(self): 88 tree = MerkleTree(hash_fn, data) 89 head = tree.head() 90 self.assertEqual(head, nodeF) 91 92 for i in range(len(data)): 93 proof = tree.inclusion_proof(i) 94 95 self.assertTrue(proof.verify(hash_fn, data[i], i, len(data), head)) 96 self.assertEqual(proof.leaf_index, i) 97 self.assertEqual(proof.tree_size, tree.n) 98 self.assertEqual(proof.path_elements, proofs[i]) 99 100 def testInclusionProofEncodeDecode(self): 101 tree = MerkleTree(hash_fn, data) 102 103 # Inclusion proof encode/decode round trip test 104 proof5 = tree.inclusion_proof(5) 105 serialized5 = proof5.to_rfc6962_bis() 106 deserialized5 = InclusionProof.from_rfc6962_bis(serialized5) 107 reserialized5 = deserialized5.to_rfc6962_bis() 108 self.assertEqual(serialized5, reserialized5) 109 110 # Inclusion proof encode known answer test 111 serialized5 = proof5.to_rfc6962_bis() 112 self.assertEqual(serialized5, known_proof5) 113 114 # Inclusion proof decode known answer test 115 known_deserialized5 = InclusionProof.from_rfc6962_bis(known_proof5) 116 self.assertEqual(proof5.leaf_index, known_deserialized5.leaf_index) 117 self.assertEqual(proof5.tree_size, known_deserialized5.tree_size) 118 self.assertEqual(proof5.path_elements, known_deserialized5.path_elements) 119 120 def testLargeTree(self): 121 TEST_SIZE = 5000 122 ELEM_SIZE_BYTES = 16 123 data = [ 124 bytearray(random.getrandbits(8) for _ in range(ELEM_SIZE_BYTES)) 125 for _ in range(TEST_SIZE) 126 ] 127 tree = MerkleTree(hash_fn, data) 128 head = tree.head() 129 130 for i in range(len(data)): 131 proof = tree.inclusion_proof(i) 132 133 self.assertTrue(proof.verify(hash_fn, data[i], i, len(data), head)) 134 self.assertEqual(proof.leaf_index, i) 135 self.assertEqual(proof.tree_size, tree.n) 136 137 138 if __name__ == "__main__": 139 mozunit.main()