├── tests ├── __init__.py └── test_merkle_tools.py ├── requirements.txt ├── MANIFEST.in ├── .travis.yml ├── setup.py ├── LICENSE ├── .gitignore ├── merkletools └── __init__.py └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pysha3>=1.0b1 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.py *.txt 2 | recursive-include tests *.py *.md 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.5" 5 | # install 6 | install: "pip install ." 7 | # command to run tests 8 | script: nosetests 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import find_packages 4 | from setuptools import setup 5 | 6 | here = os.path.abspath(os.path.dirname(__file__)) 7 | install_requires = [ 8 | "pysha3>=1.0b1; python_version<\"3.6\"" 9 | ] 10 | 11 | setup( 12 | name='merkletools', 13 | version='1.0.4', 14 | description='Merkle Tools', 15 | classifiers=[ 16 | "Intended Audience :: Developers", 17 | "Intended Audience :: Education", 18 | "Intended Audience :: Science/Research", 19 | "License :: OSI Approved :: MIT License", 20 | "Programming Language :: Python :: 2.7", 21 | "Programming Language :: Python :: 3.6", 22 | "Topic :: Software Development :: Libraries", 23 | "Topic :: Software Development :: Libraries :: Python Modules" 24 | ], 25 | url='https://github.com/Tierion/pymerkletools', 26 | author='Eder Santana', 27 | keywords='merkle tree, blockchain, tierion', 28 | license="MIT", 29 | packages=find_packages(exclude=["tests"]), 30 | include_package_data=False, 31 | zip_safe=False, 32 | install_requires=install_requires 33 | ) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tierion 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /merkletools/__init__.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import binascii 3 | import sys 4 | 5 | 6 | if sys.version_info < (3, 6): 7 | try: 8 | import sha3 9 | except: 10 | from warnings import warn 11 | warn("sha3 is not working!") 12 | 13 | 14 | class MerkleTools(object): 15 | def __init__(self, hash_type="sha256"): 16 | hash_type = hash_type.lower() 17 | if hash_type in ['sha256', 'md5', 'sha224', 'sha384', 'sha512', 18 | 'sha3_256', 'sha3_224', 'sha3_384', 'sha3_512']: 19 | self.hash_function = getattr(hashlib, hash_type) 20 | else: 21 | raise Exception('`hash_type` {} nor supported'.format(hash_type)) 22 | 23 | self.reset_tree() 24 | 25 | def _to_hex(self, x): 26 | try: # python3 27 | return x.hex() 28 | except: # python2 29 | return binascii.hexlify(x) 30 | 31 | def reset_tree(self): 32 | self.leaves = list() 33 | self.levels = None 34 | self.is_ready = False 35 | 36 | def add_leaf(self, values, do_hash=False): 37 | self.is_ready = False 38 | # check if single leaf 39 | if not isinstance(values, tuple) and not isinstance(values, list): 40 | values = [values] 41 | for v in values: 42 | if do_hash: 43 | v = v.encode('utf-8') 44 | v = self.hash_function(v).hexdigest() 45 | v = bytearray.fromhex(v) 46 | self.leaves.append(v) 47 | 48 | def get_leaf(self, index): 49 | return self._to_hex(self.leaves[index]) 50 | 51 | def get_leaf_count(self): 52 | return len(self.leaves) 53 | 54 | def get_tree_ready_state(self): 55 | return self.is_ready 56 | 57 | def _calculate_next_level(self): 58 | solo_leave = None 59 | N = len(self.levels[0]) # number of leaves on the level 60 | if N % 2 == 1: # if odd number of leaves on the level 61 | solo_leave = self.levels[0][-1] 62 | N -= 1 63 | 64 | new_level = [] 65 | for l, r in zip(self.levels[0][0:N:2], self.levels[0][1:N:2]): 66 | new_level.append(self.hash_function(l+r).digest()) 67 | if solo_leave is not None: 68 | new_level.append(solo_leave) 69 | self.levels = [new_level, ] + self.levels # prepend new level 70 | 71 | def make_tree(self): 72 | self.is_ready = False 73 | if self.get_leaf_count() > 0: 74 | self.levels = [self.leaves, ] 75 | while len(self.levels[0]) > 1: 76 | self._calculate_next_level() 77 | self.is_ready = True 78 | 79 | def get_merkle_root(self): 80 | if self.is_ready: 81 | if self.levels is not None: 82 | return self._to_hex(self.levels[0][0]) 83 | else: 84 | return None 85 | else: 86 | return None 87 | 88 | def get_proof(self, index): 89 | if self.levels is None: 90 | return None 91 | elif not self.is_ready or index > len(self.leaves)-1 or index < 0: 92 | return None 93 | else: 94 | proof = [] 95 | for x in range(len(self.levels) - 1, 0, -1): 96 | level_len = len(self.levels[x]) 97 | if (index == level_len - 1) and (level_len % 2 == 1): # skip if this is an odd end node 98 | index = int(index / 2.) 99 | continue 100 | is_right_node = index % 2 101 | sibling_index = index - 1 if is_right_node else index + 1 102 | sibling_pos = "left" if is_right_node else "right" 103 | sibling_value = self._to_hex(self.levels[x][sibling_index]) 104 | proof.append({sibling_pos: sibling_value}) 105 | index = int(index / 2.) 106 | return proof 107 | 108 | def validate_proof(self, proof, target_hash, merkle_root): 109 | merkle_root = bytearray.fromhex(merkle_root) 110 | target_hash = bytearray.fromhex(target_hash) 111 | if len(proof) == 0: 112 | return target_hash == merkle_root 113 | else: 114 | proof_hash = target_hash 115 | for p in proof: 116 | try: 117 | # the sibling is a left node 118 | sibling = bytearray.fromhex(p['left']) 119 | proof_hash = self.hash_function(sibling + proof_hash).digest() 120 | except: 121 | # the sibling is a right node 122 | sibling = bytearray.fromhex(p['right']) 123 | proof_hash = self.hash_function(proof_hash + sibling).digest() 124 | return proof_hash == merkle_root 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pymerkletools 2 | [![PyPI version](https://badge.fury.io/py/merkletools.svg)](https://badge.fury.io/py/merkletools) [![Build Status](https://travis-ci.org/Tierion/pymerkletools.svg?branch=master)](https://travis-ci.org/Tierion/pymerkletools) 3 | 4 | This is a Python port of [merkle-tools](https://github.com/tierion/merkle-tools). 5 | 6 | Tools for creating Merkle trees, generating merkle proofs, and verification of merkle proofs. 7 | 8 | ## Installation 9 | 10 | ``` 11 | pip install merkletools 12 | ``` 13 | 14 | ### Create MerkleTools Object 15 | 16 | ```python 17 | import merkletools 18 | 19 | mt = MerkleTools(hash_type="md5") # default is sha256 20 | # valid hashTypes include all crypto hash algorithms 21 | # such as 'MD5', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512' 22 | # as well as the SHA3 family of algorithms 23 | # including 'SHA3-224', 'SHA3-256', 'SHA3-384', and 'SHA3-512' 24 | ``` 25 | 26 | To use `sha3`, this module depends on [pysha3](https://pypi.python.org/pypi/pysha3). It will be installed as part of this module or you can install it manually with : 27 | ```bash 28 | pip install pysha3==1.0b1 29 | ``` 30 | 31 | 32 | ## Methods 33 | 34 | ### add_leaf(value, do_hash) 35 | 36 | Adds a value as a leaf or a list of leafs to the tree. The value must be a hex string, otherwise set the optional `do_hash` to true to have your value hashed prior to being added to the tree. 37 | 38 | ```python 39 | hex_data = '05ae04314577b2783b4be98211d1b72476c59e9c413cfb2afa2f0c68e0d93911' 40 | list_data = ['Some text data', 'perhaps'] 41 | 42 | mt.add_leaf(hex_data) 43 | mt.add_leaf(list_data, True) 44 | ``` 45 | 46 | ### get_leaf_count() 47 | 48 | Returns the number of leaves that are currently added to the tree. 49 | 50 | ```python 51 | leaf_count = mt.get_leaf_count(); 52 | ``` 53 | 54 | ### get_leaf(index) 55 | 56 | Returns the value of the leaf at the given index as a hex string. 57 | 58 | ```python 59 | leaf_value = mt.get_leaf(1) 60 | ``` 61 | 62 | ### reset_tree() 63 | 64 | Removes all the leaves from the tree, prepararing to to begin creating a new tree. 65 | 66 | ```python 67 | mt.reset_tree() 68 | ``` 69 | 70 | ### make_tree() 71 | 72 | Generates the merkle tree using the leaves that have been added. 73 | 74 | ```python 75 | mt.make_tree(); 76 | ``` 77 | 78 | ### is_ready 79 | 80 | `.is_ready` is a boolean property indicating if the tree is built and ready to supply its root and proofs. The `is_ready` state is `True` only after calling 'make_tree()'. Adding leaves or resetting the tree will change the ready state to False. 81 | 82 | ```python 83 | is_ready = mt.is_ready 84 | ``` 85 | 86 | ### get_merkle_root() 87 | 88 | Returns the merkle root of the tree as a hex string. If the tree is not ready, `None` is returned. 89 | 90 | ```python 91 | root_value = mt.get_merkle_root(); 92 | ``` 93 | 94 | ### get_proof(index) 95 | 96 | Returns the proof as an array of hash objects for the leaf at the given index. If the tree is not ready or no leaf exists at the given index, null is returned. 97 | 98 | ```python 99 | proof = mt.get_proof(1) 100 | ``` 101 | 102 | The proof array contains a set of merkle sibling objects. Each object contains the sibling hash, with the key value of either right or left. The right or left value tells you where that sibling was in relation to the current hash being evaluated. This information is needed for proof validation, as explained in the following section. 103 | 104 | ### validate_proof(proof, target_hash, merkle_root) 105 | 106 | Returns a boolean indicating whether or not the proof is valid and correctly connects the `target_hash` to the `merkle_root`. `proof` is a proof array as supplied by the `get_proof` method. The `target_hash` and `merkle_root` parameters must be a hex strings. 107 | 108 | ```python 109 | proof = [ 110 | { right: '09096dbc49b7909917e13b795ebf289ace50b870440f10424af8845fb7761ea5' }, 111 | { right: 'ed2456914e48c1e17b7bd922177291ef8b7f553edf1b1f66b6fc1a076524b22f' }, 112 | { left: 'eac53dde9661daf47a428efea28c81a021c06d64f98eeabbdcff442d992153a8' }, 113 | ] 114 | target_hash = '36e0fd847d927d68475f32a94efff30812ee3ce87c7752973f4dd7476aa2e97e' 115 | merkle_root = 'b8b1f39aa2e3fc2dde37f3df04e829f514fb98369b522bfb35c663befa896766' 116 | 117 | is_valid = mt.validate_proof(proof, targetHash, merkleRoot) 118 | ``` 119 | 120 | The proof process uses all the proof objects in the array to attempt to prove a relationship between the `target_hash` and the `merkle_root` values. The steps to validate a proof are: 121 | 122 | 1. Concatenate `target_hash` and the first hash in the proof array. The right or left designation specifies which side of the concatenation that the proof hash value should be on. 123 | 2. Hash the resulting value. 124 | 3. Concatenate the resulting hash with the next hash in the proof array, using the same left and right rules. 125 | 4. Hash that value and continue the process until you’ve gone through each item in the proof array. 126 | 5. The final hash value should equal the `merkle_root` value if the proof is valid, otherwise the proof is invalid. 127 | 128 | ## Common Usage 129 | 130 | ### Creating a tree and generating the proofs 131 | 132 | ```python 133 | mt = MerkleTools() 134 | 135 | mt.add_leaf("tierion", True) 136 | mt.add_leaf(["bitcoin", "blockchain"], True) 137 | 138 | mt.make_tree() 139 | 140 | print "root:", mt.get_merkle_root() # root: '765f15d171871b00034ee55e48ffdf76afbc44ed0bcff5c82f31351d333c2ed1' 141 | 142 | print mt.get_proof(1) # [{left: '2da7240f6c88536be72abe9f04e454c6478ee29709fc3729ddfb942f804fbf08'}, 143 | # {right: 'ef7797e13d3a75526946a3bcf00daec9fc9c9c4d51ddc7cc5df888f74dd434d1'}] 144 | 145 | print mt.validate_proof(mt.get_proof(1), mt.get_leaf(1), mt.get_merkle_root()) # True 146 | ``` 147 | 148 | ## Notes 149 | 150 | ### About tree generation 151 | 152 | 1. Internally, leaves are stored as `bytearray`. When the tree is built, it is generated by hashing together the `bytearray` values. 153 | 2. Lonely leaf nodes are promoted to the next level up, as depicted below. 154 | 155 | ROOT=Hash(H+E) 156 | / \ 157 | / \ 158 | H=Hash(F+G) E 159 | / \ \ 160 | / \ \ 161 | F=Hash(A+B) G=Hash(C+D) E 162 | / \ / \ \ 163 | / \ / \ \ 164 | A B C D E 165 | 166 | 167 | ### Development 168 | This module uses Python's `hashlib` for hashing. Inside a `MerkleTools` object all 169 | hashes are stored as Python `bytearray`. This way hashes can be concatenated simply with `+` and the result 170 | used as input for the hash function. But for 171 | simplicity and easy to use `MerkleTools` methods expect that both input and outputs are hex 172 | strings. We can convert from one type to the other using default Python string methods. 173 | For example: 174 | ```python 175 | hash = hashlib.sha256('a').digest() # '\xca\x97\x81\x12\xca\x1b\xbd\xca\xfa\xc21\xb3\x9a#\xdcM\xa7\x86\xef\xf8\x14|Nr\xb9\x80w\x85\xaf\xeeH\xbb' 176 | hex_string = hash.encode('hex') # 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb' 177 | back_to_hash = hex_string.decode('hex') # '\xca\x97\x81\x12\xca\x1b\xbd\xca\xfa\xc21\xb3\x9a#\xdcM\xa7\x86\xef\xf8\x14|Nr\xb9\x80w\x85\xaf\xeeH\xbb' 178 | ``` 179 | -------------------------------------------------------------------------------- /tests/test_merkle_tools.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | from math import sqrt 3 | from merkletools import MerkleTools 4 | 5 | def test_add_leaf(): 6 | mt = MerkleTools() 7 | mt.add_leaf("tierion", do_hash=True) 8 | mt.add_leaf(["bitcoin", "blockchain"], do_hash=True) 9 | assert mt.get_leaf_count() == 3 10 | assert mt.is_ready == False 11 | 12 | def test_build_tree(): 13 | mt = MerkleTools() 14 | mt.add_leaf("tierion", do_hash=True) 15 | mt.add_leaf(["bitcoin", "blockchain"], do_hash=True) 16 | mt.make_tree() 17 | assert mt.is_ready == True 18 | mt.get_merkle_root() == '765f15d171871b00034ee55e48ffdf76afbc44ed0bcff5c82f31351d333c2ed1' 19 | 20 | 21 | def test_get_proof(): 22 | mt = MerkleTools() 23 | mt.add_leaf("tierion", do_hash=True) 24 | mt.add_leaf(["bitcoin", "blockchain"], do_hash=True) 25 | mt.make_tree() 26 | proof_1 = mt.get_proof(1) 27 | for p in proof_1: 28 | try: 29 | assert p['left'] == '2da7240f6c88536be72abe9f04e454c6478ee29709fc3729ddfb942f804fbf08' 30 | except: 31 | assert p['right'] == 'ef7797e13d3a75526946a3bcf00daec9fc9c9c4d51ddc7cc5df888f74dd434d1' 32 | 33 | 34 | # Standard tests 35 | def test_basics(): 36 | bLeft = 'a292780cc748697cb499fdcc8cb89d835609f11e502281dfe3f6690b1cc23dcb' 37 | bRight = 'cb4990b9a8936bbc137ddeb6dcab4620897b099a450ecdc5f3e86ef4b3a7135c' 38 | mRoot = hashlib.sha256(bytearray.fromhex(bLeft) + bytearray.fromhex(bRight)).hexdigest() 39 | 40 | # tree with no leaves 41 | mt = MerkleTools() 42 | mt.make_tree() 43 | assert mt.get_merkle_root() is None 44 | 45 | # tree with hex add_leaf 46 | mt.add_leaf([bLeft, bRight]) 47 | mt.make_tree() 48 | assert mt.get_merkle_root() == mRoot 49 | 50 | 51 | def test_bad_hex(): 52 | # try to add bad hex 53 | mt = MerkleTools() 54 | try: 55 | mt.add_leaf('nothexandnothashed') 56 | assert False # should not get here! 57 | except: 58 | pass 59 | 60 | 61 | def test_one_leaf(): 62 | # make tree with one leaf 63 | mt = MerkleTools() 64 | mt.add_leaf(['ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', ]) 65 | mt.make_tree() 66 | assert mt.get_merkle_root() == 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb' 67 | 68 | 69 | def test_5_leaves(): 70 | mt = MerkleTools() 71 | mt.add_leaf([ 72 | 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', 73 | '3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d', 74 | '2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6', 75 | '18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4', 76 | '3f79bb7b435b05321651daefd374cdc681dc06faa65e374e38337b88ca046dea' 77 | ]) 78 | mt.make_tree() 79 | assert mt.get_merkle_root() == 'd71f8983ad4ee170f8129f1ebcdd7440be7798d8e1c80420bf11f1eced610dba' 80 | 81 | 82 | def test_unhashed_leaves(): 83 | mt = MerkleTools() 84 | mt.add_leaf('a', True) 85 | mt.add_leaf('b', True) 86 | mt.add_leaf('c', True) 87 | mt.add_leaf('d', True) 88 | mt.add_leaf('e', True) 89 | mt.make_tree() 90 | assert mt.get_merkle_root() == 'd71f8983ad4ee170f8129f1ebcdd7440be7798d8e1c80420bf11f1eced610dba' 91 | 92 | mt.reset_tree() 93 | mt.add_leaf(['a', 'b', 'c', 'd', 'e'], True) 94 | mt.make_tree() 95 | assert mt.get_merkle_root() == 'd71f8983ad4ee170f8129f1ebcdd7440be7798d8e1c80420bf11f1eced610dba' 96 | 97 | 98 | def test_md5_tree(): 99 | bLeftmd5 = '0cc175b9c0f1b6a831c399e269772661' 100 | bRightmd5 = '92eb5ffee6ae2fec3ad71c777531578f' 101 | mRootmd5 = hashlib.md5(bytearray.fromhex(bLeftmd5)+bytearray.fromhex(bRightmd5)).hexdigest() 102 | 103 | mt = MerkleTools('md5') 104 | mt.add_leaf([bLeftmd5, bRightmd5]) 105 | mt.make_tree() 106 | assert mt.get_merkle_root() == mRootmd5 107 | 108 | 109 | def test_proof_nodes(): 110 | bLeft = 'a292780cc748697cb499fdcc8cb89d835609f11e502281dfe3f6690b1cc23dcb' 111 | bRight = 'cb4990b9a8936bbc137ddeb6dcab4620897b099a450ecdc5f3e86ef4b3a7135c' 112 | mRoot = hashlib.sha256(bytearray.fromhex(bLeft) + bytearray.fromhex(bRight)).hexdigest() 113 | 114 | mt = MerkleTools() 115 | mt.add_leaf(bLeft) 116 | mt.add_leaf(bRight) 117 | mt.make_tree() 118 | proof = mt.get_proof(0) 119 | assert proof[0]['right'] == 'cb4990b9a8936bbc137ddeb6dcab4620897b099a450ecdc5f3e86ef4b3a7135c' 120 | proof = mt.get_proof(1) 121 | assert proof[0]['left'] == 'a292780cc748697cb499fdcc8cb89d835609f11e502281dfe3f6690b1cc23dcb' 122 | 123 | 124 | def test_proof_no_leaves(): 125 | mt = MerkleTools() 126 | mt.make_tree() 127 | proof = mt.get_proof(0) 128 | assert proof is None 129 | 130 | 131 | def test_bad_proof(): 132 | bLeft = 'a292780cc748697cb499fdcc8cb89d835609f11e502281dfe3f6690b1cc23dcb' 133 | bRight = 'cb4990b9a8936bbc137ddeb6dcab4620897b099a450ecdc5f3e86ef4b3a7135c' 134 | 135 | mt = MerkleTools() 136 | mt.add_leaf(bLeft) 137 | mt.add_leaf(bRight) 138 | mt.make_tree() 139 | proof = mt.get_proof(1) 140 | is_valid = mt.validate_proof(proof, bRight, bLeft) 141 | assert not is_valid 142 | 143 | 144 | def test_validate_5_leaves(): 145 | mt = MerkleTools() 146 | mt.add_leaf([ 147 | 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', 148 | '3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d', 149 | '2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6', 150 | '18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4', 151 | '3f79bb7b435b05321651daefd374cdc681dc06faa65e374e38337b88ca046dea' 152 | ]) 153 | mt.make_tree() 154 | 155 | # bad proof 156 | proof = mt.get_proof(3) 157 | is_valid = mt.validate_proof(proof, 'badc3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4', 'd71f8983ad4ee170f8129f1ebcdd7440be7798d8e1c80420bf11f1eced610dba') 158 | assert is_valid == False 159 | 160 | # good proof 161 | proof = mt.get_proof(4) 162 | is_valid = mt.validate_proof(proof, '3f79bb7b435b05321651daefd374cdc681dc06faa65e374e38337b88ca046dea', 'd71f8983ad4ee170f8129f1ebcdd7440be7798d8e1c80420bf11f1eced610dba') 163 | assert is_valid == True 164 | 165 | proof = mt.get_proof(1) 166 | is_valid = mt.validate_proof(proof, '3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d', 'd71f8983ad4ee170f8129f1ebcdd7440be7798d8e1c80420bf11f1eced610dba') 167 | assert is_valid == True 168 | 169 | 170 | # testing other hash functions 171 | def test_sha224(): 172 | mt = MerkleTools(hash_type='sha224') 173 | mt.add_leaf([ 174 | '90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809', 175 | '35f757ad7f998eb6dd3dd1cd3b5c6de97348b84a951f13de25355177' 176 | ]) 177 | mt.make_tree() 178 | assert mt.get_merkle_root() == 'f48bc49bb77d3a3b1c8f8a70db693f41d879189cd1919f8326067ad7' 179 | assert mt.get_proof(0)[0]['right'] == '35f757ad7f998eb6dd3dd1cd3b5c6de97348b84a951f13de25355177' 180 | is_valid = mt.validate_proof(mt.get_proof(0), '90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809', 'f48bc49bb77d3a3b1c8f8a70db693f41d879189cd1919f8326067ad7') 181 | assert is_valid == True 182 | 183 | 184 | def test_sha256(): 185 | mt = MerkleTools(hash_type='sha256') 186 | mt.add_leaf([ 187 | '1516f000de6cff5c8c63eef081ebcec2ad2fdcf7034db16045d024a90341e07d', 188 | 'e20af19f85f265579ead2578859bf089c92b76a048606983ad83f27ba8f32f1a' 189 | ]) 190 | mt.make_tree() 191 | assert mt.get_merkle_root() == '77c654b3d1605f78ed091cbd420c939c3feff7d57dc30c171fa45a5a3c81fd7d' 192 | assert mt.get_proof(0)[0]['right'] == 'e20af19f85f265579ead2578859bf089c92b76a048606983ad83f27ba8f32f1a' 193 | is_valid = mt.validate_proof(mt.get_proof(0), '1516f000de6cff5c8c63eef081ebcec2ad2fdcf7034db16045d024a90341e07d', '77c654b3d1605f78ed091cbd420c939c3feff7d57dc30c171fa45a5a3c81fd7d') 194 | assert is_valid == True 195 | 196 | 197 | def test_sha384(): 198 | mt = MerkleTools(hash_type='sha384') 199 | mt.add_leaf([ 200 | '84ae8c6367d64899aef44a951edfa4833378b9e213f916c5eb8492cc37cb951c726e334dace7dbe4bb1dc80c1efe33d0', 201 | '368c89a00446010def75ad7b179cea9a3d24f8cbb7e2755a28638d194809e7b614eb45453665032860b6c1a135fb6e8b' 202 | ]) 203 | mt.make_tree() 204 | assert mt.get_merkle_root() == 'c363aa3b824e3f3b927034fab826eff61a9bfa2030ae9fc4598992edf9f3e42f8b497d6742946caf7a771429eb1745cf' 205 | assert mt.get_proof(0)[0]['right'] == '368c89a00446010def75ad7b179cea9a3d24f8cbb7e2755a28638d194809e7b614eb45453665032860b6c1a135fb6e8b' 206 | is_valid = mt.validate_proof(mt.get_proof(0), '84ae8c6367d64899aef44a951edfa4833378b9e213f916c5eb8492cc37cb951c726e334dace7dbe4bb1dc80c1efe33d0', 'c363aa3b824e3f3b927034fab826eff61a9bfa2030ae9fc4598992edf9f3e42f8b497d6742946caf7a771429eb1745cf') 207 | assert is_valid == True 208 | 209 | 210 | def test_sha512(): 211 | mt = MerkleTools(hash_type='sha512') 212 | mt.add_leaf([ 213 | 'c0a8907588c1da716ce31cbef05da1a65986ec23afb75cd42327634dd53d754be6c00a22d6862a42be5f51187a8dff695c530a797f7704e4eb4b473a14ab416e', 214 | 'df1e07eccb2a2d4e1b30d11e646ba13ddc426c1aefbefcff3639405762f216fdcc40a684f3d1855e6d465f99fd9547e53fa8a485f18649fedec5448b45963976' 215 | ]) 216 | mt.make_tree() 217 | assert mt.get_merkle_root() == 'd9d27704a3a785d204257bfa2b217a1890e55453b6686f091fa1be8aa2b265bc06c285a909459996e093546677c3f392458d7b1fc34a994a86689ed4100e8337' 218 | assert mt.get_proof(0)[0]['right'] == 'df1e07eccb2a2d4e1b30d11e646ba13ddc426c1aefbefcff3639405762f216fdcc40a684f3d1855e6d465f99fd9547e53fa8a485f18649fedec5448b45963976' 219 | is_valid = mt.validate_proof(mt.get_proof(0), 'c0a8907588c1da716ce31cbef05da1a65986ec23afb75cd42327634dd53d754be6c00a22d6862a42be5f51187a8dff695c530a797f7704e4eb4b473a14ab416e', 'd9d27704a3a785d204257bfa2b217a1890e55453b6686f091fa1be8aa2b265bc06c285a909459996e093546677c3f392458d7b1fc34a994a86689ed4100e8337') 220 | assert is_valid == True 221 | 222 | 223 | def test_sha3_224(): 224 | mt = MerkleTools(hash_type='sha3_224') 225 | mt.add_leaf([ 226 | '6ed712b9472b671fd70bb950dc4ccfce197c92a7969f6bc2aa6b6d9f', 227 | '08db5633d406804d044a3e67683e179b5ee51249ed2139c239d1e65a' 228 | ]) 229 | mt.make_tree() 230 | assert mt.get_merkle_root() == '674bc9f53d5c666174cdd3ccb9df04768dfb7759655e7d937aef0c3a' 231 | assert mt.get_proof(0)[0]['right'] == '08db5633d406804d044a3e67683e179b5ee51249ed2139c239d1e65a' 232 | is_valid = mt.validate_proof(mt.get_proof(0), '6ed712b9472b671fd70bb950dc4ccfce197c92a7969f6bc2aa6b6d9f', '674bc9f53d5c666174cdd3ccb9df04768dfb7759655e7d937aef0c3a') 233 | assert is_valid == True 234 | 235 | 236 | def test_sha3_224(): 237 | mt = MerkleTools(hash_type='sha3_224') 238 | mt.add_leaf([ 239 | '6ed712b9472b671fd70bb950dc4ccfce197c92a7969f6bc2aa6b6d9f', 240 | '08db5633d406804d044a3e67683e179b5ee51249ed2139c239d1e65a' 241 | ]) 242 | mt.make_tree() 243 | assert mt.get_merkle_root() == '674bc9f53d5c666174cdd3ccb9df04768dfb7759655e7d937aef0c3a' 244 | assert mt.get_proof(0)[0]['right'] == '08db5633d406804d044a3e67683e179b5ee51249ed2139c239d1e65a' 245 | is_valid = mt.validate_proof(mt.get_proof(0), '6ed712b9472b671fd70bb950dc4ccfce197c92a7969f6bc2aa6b6d9f', '674bc9f53d5c666174cdd3ccb9df04768dfb7759655e7d937aef0c3a') 246 | assert is_valid == True 247 | 248 | 249 | def test_sha3_256(): 250 | mt = MerkleTools(hash_type='sha3_256') 251 | mt.add_leaf([ 252 | '1d7d4ea1cc029ca460e486642830c284657ea0921235c46298b51f0ed1bb7bf7', 253 | '89b9e14eae37e999b096a6f604adefe7feea4dc240ccecb5e4e92785cffc7070' 254 | ]) 255 | mt.make_tree() 256 | assert mt.get_merkle_root() == '6edf674f5ce762e096c3081aee2a0a977732e07f4d704baf34f5e3804db03343' 257 | assert mt.get_proof(0)[0]['right'] == '89b9e14eae37e999b096a6f604adefe7feea4dc240ccecb5e4e92785cffc7070' 258 | is_valid = mt.validate_proof(mt.get_proof(0), '1d7d4ea1cc029ca460e486642830c284657ea0921235c46298b51f0ed1bb7bf7', '6edf674f5ce762e096c3081aee2a0a977732e07f4d704baf34f5e3804db03343') 259 | assert is_valid == True 260 | 261 | 262 | def test_sha3_384(): 263 | mt = MerkleTools(hash_type='sha3_384') 264 | mt.add_leaf([ 265 | 'e222605f939aa69b964a0a03d7075676bb3dbb40c3bd10b22f0adcb149434e7c1085c206f0e3371470a49817aa6d5b16', 266 | 'ae331b6f8643ed7e404471c81be9a74f73fc84ffd5140a0ec9aa8596fa0d0a2ded5f7b780bb2fbfc4e2226ee2a04a2fa' 267 | ]) 268 | mt.make_tree() 269 | assert mt.get_merkle_root() == 'bd54df0015fa0d4fee713fbf5c8ae232c93239c75fb9d41c7dd7a9278711764a6ee83c81766b3945ed94030254537b57' 270 | assert mt.get_proof(0)[0]['right'] == 'ae331b6f8643ed7e404471c81be9a74f73fc84ffd5140a0ec9aa8596fa0d0a2ded5f7b780bb2fbfc4e2226ee2a04a2fa' 271 | is_valid = mt.validate_proof(mt.get_proof(0), 'e222605f939aa69b964a0a03d7075676bb3dbb40c3bd10b22f0adcb149434e7c1085c206f0e3371470a49817aa6d5b16', 'bd54df0015fa0d4fee713fbf5c8ae232c93239c75fb9d41c7dd7a9278711764a6ee83c81766b3945ed94030254537b57') 272 | assert is_valid == True 273 | 274 | 275 | def test_sha3_512(): 276 | mt = MerkleTools(hash_type='sha3_512') 277 | mt.add_leaf([ 278 | '004a237ea808cd9375ee9db9f85625948a890c54e2c30f736f54c969074eb56f0ff3d43dafb4b40d5d974acc1c2a68c046fa4d7c2c20cab6df956514040d0b8b', 279 | '0b43a85d08c05252d0e23c96bc6b1bda11dfa787049ff452b3c86f4c6135e870c058c05131f199ef8619cfac937a736bbc936a667e4d96a5bf68e4056ce5fdce' 280 | ]) 281 | mt.make_tree() 282 | assert mt.get_merkle_root() == '3dff3f19b67628591d294cba2c07ed20d20d83e1624af8c1dca8fcf096127b9f86435e2d6a84ca4cee526525cacd1c628bf06ee938983413afafbb4598c5862a' 283 | assert mt.get_proof(0)[0]['right'] == '0b43a85d08c05252d0e23c96bc6b1bda11dfa787049ff452b3c86f4c6135e870c058c05131f199ef8619cfac937a736bbc936a667e4d96a5bf68e4056ce5fdce' 284 | is_valid = mt.validate_proof(mt.get_proof(0), '004a237ea808cd9375ee9db9f85625948a890c54e2c30f736f54c969074eb56f0ff3d43dafb4b40d5d974acc1c2a68c046fa4d7c2c20cab6df956514040d0b8b', '3dff3f19b67628591d294cba2c07ed20d20d83e1624af8c1dca8fcf096127b9f86435e2d6a84ca4cee526525cacd1c628bf06ee938983413afafbb4598c5862a') 285 | assert is_valid == True 286 | --------------------------------------------------------------------------------