Skip to content

Commit

Permalink
Merge pull request #68 from privacy-scaling-explorations/feat/new-imt
Browse files Browse the repository at this point in the history
New `LeanIMT` incremental Merkle tree
  • Loading branch information
cedoor authored Nov 27, 2023
2 parents 4191997 + 8840a24 commit 34ffc3d
Show file tree
Hide file tree
Showing 84 changed files with 6,716 additions and 7,418 deletions.
52 changes: 26 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,77 +67,77 @@
<tbody>
<tr>
<td>
<a href="https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/incremental-merkle-tree">
@zk-kit/incremental-merkle-tree
<a href="https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/imt">
@zk-kit/imt
</a>
<a href="https://zkkit.pse.dev/modules/_zk_kit_incremental_merkle_tree.html">
<a href="https://zkkit.pse.dev/modules/_zk_kit_imt.html">
(docs)
</a>
</td>
<td>
<!-- NPM version -->
<a href="https://npmjs.org/package/@zk-kit/incremental-merkle-tree">
<img src="https://img.shields.io/npm/v/@zk-kit/incremental-merkle-tree.svg?style=flat-square" alt="NPM version" />
<a href="https://npmjs.org/package/@zk-kit/imt">
<img src="https://img.shields.io/npm/v/@zk-kit/imt.svg?style=flat-square" alt="NPM version" />
</a>
</td>
<td>
<!-- Downloads -->
<a href="https://npmjs.org/package/@zk-kit/incremental-merkle-tree">
<img src="https://img.shields.io/npm/dm/@zk-kit/incremental-merkle-tree.svg?style=flat-square" alt="Downloads" />
<a href="https://npmjs.org/package/@zk-kit/imt">
<img src="https://img.shields.io/npm/dm/@zk-kit/imt.svg?style=flat-square" alt="Downloads" />
</a>
</td>
<td>
<!-- Size -->
<a href="https://bundlephobia.com/package/@zk-kit/incremental-merkle-tree">
<img src="https://img.shields.io/bundlephobia/minzip/@zk-kit/incremental-merkle-tree" alt="npm bundle size (scoped)" />
<a href="https://bundlephobia.com/package/@zk-kit/imt">
<img src="https://img.shields.io/bundlephobia/minzip/@zk-kit/imt" alt="npm bundle size (scoped)" />
</a>
</td>
</tr>
<tr>
<td>
<a href="https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/incremental-merkle-tree.sol">
@zk-kit/incremental-merkle-tree.sol
<a href="https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/imt.sol">
@zk-kit/imt.sol
</a>
</td>
<td>
<!-- NPM version -->
<a href="https://npmjs.org/package/@zk-kit/incremental-merkle-tree.sol">
<img src="https://img.shields.io/npm/v/@zk-kit/incremental-merkle-tree.sol.svg?style=flat-square" alt="NPM version" />
<a href="https://npmjs.org/package/@zk-kit/imt.sol">
<img src="https://img.shields.io/npm/v/@zk-kit/imt.sol.svg?style=flat-square" alt="NPM version" />
</a>
</td>
<td>
<!-- Downloads -->
<a href="https://npmjs.org/package/@zk-kit/incremental-merkle-tree.sol">
<img src="https://img.shields.io/npm/dm/@zk-kit/incremental-merkle-tree.sol.svg?style=flat-square" alt="Downloads" />
<a href="https://npmjs.org/package/@zk-kit/imt.sol">
<img src="https://img.shields.io/npm/dm/@zk-kit/imt.sol.svg?style=flat-square" alt="Downloads" />
</a>
</td>
<td></td>
</tr>
<tr>
<td>
<a href="https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/sparse-merkle-tree">
@zk-kit/sparse-merkle-tree
<a href="https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/smt">
@zk-kit/smt
</a>
<a href="https://zkkit.pse.dev/modules/_zk_kit_sparse_merkle_tree.html">
<a href="https://zkkit.pse.dev/modules/_zk_kit_smt.html">
(docs)
</a>
</td>
<td>
<!-- NPM version -->
<a href="https://npmjs.org/package/@zk-kit/sparse-merkle-tree">
<img src="https://img.shields.io/npm/v/@zk-kit/sparse-merkle-tree.svg?style=flat-square" alt="NPM version" />
<a href="https://npmjs.org/package/@zk-kit/smt">
<img src="https://img.shields.io/npm/v/@zk-kit/smt.svg?style=flat-square" alt="NPM version" />
</a>
</td>
<td>
<!-- Downloads -->
<a href="https://npmjs.org/package/@zk-kit/sparse-merkle-tree">
<img src="https://img.shields.io/npm/dm/@zk-kit/sparse-merkle-tree.svg?style=flat-square" alt="Downloads" />
<a href="https://npmjs.org/package/@zk-kit/smt">
<img src="https://img.shields.io/npm/dm/@zk-kit/smt.svg?style=flat-square" alt="Downloads" />
</a>
</td>
<td>
<!-- Size -->
<a href="https://bundlephobia.com/package/@zk-kit/sparse-merkle-tree">
<img src="https://img.shields.io/bundlephobia/minzip/@zk-kit/sparse-merkle-tree" alt="npm bundle size (scoped)" />
<a href="https://bundlephobia.com/package/@zk-kit/smt">
<img src="https://img.shields.io/bundlephobia/minzip/@zk-kit/smt" alt="npm bundle size (scoped)" />
</a>
</td>
</tr>
Expand Down Expand Up @@ -286,9 +286,9 @@ ZK-kit provides a set of pre-configured development tools. All you have to deal

