Merge pull request #11 from appliedzkp/feature/nary-tree

nary incrementaly merkle tree
This commit is contained in:
Omar Desogus
2022-01-17 21:40:19 +01:00
committed by GitHub
15 changed files with 72628 additions and 42602 deletions

3
.gitignore vendored
View File

@@ -50,8 +50,9 @@ gh-pages/*
# Optional npm cache directory
.npm
.DS_Store
zkeyFiles
coverage
benchmarks
benchmark
build

269
package-lock.json generated
View File

@@ -16,6 +16,7 @@
"@types/node": "^17.0.9",
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"benny": "^3.7.1",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.7.0",
@@ -33,6 +34,48 @@
"typescript": "^4.5.4"
}
},
"node_modules/@arrows/array": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@arrows/array/-/array-1.4.1.tgz",
"integrity": "sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==",
"dev": true,
"dependencies": {
"@arrows/composition": "^1.2.2"
}
},
"node_modules/@arrows/composition": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@arrows/composition/-/composition-1.2.2.tgz",
"integrity": "sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==",
"dev": true
},
"node_modules/@arrows/dispatch": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@arrows/dispatch/-/dispatch-1.0.3.tgz",
"integrity": "sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==",
"dev": true,
"dependencies": {
"@arrows/composition": "^1.2.2"
}
},
"node_modules/@arrows/error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@arrows/error/-/error-1.0.2.tgz",
"integrity": "sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==",
"dev": true
},
"node_modules/@arrows/multimethod": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@arrows/multimethod/-/multimethod-1.4.1.tgz",
"integrity": "sha512-AZnAay0dgPnCJxn3We5uKiB88VL+1ZIF2SjZohLj6vqY2UyvB/sKdDnFP+LZNVsTC5lcnGPmLlRRkAh4sXkXsQ==",
"dev": true,
"dependencies": {
"@arrows/array": "^1.4.1",
"@arrows/composition": "^1.2.2",
"@arrows/error": "^1.0.2",
"fast-deep-equal": "^3.1.3"
}
},
"node_modules/@babel/code-frame": {
"version": "7.15.8",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz",
@@ -3546,6 +3589,59 @@
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==",
"dev": true
},
"node_modules/benchmark": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz",
"integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=",
"dev": true,
"dependencies": {
"lodash": "^4.17.4",
"platform": "^1.3.3"
}
},
"node_modules/benny": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/benny/-/benny-3.7.1.tgz",
"integrity": "sha512-USzYxODdVfOS7JuQq/L0naxB788dWCiUgUTxvN+WLPt/JfcDURNNj8kN/N+uK6PDvuR67/9/55cVKGPleFQINA==",
"dev": true,
"dependencies": {
"@arrows/composition": "^1.0.0",
"@arrows/dispatch": "^1.0.2",
"@arrows/multimethod": "^1.1.6",
"benchmark": "^2.1.4",
"common-tags": "^1.8.0",
"fs-extra": "^10.0.0",
"json2csv": "^5.0.6",
"kleur": "^4.1.4",
"log-update": "^4.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/benny/node_modules/fs-extra": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/benny/node_modules/kleur": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz",
"integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -4403,6 +4499,15 @@
"node": ">= 4.0.0"
}
},
"node_modules/common-tags": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
"integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
"dev": true,
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/compare-func": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
@@ -8043,6 +8148,33 @@
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
"dev": true
},
"node_modules/json2csv": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/json2csv/-/json2csv-5.0.6.tgz",
"integrity": "sha512-0/4Lv6IenJV0qj2oBdgPIAmFiKKnh8qh7bmLFJ+/ZZHLjSeiL3fKKGX3UryvKPbxFbhV+JcYo9KUC19GJ/Z/4A==",
"dev": true,
"dependencies": {
"commander": "^6.1.0",
"jsonparse": "^1.3.1",
"lodash.get": "^4.4.2"
},
"bin": {
"json2csv": "bin/json2csv.js"
},
"engines": {
"node": ">= 10",
"npm": ">= 6.13.0"
}
},
"node_modules/json2csv/node_modules/commander": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
"integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
"dev": true,
"engines": {
"node": ">= 6"
}
},
"node_modules/json5": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
@@ -8500,6 +8632,12 @@
"integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=",
"dev": true
},
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
"dev": true
},
"node_modules/lodash.ismatch": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz",
@@ -10047,6 +10185,12 @@
"node": ">=8"
}
},
"node_modules/platform": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
"dev": true
},
"node_modules/prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@@ -12466,6 +12610,48 @@
}
},
"dependencies": {
"@arrows/array": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@arrows/array/-/array-1.4.1.tgz",
"integrity": "sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==",
"dev": true,
"requires": {
"@arrows/composition": "^1.2.2"
}
},
"@arrows/composition": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@arrows/composition/-/composition-1.2.2.tgz",
"integrity": "sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==",
"dev": true
},
"@arrows/dispatch": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@arrows/dispatch/-/dispatch-1.0.3.tgz",
"integrity": "sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==",
"dev": true,
"requires": {
"@arrows/composition": "^1.2.2"
}
},
"@arrows/error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@arrows/error/-/error-1.0.2.tgz",
"integrity": "sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==",
"dev": true
},
"@arrows/multimethod": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@arrows/multimethod/-/multimethod-1.4.1.tgz",
"integrity": "sha512-AZnAay0dgPnCJxn3We5uKiB88VL+1ZIF2SjZohLj6vqY2UyvB/sKdDnFP+LZNVsTC5lcnGPmLlRRkAh4sXkXsQ==",
"dev": true,
"requires": {
"@arrows/array": "^1.4.1",
"@arrows/composition": "^1.2.2",
"@arrows/error": "^1.0.2",
"fast-deep-equal": "^3.1.3"
}
},
"@babel/code-frame": {
"version": "7.15.8",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz",
@@ -15250,6 +15436,52 @@
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==",
"dev": true
},
"benchmark": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz",
"integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=",
"dev": true,
"requires": {
"lodash": "^4.17.4",
"platform": "^1.3.3"
}
},
"benny": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/benny/-/benny-3.7.1.tgz",
"integrity": "sha512-USzYxODdVfOS7JuQq/L0naxB788dWCiUgUTxvN+WLPt/JfcDURNNj8kN/N+uK6PDvuR67/9/55cVKGPleFQINA==",
"dev": true,
"requires": {
"@arrows/composition": "^1.0.0",
"@arrows/dispatch": "^1.0.2",
"@arrows/multimethod": "^1.1.6",
"benchmark": "^2.1.4",
"common-tags": "^1.8.0",
"fs-extra": "^10.0.0",
"json2csv": "^5.0.6",
"kleur": "^4.1.4",
"log-update": "^4.0.0"
},
"dependencies": {
"fs-extra": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
}
},
"kleur": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz",
"integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==",
"dev": true
}
}
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -15912,6 +16144,12 @@
}
}
},
"common-tags": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
"integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
"dev": true
},
"compare-func": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
@@ -18748,6 +18986,25 @@
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
"dev": true
},
"json2csv": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/json2csv/-/json2csv-5.0.6.tgz",
"integrity": "sha512-0/4Lv6IenJV0qj2oBdgPIAmFiKKnh8qh7bmLFJ+/ZZHLjSeiL3fKKGX3UryvKPbxFbhV+JcYo9KUC19GJ/Z/4A==",
"dev": true,
"requires": {
"commander": "^6.1.0",
"jsonparse": "^1.3.1",
"lodash.get": "^4.4.2"
},
"dependencies": {
"commander": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
"integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
"dev": true
}
}
},
"json5": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
@@ -19106,6 +19363,12 @@
"integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=",
"dev": true
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
"dev": true
},
"lodash.ismatch": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz",
@@ -20317,6 +20580,12 @@
"find-up": "^4.0.0"
}
},
"platform": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
"dev": true
},
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",

View File

@@ -39,6 +39,7 @@
"@types/node": "^17.0.9",
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"benny": "^3.7.1",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.7.0",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
// const b = require("benny");
// const { poseidon } = require("circomlibjs");
// const { IncrementalMerkleTree } = require("../dist/types")
// const Tree = require("incrementalquintree/build/IncrementalQuinTree")
// const tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2)
// const old = new Tree.IncrementalQuinTree(20, BigInt(0), 2, poseidon)
// const numberOfLeaves = 2 ** 10;
// b.suite(
// 'incremental merkle tree benchmarks',
// b.add('generate proof for nary binary tree', () => {
// for (let i = 0; i < numberOfLeaves; i+=1) {
// tree.insert(BigInt(i + 1))
// }
// for (let i = 0; i < numberOfLeaves; i += 1) {
// tree.createProof(i)
// }
// }),
// b.add('generate proof for old binary tree', () => {
// for (let i = 0; i < numberOfLeaves; i+=1) {
// old.insert(BigInt(i + 1))
// }
// for (let i = 0; i < numberOfLeaves; i += 1) {
// old.genMerklePath(i)
// }
// }),
// b.cycle(),
// b.complete(),
// b.save({ file: 'reduce', version: '1.0.0', details: true }),
// b.save({ file: 'reduce', format: 'chart.html', details: true }),
// )

29802
packages/merkle-tree/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,7 @@
"devDependencies": {
"@rollup/plugin-typescript": "^8.3.0",
"circomlibjs": "^0.0.8",
"incrementalquintree": "^1.0.9",
"rollup-plugin-terser": "^7.0.2"
}
}

View File

@@ -1,34 +1,22 @@
import checkParameter from "./checkParameter"
import { HashFunction, Proof, Node } from "./types"
/**
* A Merkle tree is a tree in which every leaf node is labelled with the cryptographic hash of a
* data block, and every non-leaf node is labelled with the cryptographic hash of the labels of its child nodes.
* It allows efficient and secure verification of the contents of large data structures.
* The MerkleTree class is a TypeScript implementation of Merkle tree and it provides all the functions to create
* efficient trees and to generate and verify proofs of membership.
*/
export default class MerkleTree {
static readonly maxDepth = 32 // 2**32 = 4294967296 possible leaves.
export default class IncrementalMerkleTree {
static readonly maxDepth = 32
private _root: Node
private readonly _nodes: Node[][]
private readonly _zeroes: Node[]
private readonly _hash: HashFunction
private readonly _depth: number
protected _root: Node
protected readonly _nodes: Node[][]
protected readonly _zeroes: Node[]
protected readonly _hash: HashFunction
protected readonly _depth: number
protected readonly _arity: number
/**
* Initializes the Merkle tree with the hash function, the depth and the zero value to use for zeroes.
* @param hash Hash function.
* @param depth Tree depth.
* @param zeroValue Zero values for zeroes.
*/
constructor(hash: HashFunction, depth: number, zeroValue: Node) {
constructor(hash: HashFunction, depth: number, zeroValue: Node, arity: number) {
checkParameter(hash, "hash", "function")
checkParameter(depth, "depth", "number")
checkParameter(zeroValue, "zeroValue", "number", "string", "bigint")
if (depth < 1 || depth > MerkleTree.maxDepth) {
if (depth < 1 || depth > IncrementalMerkleTree.maxDepth) {
throw new Error("The tree depth must be between 1 and 32")
}
@@ -37,12 +25,13 @@ export default class MerkleTree {
this._depth = depth
this._zeroes = []
this._nodes = []
this._arity = arity
for (let i = 0; i < depth; i += 1) {
this._zeroes.push(zeroValue)
this._nodes[i] = []
// There must be a zero value for each tree level (except the root).
zeroValue = hash([zeroValue, zeroValue])
zeroValue = hash(Array(this._arity).fill(zeroValue))
}
// The default root is the last zero value.
@@ -85,6 +74,25 @@ export default class MerkleTree {
return this._zeroes
}
/**
* Returns the zeroes nodes of the tree.
* @returns List of zeroes.
*/
public get arity(): number {
return this._arity
}
/**
* Returns the index of a leaf. If the leaf does not exist it returns -1.
* @param leaf Tree leaf.
* @returns Index of the leaf.
*/
public indexOf(leaf: Node): number {
checkParameter(leaf, "leaf", "number", "string", "bigint")
return this.leaves.indexOf(leaf)
}
/**
* Inserts a new leaf in the tree.
* @param leaf New leaf.
@@ -92,51 +100,23 @@ export default class MerkleTree {
public insert(leaf: Node) {
checkParameter(leaf, "leaf", "number", "string", "bigint")
if (leaf === this._zeroes[0]) {
if (leaf === this.zeroes[0]) {
throw new Error("The leaf cannot be a zero value")
}
if (this.leaves.length >= 2 ** this._depth) {
if (this.leaves.length >= this.arity ** this.depth) {
throw new Error("The tree is full")
}
let node = leaf
this.forEachLevel(this.leaves.length, (l, i, d) => {
this._nodes[l][i] = node
this.forEachLevel(this.leaves.length, (level, index, position) => {
this._nodes[level][index] = node
if (d) {
node = this._hash([node, this._zeroes[l]])
} else {
node = this._hash([this._nodes[l][i - 1], node])
}
})
const leftNeighbours: Array<Node> = this._nodes[level].slice(index - position, index)
const rightNeighbours: Array<Node> = Array(this._arity - 1 - position).fill(this.zeroes[level])
this._root = node
}
/**
* Deletes a leaf from the tree. It does not remove the leaf from
* the data structure. It set the leaf to be deleted to a zero value.
* @param index Index of the leaf to be deleted.
*/
public delete(index: number) {
checkParameter(index, "index", "number")
if (index < 0 || index >= this.leaves.length) {
throw new Error("The leaf does not exist in this tree")
}
let node = this._zeroes[0]
this.forEachLevel(index, (l, i, d) => {
this._nodes[l][i] = node
if (d) {
node = this._hash([node, this._nodes[l][i + 1] || this._zeroes[l]])
} else {
node = this._hash([this._nodes[l][i - 1], node])
}
node = this._hash(leftNeighbours.concat([node], rightNeighbours))
})
this._root = node
@@ -155,16 +135,21 @@ export default class MerkleTree {
}
const siblingNodes: Node[] = []
const path: (0 | 1)[] = []
const path: Array<number> = []
this.forEachLevel(index, (l, i, d) => {
if (d) {
path.push(0)
siblingNodes.push(this._nodes[l][i + 1] || this._zeroes[l])
} else {
path.push(1)
siblingNodes.push(this._nodes[l][i - 1])
this.forEachLevel(index, (level, index, position) => {
path.push(position)
const leftNeighbours: Array<Node> = this._nodes[level].slice(index - position, index)
const lastRighIndex = index + this._arity - position
const numOfLeaves = this._nodes[level].length
let rightNeighbours: Array<Node> = this._nodes[level].slice(index + 1, Math.min(numOfLeaves, lastRighIndex))
if (numOfLeaves < lastRighIndex) {
rightNeighbours = rightNeighbours.concat(Array(lastRighIndex - numOfLeaves).fill(this.zeroes[level]))
}
siblingNodes[level] = leftNeighbours.concat(rightNeighbours)
})
return { root: this._root, leaf: this.leaves[index], siblingNodes, path }
@@ -185,37 +170,18 @@ export default class MerkleTree {
let node = proof.leaf
for (let i = 0; i < proof.siblingNodes.length; i += 1) {
if (proof.path[i]) {
node = this._hash([proof.siblingNodes[i], node])
} else {
node = this._hash([node, proof.siblingNodes[i]])
}
proof.siblingNodes[i].splice(proof.path[i], 0, node)
node = this._hash(proof.siblingNodes[i])
}
return proof.root === node
}
/**
* Returns the index of a leaf. If the leaf does not exist it returns -1.
* @param leaf Tree leaf.
* @returns Index of the leaf.
*/
public indexOf(leaf: Node): number {
checkParameter(leaf, "leaf", "number", "string", "bigint")
return this.leaves.indexOf(leaf)
}
/**
* Provides a bottom-up tree traversal where for each level it calls a callback.
* @param index Index of the leaf.
* @param callback Callback with tree level, index of node in that level and direction (left node: true, right node: false).
*/
private forEachLevel(index: number, callback: (level: number, index: number, direction: boolean) => void) {
private forEachLevel(index: number, callback: (level: number, index: number, position: number) => void) {
for (let level = 0; level < this._depth; level += 1) {
callback(level, index, index % 2 === 0)
callback(level, index, index % this._arity)
index = Math.floor(index / 2)
index = Math.floor(index / this.arity)
}
}
}

View File

@@ -1,4 +1,4 @@
import MerkleTree from "./merkle-tree"
import IncrementalMerkleTree from "./incremental-merkle-tree"
export { MerkleTree }
export { IncrementalMerkleTree }
export * from "./types"

View File

@@ -6,5 +6,5 @@ export type Proof = {
root: Node
leaf: Node
siblingNodes: Node[]
path: (0 | 1)[]
path: Array<number>
}

View File

@@ -1,30 +1,34 @@
import { poseidon } from "circomlibjs"
import { MerkleTree } from "../src"
import { IncrementalMerkleTree } from "../src"
describe("InterRep Merkle Tree", () => {
const depth = 16
let tree: MerkleTree
describe("Nary Merkle Tree", () => {
const depth = 20
const arity = 5
let tree: IncrementalMerkleTree
describe("Merkle Tree class", () => {
beforeEach(() => {
tree = new MerkleTree(poseidon, depth, BigInt(0))
tree = new IncrementalMerkleTree(poseidon, depth, BigInt(0), arity)
})
it("Should not initialize a Merkle tree with wrong parameters", () => {
expect(() => new MerkleTree(undefined as any, 33, 0)).toThrow("Parameter 'hash' is not defined")
expect(() => new MerkleTree(1 as any, 33, 0)).toThrow("Parameter 'hash' is none of these types: function")
expect(() => new IncrementalMerkleTree(undefined as any, 33, 0, arity)).toThrow("Parameter 'hash' is not defined")
expect(() => new IncrementalMerkleTree(1 as any, 33, 0, arity)).toThrow(
"Parameter 'hash' is none of these types: function"
)
})
it("Should not initialize a Merkle tree with depth > 32", () => {
expect(() => new MerkleTree(poseidon, 33, BigInt(0))).toThrow("The tree depth must be between 1 and 32")
expect(() => new IncrementalMerkleTree(poseidon, 33, BigInt(0), arity)).toThrow(
"The tree depth must be between 1 and 32"
)
})
it("Should initialize a Merkle tree", () => {
expect(tree.depth).toEqual(depth)
expect(tree.root).toEqual(BigInt("19217088683336594659449020493828377907203207941212636669271704950158751593251"))
expect(tree.leaves).toHaveLength(0)
expect(tree.zeroes).toHaveLength(depth)
expect(tree.arity).toEqual(arity)
})
it("Should not insert a zero leaf", () => {
@@ -32,83 +36,26 @@ describe("InterRep Merkle Tree", () => {
})
it("Should not insert a leaf in a full tree", () => {
const fullTree = new MerkleTree(poseidon, 1, BigInt(0))
const fullTree = new IncrementalMerkleTree(poseidon, 1, BigInt(0), 3)
fullTree.insert(BigInt(1))
fullTree.insert(BigInt(2))
fullTree.insert(BigInt(3))
expect(() => fullTree.insert(BigInt(3))).toThrow("The tree is full")
})
it("Should insert a leaf", () => {
tree.insert(BigInt(1))
expect(tree.root).toEqual(BigInt("16211261537006706331557500769845541584780950636316907182067421710925347020533"))
expect(tree.leaves).toHaveLength(1)
})
it("Should not delete a leaf that does not exist", () => {
expect(() => tree.delete(0)).toThrow("The leaf does not exist in this tree")
})
it("Should delete a leaf", () => {
tree.insert(BigInt(1))
tree.delete(0)
expect(tree.root).toEqual(BigInt("19217088683336594659449020493828377907203207941212636669271704950158751593251"))
expect(tree.leaves[0]).toEqual(BigInt(0))
})
it("Should delete all the leaves of a tree", () => {
tree.insert(BigInt(1))
tree.insert(BigInt(2))
tree.insert(BigInt(3))
tree.delete(0)
tree.delete(1)
tree.delete(2)
expect(tree.root).toEqual(BigInt("19217088683336594659449020493828377907203207941212636669271704950158751593251"))
})
it("Should return the index of a leaf", () => {
tree.insert(BigInt(1))
tree.insert(BigInt(2))
const index = tree.indexOf(BigInt(2))
expect(index).toEqual(1)
})
it("Should not create any proof if the leaf does not exist", () => {
tree.insert(BigInt(1))
expect(() => tree.createProof(1)).toThrow("The leaf does not exist in this tree")
})
it("Should create a valid proof", () => {
tree.insert(BigInt(1))
tree.insert(BigInt(2))
tree.insert(BigInt(3))
tree.insert(BigInt(4))
tree.insert(BigInt(5))
tree.delete(0)
tree.delete(2)
for (let i = 0; i < 5; i += 1) {
const proof = tree.createProof(i)
expect(tree.verifyProof(proof)).toBeTruthy()
}
})
it("Should not verify any proof if the parameters are wrong", () => {
expect(() => tree.verifyProof(1 as any)).toThrow("Parameter 'proof' is none of these types: object")
expect(() => tree.verifyProof({ root: true } as any)).toThrow(
"Parameter 'proof.root' is none of these types: number, string, bigint"
)
expect(() => fullTree.insert(BigInt(4))).toThrow("The tree is full")
})
})
it("Should create a valid proof", () => {
const numberOfLeaves = 50
for (let i = 0; i < numberOfLeaves; i += 1) {
tree.insert(BigInt(i + 1))
}
for (let i = 0; i < numberOfLeaves; i += 1) {
const proof = tree.createProof(i)
expect(tree.verifyProof(proof)).toBeTruthy()
}
})
})

View File

@@ -1,4 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src", "tests", "rollup.config.ts"]
"include": ["src", "tests", "rollup.config.ts", "benchmarks"]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
/* eslint @typescript-eslint/no-var-requires: "off" */
const Tree = require("incrementalquintree/build/IncrementalQuinTree")
// import { IncrementalMerkleTree } from "@zk-kit/merkle-tree"
import * as ciromlibjs from "circomlibjs"
import * as ethers from "ethers"
import { MerkleProof } from "@zk-kit/types"

File diff suppressed because it is too large Load Diff