initial typescript edit
This commit is contained in:
parent
e3c54ea818
commit
4501cae4d6
65
.eslintrc
65
.eslintrc
@ -1,26 +1,57 @@
|
|||||||
{
|
{
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
"browser": true,
|
|
||||||
"es6": true,
|
|
||||||
"mocha": true
|
|
||||||
},
|
|
||||||
"extends": "eslint:recommended",
|
|
||||||
"globals": {
|
"globals": {
|
||||||
"Atomics": "readonly",
|
"Atomics": "readonly",
|
||||||
"SharedArrayBuffer": "readonly"
|
"SharedArrayBuffer": "readonly"
|
||||||
},
|
},
|
||||||
"parser": "babel-eslint",
|
"parser": "@typescript-eslint/parser",
|
||||||
"parserOptions": {
|
"plugins": [
|
||||||
"ecmaVersion": 2018
|
"@typescript-eslint"
|
||||||
},
|
],
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"ignorePatterns": [
|
||||||
|
"test/*.spec.ts",
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": ["error", 2],
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
"linebreak-style": ["error", "unix"],
|
"@typescript-eslint/consistent-type-definitions": [
|
||||||
"quotes": ["error", "single"],
|
"error",
|
||||||
"semi": ["error", "never"],
|
"type"
|
||||||
"object-curly-spacing": ["error", "always"],
|
],
|
||||||
"comma-dangle": ["error", "always-multiline"],
|
"indent": [
|
||||||
|
"error",
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"object-curly-spacing": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"comma-dangle": [
|
||||||
|
"error",
|
||||||
|
"always-multiline"
|
||||||
|
],
|
||||||
"require-await": "error"
|
"require-await": "error"
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true,
|
||||||
|
"node": true,
|
||||||
|
"mocha": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -1 +1,5 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
build
|
||||||
|
lib
|
||||||
|
yarn-error.log
|
||||||
|
.idea
|
||||||
|
5454
package-lock.json
generated
5454
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@ -3,9 +3,11 @@
|
|||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"description": "Fixed depth merkle tree implementation with sequential inserts",
|
"description": "Fixed depth merkle tree implementation with sequential inserts",
|
||||||
"repository": "https://github.com/tornadocash/fixed-merkle-tree.git",
|
"repository": "https://github.com/tornadocash/fixed-merkle-tree.git",
|
||||||
"main": "src/merkleTree.js",
|
"main": "lib/index.js",
|
||||||
|
"types": "lib/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha",
|
"test": "ts-mocha 'test/*.spec.ts'",
|
||||||
|
"build": "tsc",
|
||||||
"lint": "eslint ."
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@ -19,13 +21,18 @@
|
|||||||
"src/*"
|
"src/*"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5",
|
|
||||||
"circomlib": "git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c"
|
"circomlib": "git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-eslint": "^10.1.0",
|
"@types/expect": "^24.3.0",
|
||||||
|
"@types/mocha": "^9.1.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.12.0",
|
||||||
|
"@typescript-eslint/parser": "^5.12.0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"eslint": "^7.5.0",
|
"eslint": "^8.9.0",
|
||||||
"mocha": "^8.1.0"
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"mocha": "^9.2.1",
|
||||||
|
"ts-mocha": "^9.0.2",
|
||||||
|
"typescript": "^4.5.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,28 @@
|
|||||||
|
import { mimcsponge } from 'circomlib'
|
||||||
|
|
||||||
// keccak256("tornado") % BN254_FIELD_SIZE
|
// keccak256("tornado") % BN254_FIELD_SIZE
|
||||||
const DEFAULT_ZERO = '21663839004416932945382355908790599225266501822907911457504978515578255421292'
|
const DEFAULT_ZERO = '21663839004416932945382355908790599225266501822907911457504978515578255421292'
|
||||||
const defaultHash = require('./mimc')
|
|
||||||
|
|
||||||
// todo ensure consistent types in tree and inserted elements?
|
const defaultHash = (left: Element, right: Element): string => mimcsponge.multiHash([BigInt(left), BigInt(right)]).toString()
|
||||||
// todo make sha3 default hasher (and update tests) to get rid of mimc/snarkjs/circomlib dependency
|
|
||||||
|
|
||||||
/**
|
export default class MerkleTree {
|
||||||
* @callback hashFunction
|
levels: number
|
||||||
* @param left Left leaf
|
capacity: number
|
||||||
* @param right Right leaf
|
private _hash: HashFunction
|
||||||
*/
|
private zeroElement: Element
|
||||||
/**
|
private _zeros: Element[]
|
||||||
* Merkle tree
|
private _layers: Array<Element[]>
|
||||||
*/
|
|
||||||
class MerkleTree {
|
constructor(levels: number, elements: Element[] = [], {
|
||||||
/**
|
hashFunction = defaultHash,
|
||||||
* Constructor
|
zeroElement = DEFAULT_ZERO,
|
||||||
* @param {number} levels Number of levels in the tree
|
}: MerkleTreeOptions = {}) {
|
||||||
* @param {Array} [elements] Initial elements
|
|
||||||
* @param {Object} options
|
|
||||||
* @param {hashFunction} [options.hashFunction] Function used to hash 2 leaves
|
|
||||||
* @param [options.zeroElement] Value for non-existent leaves
|
|
||||||
*/
|
|
||||||
constructor(levels, elements = [], { hashFunction, zeroElement = DEFAULT_ZERO } = {}) {
|
|
||||||
this.levels = levels
|
this.levels = levels
|
||||||
this.capacity = 2 ** levels
|
this.capacity = 2 ** levels
|
||||||
if (elements.length > this.capacity) {
|
if (elements.length > this.capacity) {
|
||||||
throw new Error('Tree is full')
|
throw new Error('Tree is full')
|
||||||
}
|
}
|
||||||
this._hash = hashFunction || defaultHash
|
this._hash = hashFunction
|
||||||
this.zeroElement = zeroElement
|
this.zeroElement = zeroElement
|
||||||
this._zeros = []
|
this._zeros = []
|
||||||
this._zeros[0] = zeroElement
|
this._zeros[0] = zeroElement
|
||||||
@ -56,17 +50,16 @@ class MerkleTree {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get tree root
|
* Get tree root
|
||||||
* @returns {*}
|
|
||||||
*/
|
*/
|
||||||
root() {
|
root(): string {
|
||||||
return this._layers[this.levels].length > 0 ? this._layers[this.levels][0] : this._zeros[this.levels]
|
return `${this._layers[this.levels].length > 0 ? this._layers[this.levels][0] : this._zeros[this.levels]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert new element into the tree
|
* Insert new element into the tree
|
||||||
* @param element Element to insert
|
* @param element Element to insert
|
||||||
*/
|
*/
|
||||||
insert(element) {
|
insert(element: 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')
|
||||||
}
|
}
|
||||||
@ -77,7 +70,7 @@ class MerkleTree {
|
|||||||
* Insert multiple elements into the tree.
|
* Insert multiple elements into the tree.
|
||||||
* @param {Array} elements Elements to insert
|
* @param {Array} elements Elements to insert
|
||||||
*/
|
*/
|
||||||
bulkInsert(elements) {
|
bulkInsert(elements: Element[]) {
|
||||||
if (!elements.length) {
|
if (!elements.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -109,7 +102,7 @@ class MerkleTree {
|
|||||||
* @param {number} index Index of element to change
|
* @param {number} index Index of element to change
|
||||||
* @param element Updated element value
|
* @param element Updated element value
|
||||||
*/
|
*/
|
||||||
update(index, element) {
|
update(index: number, element: Element) {
|
||||||
if (isNaN(Number(index)) || index < 0 || index > this._layers[0].length || index >= this.capacity) {
|
if (isNaN(Number(index)) || 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)
|
||||||
}
|
}
|
||||||
@ -130,17 +123,18 @@ class MerkleTree {
|
|||||||
* @param {number} index Leaf index to generate path for
|
* @param {number} index Leaf index to generate path for
|
||||||
* @returns {{pathElements: Object[], pathIndex: number[]}} An object containing adjacent elements and left-right index
|
* @returns {{pathElements: Object[], pathIndex: number[]}} An object containing adjacent elements and left-right index
|
||||||
*/
|
*/
|
||||||
path(index) {
|
path(index: Element) {
|
||||||
if (isNaN(Number(index)) || index < 0 || index >= this._layers[0].length) {
|
if (isNaN(Number(index)) || index < 0 || index >= this._layers[0].length) {
|
||||||
throw new Error('Index out of bounds: ' + index)
|
throw new Error('Index out of bounds: ' + index)
|
||||||
}
|
}
|
||||||
const pathElements = []
|
let elIndex = +index
|
||||||
const pathIndices = []
|
const pathElements: Element[] = []
|
||||||
|
const pathIndices: number[] = []
|
||||||
for (let level = 0; level < this.levels; level++) {
|
for (let level = 0; level < this.levels; level++) {
|
||||||
pathIndices[level] = index % 2
|
pathIndices[level] = elIndex % 2
|
||||||
pathElements[level] =
|
pathElements[level] =
|
||||||
(index ^ 1) < this._layers[level].length ? this._layers[level][index ^ 1] : this._zeros[level]
|
(elIndex ^ 1) < this._layers[level].length ? this._layers[level][elIndex ^ 1] : this._zeros[level]
|
||||||
index >>= 1
|
elIndex >>= 1
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
pathElements,
|
pathElements,
|
||||||
@ -154,17 +148,16 @@ class MerkleTree {
|
|||||||
* @param comparator A function that checks leaf value equality
|
* @param comparator A function that checks leaf value equality
|
||||||
* @returns {number} Index if element is found, otherwise -1
|
* @returns {number} Index if element is found, otherwise -1
|
||||||
*/
|
*/
|
||||||
indexOf(element, comparator) {
|
indexOf(element: Element, comparator?: <T, R> (arg0: T, arg1: T) => R): number {
|
||||||
if (comparator) {
|
if (comparator) {
|
||||||
return this._layers[0].findIndex((el) => comparator(element, el))
|
return this._layers[0].findIndex((el) => comparator<Element, number>(element, el))
|
||||||
} else {
|
} else {
|
||||||
return this._layers[0].indexOf(element)
|
return this._layers[0].indexOf(element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a copy of non-zero tree elements
|
* Returns a copy of non-zero tree elements.
|
||||||
* @returns {Object[]}
|
|
||||||
*/
|
*/
|
||||||
elements() {
|
elements() {
|
||||||
return this._layers[0].slice()
|
return this._layers[0].slice()
|
||||||
@ -172,7 +165,6 @@ class MerkleTree {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a copy of n-th zero elements array
|
* Returns a copy of n-th zero elements array
|
||||||
* @returns {Object[]}
|
|
||||||
*/
|
*/
|
||||||
zeros() {
|
zeros() {
|
||||||
return this._zeros.slice()
|
return this._zeros.slice()
|
||||||
@ -183,7 +175,7 @@ class MerkleTree {
|
|||||||
* Deserializing it back will not require to recompute any hashes
|
* Deserializing it back will not require to recompute any hashes
|
||||||
* Elements are not converted to a plain type, this is responsibility of the caller
|
* Elements are not converted to a plain type, this is responsibility of the caller
|
||||||
*/
|
*/
|
||||||
serialize() {
|
serialize(): SerializedTreeState {
|
||||||
return {
|
return {
|
||||||
levels: this.levels,
|
levels: this.levels,
|
||||||
_zeros: this._zeros,
|
_zeros: this._zeros,
|
||||||
@ -195,18 +187,27 @@ class MerkleTree {
|
|||||||
* Deserialize data into a MerkleTree instance
|
* Deserialize data into a MerkleTree instance
|
||||||
* Make sure to provide the same hashFunction as was used in the source tree,
|
* Make sure to provide the same hashFunction as was used in the source tree,
|
||||||
* otherwise the tree state will be invalid
|
* otherwise the tree state will be invalid
|
||||||
*
|
|
||||||
* @param data
|
|
||||||
* @param hashFunction
|
|
||||||
* @returns {MerkleTree}
|
|
||||||
*/
|
*/
|
||||||
static deserialize(data, hashFunction) {
|
static deserialize(data: SerializedTreeState, hashFunction?: HashFunction): MerkleTree {
|
||||||
const instance = Object.assign(Object.create(this.prototype), data)
|
return new MerkleTree(data.levels, data._layers[0], { hashFunction, zeroElement: data._zeros[0] })
|
||||||
instance._hash = hashFunction || defaultHash
|
|
||||||
instance.capacity = 2 ** instance.levels
|
|
||||||
instance.zeroElement = instance._zeros[0]
|
|
||||||
return instance
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = MerkleTree
|
export type HashFunction = {
|
||||||
|
(left: string | number, right: string | number): string
|
||||||
|
}
|
||||||
|
export type MerkleTreeOptions = {
|
||||||
|
hashFunction?: HashFunction
|
||||||
|
zeroElement?: Element
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Element = string | number
|
||||||
|
|
||||||
|
export type SerializedTreeState = {
|
||||||
|
levels: number,
|
||||||
|
_zeros: Array<Element>,
|
||||||
|
_layers: Array<Element[]>
|
||||||
|
}
|
||||||
|
export type Mimcsponge = {
|
||||||
|
multiHash: (arr: BigInt[], key?: Element, numOutputs?) => string
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
const { mimcsponge } = require('circomlib')
|
|
||||||
const { bigInt } = require('snarkjs')
|
|
||||||
module.exports = (left, right) => mimcsponge.multiHash([bigInt(left), bigInt(right)]).toString()
|
|
@ -1,26 +1,28 @@
|
|||||||
const MerkleTree = require('../src/merkleTree')
|
import MerkleTree from '../src'
|
||||||
require('chai').should()
|
import { assert, should } from 'chai'
|
||||||
|
|
||||||
describe('MerkleTree', () => {
|
describe('MerkleTree', () => {
|
||||||
|
|
||||||
describe('#constructor', () => {
|
describe('#constructor', () => {
|
||||||
|
|
||||||
it('should have correct zero root', () => {
|
it('should have correct zero root', () => {
|
||||||
const tree = new MerkleTree(10)
|
const tree = new MerkleTree(10, [])
|
||||||
return tree.root().should.equal('14030416097908897320437553787826300082392928432242046897689557706485311282736')
|
return should().equal(tree.root(), '14030416097908897320437553787826300082392928432242046897689557706485311282736')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should have correct 1 element root', () => {
|
it('should have correct 1 element root', () => {
|
||||||
const tree = new MerkleTree(10, [1])
|
const tree = new MerkleTree(10, [1])
|
||||||
tree.root().should.equal('8423266420989796135179818298985240707844287090553672312129988553683991994663')
|
should().equal(tree.root(), '8423266420989796135179818298985240707844287090553672312129988553683991994663')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should have correct even elements root', () => {
|
it('should have correct even elements root', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2])
|
const tree = new MerkleTree(10, [1, 2])
|
||||||
tree.root().should.equal('6632020347849276860492323008882350357301732786233864934344775324188835172576')
|
should().equal(tree.root(), '6632020347849276860492323008882350357301732786233864934344775324188835172576')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should have correct odd elements root', () => {
|
it('should have correct odd elements root', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3])
|
const tree = new MerkleTree(10, [1, 2, 3])
|
||||||
tree.root().should.equal('13605252518346649016266481317890801910232739395710162921320863289825142055129')
|
should().equal(tree.root(), '13605252518346649016266481317890801910232739395710162921320863289825142055129')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should be able to create a full tree', () => {
|
it('should be able to create a full tree', () => {
|
||||||
@ -29,7 +31,7 @@ describe('MerkleTree', () => {
|
|||||||
|
|
||||||
it('should fail to create tree with too many elements', () => {
|
it('should fail to create tree with too many elements', () => {
|
||||||
const call = () => new MerkleTree(2, [1, 2, 3, 4, 5])
|
const call = () => new MerkleTree(2, [1, 2, 3, 4, 5])
|
||||||
call.should.throw('Tree is full')
|
should().throw(call, 'Tree is full')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -37,19 +39,19 @@ describe('MerkleTree', () => {
|
|||||||
it('should insert into empty tree', () => {
|
it('should insert into empty tree', () => {
|
||||||
const tree = new MerkleTree(10)
|
const tree = new MerkleTree(10)
|
||||||
tree.insert(42)
|
tree.insert(42)
|
||||||
tree.root().should.equal('5305397050004975530787056746976521882221645950652996479084366175595194436378')
|
should().equal(tree.root(), '5305397050004975530787056746976521882221645950652996479084366175595194436378')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should insert into odd tree', () => {
|
it('should insert into odd tree', () => {
|
||||||
const tree = new MerkleTree(10, [1])
|
const tree = new MerkleTree(10, [1])
|
||||||
tree.insert(42)
|
tree.insert(42)
|
||||||
tree.root().should.equal('4732716818150428188641303198013632061441036732749853605989871103991103096471')
|
should().equal(tree.root(), '4732716818150428188641303198013632061441036732749853605989871103991103096471')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should insert into even tree', () => {
|
it('should insert into even tree', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2])
|
const tree = new MerkleTree(10, [1, 2])
|
||||||
tree.insert(42)
|
tree.insert(42)
|
||||||
tree.root().should.equal('6204016789747878948181936326719724987136198810274146408545977300318734508764')
|
should().equal(tree.root(), '6204016789747878948181936326719724987136198810274146408545977300318734508764')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should insert last element', () => {
|
it('should insert last element', () => {
|
||||||
@ -60,7 +62,7 @@ describe('MerkleTree', () => {
|
|||||||
it('should fail to insert when tree is full', () => {
|
it('should fail to insert when tree is full', () => {
|
||||||
const tree = new MerkleTree(2, [1, 2, 3, 4])
|
const tree = new MerkleTree(2, [1, 2, 3, 4])
|
||||||
const call = () => tree.insert(5)
|
const call = () => tree.insert(5)
|
||||||
call.should.throw('Tree is full')
|
should().throw(call, 'Tree is full')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ describe('MerkleTree', () => {
|
|||||||
it('should work', () => {
|
it('should work', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3])
|
const tree = new MerkleTree(10, [1, 2, 3])
|
||||||
tree.bulkInsert([4, 5, 6])
|
tree.bulkInsert([4, 5, 6])
|
||||||
tree.root().should.equal('10132905325673518287563057607527946096399700874345297651940963130460267058606')
|
should().equal(tree.root(), '10132905325673518287563057607527946096399700874345297651940963130460267058606')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should give the same result as sequental inserts', () => {
|
it('should give the same result as sequental inserts', () => {
|
||||||
@ -92,7 +94,7 @@ describe('MerkleTree', () => {
|
|||||||
for (const item of inserted) {
|
for (const item of inserted) {
|
||||||
tree2.insert(item)
|
tree2.insert(item)
|
||||||
}
|
}
|
||||||
tree1.root().should.equal(tree2.root())
|
should().equal(tree1.root(), tree2.root())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).timeout(10000)
|
}).timeout(10000)
|
||||||
@ -105,7 +107,7 @@ describe('MerkleTree', () => {
|
|||||||
it('should fail to insert too many elements', () => {
|
it('should fail to insert too many elements', () => {
|
||||||
const tree = new MerkleTree(2, [1, 2])
|
const tree = new MerkleTree(2, [1, 2])
|
||||||
const call = () => tree.bulkInsert([3, 4, 5])
|
const call = () => tree.bulkInsert([3, 4, 5])
|
||||||
call.should.throw('Tree is full')
|
should().throw(call, 'Tree is full')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -113,56 +115,57 @@ describe('MerkleTree', () => {
|
|||||||
it('should update first element', () => {
|
it('should update first element', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
tree.update(0, 42)
|
tree.update(0, 42)
|
||||||
tree.root().should.equal('153077538697962715163231177553585573790587443799974092612333826693999310199')
|
should().equal(tree.root(), '153077538697962715163231177553585573790587443799974092612333826693999310199')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update last element', () => {
|
it('should update last element', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
tree.update(4, 42)
|
tree.update(4, 42)
|
||||||
tree.root().should.equal('1955192134603843666100093417117434845771298375724087600313714421260719033775')
|
should().equal(tree.root(), '1955192134603843666100093417117434845771298375724087600313714421260719033775')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update odd element', () => {
|
it('should update odd element', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
tree.update(1, 42)
|
tree.update(1, 42)
|
||||||
tree.root().should.equal('6642888742811380760154112624880866754768235565211186414088321870395007150538')
|
should().equal(tree.root(), '6642888742811380760154112624880866754768235565211186414088321870395007150538')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update even element', () => {
|
it('should update even element', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
tree.update(2, 42)
|
tree.update(2, 42)
|
||||||
tree.root().should.equal('11739358667442647096377238675718917508981868161724701476635082606510350785683')
|
should().equal(tree.root(), '11739358667442647096377238675718917508981868161724701476635082606510350785683')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update extra element', () => {
|
it('should update extra element', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4])
|
const tree = new MerkleTree(10, [1, 2, 3, 4])
|
||||||
tree.update(4, 5)
|
tree.update(4, 5)
|
||||||
tree.root().should.equal('6341751103515285836339987888606244815365572869367801108789753151704260302930')
|
should().equal(tree.root(), '6341751103515285836339987888606244815365572869367801108789753151704260302930')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fail to update incorrect index', () => {
|
it('should fail to update incorrect index', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5]);
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
(() => tree.update(-1, 42)).should.throw('Insert index out of bounds: -1');
|
should().throw((() => tree.update(-1, 42)), 'Insert index out of bounds: -1')
|
||||||
(() => tree.update(6, 42)).should.throw('Insert index out of bounds: 6');
|
should().throw((() => tree.update(6, 42)), 'Insert index out of bounds: 6')
|
||||||
(() => tree.update('qwe', 42)).should.throw('Insert index out of bounds: qwe')
|
// @ts-ignore
|
||||||
|
should().throw((() => tree.update('qwe', 42)), 'Insert index out of bounds: qwe')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fail to update over capacity', () => {
|
it('should fail to update over capacity', () => {
|
||||||
const tree = new MerkleTree(2, [1, 2, 3, 4])
|
const tree = new MerkleTree(2, [1, 2, 3, 4])
|
||||||
const call = () => tree.update(4, 42)
|
const call = () => tree.update(4, 42)
|
||||||
call.should.throw('Insert index out of bounds: 4')
|
should().throw(call, 'Insert index out of bounds: 4')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#indexOf', () => {
|
describe('#indexOf', () => {
|
||||||
it('should find index', () => {
|
it('should find index', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
tree.indexOf(3).should.equal(2)
|
should().equal(tree.indexOf(3), 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return -1 for non existent element', () => {
|
it('should return -1 for non existent element', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
tree.indexOf(42).should.equal(-1)
|
should().equal(tree.indexOf(42), -1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -170,8 +173,8 @@ describe('MerkleTree', () => {
|
|||||||
it('should work for even index', () => {
|
it('should work for even index', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
const path = tree.path(2)
|
const path = tree.path(2)
|
||||||
path.pathIndices.should.be.deep.equal([0, 1, 0, 0, 0, 0, 0, 0, 0, 0])
|
assert.deepEqual(path.pathIndices, [0, 1, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
path.pathElements.should.be.deep.equal([
|
assert.deepEqual(path.pathElements, [
|
||||||
4,
|
4,
|
||||||
'19814528709687996974327303300007262407299502847885145507292406548098437687919',
|
'19814528709687996974327303300007262407299502847885145507292406548098437687919',
|
||||||
'21305827034995891902714687670641862055126514524916463201449278400604999416145',
|
'21305827034995891902714687670641862055126514524916463201449278400604999416145',
|
||||||
@ -188,8 +191,8 @@ describe('MerkleTree', () => {
|
|||||||
it('should work for odd index', () => {
|
it('should work for odd index', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
const path = tree.path(3)
|
const path = tree.path(3)
|
||||||
path.pathIndices.should.be.deep.equal([1, 1, 0, 0, 0, 0, 0, 0, 0, 0])
|
assert.deepEqual(path.pathIndices, [1, 1, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
path.pathElements.should.be.deep.equal([
|
assert.deepEqual(path.pathElements, [
|
||||||
3,
|
3,
|
||||||
'19814528709687996974327303300007262407299502847885145507292406548098437687919',
|
'19814528709687996974327303300007262407299502847885145507292406548098437687919',
|
||||||
'21305827034995891902714687670641862055126514524916463201449278400604999416145',
|
'21305827034995891902714687670641862055126514524916463201449278400604999416145',
|
||||||
@ -204,17 +207,17 @@ describe('MerkleTree', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should fail on incorrect index', () => {
|
it('should fail on incorrect index', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4]);
|
const tree = new MerkleTree(10, [1, 2, 3, 4])
|
||||||
(() => tree.path(-1)).should.throw('Index out of bounds: -1');
|
should().throw((() => tree.path(-1)), 'Index out of bounds: -1')
|
||||||
(() => tree.path(5)).should.throw('Index out of bounds: 5');
|
should().throw((() => tree.path(5)), 'Index out of bounds: 5')
|
||||||
(() => tree.path('qwe')).should.throw('Index out of bounds: qwe')
|
should().throw((() => tree.path('qwe')), 'Index out of bounds: qwe')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work for correct string index', () => {
|
it('should work for correct string index', () => {
|
||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5])
|
||||||
const path = tree.path('2')
|
const path = tree.path('2')
|
||||||
path.pathIndices.should.be.deep.equal([0, 1, 0, 0, 0, 0, 0, 0, 0, 0])
|
assert.deepEqual(path.pathIndices, [0, 1, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
path.pathElements.should.be.deep.equal([
|
assert.deepEqual(path.pathElements, [
|
||||||
4,
|
4,
|
||||||
'19814528709687996974327303300007262407299502847885145507292406548098437687919',
|
'19814528709687996974327303300007262407299502847885145507292406548098437687919',
|
||||||
'21305827034995891902714687670641862055126514524916463201449278400604999416145',
|
'21305827034995891902714687670641862055126514524916463201449278400604999416145',
|
||||||
@ -231,16 +234,15 @@ describe('MerkleTree', () => {
|
|||||||
|
|
||||||
describe('#serialize', () => {
|
describe('#serialize', () => {
|
||||||
it('should work', () => {
|
it('should work', () => {
|
||||||
const src = new MerkleTree(10, [1, 2, 3])
|
const src = new MerkleTree(10, [1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
const data = src.serialize()
|
const data = src.serialize()
|
||||||
const dst = MerkleTree.deserialize(data)
|
const dst = MerkleTree.deserialize(data)
|
||||||
|
should().equal(src.root(), dst.root())
|
||||||
src.root().should.equal(dst.root())
|
|
||||||
|
|
||||||
src.insert(10)
|
src.insert(10)
|
||||||
dst.insert(10)
|
dst.insert(10)
|
||||||
|
|
||||||
src.root().should.equal(dst.root())
|
should().equal(src.root(), dst.root())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": [
|
||||||
|
"es2019"
|
||||||
|
],
|
||||||
|
"target": "es2018",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"outDir": "./lib",
|
||||||
|
"rootDir": "src",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"sourceMap": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"types",
|
||||||
|
"**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user