```bash
cd zk-kit
cp -r packages/sparse-merkle-tree packages/my-package
cp -r packages/smt packages/my-package
cd packages/my-package && rm -fr node_modules dist
grep -r -l "sparse-merkle-tree" . | xargs sed -i 's/sparse-merkle-tree/my-package/'
grep -r -l "smt" . | xargs sed -i 's/smt/my-package/'
# Update the remaining description/usage sections, and write your code in the src & tests folders!
```

Expand Down
36 changes: 36 additions & 0 deletions benchmarks/imt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import b from "benny"
import { poseidon2 } from "poseidon-lite"
import { IMT, LeanIMT } from "../packages/imt/src"

const name = "incremental-merkle-trees"

export default async function run() {
const treeDepth = 7
const numberOfLeaves = 2 ** treeDepth

b.suite(
name,

b.add(`IMT - Insert ${numberOfLeaves} leaves`, () => {
const tree1 = new IMT(poseidon2, treeDepth, 0, 2)

for (let i = 0; i < numberOfLeaves; i += 1) {
tree1.insert(i)
}
}),
b.add(`LeanIMT - Insert ${numberOfLeaves} leaves`, () => {
const tree2 = new LeanIMT((a, b) => poseidon2([a, b]))

for (let i = 0; i < numberOfLeaves; i += 1) {
tree2.insert(BigInt(i))
}
}),

b.cycle(),
b.complete(),

b.save({ folder: "benchmarks/results", file: name, version: "1.0.0", details: true }),
b.save({ folder: "benchmarks/results", file: name, format: "chart.html", details: true }),
b.save({ folder: "benchmarks/results", file: name, format: "table.html", details: true })
)
}
41 changes: 0 additions & 41 deletions benchmarks/incremental-merkle-tree.ts

This file was deleted.

8 changes: 4 additions & 4 deletions benchmarks/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import runIncrementalMerkleTree from "./incremental-merkle-tree"
import runIMT from "./imt"

const [benchmark] = process.argv.slice(2)

