Skip to main content
# Merkle Trees

Merkle trees are hash-based data structures used to validate large amounts of data in an efficient manner. This data structure is used to solve the previously time-consuming and computationally expensive problem of keeping data consistent across multiple computers. Prominent uses of a Merkle tree - and its variations- are in peer-to-peer networks such as Bitcoin, Ethereum, Git, and Tor.


### Merkle Tree Diagram

This diagram illustrates a fully balanced Merkle tree. As you can guess from the illustration, the Merkle hash root maintains the integrity of the data. If any of the nodes are changed, or the order of the data items is changed, the Merkle hash root will be completely different.

<img src="https://github.com/vpaliy/merkle-trees/blob/master/ext/merkle.jpg" />


This is what an "artificially" balanced tree looks like (a tree whose number of leaves is not a power of two):

<img src="https://github.com/vpaliy/merkle-trees/blob/master/ext/empty.jpg" />

We had to add an empty light-weight node in order to keep it balanced.
Therefore, when we append a new leaf, we can just replace that empty node and recalculate the hash root.


### Merkle Audit Proof

Audit proof lets you verify that a specific data record is included in the database. Usually, the server maintaining the database provides the client with a proof that the record exists in that database. If a Merkle audit proof fails to produce a root hash that matches the Merkle root hash (which is obtained from a trusted authority), it means that the data record is not in the database.

The diagram below illustrates how you should construct an audit proof:

<img src="https://github.com/vpaliy/merkle-trees/blob/master/ext/proof.jpg" />

