[![Build Status](https://api.travis-ci.com/thyeem/monotree.svg?token=QYwxZ27j8uz6zsrzY6bk&branch=master)](https://app.travis-ci.com/thyeem/monotree) [![Crates.io](https://img.shields.io/crates/v/monotree.svg)](https://crates.io/crates/monotree) # Monotree Rust implementation of an optimized Sparse Merkle Tree. This is a kind of binary-radix tree based on bitwise branching, _currently_, no nibble of bit. For now, branching unit is just ___a single bit___, _neither a 4-bit nor a byte nibble_. ## Features - Very __simple__ and __lightweight__, but __fast__ and __robust__. - __Fully featured__ Sparse Merkle Tree (SMT) as a storage - This includes: __non-inclusion proof__, as well as __inclusion proof__, and its verification. - Again, _NOT verbose_ at all. This library mostly relies on the _Rust standard library only_ except for `database APIs` and `hashers`. Currently, `monotree` supports these databases and hash functions following, but is designed to be super easy to customize and add: _Databases include_: - [`HashMap`](https://lib.rs/crates/hashbrown) - [`RocksDB`](https://lib.rs/crates/rocksdb) - [`Sled`](https://lib.rs/crates/sled) _Hashers include_: - [`Blake3`](https://lib.rs/crates/blake3) - [`Blake2s`](https://lib.rs/crates/blake2-rfc) and [`Blake2b`](https://lib.rs/crates/blake2-rfc) - [`SHA-2`](https://lib.rs/crates/sha2) - [`SHA-3 (Keccak)`](https://lib.rs/crates/sha3) ## Quick start > _from `examples/basic.rs`_ > Regarding __non-inclusion proof__ and __inclusion proof__, See _Merkle proof_ section in More Examples below. ```rust use monotree::{Monotree, Result}; use monotree::utils::random_hash; fn main() -> Result<()> { // Init a monotree instance // by default, with 'HashMap' and 'Blake3' hash function let mut tree = Monotree::default(); // It is natural the tree root initially has 'None' let root = None; // Prepare a random pair of key and leaf. // random_hash() gives a fixed length of random array, // where Hash -> [u8; HASH_LEN], HASH_LEN = 32 let key = random_hash(); let leaf = random_hash(); // Insert the entry (key, leaf) into tree, yielding a new root of tree let root = tree.insert(root.as_ref(), &key, &leaf)?; assert_ne!(root, None); // Get the leaf inserted just before. Note that the last root was used. let found = tree.get(root.as_ref(), &key)?; assert_eq!(found, Some(leaf)); // Remove the entry let root = tree.remove(root.as_ref(), &key)?; // surely, the tree has nothing and the root back to 'None' assert_eq!(tree.get(root.as_ref(), &key)?, None); assert_eq!(root, None); Ok(()) } ``` ## Performance **All benchmarks were performed in a cumulative way**, where the root resulting from an operation just before was reused for subsequent operations. They were carried out with randomly-generated bytes entries and from the root of an empty tree. (Deletion starts from the last root.) > Tested on: _Intel Core i5-3570 CPU @ 3.4GHz and 16 GB RAM / Ubuntu 18.04_ with `rustc stable 1.42.0` > As a hasher, `Blake3` was used in this benchmark. | Op. | DB used | #Entries | Time Measured | Std Error | per Op. | | :----: | :-----: | -------: | :-----------: | :-------: | :------: | | Insert | HashMap | 10 | 8.50 us | 0.01 us | 0.85 us | | Insert | HashMap | 100 | 165.71 us | 0.14 us | 1.66 us | | Insert | HashMap | 1000 | 2.32 ms | 0.01 ms | 2.32 us | | Insert | HashMap | 10000 | 37.39 ms | 0.02 ms | 3.74 us | | Get | HashMap | 10 | 7.82 us | 0.01 us | 0.78 us | | Get | HashMap | 100 | 114.51 us | 0.14 us | 1.15 us | | Get | HashMap | 1000 | 1.52 ms | 0.01 ms | 1.52 us | | Get | HashMap | 10000 | 20.40 ms | 0.01 ms | 2.40 us | | Remove | HashMap | 10 | 15.65 us | 0.01 us | 1.57 us | | Remove | HashMap | 100 | 282.68 us | 0.09 us | 2.83 us | | Remove | HashMap | 1000 | 4.23 ms | 0.01 ms | 4.23 us | | Remove | HashMap | 10000 | 69.15 ms | 0.07 ms | 6.92 us | | | | | | | | | Insert | RocksDB | 10 | 34.80 us | 0.27 us | 3.48 us | | Insert | RocksDB | 100 | 602.55 us | 2.72 us | 6.03 us | | Insert | RocksDB | 1000 | 10.35 ms | 0.06 ms | 10.35 us | | Insert | RocksDB | 10000 | 145.83 ms | 0.20 ms | 14.58 us | | Get | RocksDB | 10 | 10.20 us | 0.06 us | 1.02 us | | Get | RocksDB | 100 | 142.20 us | 0.79 us | 1.42 us | | Get | RocksDB | 1000 | 1.74 ms | 0.01 ms | 1.74 us | | Get | RocksDB | 10000 | 22.94 ms | 0.03 ms | 2.29 us | | Remove | RocksDB | 10 | 58.33 us | 0.50 us | 5.83 us | | Remove | RocksDB | 100 | 1.02 ms | 6.40 us | 10.20 us | | Remove | RocksDB | 1000 | 20.22 ms | 0.10 ms | 20.22 us | | Remove | RocksDB | 10000 | 394.95 ms | 1.46 ms | 39.50 us | ## Integration tests and benchmark performs integration tests with full combinations of operations and tree types consisting of _Databases_ and _Hashers_ included. ```bash ## Some tests are time consuming. ## --release is optional, but without it, it will take a longer time to complete the tests $ cargo test --release --features "db_rocksdb, db_sled" ``` performs a micro-benchmark based on [`Criterion`](https://crates.io/crates/criterion), with full combinations of operations and tree types consisting of _Databases_ and _Hashers_ included. ```bash $ cargo bench --features "db_rocksdb, db_sled" ``` and macroscopic-time benchmarks (but rather wider error bars) with all tree types were also prepared in _`examples/perf.rs`_. ```bash $ cargo run --release --example perf --features "db_rocksdb, db_sled" ``` and some examples describing how to manipulate `monotree` ```bash $ cargo run --example basic $ cargo run --example advanced --features "db_rocksdb" ``` ## Further improvement `monotree` is a special case among the generalized binary radix trees, I'd like to call it `PoT (Power Of Two) radix tree`. If `monotree` were generalized with `pow(2, n)` of nibbles as a branching unit, _there would have been room for further performance improvement_. This generalization is now on progress. ## More Examples > _from `examples/advanced.rs`_ ```rust use monotree::database::*; use monotree::hasher::*; use monotree::utils::*; use monotree::*; fn main() -> Result<()> { // Init a monotree instance: // manually select a db and a hasher as your preference // Monotree::