Files
merkletreejs/README-functional.md
Miguel Mota a8b06d34af feat: add functional interface
- Add viem-inspired functional interface for MerkleTree operations
- Add complete test coverage for all exported functions
- Include detailed documentation and examples
2025-09-14 23:59:16 -07:00

9.4 KiB

Functional API for MerkleTreeJS

A functional programming interface for merkletreejs that provides an intuitive, viem-inspired API for working with Merkle trees. This wrapper makes it easy to create, manipulate, and verify Merkle trees with a clean, functional interface.

Features

  • Functional API: Intuitive, functional interface inspired by viem
  • Type Safety: Full TypeScript support with proper type definitions
  • Flexible Input: Accepts strings, buffers, objects, and numbers as leaves
  • Multiple Hash Functions: Built-in support for SHA256 (default), keccak256, and custom hash functions
  • Proof Generation: Easy proof creation and verification
  • Tree Manipulation: Add, remove, and update leaves
  • Serialization: Marshal/unmarshal trees and proofs to JSON
  • Advanced Options: Support for Bitcoin-style trees, sorted trees, and complete trees

Installation

npm install merkletreejs

Quick Start

import { createMerkleTree, getHexRoot, getProof, verifyProof } from 'merkletreejs'

// Create a simple Merkle tree
const leaves = ['a', 'b', 'c', 'd']
const tree = createMerkleTree(leaves)
const root = getHexRoot(tree)

// Generate and verify a proof
const proof = getProof(tree, 'b')
const isValid = verifyProof(proof, 'b', root)
console.log('Proof valid:', isValid) // true

You can also import the original MerkleTree class if needed:

import { MerkleTree, createMerkleTree } from 'merkletreejs'

// Using the functional API
const functionalTree = createMerkleTree(['a', 'b', 'c'])

// Using the original class API
const classTree = new MerkleTree(['a', 'b', 'c'])

API Reference

Core Functions

createMerkleTree(leaves, hashFn?, options?)

Creates a Merkle tree from an array of leaves. Uses SHA256 as the default hash function.

const tree = createMerkleTree(['a', 'b', 'c'])
// Using custom hash function
const customHashFn = (data) => {
  const crypto = require('crypto')
  return crypto.createHash('sha256').update(data).digest()
}
const treeWithOptions = createMerkleTree(leaves, customHashFn, { sort: true })

getHexRoot(tree)

Gets the root hash as a hex string.

const root = getHexRoot(tree)

getRoot(tree)

Gets the root hash as a Buffer.

const rootBuffer = getRoot(tree)

Tree Manipulation

addLeaf(tree, leaf, options?)

Adds a single leaf to the tree.

const updatedTree = addLeaf(tree, 'newLeaf')
const updatedTreeWithHash = addLeaf(tree, 'newLeaf', { shouldHash: true })

addLeaves(tree, leaves, options?)

Adds multiple leaves to the tree.

const updatedTree = addLeaves(tree, ['leaf1', 'leaf2'])
const updatedTreeWithHash = addLeaves(tree, ['leaf1', 'leaf2'], { shouldHash: true })

removeLeaf(tree, index)

Removes a leaf by index.

const removedLeaf = removeLeaf(tree, 0)

updateLeaf(tree, index, value, options?)

Updates a leaf at a specific index.

updateLeaf(tree, 0, 'newValue')
updateLeaf(tree, 0, 'newValue', { shouldHash: true })

Proof Operations

getProof(tree, leaf, index?)

Gets a proof for a specific leaf.

const proof = getProof(tree, 'targetLeaf')

getHexProof(tree, leaf, index?)

Gets a proof as hex strings.

const hexProof = getHexProof(tree, 'targetLeaf')

verifyProof(proof, leaf, root, hashFn?, options?)

Verifies a proof against a root and target leaf.

const isValid = verifyProof(proof, 'targetLeaf', root)

getMultiProof(tree, indices)

Gets a multiproof for multiple indices.

const multiProof = getMultiProof(tree, [0, 2, 4])

verifyMultiProof(root, proofIndices, proofLeaves, leavesCount, proof, hashFn?, options?)

Verifies a multiproof.

const isValid = verifyMultiProof(root, [0, 2], [leaf0, leaf2], 5, proof)

Tree Information

getLeaves(tree)

Gets all leaves as Buffers.

const leaves = getLeaves(tree)

getHexLeaves(tree)

Gets all leaves as hex strings.

const hexLeaves = getHexLeaves(tree)

getLeafCount(tree)

Gets the number of leaves.

const count = getLeafCount(tree)

getLeaf(tree, index)

Gets a specific leaf by index.

const leaf = getLeaf(tree, 0)

getHexLeaf(tree, index)

Gets a specific leaf by index as a hex string.

const hexLeaf = getHexLeaf(tree, 0)

getLeafIndex(tree, leaf)

Gets the index of a specific leaf.

const index = getLeafIndex(tree, 'targetLeaf')

Serialization

