{"id":28778644,"url":"https://github.com/thyeem/monotree","last_synced_at":"2026-03-06T08:01:33.416Z","repository":{"id":54614465,"uuid":"243438574","full_name":"thyeem/monotree","owner":"thyeem","description":"An optimized Sparse Merkle Tree in Rust","archived":false,"fork":false,"pushed_at":"2025-09-02T10:53:41.000Z","size":235,"stargazers_count":60,"open_issues_count":3,"forks_count":19,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-11-15T13:06:35.819Z","etag":null,"topics":["cryptocurrency","database","inclusion-proof","optimization-algorithms","rust","sparse-merkle-tree","sparse-merkle-trie"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thyeem.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-02-27T05:27:40.000Z","updated_at":"2025-10-23T12:25:07.000Z","dependencies_parsed_at":"2025-07-22T09:25:22.848Z","dependency_job_id":"14b2586d-60b9-4a14-840e-dcae93e23042","html_url":"https://github.com/thyeem/monotree","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/thyeem/monotree","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thyeem%2Fmonotree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thyeem%2Fmonotree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thyeem%2Fmonotree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thyeem%2Fmonotree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thyeem","download_url":"https://codeload.github.com/thyeem/monotree/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thyeem%2Fmonotree/sbom","scorecard":{"id":883721,"data":{"date":"2025-08-11","repo":{"name":"github.com/thyeem/monotree","commit":"5731b00e0b31f76a834c55aa96ee563e7fa177a4"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":1,"reason":"Found 3/24 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":1,"reason":"1 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 9 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":8,"reason":"2 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: RUSTSEC-2024-0384","Warn: Project is vulnerable to: RUSTSEC-2024-0436"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T09:15:29.762Z","repository_id":54614465,"created_at":"2025-08-24T09:15:29.762Z","updated_at":"2025-08-24T09:15:29.762Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30166856,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T07:56:45.623Z","status":"ssl_error","status_checked_at":"2026-03-06T07:55:55.621Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cryptocurrency","database","inclusion-proof","optimization-algorithms","rust","sparse-merkle-tree","sparse-merkle-trie"],"created_at":"2025-06-17T16:42:28.794Z","updated_at":"2026-03-06T08:01:33.291Z","avatar_url":"https://github.com/thyeem.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e \u003cimg src=\"https://raw.githubusercontent.com/thyeem/monotree/master/monotree.png\" height=\"200\"/\u003e\u003c/p\u003e\n\n[![Build Status](https://api.travis-ci.com/thyeem/monotree.svg?token=QYwxZ27j8uz6zsrzY6bk\u0026branch=master)](https://app.travis-ci.com/thyeem/monotree)\n[![Crates.io](https://img.shields.io/crates/v/monotree.svg)](https://crates.io/crates/monotree)\n\n# Monotree\nRust implementation of an optimized Sparse Merkle Tree.   \nThis is a kind of binary-radix tree based on bitwise branching, _currently_, no nibble of bit.   \nFor now, branching unit is just ___a single bit___, _neither a 4-bit nor a byte nibble_.  \n\nSee [`monotree.py`](https://github.com/thyeem/compare-sparse-merkle-tries/blob/main/monotree.py) for a pure _Python_ implementation of `monotree`.\n\n## Features\n- Very __simple__ and __lightweight__, but __fast__ and __robust__.\n- __Fully featured__ Sparse Merkle Tree (SMT) as a storage\n- \u003cu\u003eThis includes: __non-inclusion proof__, as well as __inclusion proof__, and its verification.\u003c/u\u003e\n- Again, _NOT verbose_ at all.\n\nThis library mostly relies on the _Rust standard library only_ except for `database APIs` and `hashers`.\nCurrently, `monotree` supports these databases and hash functions following, but is designed to be super easy to customize and add:\n\n_Databases include_:\n- [`HashMap`](https://lib.rs/crates/hashbrown)\n- [`RocksDB`](https://lib.rs/crates/rocksdb)\n- [`Sled`](https://lib.rs/crates/sled)\n\n_Hashers include_:\n- [`Blake3`](https://lib.rs/crates/blake3)\n- [`Blake2s`](https://lib.rs/crates/blake2-rfc) and [`Blake2b`](https://lib.rs/crates/blake2-rfc)\n- [`SHA-2`](https://lib.rs/crates/sha2)\n- [`SHA-3 (Keccak)`](https://lib.rs/crates/sha3)\n\n## Install\nAdd dependency to `Cargo.toml`\n\n```toml\n[dependencies]\nmonotree = \"0.4.0\"\n\n# If you want to use it with specific database backends such as 'rocksdb' or 'sled',\nmonotree = { version = \"0.4.0\", features = [\"db_rocksdb\", \"db_sled\"] }\n```\n\nor use `cargo add`\n\n```bash\n$ cargo add monotree\n\n$ cargo add monotree -F \"db_rocksdb, db_sled\"\n```\n\n## Quick start\n\u003e Refer to _`examples/basic.rs`_ for a complete working example.\n\u003e\n\u003e Regarding __non-inclusion proof__ and __inclusion proof__, See _Merkle proof_ section in [More Examples](#more-examples) below.\n\n#### Initialize\n```rust\n// If no database or hash function is specified, \n// the tree defaults to using a HashMap and the Blake3 hash function.\nlet mut tree = Monotree::default();\n```\n#### Insert\n```rust\n// Generate a random key and leaf pair.\n// random_hash() creates a fixed-length random array of 32 bytes.\nlet key = random_hash();\nlet leaf = random_hash();\n\n// It is natural the tree root initially has 'None'.\nlet root = None;\n\n// Insert the entry (key, leaf) into tree, yielding a new root of tree\nlet root = tree.insert(root.as_ref(), \u0026key, \u0026leaf)?;\nassert_ne!(root, None);\n```\n#### Retrieve\n```rust\n// Retrieve the leaf inserted just before. Note that the last root was used.\nlet found = tree.get(root.as_ref(), \u0026key)?;\nassert_eq!(found, Some(leaf));\n```\n#### Remove\n```rust\n// Remove the entry\nlet root = tree.remove(root.as_ref(), \u0026key)?;\n\n// The tree is empty now and the root back to 'None'\nassert_eq!(tree.get(root.as_ref(), \u0026key)?, None);\nassert_eq!(root, None);\n```\n#### Batch: Atomic Transaction\nInstead of executing each operation one by one, write them in a batch and then commit them all at once.  \nOne can do the same thing above using batch operation for ___performance gain___ and ___atomicity___. In short,  \n\n`prepare` → many of {`insert`, `get`, `remove`} → `commit`  \n\n##### Prepare Transaction\n```rust\n// initialize an empty batch: prepare transaction\ntree.prepare();\n```\n##### Freely `insert`, `get`, and `remove`\n```rust\n// Insert the entry (key, leaf) within the batch\nlet root = tree.insert(root.as_ref(), \u0026key, \u0026leaf)?;\nassert_ne!(root, None);\n\n// Retrieve the inserted leaf using the batch root\nlet found = tree.get(root.as_ref(), \u0026key)?;\nassert_eq!(found, Some(leaf));\n\n// Remove the entry within the same batch\nlet root = tree.remove(root.as_ref(), \u0026key)?;\n\n// Ensure the entry was removed within the batch\nassert_eq!(tree.get(root.as_ref(), \u0026key)?, None);\n```\n##### Commit Transaction\n```rust\n// Commit the batch operations: commit transaction\ntree.commit();\n\n// Verify that the final root is `None` after commit\nassert_eq!(tree.get(root.as_ref(), \u0026key)?, None);\n```\n\n## Performance\n___All benchmarks were performed in a cumulative way___, where the root resulting from an operation just before was reused for subsequent operations.\nThey were carried out ___with randomly-generated bytes entries___ and ___from the root of an empty tree___. (Deletion starts from the last root.)\n\n\u003e Tested on: _Intel Core i5-3570 CPU @ 3.4GHz and 16 GB RAM / Ubuntu 18.04_ with `rustc stable 1.42.0`\n\u003e As a hasher, `Blake3` was used in this benchmark.\n\n|  Op.   | DB used | #Entries | Time Measured | Std Error | per Op.  |\n| :----: | :-----: | -------: | :-----------: | :-------: | :------: |\n| Insert | HashMap |       10 |    8.50 us    |  0.01 us  | 0.85 us  |\n| Insert | HashMap |      100 |   165.71 us   |  0.14 us  | 1.66 us  |\n| Insert | HashMap |     1000 |    2.32 ms    |  0.01 ms  | 2.32 us  |\n| Insert | HashMap |    10000 |   37.39 ms    |  0.02 ms  | 3.74 us  |\n|  Get   | HashMap |       10 |    7.82 us    |  0.01 us  | 0.78 us  |\n|  Get   | HashMap |      100 |   114.51 us   |  0.14 us  | 1.15 us  |\n|  Get   | HashMap |     1000 |    1.52 ms    |  0.01 ms  | 1.52 us  |\n|  Get   | HashMap |    10000 |   20.40 ms    |  0.01 ms  | 2.40 us  |\n| Remove | HashMap |       10 |   15.65 us    |  0.01 us  | 1.57 us  |\n| Remove | HashMap |      100 |   282.68 us   |  0.09 us  | 2.83 us  |\n| Remove | HashMap |     1000 |    4.23 ms    |  0.01 ms  | 4.23 us  |\n| Remove | HashMap |    10000 |   69.15 ms    |  0.07 ms  | 6.92 us  |\n|        |         |          |               |           |          |\n| Insert | RocksDB |       10 |   34.80 us    |  0.27 us  | 3.48 us  |\n| Insert | RocksDB |      100 |   602.55 us   |  2.72 us  | 6.03 us  |\n| Insert | RocksDB |     1000 |   10.35 ms    |  0.06 ms  | 10.35 us |\n| Insert | RocksDB |    10000 |   145.83 ms   |  0.20 ms  | 14.58 us |\n|  Get   | RocksDB |       10 |   10.20 us    |  0.06 us  | 1.02 us  |\n|  Get   | RocksDB |      100 |   142.20 us   |  0.79 us  | 1.42 us  |\n|  Get   | RocksDB |     1000 |    1.74 ms    |  0.01 ms  | 1.74 us  |\n|  Get   | RocksDB |    10000 |   22.94 ms    |  0.03 ms  | 2.29 us  |\n| Remove | RocksDB |       10 |   58.33 us    |  0.50 us  | 5.83 us  |\n| Remove | RocksDB |      100 |    1.02 ms    |  6.40 us  | 10.20 us |\n| Remove | RocksDB |     1000 |   20.22 ms    |  0.10 ms  | 20.22 us |\n| Remove | RocksDB |    10000 |   394.95 ms   |  1.46 ms  | 39.50 us |\n\n\n\n## Integration tests and benchmark\nperforms integration tests with full combinations of operations and tree types consisting of _Databases_ and _Hashers_ included.\n\n```bash\n    # Some tests are time consuming.\n    # --release or -r is optional, but without it, it will take a longer time to complete the tests\n    $ cargo test -r --all-features\n```\n\nperforms 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.\n\n```bash\n    $ cargo bench --all-features\n```\n\nand macroscopic-time benchmarks (but rather wider error bars) with all tree types were also prepared in _`examples/perf.rs`_.\n\n```bash\n    $ cargo run -r --example perf --all-features\n```\n\nand some examples describing how to manipulate `monotree`\n\n```bash\n    $ cargo run --example basic\n    $ cargo run -r --example advanced --all-features\n```\n\n## More Examples\n\n\u003e Refer to _`examples/advanced.rs`_ for a complete working example.\n\n#### Initialize with a specific database and hash function\n```rust\n// Init a monotree instance with a database and hash function\n// \n// Monotree::\u003cDATABASE, HASHER\u003e::new(DB_PATH)\n//      where DATABASE = {MemoryDB, RocksDB, Sled}\n//            HASHER = {Blake3, Blake2s, Blake2b, Sha2, Sha3}\nlet mut tree = Monotree::\u003cRocksDB, Blake2b\u003e::new(\"/tmp/monotree\");\n```\n\n#### Intrinsic batch processing: `inserts` and `removes`.\nAs shown in [Quick Start](#quick-start) above, __operations executed between__ `tree.prepare()` __and__ `tree.commit()` can be viewed as _a single batch operation_. By default, they are automatically cached and are written together in a batch unit.\n\nHowever, if you need to repeat the same operation, such as `insert` or `remove`, you can easily optimize performance by using `inserts` and `removes`.\n\n`inserts()` is ___significantly faster___ than `insert()` for the following reason:\n- Batch writes\n- Sorting keys prior to insertion\n- In-memory caching\n```rust\n// Prepare 100 random pairs of key and leaf.\n// random_hash(SIZE) creates a vector of fixed-length random array of 32 bytes.\nlet keys = random_hashes(100);\nlet leaves = random_hashes(100);\n\n// It is natural the tree root initially has 'None'\nlet root = None;\n\n// Insert a vector of entries of (key, leaf) into tree.\nlet root = tree.inserts(root.as_ref(), \u0026keys, \u0026leaves)?;\nassert_ne!(root, None);\n```\n\nSimilarly, `gets()` and `removes()` also are designed for batch usage. \n```rust\nlet result = tree.gets(root.as_ref(), \u0026keys)?;\nassert_eq!(result.len(), keys.len());\n\nlet root = tree.removes(root.as_ref(), \u0026keys)?;\n// surely, the tree has nothing nad the root back to 'None'\nassert_eq!(root, None);\n```\n\n#### Non-inclusion proof and inclusion proof, _Merkle Proof_\n`monotree` has compressed representations, but it fully retains the core property of the ___Sparse Merkle Trie___.   \n_non-inclusion proof_ is quite straightforward: Just go walk down the tree with a key (or a path) given.  \nIf we ___cannot successfully get a leaf___, we can assure that ___the leaf is not a part of the tree___.  \n\nThe process of __inclusion proof__ is outlined below:\n\n##### Generate a Merkle Proof\nPrepare a random tree for testing.\n```rust\n// random insertions for testing Merkle proof generation\nlet root = tree.inserts(root.as_ref(), \u0026keys, \u0026leaves)?;\n\n// pick a random key from the keys among inserted just before\nlet key = keys[99];\n```\n\nGenerate a Merkle proof for a given root and key.\n```rust\nlet proof = tree.get_merkle_proof(root.as_ref(), \u0026key)?;\n```\n\n##### Verify the Merkle Proof\nPrepare a __target leaf__ matched with the __target root__ above.\n```rust\n// where the Merkle proof verification starts off\nlet leaf = leaves[99];\n```\nTo verify the proof correctly, you ___need to provide a hasher matched___.\n```rust\n// Previously the tree was initialized with `Blake2b`\nlet hasher = Blake2b::new();\n```\n\nJust call `verify_proof(\u0026HASHER, \u0026ROOT, \u0026LEAF, \u0026PROOF)`. That's all!\n```rust\nlet verified = verify_proof(\u0026hasher, root.as_ref(), \u0026leaf, proof.as_ref());\nassert_eq!(verified, true);\n```\n\n#### Tracking the Latest Root\n___Sparse Merkle Trie___ is a space where ___multiple states (roots) coexist simultaneously___.   \nThere is no reason why a particular state must be stored more preciously than another. The __lastest root__ is _nothing more than the most recently updated state_.\n\n`monotree` has a minimal design. It __does not automatically update or track the latest root__.   \nHowever, `monotree` provides tools to update and fetch the latest root.\n\nUse `set_headroot(\u0026LATEST_ROOT)` to set the latest root to the database.\n```rust\ntree.set_headroot(root.as_ref());\n```\nUse `get_headroot()` to get the latest root from the database.\n```rust\nlet headroot = tree.get_headroot()?;\nassert_eq!(headroot, root);\n```\n\n## Further improvement\n\n`monotree` is a special case among the generalized binary radix trees, I propose to refer to it as ___Power Of Two radix tree or PoT___.\nIf `monotree` were generalized with `pow(2, n)` where `n`-nibbles is a branching unit, ___there would have been room for further performance improvement___.\n\n_One can easily make this improvement by building tries based on_ `monotree`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthyeem%2Fmonotree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthyeem%2Fmonotree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthyeem%2Fmonotree/lists"}