UNPKG

@jayanth-kumar-morem/sparse-merkle-tree

Version:

A TypeScript implementation of Sparse Merkle Trees with Poseidon hash function support

349 lines (248 loc) โ€ข 8.01 kB
# Sparse Merkle Tree TypeScript A TypeScript implementation of Sparse Merkle Trees with Poseidon hash function support. This library provides efficient sparse merkle tree operations with non-membership proof generation and verification. [![npm version](https://badge.fury.io/js/@jayanth-kumar-morem/sparse-merkle-tree.svg)](https://badge.fury.io/js/@jayanth-kumar-morem/sparse-merkle-tree) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) ## Features - ๐ŸŒณ **Sparse Merkle Tree**: Efficient sparse merkle tree implementation - ๐Ÿ” **Poseidon Hash**: Support for Poseidon hash function (requires circomlib) - โœ… **Non-membership Proofs**: Generate and verify non-membership proofs - ๐ŸŽฏ **TypeScript**: Full TypeScript support with type definitions - โšก **Efficient**: Optimized for performance with minimal memory usage - ๐Ÿงช **Well-tested**: Comprehensive test suite with 100% coverage - ๐Ÿ“ฆ **Zero Dependencies**: No runtime dependencies (peer dependency on circomlib) ## Installation ```bash npm install @jayanth-kumar-morem/sparse-merkle-tree circomlib ``` ## Quick Start ```typescript import { SMT } from '@jayanth-kumar-morem/sparse-merkle-tree'; import { buildPoseidon } from 'circomlib'; async function example() { // Initialize Poseidon hash function const poseidon = await buildPoseidon(); // Create a new Sparse Merkle Tree with depth 20 (default) const smt = new SMT(poseidon, 20); // Add key-value pairs smt.add(1n, 100n); smt.add(2n, 200n); smt.add(42n, 4200n); // Get values console.log(smt.get(1n)); // 100n console.log(smt.has(2n)); // true console.log(smt.get(999n)); // undefined // Get the merkle root console.log(smt.root); // bigint root hash // Generate non-membership proof for a key not in the tree const proof = smt.createNonMembershipProof(999n); // Verify the proof const isValid = smt.verifyNonMembershipProof( 999n, proof.siblings, proof.pathBits, proof.root ); console.log(isValid); // true } example(); ``` ## API Reference ### Constructor #### `new SMT(poseidon, depth?)` Creates a new Sparse Merkle Tree instance. **Parameters:** - `poseidon` - Poseidon hash function instance from circomlib - `depth` (optional) - Tree depth (default: 20) **Example:** ```typescript const smt = new SMT(poseidon, 16); // Tree with depth 16 ``` ### Methods #### `add(key: bigint, value: bigint): void` Adds a key-value pair to the tree. **Parameters:** - `key` - The key as a bigint - `value` - The value as a bigint **Example:** ```typescript smt.add(42n, 1337n); ``` #### `get(key: bigint): bigint | undefined` Retrieves the value for a given key. **Parameters:** - `key` - The key to look up **Returns:** - The value if the key exists, `undefined` otherwise **Example:** ```typescript const value = smt.get(42n); // Returns 1337n or undefined ``` #### `has(key: bigint): boolean` Checks if a key exists in the tree. **Parameters:** - `key` - The key to check **Returns:** - `true` if the key exists, `false` otherwise **Example:** ```typescript if (smt.has(42n)) { console.log('Key exists!'); } ``` #### `root: bigint` (getter) Gets the current merkle root of the tree. **Returns:** - The root hash as a bigint **Example:** ```typescript const currentRoot = smt.root; ``` #### `createNonMembershipProof(key: bigint): NonMembershipProof` Creates a non-membership proof for a key that is not in the tree. **Parameters:** - `key` - The key to prove non-membership for **Returns:** - `NonMembershipProof` object containing: - `siblings: bigint[]` - Array of sibling hashes - `pathBits: number[]` - Array of path bits (0 or 1) - `root: bigint` - The tree root **Example:** ```typescript const proof = smt.createNonMembershipProof(999n); ``` #### `verifyNonMembershipProof(key: bigint, siblings: bigint[], pathBits: number[], expectedRoot: bigint): boolean` Verifies a non-membership proof. **Parameters:** - `key` - The key that should not be in the tree - `siblings` - Array of sibling hashes from the proof - `pathBits` - Array of path bits from the proof - `expectedRoot` - The expected root hash **Returns:** - `true` if the proof is valid, `false` otherwise **Example:** ```typescript const isValid = smt.verifyNonMembershipProof( 999n, proof.siblings, proof.pathBits, proof.root ); ``` ## Types ### NonMembershipProof ```typescript interface NonMembershipProof { siblings: bigint[]; // Sibling hashes for the merkle path pathBits: number[]; // Path bits (0 for left, 1 for right) root: bigint; // Tree root hash } ``` ## Use Cases This library is particularly useful for: - **Zero-Knowledge Proofs**: Proving non-membership without revealing the entire tree - **Blockchain Applications**: Efficient state management and proof generation - **Privacy-Preserving Systems**: Selective disclosure and private set membership - **Cryptographic Protocols**: Building blocks for advanced cryptographic schemes ## Performance Considerations - **Tree Depth**: Smaller depths (16-20) are recommended for better performance - **Batch Operations**: Add multiple values before computing the root for efficiency - **Memory Usage**: The tree stores only non-zero leaves, making it memory efficient - **Proof Size**: Proof size is O(depth), typically 20-32 elements ## Examples ### Basic Usage ```typescript import { SMT } from '@jayanth-kumar-morem/sparse-merkle-tree'; import { buildPoseidon } from 'circomlib'; async function basicExample() { const poseidon = await buildPoseidon(); const smt = new SMT(poseidon); // Add some data smt.add(1n, 100n); smt.add(2n, 200n); // Check membership console.log(smt.has(1n)); // true console.log(smt.has(3n)); // false console.log('Tree root:', smt.root); } ``` ### Non-membership Proofs ```typescript async function proofExample() { const poseidon = await buildPoseidon(); const smt = new SMT(poseidon); // Add some data smt.add(10n, 1000n); smt.add(20n, 2000n); // Prove that key 15 is not in the tree const proof = smt.createNonMembershipProof(15n); // Verify the proof const isValid = smt.verifyNonMembershipProof( 15n, proof.siblings, proof.pathBits, proof.root ); console.log('Proof is valid:', isValid); // true console.log('Proof size:', proof.siblings.length); // equals tree depth } ``` ### Tree Comparison ```typescript async function comparisonExample() { const poseidon = await buildPoseidon(); const smt1 = new SMT(poseidon); const smt2 = new SMT(poseidon); // Add same data in different order smt1.add(1n, 100n); smt1.add(2n, 200n); smt2.add(2n, 200n); smt2.add(1n, 100n); // Trees have the same root regardless of insertion order console.log(smt1.root === smt2.root); // true } ``` ## Testing Run the test suite: ```bash npm test ``` Run tests with coverage: ```bash npm run test:coverage ``` Run tests in watch mode: ```bash npm run test:watch ``` ## Building Build the TypeScript code: ```bash npm run build ``` ## Linting Lint the code: ```bash npm run lint ``` Fix linting issues: ```bash npm run lint:fix ``` ## Contributing 1. Fork the repository 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 3. Commit your changes (`git commit -m 'Add some amazing feature'`) 4. Push to the branch (`git push origin feature/amazing-feature`) 5. Open a Pull Request ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## Related Projects - [circomlib](https://github.com/iden3/circomlib) - Poseidon hash function implementation - [merkletreejs](https://github.com/miguelmota/merkletreejs) - Standard Merkle Tree implementation ## Changelog ### 1.0.0 - Initial release - Basic sparse merkle tree operations - Non-membership proof generation and verification - TypeScript support - Comprehensive test suite