Optimized sparse Merkle tree
Project description
Optimized Sparse Merkle Tree Implementation
Copyright and License
Copyright © 2025 Legendary Requirements
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Overview
To understand the design of this library, see the Algorithm documentation.
This library doesn't have its own methods for determining the index and hash of a leaf node; they are provided at the discretion of the client implementation, with the only restrictions being that the index must be in the range of 0 to 2256-1 and the hash must be exactly 32 bytes long. The library's sole functions are to calculate the root hash, provide the proofs for each of the indexes, and validate proofs. All internal hash calculations use SHA-256.
Installation
This library is available as a Python package.
python3 -m pip install "optimized-smt"
Usage
The only parameter accepted by the constructor defines whether to allow non-inclusion. If a leaf node hash is not provided, the library will throw an exception (allow non-inclusion set to false) or set the hash to the hash of the null value (allow non-inclusion set to true).
# Construct an optimized SMT.
optimized_smt = OptimizedSMT(allow_non_inclusion)
Populating the SMT is a three-step operation: adding indexes (to define the tree structure), setting hashes, and finalizing the SMT. Adding indexes and setting their hashes are separate operations as a tree's structure is often defined early and hashes arrive gradually over time.
# Add some participating indexes (array of int).
optimized_smt.add(some_indexes)
# Add more participating indexes (array of int).
optimized_smt.add(more_indexes)
# Set the hash for an index.
optimized_smt.set_hash(index, hash)
Finalizing the SMT calculates the root hash and the proofs for the indexes.
# Finalize the SMT.
optimized_smt.finalize()
Validation requires the index, the hash, the root hash, and the proof.
# Extract the root hash.
root_hash = optimized_smt.root_hash
# Get the proof for an index.
proof = optimized_smt.proof(index)
# Check that the proof is valid for the index, hash, and root hash.
print("Valid" if proof.is_valid(index, hash, root_hash) else "Not valid")
Multiple proofs may be validated in batch by bundling each index, hash, and proof into a proof candidate.
# Check that multiple proofs are valid.
for proof_result in SMTProof.is_valid_batch(proof_candidates, root_hash):
print(f"Proof at index {hex(proof_result.index)[2:]} is {"valid" if proof_result.valid else "not valid"}")
Finally, to reuse the SMT, reset it.
# Reset the SMT.
optimized_smt.reset()
Exporting and Importing Proofs
Proofs may be exported to and imported from JSON and binary.
JSON
# Export to JSON.
s = proof.to_json()
# Import from JSON.
proof = SMTProof.from_json(s)
Because JSON doesn't handle large numbers or byte arrays, values are encoded in hexadecimal (default) or in base64. The JSON export may also be compact (no extraneous whitespace, default) or formatted for ease of readability.
Binary
# Export to binary.
b = proof.to_binary()
# Import from binary.
proof = SMTProof.from_binary(b)
The binary import will work with byte streams. Any source that is Iterable[int], Iterator[int], or BufferedIOBase may be used, and the import will stop when the proof is read completely. This allows multiple proofs to be stored in a single file, possibly mixed with other data (e.g., leaf node index and hash, root hash).
Performance
On a MacBook Pro with an Apple M3 Pro chip running macOS 26, a tree with 1,000,000 indexes and hashes performs roughly as follows:
| Operation | Time |
|---|---|
| Add | 9.8s |
| Set hash | 2.9s |
| Finalize | 25s |
| Validate (single) | 51s |
| Validate (batch) | 41s |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file optimized_smt-1.0.2b0.tar.gz.
File metadata
- Download URL: optimized_smt-1.0.2b0.tar.gz
- Upload date:
- Size: 12.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.1 CPython/3.13.2 Darwin/25.0.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c674d02e47febcdf035afdb30bd3486aa64d202d05a45ed69fb0460606b7426
|
|
| MD5 |
579b7056433fff5c5d79932b6ea91914
|
|
| BLAKE2b-256 |
f274a5cc6086ee6813fc83c26efc08bd10b9291ea0c9e0ef133f822b2050a445
|
File details
Details for the file optimized_smt-1.0.2b0-py3-none-any.whl.
File metadata
- Download URL: optimized_smt-1.0.2b0-py3-none-any.whl
- Upload date:
- Size: 16.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.1 CPython/3.13.2 Darwin/25.0.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ce456f13daa6ac44e57179cffe99aa81975e3d969dccf401fbe4510067be98c
|
|
| MD5 |
dfdb05f479f291378b5325373c0014fe
|
|
| BLAKE2b-256 |
33cadb2ff3ce760ce3e736306af65796507d9de3ae667c691531fbf7053da62c
|