npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@accumulators/merkle-mountain-range

v4.2.3

Published

A TypeScript implementation of Merkle Mountain Ranges

Downloads

51

Readme

Merkle Mountain Range

MMR is a structure that allows appending and proving efficiently. Time complexity of both operations is O(log tree_size).

Example

import MemoryStore from "@accumulators/memory";
import { KeccakHasher } from "@accumulators/hashers";
import Mmr from "@accumulators/merkle-mountain-range";

const store = new MemoryStore();
const hasher = new KeccakHasher();

const mmr = new Mmr(store, hasher);

await mmr.append("1");
await mmr.append("2");
await mmr.append("3");
const { elementIndex } = await mmr.append("4");
await mmr.append("5");

const proof = await mmr.getProof(elementIndex);

console.log(await mmr.verifyProof(proof, "4")); // true

Functions


constructor(store: Store, hasher: Hasher, mmrId?: string)

Creates a new MMR with a given hasher instance and stores its value in provided store.

All the values in store have keys prefixes with mmrId. If you want to recreate an existing MMR, you need to provide the same mmrId as the original MMR. In case you want to create a new MMR, you can omit mmrId and it will be generated automatically. mmrId can be later accessed using the property (e.g. myMmr.mmrId).


append(value: string)

Appends a new value to the MMR. Returns the promise of AppendResult.

interface AppendResult {
  leavesCount: number; // number of leaves in the mmr after append
  elementsCount: number; // number of all nodes in the mmr after append
  elementIndex: number; // index in the mmr of the appended element
  rootHash: string; // root hash of the mmr after append
}

For instance after appending 6th element ot the MMR, the result would be:

{
    leavesCount: 6,
    elementsCount: 10,
    elementIndex: 9,
    rootHash: "0x..."
}

getProof(elementIndex: number, options?: ProofOptions)

Returns information that is needed to verify the proof of the element with a given elementIndex.

interface Proof {
  elementIndex: number; // index in the mmr of requested element (same as in function argument)
  elementHash: number; // hash of the requested element
  siblingHashes: string[]; // hashes of all siblings on the path from requested element to the peak
  peaksHashes: string[]; // hashes of all peaks
  elementsCount: number; // number of all nodes in the mmr
}
interface ProofOptions {
  elementsCount?: number; // You can provide elementsCount if you know its value, so it doesn't have to be fetched from the store
  formattingOpts?: {
    proof: FormattingOptions;
    peaks: FormattingOptions;
  };
}

formattingOpts.proof apply to siblingHashes and formattingOpts.peaks apply to peaksHashes.

More about FormattingOptions


verifyProof(proof: Proof, elementValue: string, options?: ProofOptions)

Verifies if a certain element in the MMR has a given value. Returns true if the proof is valid, false otherwise.


getProofs(elementsIds: number[], options?: ProofOptions)

Same as getProof but for multiple elements. Returns an array of Proof. Store is accessed for all proofs at once, so it's more efficient than calling getProof multiple times.


verifyProofs(proofs: Proof[], elementsValues: string[], options?: ProofOptions)


Same as verifyProof but for multiple elements. Returns an array of booleans. Store is accessed for all proofs at once, so it's more efficient than calling verifyProof multiple times.


getPeaks(options?: PeaksOptions)

Returns an array of hashes of all peaks in the MMR. The return type is promise of string[].

interface PeaksOptions {
  elementsCount?: number; // You can provide elementsCount if you know its value, so it doesn't have to be fetched from the store
  formattingOpts?: FormattingOptions;
}

More about FormattingOptions


bagThePeaks(elementsCount?: number)

Bags all peaks in the MMR and returns the final hash of type promise of string.

You can provide elementsCount if you know its value, so it doesn't have to be fetched from the store.


calculateRootHash(bag: string, leafCount: number)

Calculates the root hash of the MMR based on the hash returned from bagThePeaks function and the size of the MMR. Returns the promise of string.


retrievePeaksHashes(peaksIdxs: number[], formattingOptions?: FormattingOptions)

Returns promise of an array of hashes of peaks with given indexes. If formattingOptions are provided, the array will be formatted according to them.

More about FormattingOptions


clear()

Clears all the MMR data from the store.


Helper functions

All helper functions are static.


mapLeafIndexToElementIndex(leafIndex: number)

Converts leaf index (0-based) to element index.

Table of first few values:

| leafIndex | elementIndex | | :-------: | :----------: | | 0 | 1 | | 1 | 2 | | 2 | 4 | | 3 | 5 | | 4 | 8 |


mapElementIndexToLeafIndex(elementIndex: number)

Converts element index to leaf index (0-based). If the element is not a leaf, it will throw an error.


FormattingOptions

In some cases you may want peaks and siblings arrays to have a constant size between all requests. In that case you can set formatting options and provide nullValue that will be added at the end of an array if it's shorter than outputSize. If the array is longer than outputSize, it will throw an error.

interface FormattingOptions {
  outputSize: number; // size of the output array
  nullValue: string; // value with which the array will be filled
}

For example this code

const mmr = new Mmr(store, hasher);

await mmr.append("0x1");
await mmr.append("0x2");
await mmr.append("0x3");

const formattingOptions = {
  nullValue: `0x0`,
  outputSize: 4,
};
const options = {
  formattingOpts: {
    peaks: formattingOptions,
    proof: formattingOptions,
  },
};

const proof = await mmr.getProof(2, options);

console.log(proof.peaksHashes);

without formatting options would return

["0xe90b7bce...", "0x3"]

but with formatting options it will return

["0xe90b7bce...", "0x3", "0x0", "0x0"]

(0xe90b7bce... is just a hash("0x1", "0x2"))