marshalTree(tree)

Converts a tree to JSON string.

const json = marshalTree(tree)

unmarshalTree(jsonStr, hashFn?, options?)

Creates a tree from JSON string.

const tree = unmarshalTree(jsonStr)

marshalProof(proof)

Converts a proof to JSON string.

const proofJson = marshalProof(proof)

unmarshalProof(jsonStr)

Creates a proof from JSON string.

const proof = unmarshalProof(proofJson)

Hash Functions

The functional API uses SHA256 as the default hash function. You can specify a custom hash function as the second parameter:

import { createMerkleTree } from 'merkletreejs'

// Default SHA256
const tree = createMerkleTree(['a', 'b', 'c'])

// Custom keccak256
const keccakTree = createMerkleTree(['a', 'b', 'c'], (data) => {
  const keccak256 = require('keccak256')
  return keccak256(Buffer.from(String(data)))
})

// Custom SHA1
const sha1Tree = createMerkleTree(['a', 'b', 'c'], (data) => {
  const crypto = require('crypto')
  return crypto.createHash('sha1').update(data).digest()
})

Options

The wrapper supports all options from the original MerkleTree class:

const options = {
  duplicateOdd: false,        // Duplicate odd nodes
  hashLeaves: false,          // Hash leaves before adding (default: false)
  isBitcoinTree: false,       // Use Bitcoin-style tree
  sortLeaves: false,          // Sort leaves
  sortPairs: false,           // Sort pairs
  sort: false,                // Sort leaves and pairs
  fillDefaultHash: null,      // Fill default hash function
  complete: false,            // Create complete tree (recommended for multiproofs)
  concatenator: Buffer.concat // Concatenation function
}

Examples

Basic Usage

import { createMerkleTree, getHexRoot, getProof, verifyProof } from 'merkletreejs'

// Create tree
const leaves = ['a', 'b', 'c', 'd']
const tree = createMerkleTree(leaves)
const root = getHexRoot(tree)

// Generate proof
const proof = getProof(tree, 'b')

// Verify proof
const isValid = verifyProof(proof, 'b', root)
console.log('Valid:', isValid) // true

Object Data

import { createMerkleTree, getHexRoot, getProof } from 'merkletreejs'

const data = [
  { owner: "0x123...", handle: "alice" },
  { owner: "0x456...", handle: "bob" }
]

// Hash the objects first, then create tree
const hashedData = data.map(item => Buffer.from(JSON.stringify(item)))
const tree = createMerkleTree(hashedData)
const root = getHexRoot(tree)

// Get proof for bob's data (need to hash it first)
const bobHash = Buffer.from(JSON.stringify(data[1]))
const bobProof = getProof(tree, bobHash)

Advanced Options

import { createMerkleTree } from 'merkletreejs'

// Bitcoin-style tree with SHA256 (default hash function)
const bitcoinTree = createMerkleTree(leaves, undefined, { 
  isBitcoinTree: true 
})

// Using keccak256 hash function
const keccakTree = createMerkleTree(leaves, (data) => {
  const keccak256 = require('keccak256')
  return keccak256(Buffer.from(String(data)))
})

// Sorted tree (recommended for multiproofs)
const sortedTree = createMerkleTree(leaves, undefined, { 
  sort: true 
})

// Complete tree (recommended for multiproofs)
const completeTree = createMerkleTree(leaves, undefined, { 
  complete: true 
})

Tree Manipulation

import { createMerkleTree, addLeaf, addLeaves, removeLeaf, updateLeaf } from 'merkletreejs'

let tree = createMerkleTree(['a', 'b'])

// Add single leaf
tree = addLeaf(tree, 'c')

// Add multiple leaves
tree = addLeaves(tree, ['d', 'e'])

// Add leaves with hashing
tree = addLeaf(tree, 'f', { shouldHash: true })
tree = addLeaves(tree, ['g', 'h'], { shouldHash: true })

// Update leaf
updateLeaf(tree, 0, 'newValue')

// Remove leaf
const removed = removeLeaf(tree, 0)

Comparison with Original API

Original MerkleTreeJS

const tree = new MerkleTree(leaves, hashFn, options)
const root = tree.getHexRoot()
const proof = tree.getProof(leaf)
const isValid = tree.verify(proof, leaf, root)

Functional API

const tree = createMerkleTree(leaves, hashFn, options)
const root = getHexRoot(tree)
const proof = getProof(tree, leaf)
const isValid = verifyProof(proof, leaf, root)

Benefits

  1. Intuitive API: Functional approach inspired by viem
  2. Type Safety: Full TypeScript support
  3. Flexible Input: Accepts various data types
  4. Easy Integration: Drop-in replacement for existing code
  5. Comprehensive: All original features available
  6. Well Documented: Clear examples and documentation

Contributing

This functional API is designed to be a thin layer over the original MerkleTreeJS library. All the original functionality is preserved while providing a more intuitive API. Feel free to contribute improvements or additional features!