// If there is an argument with a specific benchmark to run, it will run only that
// benchmark, otherwise it will run all the benchmarks.
switch (benchmark) {
case "incremental-merkle-tree":
runIncrementalMerkleTree()
case "imt":
runIMT()
break
default:
runIncrementalMerkleTree()
runIMT()
}
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
"scripts": {
"build": "yarn build:js && yarn compile:sol",
"build:js": "yarn workspaces foreach --no-private run build",
"compile:sol": "yarn workspaces foreach --no-private run compile",
"compile:sol": "yarn workspaces foreach run compile",
"test": "yarn test:js && yarn test:sol",
"test:js": "jest --coverage",
"test:sol": "yarn workspace incremental-merkle-tree.sol test:coverage",
"lint": "eslint . --ext .js,.ts && yarn workspace incremental-merkle-tree.sol lint",
"test:sol": "yarn workspace imt.sol test:coverage",
"lint": "eslint . --ext .js,.ts && yarn workspace imt.sol lint",
"prettier": "prettier -c .",
"prettier:write": "prettier -w .",
"benchmarks": "rimraf benchmarks/results && ts-node benchmarks/index.ts",
Expand Down Expand Up @@ -50,7 +50,6 @@
"@typescript-eslint/parser": "^5.9.1",
"babel-jest": "^27.4.6",
"benny": "^3.7.1",
"circomlibjs": "^0.0.8",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.2.0",
Expand All @@ -60,10 +59,10 @@
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^25.7.0",
"husky": "^8.0.3",
"incrementalquintree": "^1.0.9",
"jest": "^27.4.1",
"jest-config": "^27.4.7",
"lint-staged": "^12.1.7",
"poseidon-lite": "^0.2.0",
"prettier": "^2.5.1",
"rimraf": "^3.0.2",
"rollup": "^2.64.0",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {PoseidonT3} from "poseidon-solidity/PoseidonT3.sol";

// Each incremental tree has certain properties and data that will
// be used to add new leaves.
struct IncrementalTreeData {
struct BinaryIMTData {
uint256 depth; // Depth of the tree (levels - 1).
uint256 root; // Root hash of the tree.
uint256 numberOfLeaves; // Number of leaves of the tree.
Expand All @@ -18,9 +18,9 @@ struct IncrementalTreeData {
/// @title Incremental binary Merkle tree.
/// @dev The incremental tree allows to calculate the root hash each time a leaf is added, ensuring
/// the integrity of the tree.
library IncrementalBinaryTree {
uint8 internal constant MAX_DEPTH = 32;
uint256 internal constant SNARK_SCALAR_FIELD =
library BinaryIMT {
uint8 public constant MAX_DEPTH = 32;
uint256 public constant SNARK_SCALAR_FIELD =
21888242871839275222246405745257275088548364400416034343698204186575808495617;

uint256 public constant Z_0 = 0;
Expand Down Expand Up @@ -98,13 +98,9 @@ library IncrementalBinaryTree {
/// @param self: Tree data.
/// @param depth: Depth of the tree.
/// @param zero: Zero value to be used.
function init(
IncrementalTreeData storage self,
uint256 depth,
uint256 zero
) public {
require(zero < SNARK_SCALAR_FIELD, "IncrementalBinaryTree: leaf must be < SNARK_SCALAR_FIELD");
require(depth > 0 && depth <= MAX_DEPTH, "IncrementalBinaryTree: tree depth must be between 1 and 32");
function init(BinaryIMTData storage self, uint256 depth, uint256 zero) public {
require(zero < SNARK_SCALAR_FIELD, "BinaryIMT: leaf must be < SNARK_SCALAR_FIELD");
require(depth > 0 && depth <= MAX_DEPTH, "BinaryIMT: tree depth must be between 1 and 32");

self.depth = depth;

Expand All @@ -120,8 +116,8 @@ library IncrementalBinaryTree {
self.root = zero;
}

function initWithDefaultZeroes(IncrementalTreeData storage self, uint256 depth) public {
require(depth > 0 && depth <= MAX_DEPTH, "IncrementalBinaryTree: tree depth must be between 1 and 32");
function initWithDefaultZeroes(BinaryIMTData storage self, uint256 depth) public {
require(depth > 0 && depth <= MAX_DEPTH, "BinaryIMT: tree depth must be between 1 and 32");

self.depth = depth;
self.useDefaultZeroes = true;
Expand All @@ -132,11 +128,11 @@ library IncrementalBinaryTree {
/// @dev Inserts a leaf in the tree.
/// @param self: Tree data.
/// @param leaf: Leaf to be inserted.
function insert(IncrementalTreeData storage self, uint256 leaf) public returns (uint256) {
function insert(BinaryIMTData storage self, uint256 leaf) public returns (uint256) {
uint256 depth = self.depth;

require(leaf < SNARK_SCALAR_FIELD, "IncrementalBinaryTree: leaf must be < SNARK_SCALAR_FIELD");
require(self.numberOfLeaves < 2**depth, "IncrementalBinaryTree: tree is full");
require(leaf < SNARK_SCALAR_FIELD, "BinaryIMT: leaf must be < SNARK_SCALAR_FIELD");
require(self.numberOfLeaves < 2 ** depth, "BinaryIMT: tree is full");

uint256 index = self.numberOfLeaves;
uint256 hash = leaf;
Expand Down Expand Up @@ -169,18 +165,15 @@ library IncrementalBinaryTree {
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
/// @param proofPathIndices: Path of the proof of membership.
function update(
IncrementalTreeData storage self,
BinaryIMTData storage self,
uint256 leaf,
uint256 newLeaf,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
) public {
require(newLeaf != leaf, "IncrementalBinaryTree: new leaf cannot be the same as the old one");
require(newLeaf < SNARK_SCALAR_FIELD, "IncrementalBinaryTree: new leaf must be < SNARK_SCALAR_FIELD");
require(
verify(self, leaf, proofSiblings, proofPathIndices),
"IncrementalBinaryTree: leaf is not part of the tree"
);
require(newLeaf != leaf, "BinaryIMT: new leaf cannot be the same as the old one");
require(newLeaf < SNARK_SCALAR_FIELD, "BinaryIMT: new leaf must be < SNARK_SCALAR_FIELD");
require(verify(self, leaf, proofSiblings, proofPathIndices), "BinaryIMT: leaf is not part of the tree");

uint256 depth = self.depth;
uint256 hash = newLeaf;
Expand All @@ -207,7 +200,7 @@ library IncrementalBinaryTree {
++i;
}
}
require(updateIndex < self.numberOfLeaves, "IncrementalBinaryTree: leaf index out of range");
require(updateIndex < self.numberOfLeaves, "BinaryIMT: leaf index out of range");

self.root = hash;
}
Expand All @@ -218,7 +211,7 @@ library IncrementalBinaryTree {
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
/// @param proofPathIndices: Path of the proof of membership.
function remove(
IncrementalTreeData storage self,
BinaryIMTData storage self,
uint256 leaf,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
Expand All @@ -233,30 +226,24 @@ library IncrementalBinaryTree {
/// @param proofPathIndices: Path of the proof of membership.
/// @return True or false.
function verify(
IncrementalTreeData storage self,
BinaryIMTData storage self,
uint256 leaf,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
) private view returns (bool) {
require(leaf < SNARK_SCALAR_FIELD, "IncrementalBinaryTree: leaf must be < SNARK_SCALAR_FIELD");
require(leaf < SNARK_SCALAR_FIELD, "BinaryIMT: leaf must be < SNARK_SCALAR_FIELD");
uint256 depth = self.depth;
require(
proofPathIndices.length == depth && proofSiblings.length == depth,
"IncrementalBinaryTree: length of path is not correct"
"BinaryIMT: length of path is not correct"
);

uint256 hash = leaf;

for (uint8 i = 0; i < depth; ) {
require(
proofSiblings[i] < SNARK_SCALAR_FIELD,
"IncrementalBinaryTree: sibling node must be < SNARK_SCALAR_FIELD"
);

require(
proofPathIndices[i] == 1 || proofPathIndices[i] == 0,
"IncrementalBinaryTree: path index is neither 0 nor 1"
);
require(proofSiblings[i] < SNARK_SCALAR_FIELD, "BinaryIMT: sibling node must be < SNARK_SCALAR_FIELD");

require(proofPathIndices[i] == 1 || proofPathIndices[i] == 0, "BinaryIMT: path index is neither 0 nor 1");

if (proofPathIndices[i] == 0) {
hash = PoseidonT3.hash([hash, proofSiblings[i]]);
Expand Down
Loading

0 comments on commit 34ffc3d

Please sign in to comment.