In this example, we need to provide a proof that the record `D` exists in the database.
Since we already know the hash value of `D` (we can easily compute it), we will need `H-3` in order to compute `D-2`. Now, when we are able to compute `D-2`, we will need to get `D-1` in order to obtain the hash value of `T-1`, and so on...
You've got the gist, right? We only need to grab the sibling node and climb up the tree until we've reached the root. [This](https://github.com/vpaliy/merklelib/blob/master/merklelib/merkle.py#L468) implements everything described above.


### Merkle Consistency Proof

A Merkle consistency proof lets you verify that any two versions of a database are consistent: that is, the later version includes everything in the earlier version, in the same order, and all new entries come after the entries in the older version.



## Usage

Install it:

`pip install merklelib`

or clone it:

```
$ git clone git clone https://github.com/vpaliy/merklelib.git
```

This snippet demonstrates how to build a Merkle tree and verify leaf inclusion:

```python
import string
import hashlib

from merklelib import MerkleTree

# a sample hash function
# you can also omit it and the default hash function will be used
def hashfunc(value):
return hashlib.sha256(value).hexdigest()


# a list of all ASCII letters
data = list(string.ascii_letters)

# build a Merkle tree for that list
tree = MerkleTree(data, hashfunc)

# generate an audit proof the letter A
proof = tree.get_proof(hashfunc('A'))

# now verify that A is in the tree
# you can also pass in the hash value of 'A'
# it will hash automatically if the user forgot to hash it
if tree.verify_leaf_inclusion('A', proof):
print('A is in the tree')
else:
exit('A is not in the tree')
```

Or you may want to perform a consitency check (using `<`, `<=`, `>`, `>=` operators):

(some code will be omitted)
```python
tree = MerkleTree(get_data())
...
...
...
new_tree = MerkleTree(get_new_data())

# check if the new tree contains the same items
# and in the same order as the old version
if tree <= new_tree:
print('Versions are consistent')
else:
exit('Versions are different')
```

Alternatively, you can use the `verify_tree_consitency` function for this:
```python

from merkelib import MerkleTree, verify_tree_consistency
...
...
...
# information that we need to provide
old_hash_root = old_tree.merkle_hash
old_tree_size = len(old_tree)

# check if the new tree contains the same items
# and in the same order as the old version
if verify_tree_consistency(new_tree, old_hash_root, old_tree_size):
print('Versions are consistent')
else:
exit('Versions are different')
```


You can build a Merkle tree and nicely display it in the terminal:

```python
from merklelib import MerkleTree, beautify


transactions = get_transactions(user) # random data
tree = MerkleTree(transactions)

beautify(tree) # print the tree in the terminal
```

Output:

```
e11a20bae8379fdc0ed560561ba33f30c877e0e95051aed5acebcb9806f6521f
├── 862532e6a3c9aafc2016810598ed0cc3025af5640db73224f586b6f1138385f4
│ ├── fa13bb36c022a6943f37c638126a2c88fc8d008eb5a9fe8fcde17026807feae4
│ │ ├── 5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9
│ │ └── 6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b
│ └── 70311d9d203b2d7e4ff70d7fce219f82a4fcf73a110dc80187dfefb7c6e4bb87
│ ├── d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35
│ └── 4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce
└── f4685cb09ef9f1c86b2d8f544d89f1c1d3592a3654beb8feecad11e9545e0e72
├── 67d62ee831ff99506ce1cd9435351408c3a845fca2dc0f34d085cdb51a37ec40
│ ├── 4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a
│ └── ef2d127de37b942baad06145e54b0c619a1f22327b2ebbcfbec78f5564afe39d
└── ac6621607d32037664f03f92a4aae94d4c97f6bbcf438ff20509311681e6b259
├── e7f6c011776e8db7cd330b54174fd76f7d0216b612387a5ffcfb81e6f0919683
└── 7902699be42c8a8e46fbbb4501726517e86b22c56a189f7625a6da49081b2451
```

You can also export the output above as an image or a JSON file. This is how you'd do it:


```python
from merklelib import MerkleTree, export


transactions = get_transactions(user) # random data
tree = MerkleTree(transactions)

export(tree, filename='transactions', ext='jpg')
```

Default extension is always `.json.` You can also specify an absolute path.

However, in order to be able to use the `export` function with images, you may need to install `graphviz` on your machine.
Here is how you can do that for Mac and Ubuntu:

### Mac

`brew install graphviz`


### Ubuntu

`sudo apt-get install graphviz`



## Benchmark

I have included some basic benchmark code to measure performance. If you want to try it out, simply download the repository with:

```
$ git clone git clone https://github.com/vpaliy/merklelib.git
$ cd merklelib
```

And run it with `python3` or `python2` (additional arguments are optional):

```
$ python3 benchmark --size=2048 -a=8

Building: 0.016072 seconds.
Appending: 0.000868 seconds.
Tree size: 637401
Number of leaves: 2056

Audit proof verification times:
Average time: 0.00014690807392996172 seconds.
Total time: 0.304746 seconds.
Longest time: 0.000325 seconds.
Shortest time: 5.9e-05 seconds.

Consitency proof verification times (2056 trees):
Average time: 0.008714925583657588 seconds.
Total time: 17.925458 seconds.
Longest time: 0.035517 seconds.
Shortest time: 9e-05 seconds.

```

The above test provides the following results:
- how long it takes to build a Merkle tree.
- how long it takes append additional leaves.
- how long it takes to verify every single leaf that is included in the tree.
- how long it takes to prove that every possible sub-tree of the generated Merkle tree is consistent with the original one


You can also build your own benchmark. Here's a simple one that measure how long it takes to build a Merkle tree with 65536 leaves:

```
>>> import os
>>> import timeit
>>> from merklelib import MerkleTree
>>> leaves = [os.urandom(2048) for i in range(2**16)]
>>> def timeav(code, n=20):
>>> return timeit.timeit(
... code, setup="from __main__ import MerkleTree, leaves", number=n)/n
...
# time taken to build the tree
>>> print timeav("MerkleTree(leaves)")
0.70325

```

# Resources

* [Understanding Merkle Trees - Why use them, who uses them, and how to use them](https://www.codeproject.com/Articles/1176140/%2FArticles%2F1176140%2FUnderstanding-Merkle-Trees-Why-use-them-who-uses-t)

* [Certificate Transparency](https://tools.ietf.org/html/rfc6962#section-2.1.2)

* [Merkle Tree Brilliant](https://brilliant.org/wiki/merkle-tree/)

# License
```
MIT License

Copyright (c) 2019 Vasyl Paliy

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```


Metadata

Release files for merklelib 1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for merklelib 1.0
File Size Uploaded
merklelib-1.0.tar.gz 18.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for merklelib 1.0
File Interpreter ABI Platform
merklelib-1.0-py2.py3-none-any.whl Python 3, Python 2 none any Details

Total release size: 37.9 kB

Release files / merklelib-1.0.tar.gz

Download URL merklelib-1.0.tar.gz
Size 18.6 kB
Tags Source
SHA-256 checksum
How to use checksums
ad8deb51552fa897f5bac48f13885b260159481cdf12cd1fccafe0b942f0da10
BLAKE2b-256 checksum
How to use checksums
945a567cbe6c8425b0e625624f79b4f8e975d4d120dd392ec3fe0618fe10ce4e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.12.1 pkginfo/1.4.2 requests/2.19.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/2.7.15rc1

Release files / merklelib-1.0-py2.py3-none-any.whl

Download URL merklelib-1.0-py2.py3-none-any.whl
Size 19.3 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
f1b22ac0f21c26d61db1bbb377c368b6222bad62189ec3f9adb58c63d8a13b6b
BLAKE2b-256 checksum
How to use checksums
30bdf81892ec0e1a264e89058132fce856c87722111e0d7078f0ba0f5df43daf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.12.1 pkginfo/1.4.2 requests/2.19.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/2.7.15rc1

Release history Release notifications | RSS feed

This release

1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page