This commit is contained in:
poma 2020-07-31 07:12:45 +03:00
parent 06b7ab5835
commit 2c1656ab49
No known key found for this signature in database
GPG Key ID: BA20CB01FE165657
3 changed files with 49 additions and 4 deletions

View File

@ -2,6 +2,8 @@
This is a fixed depth merkle tree implementation with sequential inserts This is a fixed depth merkle tree implementation with sequential inserts
Requires Node.js >= 14
## Usage ## Usage
```javascript ```javascript

View File

@ -1,13 +1,13 @@
{ {
"name": "merkle-tree", "name": "merkle-tree",
"version": "1.0.0", "version": "0.1.0",
"description": "", "description": "Fixed depth merkle tree implementation with sequential inserts",
"main": "src/merkleTree.js", "main": "src/merkleTree.js",
"scripts": { "scripts": {
"test": "mocha", "test": "mocha",
"lint": "eslint ." "lint": "eslint ."
}, },
"keywords": [], "keywords": ["merkle", "tree", "merkleTree"],
"author": "Roman Semenov <semenov.roma@gmail.com>", "author": "Roman Semenov <semenov.roma@gmail.com>",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {

View File

@ -4,7 +4,23 @@ const defaultHash = require('./mimc')
// todo ensure consistent types in tree and inserted elements? // todo ensure consistent types in tree and inserted elements?
// todo make sha3 default hasher (and update tests) to get rid of mimc/snarkjs/circomlib dependency // todo make sha3 default hasher (and update tests) to get rid of mimc/snarkjs/circomlib dependency
/**
* @callback hashFunction
* @param left Left leaf
* @param right Right leaf
*/
/**
* Merkle tree
*/
class MerkleTree { class MerkleTree {
/**
* Constructor
* @param {number} levels Number of levels in the tree
* @param {Array} [elements] Initial elements
* @param [zeroElement] Value for non-existent leaves
* @param {hashFunction} [hashFunction] Function used to hash 2 leaves
*/
constructor(levels, elements = [], zeroElement = DEFAULT_ZERO, hashFunction) { constructor(levels, elements = [], zeroElement = DEFAULT_ZERO, hashFunction) {
this.levels = levels this.levels = levels
this.capacity = 2 << levels this.capacity = 2 << levels
@ -33,10 +49,18 @@ class MerkleTree {
} }
} }
/**
* Get tree root
* @returns {*}
*/
root() { root() {
return this._layers[this.levels]?.[0] ?? this._zeros[this.levels] return this._layers[this.levels]?.[0] ?? this._zeros[this.levels]
} }
/**
* Insert new element into the tree
* @param element Element to insert
*/
insert(element) { insert(element) {
if (this._layers[0].length >= this.capacity) { if (this._layers[0].length >= this.capacity) {
throw new Error('Tree is full') throw new Error('Tree is full')
@ -44,14 +68,23 @@ class MerkleTree {
this.update(this._layers[0].length, element) this.update(this._layers[0].length, element)
} }
/**
* Insert multiple elements into the tree. Tree will be fully rebuilt during this operation.
* @param {Array} elements Elements to insert
*/
bulkInsert(elements) { bulkInsert(elements) {
if (this._layers[0].length + elements > this.capacity) { if (this._layers[0].length + elements.length > this.capacity) {
throw new Error('Tree is full') throw new Error('Tree is full')
} }
this._layers[0].push(...elements) this._layers[0].push(...elements)
this._rebuild() this._rebuild()
} }
/**
* Change an element in the tree
* @param {number} index Index of element to change
* @param element Updated element value
*/
update(index, element) { update(index, element) {
if (index < 0 || index > this._layers[0].length || index >= this.capacity) { if (index < 0 || index > this._layers[0].length || index >= this.capacity) {
throw new Error('Insert index out of bounds: ' + index) throw new Error('Insert index out of bounds: ' + index)
@ -66,6 +99,11 @@ class MerkleTree {
} }
} }
/**
* Get merkle proof for a leaf
* @param index Leaf index to generate proof for
* @returns {{pathElements: Object[], pathIndex: number[]}} An object containing adjacent elements and left-right index
*/
proof(index) { proof(index) {
if (index < 0 || index >= this._layers[0].length) { if (index < 0 || index >= this._layers[0].length) {
throw new Error('Index out of bounds: ' + index) throw new Error('Index out of bounds: ' + index)
@ -83,6 +121,11 @@ class MerkleTree {
} }
} }
/**
* Find an element in the tree
* @param element An element to find
* @returns {number} Index if element is found, otherwise -1
*/
indexOf(element) { indexOf(element) {
return this._layers[0].indexOf(element) return this._layers[0].indexOf(element)
} }