@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
Markdown
# 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.
[](https://badge.fury.io/js/@jayanth-kumar-morem/sparse-merkle-tree)
[](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