mirror of
https://github.com/tornadocash/fixed-merkle-tree.git
synced 2024-11-22 09:47:15 +01:00
fix partial tree path
This commit is contained in:
parent
1f02fd3f45
commit
c1c6972a4b
2
lib/BaseTree.d.ts
vendored
2
lib/BaseTree.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
import { Element, HashFunction, ProofPath } from './index';
|
import { Element, HashFunction, ProofPath } from './';
|
||||||
export declare class BaseTree {
|
export declare class BaseTree {
|
||||||
levels: number;
|
levels: number;
|
||||||
protected _hashFn: HashFunction<Element>;
|
protected _hashFn: HashFunction<Element>;
|
||||||
|
@ -91,7 +91,7 @@ class PartialMerkleTree extends BaseTree_1.BaseTree {
|
|||||||
const leafIndex = elIndex ^ 1;
|
const leafIndex = elIndex ^ 1;
|
||||||
if (leafIndex < this._layers[level].length) {
|
if (leafIndex < this._layers[level].length) {
|
||||||
const [proofPos, proofEl] = this._proofMap.get(level);
|
const [proofPos, proofEl] = this._proofMap.get(level);
|
||||||
pathElements[level] = proofPos === leafIndex ? proofEl : this._layers[level][leafIndex];
|
pathElements[level] = this._layers[level][leafIndex] || (proofPos === leafIndex ? proofEl : null);
|
||||||
pathPositions[level] = leafIndex;
|
pathPositions[level] = leafIndex;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
4
lib/index.d.ts
vendored
4
lib/index.d.ts
vendored
@ -1,6 +1,8 @@
|
|||||||
export { default as MerkleTree } from './FixedMerkleTree';
|
import { default as MerkleTree } from './FixedMerkleTree';
|
||||||
export { PartialMerkleTree } from './PartialMerkleTree';
|
export { PartialMerkleTree } from './PartialMerkleTree';
|
||||||
export { simpleHash } from './simpleHash';
|
export { simpleHash } from './simpleHash';
|
||||||
|
export { MerkleTree };
|
||||||
|
export default MerkleTree;
|
||||||
export declare type HashFunction<T> = {
|
export declare type HashFunction<T> = {
|
||||||
(left: T, right: T): string;
|
(left: T, right: T): string;
|
||||||
};
|
};
|
||||||
|
@ -3,10 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.simpleHash = exports.PartialMerkleTree = exports.MerkleTree = void 0;
|
exports.MerkleTree = exports.simpleHash = exports.PartialMerkleTree = void 0;
|
||||||
var FixedMerkleTree_1 = require("./FixedMerkleTree");
|
const FixedMerkleTree_1 = __importDefault(require("./FixedMerkleTree"));
|
||||||
Object.defineProperty(exports, "MerkleTree", { enumerable: true, get: function () { return __importDefault(FixedMerkleTree_1).default; } });
|
Object.defineProperty(exports, "MerkleTree", { enumerable: true, get: function () { return FixedMerkleTree_1.default; } });
|
||||||
var PartialMerkleTree_1 = require("./PartialMerkleTree");
|
var PartialMerkleTree_1 = require("./PartialMerkleTree");
|
||||||
Object.defineProperty(exports, "PartialMerkleTree", { enumerable: true, get: function () { return PartialMerkleTree_1.PartialMerkleTree; } });
|
Object.defineProperty(exports, "PartialMerkleTree", { enumerable: true, get: function () { return PartialMerkleTree_1.PartialMerkleTree; } });
|
||||||
var simpleHash_1 = require("./simpleHash");
|
var simpleHash_1 = require("./simpleHash");
|
||||||
Object.defineProperty(exports, "simpleHash", { enumerable: true, get: function () { return simpleHash_1.simpleHash; } });
|
Object.defineProperty(exports, "simpleHash", { enumerable: true, get: function () { return simpleHash_1.simpleHash; } });
|
||||||
|
exports.default = FixedMerkleTree_1.default;
|
||||||
|
2
lib/simpleHash.d.ts
vendored
2
lib/simpleHash.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
import { Element } from './index';
|
import { Element } from './';
|
||||||
/***
|
/***
|
||||||
* This is insecure hash function, just for example only
|
* This is insecure hash function, just for example only
|
||||||
* @param data
|
* @param data
|
||||||
|
5788
package-lock.json
generated
5788
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,6 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^5.12.0",
|
"@typescript-eslint/eslint-plugin": "^5.12.0",
|
||||||
"@typescript-eslint/parser": "^5.12.0",
|
"@typescript-eslint/parser": "^5.12.0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"circomlibjs": "^0.1.1",
|
|
||||||
"eslint": "^8.9.0",
|
"eslint": "^8.9.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"mocha": "^9.2.2",
|
"mocha": "^9.2.2",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Element, HashFunction, ProofPath } from './index'
|
import { Element, HashFunction, ProofPath } from './'
|
||||||
|
|
||||||
export class BaseTree {
|
export class BaseTree {
|
||||||
levels: number
|
levels: number
|
||||||
|
@ -114,7 +114,7 @@ export class PartialMerkleTree extends BaseTree {
|
|||||||
const leafIndex = elIndex ^ 1
|
const leafIndex = elIndex ^ 1
|
||||||
if (leafIndex < this._layers[level].length) {
|
if (leafIndex < this._layers[level].length) {
|
||||||
const [proofPos, proofEl] = this._proofMap.get(level)
|
const [proofPos, proofEl] = this._proofMap.get(level)
|
||||||
pathElements[level] = proofPos === leafIndex ? proofEl : this._layers[level][leafIndex]
|
pathElements[level] = this._layers[level][leafIndex] || (proofPos === leafIndex ? proofEl : null)
|
||||||
pathPositions[level] = leafIndex
|
pathPositions[level] = leafIndex
|
||||||
} else {
|
} else {
|
||||||
pathElements[level] = this._zeros[level]
|
pathElements[level] = this._zeros[level]
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
export { default as MerkleTree } from './FixedMerkleTree'
|
import { default as MerkleTree } from './FixedMerkleTree'
|
||||||
export { PartialMerkleTree } from './PartialMerkleTree'
|
export { PartialMerkleTree } from './PartialMerkleTree'
|
||||||
export { simpleHash } from './simpleHash'
|
export { simpleHash } from './simpleHash'
|
||||||
|
export { MerkleTree }
|
||||||
|
export default MerkleTree
|
||||||
export type HashFunction<T> = {
|
export type HashFunction<T> = {
|
||||||
(left: T, right: T): string
|
(left: T, right: T): string
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Element } from './index'
|
import { Element } from './'
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* This is insecure hash function, just for example only
|
* This is insecure hash function, just for example only
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { MerkleTree, PartialMerkleTree, TreeEdge } from '../src'
|
import { MerkleTree, PartialMerkleTree, TreeEdge } from '../src'
|
||||||
import { assert, should } from 'chai'
|
import { assert, should } from 'chai'
|
||||||
import { buildMimcSponge } from 'circomlibjs'
|
|
||||||
import { createHash } from 'crypto'
|
import { createHash } from 'crypto'
|
||||||
import { it } from 'mocha'
|
import { it } from 'mocha'
|
||||||
|
|
||||||
@ -10,11 +9,6 @@ const ZERO_ELEMENT = '2166383900441693294538235590879059922526650182290791145750
|
|||||||
describe('MerkleTree', () => {
|
describe('MerkleTree', () => {
|
||||||
|
|
||||||
describe('#constructor', () => {
|
describe('#constructor', () => {
|
||||||
let mimcSponge
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
mimcSponge = await buildMimcSponge()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have correct zero root', () => {
|
it('should have correct zero root', () => {
|
||||||
const tree = new MerkleTree(10, [])
|
const tree = new MerkleTree(10, [])
|
||||||
@ -49,12 +43,6 @@ describe('MerkleTree', () => {
|
|||||||
const tree = new MerkleTree(10, [1, 2, 3, 4, 5, 6], { hashFunction: sha256Hash, zeroElement: 'zero' })
|
const tree = new MerkleTree(10, [1, 2, 3, 4, 5, 6], { hashFunction: sha256Hash, zeroElement: 'zero' })
|
||||||
should().equal(tree.root, 'a377b9fa0ed41add83e56f7e1d0e2ebdb46550b9d8b26b77dece60cb67283f19')
|
should().equal(tree.root, 'a377b9fa0ed41add83e56f7e1d0e2ebdb46550b9d8b26b77dece60cb67283f19')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work with mimc hash function and zero element', () => {
|
|
||||||
const mimcHash = (left, right) => mimcSponge.F.toString(mimcSponge.multiHash([BigInt(left), BigInt(right)]))
|
|
||||||
const tree = new MerkleTree(10, [1, 2, 3], { hashFunction: mimcHash, zeroElement: ZERO_ELEMENT })
|
|
||||||
should().equal(tree.root, '13605252518346649016266481317890801910232739395710162921320863289825142055129')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#insert', () => {
|
describe('#insert', () => {
|
||||||
@ -305,35 +293,39 @@ describe('MerkleTree', () => {
|
|||||||
})
|
})
|
||||||
describe('#getTreeSlices', () => {
|
describe('#getTreeSlices', () => {
|
||||||
let fullTree: MerkleTree
|
let fullTree: MerkleTree
|
||||||
before(() => {
|
before(async () => {
|
||||||
const elements = Array.from({ length: 128 }, (_, i) => i)
|
const elements = Array.from({ length: 2 ** 8 + 11 }, (_, i) => i)
|
||||||
fullTree = new MerkleTree(10, elements)
|
fullTree = new MerkleTree(10, elements)
|
||||||
|
return Promise.resolve()
|
||||||
})
|
})
|
||||||
it('should return correct slices count', () => {
|
it('should return correct slices count', () => {
|
||||||
const count = 5
|
const count = 4
|
||||||
const slicesCount = fullTree.getTreeSlices(5).length
|
const slicesCount = fullTree.getTreeSlices(4).length
|
||||||
should().equal(count, slicesCount)
|
should().equal(count, slicesCount)
|
||||||
})
|
}).timeout(10000)
|
||||||
|
|
||||||
it('should be able to create partial tree from last slice', () => {
|
it('should be able to create partial tree from last slice', () => {
|
||||||
const lastSlice = fullTree.getTreeSlices().pop()
|
const [, , , lastSlice] = fullTree.getTreeSlices()
|
||||||
const partialTree = new PartialMerkleTree(10, lastSlice.edge, lastSlice.elements)
|
const partialTree = new PartialMerkleTree(10, lastSlice.edge, lastSlice.elements)
|
||||||
should().equal(partialTree.root, fullTree.root)
|
assert.deepEqual(fullTree.root, partialTree.root)
|
||||||
})
|
}).timeout(10000)
|
||||||
|
|
||||||
it('should be able to build full tree from slices', () => {
|
it('should be able to build full tree from slices', () => {
|
||||||
const slices = fullTree.getTreeSlices()
|
const slices = fullTree.getTreeSlices()
|
||||||
const lastSlice = slices.pop()
|
const lastSlice = slices.pop()
|
||||||
const partialTree = new PartialMerkleTree(10, lastSlice.edge, lastSlice.elements)
|
const partialTree = new PartialMerkleTree(10, lastSlice.edge, lastSlice.elements)
|
||||||
slices.reverse().forEach(({ edge, elements }) => partialTree.shiftEdge(edge, elements))
|
slices.reverse().forEach(({ edge, elements }) => {
|
||||||
assert.deepEqual(partialTree.layers, fullTree.layers)
|
console.log(edge.edgeIndex, elements.length)
|
||||||
|
partialTree.shiftEdge(edge, elements)
|
||||||
})
|
})
|
||||||
|
assert.deepEqual(fullTree.layers, partialTree.layers)
|
||||||
|
}).timeout(10000)
|
||||||
|
|
||||||
it('should throw if invalid number of elements', () => {
|
it('should throw if invalid number of elements', () => {
|
||||||
const [firstSlice] = fullTree.getTreeSlices()
|
const [firstSlice] = fullTree.getTreeSlices()
|
||||||
const call = () => new PartialMerkleTree(10, firstSlice.edge, firstSlice.elements)
|
const call = () => new PartialMerkleTree(10, firstSlice.edge, firstSlice.elements)
|
||||||
should().throw(call, 'Invalid number of elements')
|
should().throw(call, 'Invalid number of elements')
|
||||||
})
|
}).timeout(10000)
|
||||||
})
|
})
|
||||||
describe('#getters', () => {
|
describe('#getters', () => {
|
||||||
const elements = [1, 2, 3, 4, 5]
|
const elements = [1, 2, 3, 4, 5]
|
||||||
|
@ -180,8 +180,8 @@ describe('PartialMerkleTree', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should return same elements count as full tree', () => {
|
it('should return same elements count as full tree', () => {
|
||||||
const levels = 20
|
const levels = 10
|
||||||
const capacity = levels ** 2
|
const capacity = 2 ** levels
|
||||||
const elements = Array.from({ length: capacity }, (_, i) => i)
|
const elements = Array.from({ length: capacity }, (_, i) => i)
|
||||||
const { fullTree, partialTree } = getTestTrees(levels, elements, 200)
|
const { fullTree, partialTree } = getTestTrees(levels, elements, 200)
|
||||||
should().equal(partialTree.elements.length, fullTree.elements.length)
|
should().equal(partialTree.elements.length, fullTree.elements.length)
|
||||||
@ -232,13 +232,17 @@ describe('PartialMerkleTree', () => {
|
|||||||
describe('#shiftEdge', () => {
|
describe('#shiftEdge', () => {
|
||||||
it('should work', () => {
|
it('should work', () => {
|
||||||
const levels = 10
|
const levels = 10
|
||||||
const elements: Element[] = Array.from({ length: 20 ** 2 }, (_, i) => i)
|
const elements: Element[] = Array.from({ length: 21 ** 2 }, (_, i) => i)
|
||||||
const tree = new MerkleTree(levels, elements)
|
const tree = new MerkleTree(levels, elements)
|
||||||
const edge1 = tree.getTreeEdge(200)
|
const edge1 = tree.getTreeEdge(200)
|
||||||
const edge2 = tree.getTreeEdge(100)
|
const edge2 = tree.getTreeEdge(100)
|
||||||
|
const edge3 = tree.getTreeEdge(10)
|
||||||
const partialTree = new PartialMerkleTree(levels, edge1, elements.slice(edge1.edgeIndex))
|
const partialTree = new PartialMerkleTree(levels, edge1, elements.slice(edge1.edgeIndex))
|
||||||
partialTree.shiftEdge(edge2, elements.slice(edge2.edgeIndex, partialTree.edgeIndex))
|
partialTree.shiftEdge(edge2, elements.slice(edge2.edgeIndex, partialTree.edgeIndex))
|
||||||
assert.deepEqual(partialTree.path(110), tree.path(110))
|
partialTree.shiftEdge(edge3, elements.slice(edge3.edgeIndex, partialTree.edgeIndex))
|
||||||
|
tree.insert('1111')
|
||||||
|
partialTree.insert('1111')
|
||||||
|
assert.deepEqual(partialTree.path(50), tree.path(50))
|
||||||
})
|
})
|
||||||
it('should fail if new edge index is over current edge', () => {
|
it('should fail if new edge index is over current edge', () => {
|
||||||
const { fullTree, partialTree } = getTestTrees(10, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4)
|
const { fullTree, partialTree } = getTestTrees(10, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { simpleHash } from '../src'
|
|
||||||
import { it } from 'mocha'
|
import { it } from 'mocha'
|
||||||
import { should } from 'chai'
|
import { should } from 'chai'
|
||||||
|
import { simpleHash } from '../src'
|
||||||
|
|
||||||
describe('SimpleHash', () => {
|
describe('SimpleHash', () => {
|
||||||
it('should return correct hash string with default params', () => {
|
it('should return correct hash string with default params', () => {
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"outDir": "./lib",
|
"outDir": "./lib",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"composite": true,
|
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
Loading…
Reference in New Issue
Block a user