Add multiproof support
This commit is contained in:
parent
b36c6f31a5
commit
cea9442939
|
@ -1,6 +1,9 @@
|
||||||
|
/// <reference types="node" />
|
||||||
interface Options {
|
interface Options {
|
||||||
/** If set to `true`, an odd node will be duplicated and combined to make a pair to generate the layer hash. */
|
/** If set to `true`, an odd node will be duplicated and combined to make a pair to generate the layer hash. */
|
||||||
duplicateOdd?: boolean;
|
duplicateOdd?: boolean;
|
||||||
|
/** If set to `true`, an odd node will not have a pair generating the layer hash. */
|
||||||
|
singleOdd?: boolean;
|
||||||
/** If set to `true`, the leaves will hashed using the set hashing algorithms. */
|
/** If set to `true`, the leaves will hashed using the set hashing algorithms. */
|
||||||
hashLeaves?: boolean;
|
hashLeaves?: boolean;
|
||||||
/** If set to `true`, constructs the Merkle Tree using the [Bitcoin Merkle Tree implementation](http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html). Enable it when you need to replicate Bitcoin constructed Merkle Trees. In Bitcoin Merkle Trees, single nodes are combined with themselves, and each output hash is hashed again. */
|
/** If set to `true`, constructs the Merkle Tree using the [Bitcoin Merkle Tree implementation](http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html). Enable it when you need to replicate Bitcoin constructed Merkle Trees. In Bitcoin Merkle Trees, single nodes are combined with themselves, and each output hash is hashed again. */
|
||||||
|
@ -22,6 +25,7 @@ declare type TLayer = any;
|
||||||
*/
|
*/
|
||||||
export declare class MerkleTree {
|
export declare class MerkleTree {
|
||||||
duplicateOdd: boolean;
|
duplicateOdd: boolean;
|
||||||
|
singleOdd: boolean;
|
||||||
hashAlgo: (value: TValue) => THashAlgo;
|
hashAlgo: (value: TValue) => THashAlgo;
|
||||||
hashLeaves: boolean;
|
hashLeaves: boolean;
|
||||||
isBitcoinTree: boolean;
|
isBitcoinTree: boolean;
|
||||||
|
@ -63,10 +67,20 @@ export declare class MerkleTree {
|
||||||
*const leaves = tree.getLeaves()
|
*const leaves = tree.getLeaves()
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
getLeaves(): any[];
|
getLeaves(data?: any[]): any[];
|
||||||
|
/**
|
||||||
|
* getHexLeaves
|
||||||
|
* @desc Returns array of leaves of Merkle Tree as hex strings.
|
||||||
|
* @return {String[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const leaves = tree.getHexLeaves()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
getHexLeaves(): string[];
|
||||||
/**
|
/**
|
||||||
* getLayers
|
* getLayers
|
||||||
* @desc Returns array of all layers of Merkle Tree, including leaves and root.
|
* @desc Returns multi-dimensional array of all layers of Merkle Tree, including leaves and root.
|
||||||
* @return {Buffer[]}
|
* @return {Buffer[]}
|
||||||
* @example
|
* @example
|
||||||
*```js
|
*```js
|
||||||
|
@ -74,6 +88,36 @@ export declare class MerkleTree {
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
getLayers(): any[];
|
getLayers(): any[];
|
||||||
|
/**
|
||||||
|
* getHexLayers
|
||||||
|
* @desc Returns multi-dimensional array of all layers of Merkle Tree, including leaves and root as hex strings.
|
||||||
|
* @return {String[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const layers = tree.getHexLayers()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
getHexLayers(): any;
|
||||||
|
/**
|
||||||
|
* getLayersFlat
|
||||||
|
* @desc Returns single flat array of all layers of Merkle Tree, including leaves and root.
|
||||||
|
* @return {Buffer[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const layers = tree.getLayersFlat()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
getLayersFlat(): any;
|
||||||
|
/**
|
||||||
|
* getHexLayersFlat
|
||||||
|
* @desc Returns single flat array of all layers of Merkle Tree, including leaves and root as hex string.
|
||||||
|
* @return {String[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const layers = tree.getHexLayersFlat()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
getHexLayersFlat(): any;
|
||||||
/**
|
/**
|
||||||
* getRoot
|
* getRoot
|
||||||
* @desc Returns the Merkle root hash as a Buffer.
|
* @desc Returns the Merkle root hash as a Buffer.
|
||||||
|
@ -84,6 +128,15 @@ export declare class MerkleTree {
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
getRoot(): any;
|
getRoot(): any;
|
||||||
|
/**
|
||||||
|
* getHexRoot
|
||||||
|
* @desc Returns the Merkle root hash as a hex string.
|
||||||
|
* @return {String}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const root = tree.getHexRoot()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
getHexRoot(): string;
|
getHexRoot(): string;
|
||||||
/**
|
/**
|
||||||
* getProof
|
* getProof
|
||||||
|
@ -106,6 +159,12 @@ export declare class MerkleTree {
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
getProof(leaf: any, index?: any): any[];
|
getProof(leaf: any, index?: any): any[];
|
||||||
|
getProofIndices(treeIndices: any, depth: any): any[];
|
||||||
|
getMultiProof(tree: any, indices: any): any[];
|
||||||
|
getHexMultiProof(tree: any, indices: any): string[];
|
||||||
|
bufIndexOf(arr: any, el: any): number;
|
||||||
|
getProofFlags(els: any, proofs: any): any[];
|
||||||
|
getPairElement(idx: any, layer: any): any;
|
||||||
getHexProof(leaf: any, index?: any): string[];
|
getHexProof(leaf: any, index?: any): string[];
|
||||||
/**
|
/**
|
||||||
* verify
|
* verify
|
||||||
|
@ -124,11 +183,20 @@ export declare class MerkleTree {
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
verify(proof: any, targetNode: any, root: any): boolean;
|
verify(proof: any, targetNode: any, root: any): boolean;
|
||||||
|
verifyMultiProof(root: any, indices: any, leaves: any, depth: any, proof: any): any;
|
||||||
|
getDepth(): number;
|
||||||
getLayersAsObject(): any;
|
getLayersAsObject(): any;
|
||||||
print(): void;
|
print(): void;
|
||||||
toTreeString(): any;
|
toTreeString(): any;
|
||||||
toString(): any;
|
toString(): any;
|
||||||
static bufferify(x: any): any;
|
static bufferify(x: any): any;
|
||||||
|
static isHexStr(v: any): boolean;
|
||||||
static print(tree: any): void;
|
static print(tree: any): void;
|
||||||
|
_bufferToHex(value: Buffer): string;
|
||||||
|
_bufferify(x: any): any;
|
||||||
|
_bufferifyFn(f: any): (x: any) => Buffer;
|
||||||
|
_isHexStr(v: any): boolean;
|
||||||
|
_log2(x: any): any;
|
||||||
|
_zip(a: any, b: any): any;
|
||||||
}
|
}
|
||||||
export default MerkleTree;
|
export default MerkleTree;
|
||||||
|
|
|
@ -1,4 +1,35 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
|
if (!m) return o;
|
||||||
|
var i = m.call(o), r, ar = [], e;
|
||||||
|
try {
|
||||||
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||||
|
}
|
||||||
|
catch (error) { e = { error: error }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||||
|
}
|
||||||
|
finally { if (e) throw e.error; }
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
var __spread = (this && this.__spread) || function () {
|
||||||
|
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
var __values = (this && this.__values) || function(o) {
|
||||||
|
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
||||||
|
if (m) return m.call(o);
|
||||||
|
if (o && typeof o.length === "number") return {
|
||||||
|
next: function () {
|
||||||
|
if (o && i >= o.length) o = void 0;
|
||||||
|
return { value: o && o[i++], done: !o };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
||||||
|
};
|
||||||
exports.__esModule = true;
|
exports.__esModule = true;
|
||||||
var reverse = require("buffer-reverse");
|
var reverse = require("buffer-reverse");
|
||||||
var CryptoJS = require("crypto-js");
|
var CryptoJS = require("crypto-js");
|
||||||
|
@ -42,11 +73,12 @@ var MerkleTree = /** @class */ (function () {
|
||||||
this.sortPairs = true;
|
this.sortPairs = true;
|
||||||
}
|
}
|
||||||
this.duplicateOdd = !!options.duplicateOdd;
|
this.duplicateOdd = !!options.duplicateOdd;
|
||||||
this.hashAlgo = bufferifyFn(hashAlgorithm);
|
this.singleOdd = !!options.singleOdd;
|
||||||
|
this.hashAlgo = this._bufferifyFn(hashAlgorithm);
|
||||||
if (this.hashLeaves) {
|
if (this.hashLeaves) {
|
||||||
leaves = leaves.map(this.hashAlgo);
|
leaves = leaves.map(this.hashAlgo);
|
||||||
}
|
}
|
||||||
this.leaves = leaves.map(bufferify);
|
this.leaves = leaves.map(this._bufferify);
|
||||||
if (this.sortLeaves) {
|
if (this.sortLeaves) {
|
||||||
this.leaves = this.leaves.sort(Buffer.compare);
|
this.leaves = this.leaves.sort(Buffer.compare);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +105,7 @@ var MerkleTree = /** @class */ (function () {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!this.duplicateOdd) {
|
if (!this.duplicateOdd && !this.singleOdd) {
|
||||||
this.layers[layerIndex].push(nodes[i]);
|
this.layers[layerIndex].push(nodes[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -81,14 +113,28 @@ var MerkleTree = /** @class */ (function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var left = nodes[i];
|
var left = nodes[i];
|
||||||
var right = i + 1 == nodes.length ? left : nodes[i + 1];
|
var right = i + 1 === nodes.length ? left : nodes[i + 1];
|
||||||
var data = null;
|
var data = null;
|
||||||
var combined = null;
|
var combined = null;
|
||||||
if (this.isBitcoinTree) {
|
if (this.isBitcoinTree) {
|
||||||
combined = [reverse(left), reverse(right)];
|
combined = [reverse(left), reverse(right)];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
combined = [left, right];
|
if (this.singleOdd) {
|
||||||
|
right = nodes[i + 1];
|
||||||
|
if (!left) {
|
||||||
|
combined = [right];
|
||||||
|
}
|
||||||
|
else if (!right) {
|
||||||
|
combined = [left];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
combined = [left, right];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
combined = [left, right];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.sortPairs) {
|
if (this.sortPairs) {
|
||||||
combined.sort(Buffer.compare);
|
combined.sort(Buffer.compare);
|
||||||
|
@ -113,12 +159,35 @@ var MerkleTree = /** @class */ (function () {
|
||||||
*const leaves = tree.getLeaves()
|
*const leaves = tree.getLeaves()
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
MerkleTree.prototype.getLeaves = function () {
|
MerkleTree.prototype.getLeaves = function (data) {
|
||||||
|
var _this = this;
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
if (this.hashLeaves) {
|
||||||
|
data = data.map(this.hashAlgo);
|
||||||
|
if (this.sortLeaves) {
|
||||||
|
data = data.sort(Buffer.compare);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.leaves.filter(function (x) { return _this.bufIndexOf(data, x) !== -1; });
|
||||||
|
}
|
||||||
return this.leaves;
|
return this.leaves;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* getHexLeaves
|
||||||
|
* @desc Returns array of leaves of Merkle Tree as hex strings.
|
||||||
|
* @return {String[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const leaves = tree.getHexLeaves()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
MerkleTree.prototype.getHexLeaves = function () {
|
||||||
|
var _this = this;
|
||||||
|
return this.leaves.map(function (x) { return _this._bufferToHex(x); });
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* getLayers
|
* getLayers
|
||||||
* @desc Returns array of all layers of Merkle Tree, including leaves and root.
|
* @desc Returns multi-dimensional array of all layers of Merkle Tree, including leaves and root.
|
||||||
* @return {Buffer[]}
|
* @return {Buffer[]}
|
||||||
* @example
|
* @example
|
||||||
*```js
|
*```js
|
||||||
|
@ -128,6 +197,62 @@ var MerkleTree = /** @class */ (function () {
|
||||||
MerkleTree.prototype.getLayers = function () {
|
MerkleTree.prototype.getLayers = function () {
|
||||||
return this.layers;
|
return this.layers;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* getHexLayers
|
||||||
|
* @desc Returns multi-dimensional array of all layers of Merkle Tree, including leaves and root as hex strings.
|
||||||
|
* @return {String[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const layers = tree.getHexLayers()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
MerkleTree.prototype.getHexLayers = function () {
|
||||||
|
var _this = this;
|
||||||
|
return this.layers.reduce(function (acc, item, i) {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
acc.push(item.map(function (x) { return _this._bufferToHex(x); }));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
acc.push(item);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* getLayersFlat
|
||||||
|
* @desc Returns single flat array of all layers of Merkle Tree, including leaves and root.
|
||||||
|
* @return {Buffer[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const layers = tree.getLayersFlat()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
MerkleTree.prototype.getLayersFlat = function () {
|
||||||
|
var layers = this.layers.reduce(function (acc, item, i) {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
acc.unshift.apply(acc, __spread(item));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
acc.unshift(item);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
layers.unshift(Buffer.from([0]));
|
||||||
|
return layers;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* getHexLayersFlat
|
||||||
|
* @desc Returns single flat array of all layers of Merkle Tree, including leaves and root as hex string.
|
||||||
|
* @return {String[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const layers = tree.getHexLayersFlat()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
MerkleTree.prototype.getHexLayersFlat = function () {
|
||||||
|
var _this = this;
|
||||||
|
return this.getLayersFlat().map(function (x) { return _this._bufferToHex(x); });
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* getRoot
|
* getRoot
|
||||||
* @desc Returns the Merkle root hash as a Buffer.
|
* @desc Returns the Merkle root hash as a Buffer.
|
||||||
|
@ -140,9 +265,17 @@ var MerkleTree = /** @class */ (function () {
|
||||||
MerkleTree.prototype.getRoot = function () {
|
MerkleTree.prototype.getRoot = function () {
|
||||||
return this.layers[this.layers.length - 1][0] || Buffer.from([]);
|
return this.layers[this.layers.length - 1][0] || Buffer.from([]);
|
||||||
};
|
};
|
||||||
// TODO: documentation
|
/**
|
||||||
|
* getHexRoot
|
||||||
|
* @desc Returns the Merkle root hash as a hex string.
|
||||||
|
* @return {String}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const root = tree.getHexRoot()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
MerkleTree.prototype.getHexRoot = function () {
|
MerkleTree.prototype.getHexRoot = function () {
|
||||||
return bufferToHex(this.getRoot());
|
return this._bufferToHex(this.getRoot());
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* getProof
|
* getProof
|
||||||
|
@ -165,7 +298,7 @@ var MerkleTree = /** @class */ (function () {
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
MerkleTree.prototype.getProof = function (leaf, index) {
|
MerkleTree.prototype.getProof = function (leaf, index) {
|
||||||
leaf = bufferify(leaf);
|
leaf = this._bufferify(leaf);
|
||||||
var proof = [];
|
var proof = [];
|
||||||
if (typeof index !== 'number') {
|
if (typeof index !== 'number') {
|
||||||
index = -1;
|
index = -1;
|
||||||
|
@ -213,8 +346,150 @@ var MerkleTree = /** @class */ (function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
|
MerkleTree.prototype.getProofIndices = function (treeIndices, depth) {
|
||||||
|
var e_1, _a, e_2, _b;
|
||||||
|
var leafCount = Math.pow(2, depth);
|
||||||
|
var maximalIndices = new Set();
|
||||||
|
try {
|
||||||
|
for (var treeIndices_1 = __values(treeIndices), treeIndices_1_1 = treeIndices_1.next(); !treeIndices_1_1.done; treeIndices_1_1 = treeIndices_1.next()) {
|
||||||
|
var index = treeIndices_1_1.value;
|
||||||
|
var x = leafCount + index;
|
||||||
|
while (x > 1) {
|
||||||
|
maximalIndices.add(x ^ 1);
|
||||||
|
x = (x / 2) | 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (treeIndices_1_1 && !treeIndices_1_1.done && (_a = treeIndices_1["return"])) _a.call(treeIndices_1);
|
||||||
|
}
|
||||||
|
finally { if (e_1) throw e_1.error; }
|
||||||
|
}
|
||||||
|
var a = treeIndices.map(function (index) { return leafCount + index; });
|
||||||
|
var b = Array.from(maximalIndices).sort(function (a, b) { return a - b; }).reverse();
|
||||||
|
maximalIndices = a.concat(b);
|
||||||
|
var redundantIndices = new Set();
|
||||||
|
var proof = [];
|
||||||
|
try {
|
||||||
|
for (var maximalIndices_1 = __values(maximalIndices), maximalIndices_1_1 = maximalIndices_1.next(); !maximalIndices_1_1.done; maximalIndices_1_1 = maximalIndices_1.next()) {
|
||||||
|
var index = maximalIndices_1_1.value;
|
||||||
|
if (!redundantIndices.has(index)) {
|
||||||
|
proof.push(index);
|
||||||
|
while (index > 1) {
|
||||||
|
redundantIndices.add(index);
|
||||||
|
if (!redundantIndices.has(index ^ 1))
|
||||||
|
break;
|
||||||
|
index = (index / 2) | 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (maximalIndices_1_1 && !maximalIndices_1_1.done && (_b = maximalIndices_1["return"])) _b.call(maximalIndices_1);
|
||||||
|
}
|
||||||
|
finally { if (e_2) throw e_2.error; }
|
||||||
|
}
|
||||||
|
return proof.filter(function (index) {
|
||||||
|
return !treeIndices.includes(index - leafCount);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// TODO: documentation
|
||||||
|
MerkleTree.prototype.getMultiProof = function (tree, indices) {
|
||||||
|
var _this = this;
|
||||||
|
if (!indices) {
|
||||||
|
indices = tree;
|
||||||
|
tree = this.getLayersFlat();
|
||||||
|
if (!indices.every(function (x) { return typeof x === 'number'; })) {
|
||||||
|
var els = indices;
|
||||||
|
if (this.sortPairs) {
|
||||||
|
els = els.sort(Buffer.compare);
|
||||||
|
}
|
||||||
|
var ids = els.map(function (el) { return _this.bufIndexOf(_this.leaves, el); }).sort(function (a, b) { return a === b ? 0 : a > b ? 1 : -1; });
|
||||||
|
if (!ids.every(function (idx) { return idx !== -1; })) {
|
||||||
|
throw new Error('Element does not exist in Merkle tree');
|
||||||
|
}
|
||||||
|
var hashes_1 = [];
|
||||||
|
var proof = [];
|
||||||
|
var nextIds = [];
|
||||||
|
for (var i = 0; i < this.layers.length; i++) {
|
||||||
|
var layer = this.layers[i];
|
||||||
|
for (var j = 0; j < ids.length; j++) {
|
||||||
|
var idx = ids[j];
|
||||||
|
var pairElement = this.getPairElement(idx, layer);
|
||||||
|
hashes_1.push(layer[idx]);
|
||||||
|
if (pairElement) {
|
||||||
|
proof.push(pairElement);
|
||||||
|
}
|
||||||
|
nextIds.push((idx / 2) | 0);
|
||||||
|
}
|
||||||
|
ids = nextIds.filter(function (value, i, self) { return self.indexOf(value) === i; });
|
||||||
|
nextIds = [];
|
||||||
|
}
|
||||||
|
return proof.filter(function (value) { return !hashes_1.includes(value); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.getProofIndices(indices, this._log2((tree.length / 2) | 0)).map(function (index) { return tree[index]; });
|
||||||
|
};
|
||||||
|
// TODO: documentation
|
||||||
|
MerkleTree.prototype.getHexMultiProof = function (tree, indices) {
|
||||||
|
return this.getMultiProof(tree, indices).map(this._bufferToHex);
|
||||||
|
};
|
||||||
|
// TODO: documentation
|
||||||
|
MerkleTree.prototype.bufIndexOf = function (arr, el) {
|
||||||
|
for (var i = 0; i < arr.length; i++) {
|
||||||
|
if (el.equals(arr[i])) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
// TODO: documentation
|
||||||
|
MerkleTree.prototype.getProofFlags = function (els, proofs) {
|
||||||
|
var _this = this;
|
||||||
|
var ids = els.map(function (el) { return _this.bufIndexOf(_this.leaves, el); }).sort(function (a, b) { return a === b ? 0 : a > b ? 1 : -1; });
|
||||||
|
if (!ids.every(function (idx) { return idx !== -1; })) {
|
||||||
|
throw new Error('Element does not exist in Merkle tree');
|
||||||
|
}
|
||||||
|
var tested = [];
|
||||||
|
var flags = [];
|
||||||
|
var _loop_1 = function (index) {
|
||||||
|
var layer = this_1.layers[index];
|
||||||
|
ids = ids.reduce(function (ids, idx) {
|
||||||
|
var skipped = tested.includes(layer[idx]);
|
||||||
|
if (!skipped) {
|
||||||
|
var pairElement = _this.getPairElement(idx, layer);
|
||||||
|
var proofUsed = proofs.includes(layer[idx]) || proofs.includes(pairElement);
|
||||||
|
pairElement && flags.push(!proofUsed);
|
||||||
|
tested.push(layer[idx]);
|
||||||
|
tested.push(pairElement);
|
||||||
|
}
|
||||||
|
ids.push((idx / 2) | 0);
|
||||||
|
return ids;
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
|
var this_1 = this;
|
||||||
|
for (var index = 0; index < this.layers.length; index++) {
|
||||||
|
_loop_1(index);
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
};
|
||||||
|
MerkleTree.prototype.getPairElement = function (idx, layer) {
|
||||||
|
var pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1;
|
||||||
|
if (pairIdx < layer.length) {
|
||||||
|
return layer[pairIdx];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// TODO: documentation
|
||||||
MerkleTree.prototype.getHexProof = function (leaf, index) {
|
MerkleTree.prototype.getHexProof = function (leaf, index) {
|
||||||
return this.getProof(leaf, index).map(function (x) { return bufferToHex(x.data); });
|
var _this = this;
|
||||||
|
return this.getProof(leaf, index).map(function (x) { return _this._bufferToHex(x.data); });
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* verify
|
* verify
|
||||||
|
@ -233,8 +508,8 @@ var MerkleTree = /** @class */ (function () {
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
MerkleTree.prototype.verify = function (proof, targetNode, root) {
|
MerkleTree.prototype.verify = function (proof, targetNode, root) {
|
||||||
var hash = bufferify(targetNode);
|
var hash = this._bufferify(targetNode);
|
||||||
root = bufferify(root);
|
root = this._bufferify(root);
|
||||||
if (!Array.isArray(proof) ||
|
if (!Array.isArray(proof) ||
|
||||||
!proof.length ||
|
!proof.length ||
|
||||||
!targetNode ||
|
!targetNode ||
|
||||||
|
@ -247,7 +522,7 @@ var MerkleTree = /** @class */ (function () {
|
||||||
var isLeftNode = null;
|
var isLeftNode = null;
|
||||||
// NOTE: case for when proof is hex values only
|
// NOTE: case for when proof is hex values only
|
||||||
if (typeof node === 'string') {
|
if (typeof node === 'string') {
|
||||||
data = bufferify(node);
|
data = this._bufferify(node);
|
||||||
isLeftNode = true;
|
isLeftNode = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -282,6 +557,56 @@ var MerkleTree = /** @class */ (function () {
|
||||||
return Buffer.compare(hash, root) === 0;
|
return Buffer.compare(hash, root) === 0;
|
||||||
};
|
};
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
|
MerkleTree.prototype.verifyMultiProof = function (root, indices, leaves, depth, proof) {
|
||||||
|
var e_3, _a, e_4, _b;
|
||||||
|
root = this._bufferify(root);
|
||||||
|
leaves = leaves.map(this._bufferify);
|
||||||
|
proof = proof.map(this._bufferify);
|
||||||
|
var tree = {};
|
||||||
|
try {
|
||||||
|
for (var _c = __values(this._zip(indices, leaves)), _d = _c.next(); !_d.done; _d = _c.next()) {
|
||||||
|
var _e = __read(_d.value, 2), index = _e[0], leaf = _e[1];
|
||||||
|
tree[(Math.pow(2, depth)) + index] = leaf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c);
|
||||||
|
}
|
||||||
|
finally { if (e_3) throw e_3.error; }
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (var _f = __values(this._zip(this.getProofIndices(indices, depth), proof)), _g = _f.next(); !_g.done; _g = _f.next()) {
|
||||||
|
var _h = __read(_g.value, 2), index = _h[0], proofitem = _h[1];
|
||||||
|
tree[index] = proofitem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (_g && !_g.done && (_b = _f["return"])) _b.call(_f);
|
||||||
|
}
|
||||||
|
finally { if (e_4) throw e_4.error; }
|
||||||
|
}
|
||||||
|
var indexqueue = Object.keys(tree).map(function (x) { return +x; }).sort(function (a, b) { return a - b; });
|
||||||
|
indexqueue = indexqueue.slice(0, indexqueue.length - 1);
|
||||||
|
var i = 0;
|
||||||
|
while (i < indexqueue.length) {
|
||||||
|
var index = indexqueue[i];
|
||||||
|
if (index >= 2 && ({}).hasOwnProperty.call(tree, index ^ 1)) {
|
||||||
|
tree[(index / 2) | 0] = this.hashAlgo(Buffer.concat([tree[index - (index % 2)], tree[index - (index % 2) + 1]]));
|
||||||
|
indexqueue.push((index / 2) | 0);
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return !indices.length || (({}).hasOwnProperty.call(tree, 1) && tree[1].equals(root));
|
||||||
|
};
|
||||||
|
// TODO: documentation
|
||||||
|
MerkleTree.prototype.getDepth = function () {
|
||||||
|
return this.getLayers().length - 1;
|
||||||
|
};
|
||||||
|
// TODO: documentation
|
||||||
MerkleTree.prototype.getLayersAsObject = function () {
|
MerkleTree.prototype.getLayersAsObject = function () {
|
||||||
var _a;
|
var _a;
|
||||||
var layers = this.getLayers().map(function (x) { return x.map(function (x) { return x.toString('hex'); }); });
|
var layers = this.getLayers().map(function (x) { return x.map(function (x) { return x.toString('hex'); }); });
|
||||||
|
@ -303,7 +628,7 @@ var MerkleTree = /** @class */ (function () {
|
||||||
}
|
}
|
||||||
arr.push(obj);
|
arr.push(obj);
|
||||||
}
|
}
|
||||||
objs.push.apply(objs, arr);
|
objs.push.apply(objs, __spread(arr));
|
||||||
}
|
}
|
||||||
return objs[0];
|
return objs[0];
|
||||||
};
|
};
|
||||||
|
@ -322,47 +647,56 @@ var MerkleTree = /** @class */ (function () {
|
||||||
};
|
};
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
MerkleTree.bufferify = function (x) {
|
MerkleTree.bufferify = function (x) {
|
||||||
return bufferify(x);
|
if (!Buffer.isBuffer(x)) {
|
||||||
|
// crypto-js support
|
||||||
|
if (typeof x === 'object' && x.words) {
|
||||||
|
return Buffer.from(x.toString(CryptoJS.enc.Hex), 'hex');
|
||||||
|
}
|
||||||
|
else if (MerkleTree.isHexStr(x)) {
|
||||||
|
return Buffer.from(x.replace(/^0x/, ''), 'hex');
|
||||||
|
}
|
||||||
|
else if (typeof x === 'string') {
|
||||||
|
return Buffer.from(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
MerkleTree.isHexStr = function (v) {
|
||||||
|
return (typeof v === 'string' && /^(0x)?[0-9A-Fa-f]*$/.test(v));
|
||||||
};
|
};
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
MerkleTree.print = function (tree) {
|
MerkleTree.print = function (tree) {
|
||||||
console.log(tree.toString());
|
console.log(tree.toString());
|
||||||
};
|
};
|
||||||
|
MerkleTree.prototype._bufferToHex = function (value) {
|
||||||
|
return '0x' + value.toString('hex');
|
||||||
|
};
|
||||||
|
MerkleTree.prototype._bufferify = function (x) {
|
||||||
|
return MerkleTree.bufferify(x);
|
||||||
|
};
|
||||||
|
MerkleTree.prototype._bufferifyFn = function (f) {
|
||||||
|
return function (x) {
|
||||||
|
var v = f(x);
|
||||||
|
if (Buffer.isBuffer(v)) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
if (this._isHexStr(v)) {
|
||||||
|
return Buffer.from(v, 'hex');
|
||||||
|
}
|
||||||
|
// crypto-js support
|
||||||
|
return Buffer.from(f(CryptoJS.enc.Hex.parse(x.toString('hex'))).toString(CryptoJS.enc.Hex), 'hex');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
MerkleTree.prototype._isHexStr = function (v) {
|
||||||
|
return MerkleTree.isHexStr(v);
|
||||||
|
};
|
||||||
|
MerkleTree.prototype._log2 = function (x) {
|
||||||
|
return x === 1 ? 0 : 1 + this._log2((x / 2) | 0);
|
||||||
|
};
|
||||||
|
MerkleTree.prototype._zip = function (a, b) {
|
||||||
|
return a.map(function (e, i) { return [e, b[i]]; });
|
||||||
|
};
|
||||||
return MerkleTree;
|
return MerkleTree;
|
||||||
}());
|
}());
|
||||||
exports.MerkleTree = MerkleTree;
|
exports.MerkleTree = MerkleTree;
|
||||||
function bufferToHex(value) {
|
|
||||||
return '0x' + value.toString('hex');
|
|
||||||
}
|
|
||||||
function bufferify(x) {
|
|
||||||
if (!Buffer.isBuffer(x)) {
|
|
||||||
// crypto-js support
|
|
||||||
if (typeof x === 'object' && x.words) {
|
|
||||||
return Buffer.from(x.toString(CryptoJS.enc.Hex), 'hex');
|
|
||||||
}
|
|
||||||
else if (isHexStr(x)) {
|
|
||||||
return Buffer.from(x.replace(/^0x/, ''), 'hex');
|
|
||||||
}
|
|
||||||
else if (typeof x === 'string') {
|
|
||||||
return Buffer.from(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
function bufferifyFn(f) {
|
|
||||||
return function (x) {
|
|
||||||
var v = f(x);
|
|
||||||
if (Buffer.isBuffer(v)) {
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
if (isHexStr(v)) {
|
|
||||||
return Buffer.from(v, 'hex');
|
|
||||||
}
|
|
||||||
// crypto-js support
|
|
||||||
return Buffer.from(f(CryptoJS.enc.Hex.parse(x.toString('hex'))).toString(CryptoJS.enc.Hex), 'hex');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function isHexStr(v) {
|
|
||||||
return (typeof v === 'string' && /^(0x)?[0-9A-Fa-f]*$/.test(v));
|
|
||||||
}
|
|
||||||
exports["default"] = MerkleTree;
|
exports["default"] = MerkleTree;
|
||||||
|
|
424
index.ts
424
index.ts
|
@ -5,6 +5,8 @@ import * as treeify from 'treeify'
|
||||||
interface Options {
|
interface Options {
|
||||||
/** If set to `true`, an odd node will be duplicated and combined to make a pair to generate the layer hash. */
|
/** If set to `true`, an odd node will be duplicated and combined to make a pair to generate the layer hash. */
|
||||||
duplicateOdd?: boolean
|
duplicateOdd?: boolean
|
||||||
|
/** If set to `true`, an odd node will not have a pair generating the layer hash. */
|
||||||
|
singleOdd?: boolean
|
||||||
/** If set to `true`, the leaves will hashed using the set hashing algorithms. */
|
/** If set to `true`, the leaves will hashed using the set hashing algorithms. */
|
||||||
hashLeaves?: boolean
|
hashLeaves?: boolean
|
||||||
/** If set to `true`, constructs the Merkle Tree using the [Bitcoin Merkle Tree implementation](http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html). Enable it when you need to replicate Bitcoin constructed Merkle Trees. In Bitcoin Merkle Trees, single nodes are combined with themselves, and each output hash is hashed again. */
|
/** If set to `true`, constructs the Merkle Tree using the [Bitcoin Merkle Tree implementation](http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html). Enable it when you need to replicate Bitcoin constructed Merkle Trees. In Bitcoin Merkle Trees, single nodes are combined with themselves, and each output hash is hashed again. */
|
||||||
|
@ -28,6 +30,7 @@ type TLayer = any
|
||||||
*/
|
*/
|
||||||
export class MerkleTree {
|
export class MerkleTree {
|
||||||
duplicateOdd: boolean
|
duplicateOdd: boolean
|
||||||
|
singleOdd: boolean
|
||||||
hashAlgo: (value: TValue) => THashAlgo
|
hashAlgo: (value: TValue) => THashAlgo
|
||||||
hashLeaves: boolean
|
hashLeaves: boolean
|
||||||
isBitcoinTree: boolean
|
isBitcoinTree: boolean
|
||||||
|
@ -59,7 +62,7 @@ export class MerkleTree {
|
||||||
*const tree = new MerkleTree(leaves, sha256)
|
*const tree = new MerkleTree(leaves, sha256)
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
constructor(leaves, hashAlgorithm, options: Options = {}) {
|
constructor (leaves, hashAlgorithm, options: Options = {}) {
|
||||||
this.isBitcoinTree = !!options.isBitcoinTree
|
this.isBitcoinTree = !!options.isBitcoinTree
|
||||||
this.hashLeaves = !!options.hashLeaves
|
this.hashLeaves = !!options.hashLeaves
|
||||||
this.sortLeaves = !!options.sortLeaves
|
this.sortLeaves = !!options.sortLeaves
|
||||||
|
@ -72,12 +75,14 @@ export class MerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.duplicateOdd = !!options.duplicateOdd
|
this.duplicateOdd = !!options.duplicateOdd
|
||||||
this.hashAlgo = bufferifyFn(hashAlgorithm)
|
this.singleOdd = !!options.singleOdd
|
||||||
|
|
||||||
|
this.hashAlgo = this._bufferifyFn(hashAlgorithm)
|
||||||
if (this.hashLeaves) {
|
if (this.hashLeaves) {
|
||||||
leaves = leaves.map(this.hashAlgo)
|
leaves = leaves.map(this.hashAlgo)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.leaves = leaves.map(bufferify)
|
this.leaves = leaves.map(this._bufferify)
|
||||||
if (this.sortLeaves) {
|
if (this.sortLeaves) {
|
||||||
this.leaves = this.leaves.sort(Buffer.compare)
|
this.leaves = this.leaves.sort(Buffer.compare)
|
||||||
}
|
}
|
||||||
|
@ -87,15 +92,13 @@ export class MerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
createHashes(nodes) {
|
createHashes (nodes) {
|
||||||
while (nodes.length > 1) {
|
while (nodes.length > 1) {
|
||||||
|
|
||||||
const layerIndex = this.layers.length
|
const layerIndex = this.layers.length
|
||||||
|
|
||||||
this.layers.push([])
|
this.layers.push([])
|
||||||
|
|
||||||
for (let i = 0; i < nodes.length; i += 2) {
|
for (let i = 0; i < nodes.length; i += 2) {
|
||||||
|
|
||||||
if (i + 1 === nodes.length) {
|
if (i + 1 === nodes.length) {
|
||||||
if (nodes.length % 2 === 1) {
|
if (nodes.length % 2 === 1) {
|
||||||
let data = nodes[nodes.length - 1]
|
let data = nodes[nodes.length - 1]
|
||||||
|
@ -111,24 +114,34 @@ export class MerkleTree {
|
||||||
this.layers[layerIndex].push(hash)
|
this.layers[layerIndex].push(hash)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
if (!this.duplicateOdd) {
|
if (!this.duplicateOdd && !this.singleOdd) {
|
||||||
this.layers[layerIndex].push(nodes[i])
|
this.layers[layerIndex].push(nodes[i])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const left = nodes[i]
|
const left = nodes[i]
|
||||||
const right = i + 1 == nodes.length ? left : nodes[i + 1];
|
let right = i + 1 === nodes.length ? left : nodes[i + 1]
|
||||||
let data = null
|
let data = null
|
||||||
let combined = null
|
let combined = null
|
||||||
|
|
||||||
if (this.isBitcoinTree) {
|
if (this.isBitcoinTree) {
|
||||||
combined = [reverse(left), reverse(right)]
|
combined = [reverse(left), reverse(right)]
|
||||||
} else {
|
} else {
|
||||||
combined = [left, right]
|
if (this.singleOdd) {
|
||||||
|
right = nodes[i + 1]
|
||||||
|
if (!left) {
|
||||||
|
combined = [right]
|
||||||
|
} else if (!right) {
|
||||||
|
combined = [left]
|
||||||
|
} else {
|
||||||
|
combined = [left, right]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
combined = [left, right]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.sortPairs) {
|
if (this.sortPairs) {
|
||||||
|
@ -160,23 +173,106 @@ export class MerkleTree {
|
||||||
*const leaves = tree.getLeaves()
|
*const leaves = tree.getLeaves()
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
getLeaves() {
|
getLeaves (data?: any[]) {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
if (this.hashLeaves) {
|
||||||
|
data = data.map(this.hashAlgo)
|
||||||
|
if (this.sortLeaves) {
|
||||||
|
data = data.sort(Buffer.compare)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.leaves.filter(x => this.bufIndexOf(data, x) !== -1)
|
||||||
|
}
|
||||||
|
|
||||||
return this.leaves
|
return this.leaves
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getHexLeaves
|
||||||
|
* @desc Returns array of leaves of Merkle Tree as hex strings.
|
||||||
|
* @return {String[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const leaves = tree.getHexLeaves()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
getHexLeaves () {
|
||||||
|
return this.leaves.map(x => this._bufferToHex(x))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getLayers
|
* getLayers
|
||||||
* @desc Returns array of all layers of Merkle Tree, including leaves and root.
|
* @desc Returns multi-dimensional array of all layers of Merkle Tree, including leaves and root.
|
||||||
* @return {Buffer[]}
|
* @return {Buffer[]}
|
||||||
* @example
|
* @example
|
||||||
*```js
|
*```js
|
||||||
*const layers = tree.getLayers()
|
*const layers = tree.getLayers()
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
getLayers() {
|
getLayers () {
|
||||||
return this.layers
|
return this.layers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getHexLayers
|
||||||
|
* @desc Returns multi-dimensional array of all layers of Merkle Tree, including leaves and root as hex strings.
|
||||||
|
* @return {String[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const layers = tree.getHexLayers()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
getHexLayers () {
|
||||||
|
return this.layers.reduce((acc, item, i) => {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
acc.push(item.map(x => this._bufferToHex(x)))
|
||||||
|
} else {
|
||||||
|
acc.push(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getLayersFlat
|
||||||
|
* @desc Returns single flat array of all layers of Merkle Tree, including leaves and root.
|
||||||
|
* @return {Buffer[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const layers = tree.getLayersFlat()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
getLayersFlat () {
|
||||||
|
const layers = this.layers.reduce((acc, item, i) => {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
acc.unshift(...item)
|
||||||
|
} else {
|
||||||
|
acc.unshift(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
layers.unshift(Buffer.from([0]))
|
||||||
|
|
||||||
|
return layers
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getHexLayersFlat
|
||||||
|
* @desc Returns single flat array of all layers of Merkle Tree, including leaves and root as hex string.
|
||||||
|
* @return {String[]}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const layers = tree.getHexLayersFlat()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
getHexLayersFlat () {
|
||||||
|
return this.getLayersFlat().map(x => this._bufferToHex(x))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getRoot
|
* getRoot
|
||||||
* @desc Returns the Merkle root hash as a Buffer.
|
* @desc Returns the Merkle root hash as a Buffer.
|
||||||
|
@ -186,13 +282,21 @@ export class MerkleTree {
|
||||||
*const root = tree.getRoot()
|
*const root = tree.getRoot()
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
getRoot() {
|
getRoot () {
|
||||||
return this.layers[this.layers.length - 1][0] || Buffer.from([])
|
return this.layers[this.layers.length - 1][0] || Buffer.from([])
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: documentation
|
/**
|
||||||
getHexRoot() {
|
* getHexRoot
|
||||||
return bufferToHex(this.getRoot())
|
* @desc Returns the Merkle root hash as a hex string.
|
||||||
|
* @return {String}
|
||||||
|
* @example
|
||||||
|
*```js
|
||||||
|
*const root = tree.getHexRoot()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
getHexRoot () {
|
||||||
|
return this._bufferToHex(this.getRoot())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -215,8 +319,8 @@ export class MerkleTree {
|
||||||
*const proof = tree.getProof(leaves[2], 2)
|
*const proof = tree.getProof(leaves[2], 2)
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
getProof(leaf, index?) {
|
getProof (leaf, index?) {
|
||||||
leaf = bufferify(leaf)
|
leaf = this._bufferify(leaf)
|
||||||
const proof = []
|
const proof = []
|
||||||
|
|
||||||
if (typeof index !== 'number') {
|
if (typeof index !== 'number') {
|
||||||
|
@ -253,7 +357,6 @@ export class MerkleTree {
|
||||||
|
|
||||||
return proof
|
return proof
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Proof Generation for Non-Bitcoin Trees
|
// Proof Generation for Non-Bitcoin Trees
|
||||||
|
|
||||||
for (let i = 0; i < this.layers.length; i++) {
|
for (let i = 0; i < this.layers.length; i++) {
|
||||||
|
@ -270,7 +373,6 @@ export class MerkleTree {
|
||||||
|
|
||||||
// set index to parent index
|
// set index to parent index
|
||||||
index = (index / 2) | 0
|
index = (index / 2) | 0
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return proof
|
return proof
|
||||||
|
@ -278,8 +380,143 @@ export class MerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
getHexProof(leaf, index?) {
|
getProofIndices (treeIndices, depth) {
|
||||||
return this.getProof(leaf, index).map(x => bufferToHex(x.data))
|
const leafCount = 2 ** depth
|
||||||
|
let maximalIndices :any = new Set()
|
||||||
|
for (const index of treeIndices) {
|
||||||
|
let x = leafCount + index
|
||||||
|
while (x > 1) {
|
||||||
|
maximalIndices.add(x ^ 1)
|
||||||
|
x = (x / 2) | 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const a = treeIndices.map(index => leafCount + index)
|
||||||
|
const b = Array.from(maximalIndices).sort((a: any, b: any) => a - b).reverse()
|
||||||
|
maximalIndices = a.concat(b)
|
||||||
|
|
||||||
|
const redundantIndices = new Set()
|
||||||
|
const proof = []
|
||||||
|
|
||||||
|
for (let index of maximalIndices) {
|
||||||
|
if (!redundantIndices.has(index)) {
|
||||||
|
proof.push(index)
|
||||||
|
while (index > 1) {
|
||||||
|
redundantIndices.add(index)
|
||||||
|
if (!redundantIndices.has(index as number ^ 1)) break
|
||||||
|
index = (index as number / 2) | 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return proof.filter(index => {
|
||||||
|
return !treeIndices.includes(index - leafCount)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: documentation
|
||||||
|
getMultiProof (tree, indices) {
|
||||||
|
if (!indices) {
|
||||||
|
indices = tree
|
||||||
|
tree = this.getLayersFlat()
|
||||||
|
|
||||||
|
if (!indices.every(x => typeof x === 'number')) {
|
||||||
|
let els = indices
|
||||||
|
if (this.sortPairs) {
|
||||||
|
els = els.sort(Buffer.compare)
|
||||||
|
}
|
||||||
|
|
||||||
|
let ids = els.map((el) => this.bufIndexOf(this.leaves, el)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1)
|
||||||
|
if (!ids.every((idx) => idx !== -1)) {
|
||||||
|
throw new Error('Element does not exist in Merkle tree')
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashes = []
|
||||||
|
const proof = []
|
||||||
|
let nextIds = []
|
||||||
|
|
||||||
|
for (let i= 0; i< this.layers.length; i++) {
|
||||||
|
const layer = this.layers[i]
|
||||||
|
for (let j = 0; j < ids.length; j++) {
|
||||||
|
const idx = ids[j]
|
||||||
|
const pairElement = this.getPairElement(idx, layer)
|
||||||
|
|
||||||
|
hashes.push(layer[idx])
|
||||||
|
if (pairElement) {
|
||||||
|
proof.push(pairElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextIds.push((idx / 2)|0)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = nextIds.filter((value, i, self) => self.indexOf(value) === i)
|
||||||
|
nextIds = []
|
||||||
|
}
|
||||||
|
|
||||||
|
return proof.filter((value) => !hashes.includes(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getProofIndices(indices, this._log2((tree.length / 2) | 0)).map(index => tree[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: documentation
|
||||||
|
getHexMultiProof (tree, indices) {
|
||||||
|
return this.getMultiProof(tree, indices).map(this._bufferToHex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: documentation
|
||||||
|
bufIndexOf (arr, el) {
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
if (el.equals(arr[i])) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: documentation
|
||||||
|
getProofFlags (els, proofs) {
|
||||||
|
let ids = els.map((el) => this.bufIndexOf(this.leaves, el)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1)
|
||||||
|
if (!ids.every((idx) => idx !== -1)) {
|
||||||
|
throw new Error('Element does not exist in Merkle tree')
|
||||||
|
}
|
||||||
|
|
||||||
|
const tested = []
|
||||||
|
const flags = []
|
||||||
|
for (let index = 0; index < this.layers.length; index++) {
|
||||||
|
const layer = this.layers[index]
|
||||||
|
ids = ids.reduce((ids, idx) => {
|
||||||
|
const skipped = tested.includes(layer[idx])
|
||||||
|
if (!skipped) {
|
||||||
|
const pairElement = this.getPairElement(idx, layer)
|
||||||
|
const proofUsed = proofs.includes(layer[idx]) || proofs.includes(pairElement)
|
||||||
|
pairElement && flags.push(!proofUsed)
|
||||||
|
tested.push(layer[idx])
|
||||||
|
tested.push(pairElement)
|
||||||
|
}
|
||||||
|
ids.push((idx / 2)|0)
|
||||||
|
return ids
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
getPairElement (idx, layer) {
|
||||||
|
const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1
|
||||||
|
|
||||||
|
if (pairIdx < layer.length) {
|
||||||
|
return layer[pairIdx]
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: documentation
|
||||||
|
getHexProof (leaf, index?) {
|
||||||
|
return this.getProof(leaf, index).map(x => this._bufferToHex(x.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -298,9 +535,9 @@ export class MerkleTree {
|
||||||
*const verified = tree.verify(proof, leaves[2], root)
|
*const verified = tree.verify(proof, leaves[2], root)
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
verify(proof, targetNode, root) {
|
verify (proof, targetNode, root) {
|
||||||
let hash = bufferify(targetNode)
|
let hash = this._bufferify(targetNode)
|
||||||
root = bufferify(root)
|
root = this._bufferify(root)
|
||||||
|
|
||||||
if (!Array.isArray(proof) ||
|
if (!Array.isArray(proof) ||
|
||||||
!proof.length ||
|
!proof.length ||
|
||||||
|
@ -316,7 +553,7 @@ export class MerkleTree {
|
||||||
|
|
||||||
// NOTE: case for when proof is hex values only
|
// NOTE: case for when proof is hex values only
|
||||||
if (typeof node === 'string') {
|
if (typeof node === 'string') {
|
||||||
data = bufferify(node)
|
data = this._bufferify(node)
|
||||||
isLeftNode = true
|
isLeftNode = true
|
||||||
} else {
|
} else {
|
||||||
data = node.data
|
data = node.data
|
||||||
|
@ -332,20 +569,19 @@ export class MerkleTree {
|
||||||
|
|
||||||
hash = this.hashAlgo(Buffer.concat(buffers))
|
hash = this.hashAlgo(Buffer.concat(buffers))
|
||||||
hash = reverse(this.hashAlgo(hash))
|
hash = reverse(this.hashAlgo(hash))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (this.sortPairs) {
|
if (this.sortPairs) {
|
||||||
if (Buffer.compare(hash, data) === -1) {
|
if (Buffer.compare(hash, data) === -1) {
|
||||||
buffers.push(hash, data)
|
buffers.push(hash, data)
|
||||||
hash = this.hashAlgo(Buffer.concat(buffers));
|
hash = this.hashAlgo(Buffer.concat(buffers))
|
||||||
} else {
|
} else {
|
||||||
buffers.push(data, hash)
|
buffers.push(data, hash)
|
||||||
hash = this.hashAlgo(Buffer.concat(buffers));
|
hash = this.hashAlgo(Buffer.concat(buffers))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buffers.push(hash);
|
buffers.push(hash)
|
||||||
buffers[isLeftNode ? 'unshift' : 'push'](data);
|
buffers[isLeftNode ? 'unshift' : 'push'](data)
|
||||||
hash = this.hashAlgo(Buffer.concat(buffers));
|
hash = this.hashAlgo(Buffer.concat(buffers))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -354,7 +590,39 @@ export class MerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
getLayersAsObject() {
|
verifyMultiProof (root, indices, leaves, depth, proof) {
|
||||||
|
root = this._bufferify(root)
|
||||||
|
leaves = leaves.map(this._bufferify)
|
||||||
|
proof = proof.map(this._bufferify)
|
||||||
|
|
||||||
|
const tree = {}
|
||||||
|
for (const [index, leaf] of this._zip(indices, leaves)) {
|
||||||
|
tree[(2 ** depth) + index] = leaf
|
||||||
|
}
|
||||||
|
for (const [index, proofitem] of this._zip(this.getProofIndices(indices, depth), proof)) {
|
||||||
|
tree[index] = proofitem
|
||||||
|
}
|
||||||
|
let indexqueue = Object.keys(tree).map(x => +x).sort((a, b) => a - b)
|
||||||
|
indexqueue = indexqueue.slice(0, indexqueue.length - 1)
|
||||||
|
let i = 0
|
||||||
|
while (i < indexqueue.length) {
|
||||||
|
const index = indexqueue[i]
|
||||||
|
if (index >= 2 && ({}).hasOwnProperty.call(tree, index ^ 1)) {
|
||||||
|
tree[(index / 2) | 0] = this.hashAlgo(Buffer.concat([tree[index - (index % 2)], tree[index - (index % 2) + 1]]))
|
||||||
|
indexqueue.push((index / 2) | 0)
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
return !indices.length || (({}).hasOwnProperty.call(tree, 1) && tree[1].equals(root))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: documentation
|
||||||
|
getDepth () {
|
||||||
|
return this.getLayers().length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: documentation
|
||||||
|
getLayersAsObject () {
|
||||||
const layers = this.getLayers().map(x => x.map(x => x.toString('hex')))
|
const layers = this.getLayers().map(x => x.map(x => x.toString('hex')))
|
||||||
const objs = []
|
const objs = []
|
||||||
for (let i = 0; i < layers.length; i++) {
|
for (let i = 0; i < layers.length; i++) {
|
||||||
|
@ -383,69 +651,81 @@ export class MerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
print() {
|
print () {
|
||||||
MerkleTree.print(this)
|
MerkleTree.print(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
toTreeString() {
|
toTreeString () {
|
||||||
const obj = this.getLayersAsObject()
|
const obj = this.getLayersAsObject()
|
||||||
return treeify.asTree(obj, true)
|
return treeify.asTree(obj, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
toString() {
|
toString () {
|
||||||
return this.toTreeString()
|
return this.toTreeString()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
static bufferify(x) {
|
static bufferify (x) {
|
||||||
return bufferify(x)
|
if (!Buffer.isBuffer(x)) {
|
||||||
|
// crypto-js support
|
||||||
|
if (typeof x === 'object' && x.words) {
|
||||||
|
return Buffer.from(x.toString(CryptoJS.enc.Hex), 'hex')
|
||||||
|
} else if (MerkleTree.isHexStr(x)) {
|
||||||
|
return Buffer.from(x.replace(/^0x/, ''), 'hex')
|
||||||
|
} else if (typeof x === 'string') {
|
||||||
|
return Buffer.from(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
static isHexStr (v) {
|
||||||
|
return (typeof v === 'string' && /^(0x)?[0-9A-Fa-f]*$/.test(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
static print(tree) {
|
static print (tree) {
|
||||||
console.log(tree.toString())
|
console.log(tree.toString())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function bufferToHex(value: Buffer) {
|
_bufferToHex (value: Buffer) {
|
||||||
return '0x' + value.toString('hex')
|
return '0x' + value.toString('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
function bufferify(x) {
|
_bufferify (x) {
|
||||||
if (!Buffer.isBuffer(x)) {
|
return MerkleTree.bufferify(x)
|
||||||
// crypto-js support
|
}
|
||||||
if (typeof x === 'object' && x.words) {
|
|
||||||
return Buffer.from(x.toString(CryptoJS.enc.Hex), 'hex')
|
_bufferifyFn (f) {
|
||||||
} else if (isHexStr(x)) {
|
return function (x) {
|
||||||
return Buffer.from(x.replace(/^0x/, ''), 'hex')
|
const v = f(x)
|
||||||
} else if (typeof x === 'string') {
|
if (Buffer.isBuffer(v)) {
|
||||||
return Buffer.from(x)
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._isHexStr(v)) {
|
||||||
|
return Buffer.from(v, 'hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
// crypto-js support
|
||||||
|
return Buffer.from(f(CryptoJS.enc.Hex.parse(x.toString('hex'))).toString(CryptoJS.enc.Hex), 'hex')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return x
|
_isHexStr (v) {
|
||||||
}
|
return MerkleTree.isHexStr(v)
|
||||||
|
|
||||||
function bufferifyFn(f) {
|
|
||||||
return function (x) {
|
|
||||||
const v = f(x)
|
|
||||||
if (Buffer.isBuffer(v)) {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isHexStr(v)) {
|
|
||||||
return Buffer.from(v, 'hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
// crypto-js support
|
|
||||||
return Buffer.from(f(CryptoJS.enc.Hex.parse(x.toString('hex'))).toString(CryptoJS.enc.Hex), 'hex')
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function isHexStr(v) {
|
_log2 (x) {
|
||||||
return (typeof v === 'string' && /^(0x)?[0-9A-Fa-f]*$/.test(v))
|
return x === 1 ? 0 : 1 + this._log2((x / 2) | 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
_zip (a, b) {
|
||||||
|
return a.map((e, i) => [e, b[i]])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MerkleTree
|
export default MerkleTree
|
||||||
|
|
19
package.json
19
package.json
|
@ -4,10 +4,13 @@
|
||||||
"description": "Construct Merkle Trees and verify proofs",
|
"description": "Construct Merkle Trees and verify proofs",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"files": ["dist"],
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "tape test/*.js",
|
"test": "tape test/*.js",
|
||||||
"build": "rm -rf dist/ && tsc",
|
"build": "rm -rf dist/ && tsc",
|
||||||
|
"lint": "standardx --fix index.ts test/*.js",
|
||||||
"docs:md": "rm -rf docs/ && typedoc --theme markdown index.ts --out docs --mdEngine github --mdDocusaurus --mdHideSources"
|
"docs:md": "rm -rf docs/ && typedoc --theme markdown index.ts --out docs --mdEngine github --mdDocusaurus --mdHideSources"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -39,9 +42,13 @@
|
||||||
"homepage": "https://github.com/miguelmota/merkletreejs",
|
"homepage": "https://github.com/miguelmota/merkletreejs",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^11.12.1",
|
"@types/node": "^11.12.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^3.0.2",
|
||||||
|
"@typescript-eslint/parser": "^3.0.2",
|
||||||
"crypto": "0.0.3",
|
"crypto": "0.0.3",
|
||||||
"ethereumjs-util": "6.1.0",
|
"ethereumjs-util": "6.1.0",
|
||||||
"sha1": "^1.1.1",
|
"sha1": "^1.1.1",
|
||||||
|
"standard": "^14.3.4",
|
||||||
|
"standardx": "^5.0.0",
|
||||||
"tape": "^4.9.2",
|
"tape": "^4.9.2",
|
||||||
"typedoc": "^0.14.2",
|
"typedoc": "^0.14.2",
|
||||||
"typedoc-plugin-markdown": "^1.2.1",
|
"typedoc-plugin-markdown": "^1.2.1",
|
||||||
|
@ -56,5 +63,15 @@
|
||||||
"is-buffer": "^2.0.3",
|
"is-buffer": "^2.0.3",
|
||||||
"merkle-lib": "^2.0.10",
|
"merkle-lib": "^2.0.10",
|
||||||
"treeify": "^1.1.0"
|
"treeify": "^1.1.0"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"rules": {
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"standardx": {
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": [ "@typescript-eslint/eslint-plugin" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
/* eslint camelcase: 0 */
|
||||||
|
|
||||||
const test = require('tape')
|
const test = require('tape')
|
||||||
const {keccak} = require('ethereumjs-util')
|
const { keccak } = require('ethereumjs-util')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const CryptoJS = require('crypto-js')
|
const CryptoJS = require('crypto-js')
|
||||||
const SHA256 = require('crypto-js/sha256')
|
const SHA256 = require('crypto-js/sha256')
|
||||||
|
@ -24,7 +26,7 @@ test('sha256 with keccak leaves with duplicate odd option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
||||||
const tree = new MerkleTree(leaves, sha256, {duplicateOdd: true})
|
const tree = new MerkleTree(leaves, sha256, { duplicateOdd: true })
|
||||||
const root = 'bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1'
|
const root = 'bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -44,7 +46,7 @@ test('sha256 with sort pairs option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(x => sha256(x))
|
const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(x => sha256(x))
|
||||||
const tree = new MerkleTree(leaves, sha256, {sortPairs: true})
|
const tree = new MerkleTree(leaves, sha256, { sortPairs: true })
|
||||||
const root = 'a30ba95a1a5dc397fe45ea20105363b08d682b864a28f4940419a29349a28325'
|
const root = 'a30ba95a1a5dc397fe45ea20105363b08d682b864a28f4940419a29349a28325'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -54,7 +56,7 @@ test('keccak with sort leaves and sort pairs option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'].map(x => keccak(x))
|
const leaves = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'].map(x => keccak(x))
|
||||||
const tree = new MerkleTree(leaves, keccak, {sortLeaves: true, sortPairs: true})
|
const tree = new MerkleTree(leaves, keccak, { sortLeaves: true, sortPairs: true })
|
||||||
const root = '60219f87561939610b484575e45c6e81156a53b86d7cd16640d930d14f21758e'
|
const root = '60219f87561939610b484575e45c6e81156a53b86d7cd16640d930d14f21758e'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -64,7 +66,7 @@ test('keccak with sort option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'].map(x => keccak(x))
|
const leaves = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'].map(x => keccak(x))
|
||||||
const tree = new MerkleTree(leaves, keccak, {sort: true})
|
const tree = new MerkleTree(leaves, keccak, { sort: true })
|
||||||
const root = '60219f87561939610b484575e45c6e81156a53b86d7cd16640d930d14f21758e'
|
const root = '60219f87561939610b484575e45c6e81156a53b86d7cd16640d930d14f21758e'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -74,7 +76,7 @@ test('sha256 with sha256 leaves and sort pairs option and duplicate odd option',
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(x => sha256(x))
|
const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(x => sha256(x))
|
||||||
const tree = new MerkleTree(leaves, sha256, {sortPairs: true, duplicateOdd: true})
|
const tree = new MerkleTree(leaves, sha256, { sortPairs: true, duplicateOdd: true })
|
||||||
const root = 'a5260b2a7ec31584e5d5689a5628c2b3d949e2397334fd71c107478e5f887eaf'
|
const root = 'a5260b2a7ec31584e5d5689a5628c2b3d949e2397334fd71c107478e5f887eaf'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -84,7 +86,7 @@ test('sha256 with hash leaves option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c', 'd', 'e', 'f']
|
const leaves = ['a', 'b', 'c', 'd', 'e', 'f']
|
||||||
const tree = new MerkleTree(leaves, sha256, {hashLeaves: true})
|
const tree = new MerkleTree(leaves, sha256, { hashLeaves: true })
|
||||||
const root = '1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2'
|
const root = '1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -94,7 +96,7 @@ test('sha256 with hash leaves option and duplicate odd option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c', 'd', 'e', 'f']
|
const leaves = ['a', 'b', 'c', 'd', 'e', 'f']
|
||||||
const tree = new MerkleTree(leaves, sha256, {hashLeaves: true, duplicateOdd: true})
|
const tree = new MerkleTree(leaves, sha256, { hashLeaves: true, duplicateOdd: true })
|
||||||
const root = '44205acec5156114821f1f71d87c72e0de395633cd1589def6d4444cc79f8103'
|
const root = '44205acec5156114821f1f71d87c72e0de395633cd1589def6d4444cc79f8103'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -114,7 +116,7 @@ test('crypto-js - sha256 with keccak leaves and duplicate odd option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
||||||
const tree = new MerkleTree(leaves, SHA256, {duplicateOdd: true})
|
const tree = new MerkleTree(leaves, SHA256, { duplicateOdd: true })
|
||||||
const root = 'bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1'
|
const root = 'bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -124,7 +126,7 @@ test('crypto-js - SHA256 with SHA256 leaves and duplicate odd option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c'].map(SHA256)
|
const leaves = ['a', 'b', 'c'].map(SHA256)
|
||||||
const tree = new MerkleTree(leaves, SHA256, {duplicateOdd: true})
|
const tree = new MerkleTree(leaves, SHA256, { duplicateOdd: true })
|
||||||
const root = 'd31a37ef6ac14a2db1470c4316beb5592e6afd4465022339adafda76a18ffabe'
|
const root = 'd31a37ef6ac14a2db1470c4316beb5592e6afd4465022339adafda76a18ffabe'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -145,7 +147,7 @@ test('crypto-js - SHA256 with keccak leaves and duplicate odd option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
||||||
const tree = new MerkleTree(leaves, SHA256, {duplicateOdd: true})
|
const tree = new MerkleTree(leaves, SHA256, { duplicateOdd: true })
|
||||||
const root = 'bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1'
|
const root = 'bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1'
|
||||||
|
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
|
@ -211,7 +213,7 @@ test('solidity keccak [keccak-256] with duplicate odd option', t => {
|
||||||
|
|
||||||
t.deepEqual(leaves.map(x => x.toString('hex')), [a_hash, b_hash, c_hash])
|
t.deepEqual(leaves.map(x => x.toString('hex')), [a_hash, b_hash, c_hash])
|
||||||
|
|
||||||
const tree = new MerkleTree(leaves, keccak, {duplicateOdd: true})
|
const tree = new MerkleTree(leaves, keccak, { duplicateOdd: true })
|
||||||
const layers = tree.getLayers().slice(1) // no leaves
|
const layers = tree.getLayers().slice(1) // no leaves
|
||||||
const layer_1 = keccak(Buffer.concat([leaves[0], leaves[1]])).toString('hex')
|
const layer_1 = keccak(Buffer.concat([leaves[0], leaves[1]])).toString('hex')
|
||||||
const layer_2 = keccak(Buffer.concat([leaves[2], leaves[2]])).toString('hex')
|
const layer_2 = keccak(Buffer.concat([leaves[2], leaves[2]])).toString('hex')
|
||||||
|
@ -277,110 +279,110 @@ test('sha-256 with option.isBitcoinTree', t => {
|
||||||
* http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html
|
* http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html
|
||||||
*/
|
*/
|
||||||
const txHashes = [
|
const txHashes = [
|
||||||
"00baf6626abc2df808da36a518c69f09b0d2ed0a79421ccfde4f559d2e42128b",
|
'00baf6626abc2df808da36a518c69f09b0d2ed0a79421ccfde4f559d2e42128b',
|
||||||
"91c5e9f288437262f218c60f986e8bc10fb35ab3b9f6de477ff0eb554da89dea",
|
'91c5e9f288437262f218c60f986e8bc10fb35ab3b9f6de477ff0eb554da89dea',
|
||||||
"46685c94b82b84fa05b6a0f36de6ff46475520113d5cb8c6fb060e043a0dbc5c",
|
'46685c94b82b84fa05b6a0f36de6ff46475520113d5cb8c6fb060e043a0dbc5c',
|
||||||
"ba7ed2544c78ad793ef5bb0ebe0b1c62e8eb9404691165ffcb08662d1733d7a8",
|
'ba7ed2544c78ad793ef5bb0ebe0b1c62e8eb9404691165ffcb08662d1733d7a8',
|
||||||
"b8dc1b7b7ed847c3595e7b02dbd7372aa221756b718c5f2943c75654faf48589",
|
'b8dc1b7b7ed847c3595e7b02dbd7372aa221756b718c5f2943c75654faf48589',
|
||||||
"25074ef168a061fcc8663b4554a31b617683abc33b72d2e2834f9329c93f8214",
|
'25074ef168a061fcc8663b4554a31b617683abc33b72d2e2834f9329c93f8214',
|
||||||
"0fb8e311bffffadc6dc4928d7da9e142951d3ba726c8bde2cf1489b62fb9ebc5",
|
'0fb8e311bffffadc6dc4928d7da9e142951d3ba726c8bde2cf1489b62fb9ebc5',
|
||||||
"c67c79204e681c8bb453195db8ca7d61d4692f0098514ca198ccfd1b59dbcee3",
|
'c67c79204e681c8bb453195db8ca7d61d4692f0098514ca198ccfd1b59dbcee3',
|
||||||
"bd27570a6cbd8ad026bfdb8909fdae9321788f0643dea195f39cd84a60a1901b",
|
'bd27570a6cbd8ad026bfdb8909fdae9321788f0643dea195f39cd84a60a1901b',
|
||||||
"41a06e53ffc5108358ddcec05b029763d714ae9f33c5403735e8dee78027fe74",
|
'41a06e53ffc5108358ddcec05b029763d714ae9f33c5403735e8dee78027fe74',
|
||||||
"cc2696b44cb07612c316f24c07092956f7d8b6e0d48f758572e0d611d1da6fb9",
|
'cc2696b44cb07612c316f24c07092956f7d8b6e0d48f758572e0d611d1da6fb9',
|
||||||
"8fc508772c60ace7bfeb3f5f3a507659285ea6f351ac0474a0a9710c7673d4fd",
|
'8fc508772c60ace7bfeb3f5f3a507659285ea6f351ac0474a0a9710c7673d4fd',
|
||||||
"62fed508c095446d971580099f976428fc069f32e966a40a991953b798b28684",
|
'62fed508c095446d971580099f976428fc069f32e966a40a991953b798b28684',
|
||||||
"928eadbc39196b95147416eedf6f635dcff818916da65419904df8fde977d5db",
|
'928eadbc39196b95147416eedf6f635dcff818916da65419904df8fde977d5db',
|
||||||
"b137e685df7c1dffe031fb966a0923bb5d0e56f381e730bc01c6d5244cfe47c1",
|
'b137e685df7c1dffe031fb966a0923bb5d0e56f381e730bc01c6d5244cfe47c1',
|
||||||
"b92207cee1f9e0bfbd797b05a738fab9de9c799b74f54f6b922f20bd5ec23dd6",
|
'b92207cee1f9e0bfbd797b05a738fab9de9c799b74f54f6b922f20bd5ec23dd6',
|
||||||
"29d6f37ada0481375b6903c6480a81f8deaf2dcdba03411ed9e8d3e5684d02dd",
|
'29d6f37ada0481375b6903c6480a81f8deaf2dcdba03411ed9e8d3e5684d02dd',
|
||||||
"48158deb116e4fd0429fbbbae61e8e68cb6d0e0c4465ff9a6a990037f88c489c",
|
'48158deb116e4fd0429fbbbae61e8e68cb6d0e0c4465ff9a6a990037f88c489c',
|
||||||
"be64ea86960864cc0a0236bbb11f232faf5b19ae6e2c85518628f5fae37ec1ca",
|
'be64ea86960864cc0a0236bbb11f232faf5b19ae6e2c85518628f5fae37ec1ca',
|
||||||
"081363552e9fff7461f1fc6663e1abd0fb2dd1c54931e177479a18c4c26260e8",
|
'081363552e9fff7461f1fc6663e1abd0fb2dd1c54931e177479a18c4c26260e8',
|
||||||
"eb87c25dd2b2537b1ff3dbabc420e422e2a801f1bededa6fa49ef7980feaef70",
|
'eb87c25dd2b2537b1ff3dbabc420e422e2a801f1bededa6fa49ef7980feaef70',
|
||||||
"339e16fcc11deb61ccb548239270af43f5ad34c321416bada4b8d66467b1c697",
|
'339e16fcc11deb61ccb548239270af43f5ad34c321416bada4b8d66467b1c697',
|
||||||
"4ad6417a3a04179482ed2e4b7251c396e38841c6fba8d2ce9543337ab7c93c02",
|
'4ad6417a3a04179482ed2e4b7251c396e38841c6fba8d2ce9543337ab7c93c02',
|
||||||
"c28a45cded020bf424b400ffc9cb6f2f85601934f18c34a4f78283247192056a",
|
'c28a45cded020bf424b400ffc9cb6f2f85601934f18c34a4f78283247192056a',
|
||||||
"882037cc9e3ee6ddc2d3eba86b7ca163533b5d3cbb16eaa38696bb0a2ea1137e",
|
'882037cc9e3ee6ddc2d3eba86b7ca163533b5d3cbb16eaa38696bb0a2ea1137e',
|
||||||
"179bb936305b46bb0a9df330f8701984c725a60e063ad5892fa97461570b5c04",
|
'179bb936305b46bb0a9df330f8701984c725a60e063ad5892fa97461570b5c04',
|
||||||
"9517c585d1578cb327b7988f38e1a15c663955ea288a2292b40d27f232fbb980",
|
'9517c585d1578cb327b7988f38e1a15c663955ea288a2292b40d27f232fbb980',
|
||||||
"2c7e07d0cf42e5520bcbfe2f5ef63761a9ab9d7ccb00ea346195eae030f3b86f",
|
'2c7e07d0cf42e5520bcbfe2f5ef63761a9ab9d7ccb00ea346195eae030f3b86f',
|
||||||
"534f631fc42ae2d309670e01c7a0890e4bfb65bae798522ca14df09c81b09734",
|
'534f631fc42ae2d309670e01c7a0890e4bfb65bae798522ca14df09c81b09734',
|
||||||
"104643385619adb848593eb668a8066d1f32650edf35e74b0fc3306cb6719448",
|
'104643385619adb848593eb668a8066d1f32650edf35e74b0fc3306cb6719448',
|
||||||
"87ac990808239c768182a752f4f71cd98558397072883c7e137efb49d22b9231",
|
'87ac990808239c768182a752f4f71cd98558397072883c7e137efb49d22b9231',
|
||||||
"9b3e2f1c47d59a444e9b6dc725f0ac6baf160d22f3a9d399434e5e65b14eccb0",
|
'9b3e2f1c47d59a444e9b6dc725f0ac6baf160d22f3a9d399434e5e65b14eccb0',
|
||||||
"fbe123066ae5add633a542f151663db4eb5a7053e388faadb40240671ae1b09b",
|
'fbe123066ae5add633a542f151663db4eb5a7053e388faadb40240671ae1b09b',
|
||||||
"1dd07e92e20b3cb9208af040031f7cfc4efd46cc31ec27be20a1047965a42849",
|
'1dd07e92e20b3cb9208af040031f7cfc4efd46cc31ec27be20a1047965a42849',
|
||||||
"2709bb9ed27353c1fd76b9240cab7576a44de68945e256ad44b2cb8d849a8060",
|
'2709bb9ed27353c1fd76b9240cab7576a44de68945e256ad44b2cb8d849a8060',
|
||||||
"d0174db2c712573432a7869c1508f371f3a1058aeedddc1b53a7e04d7c56c725",
|
'd0174db2c712573432a7869c1508f371f3a1058aeedddc1b53a7e04d7c56c725',
|
||||||
"b4a16f724cddb8f77ddf3d2146a12c4be13d503885eaba3518a03da005009f62",
|
'b4a16f724cddb8f77ddf3d2146a12c4be13d503885eaba3518a03da005009f62',
|
||||||
"2aa706d75decbe57745e01d46f9f5d30a08dedaf3288cee14cc4948e3684e1d4",
|
'2aa706d75decbe57745e01d46f9f5d30a08dedaf3288cee14cc4948e3684e1d4',
|
||||||
"ee49c5f6a5129ccaf2abebbc1d6d07a402a600af6221476b89aafaa683ca95b7",
|
'ee49c5f6a5129ccaf2abebbc1d6d07a402a600af6221476b89aafaa683ca95b7',
|
||||||
"bea1011c77874845e9b4c876ed2ceebd530d428dd4a564ad003d9211d40bb091",
|
'bea1011c77874845e9b4c876ed2ceebd530d428dd4a564ad003d9211d40bb091',
|
||||||
"f1e88ffc2b1de2aa4827002f06943ce5468735f7433f960bf01e75885b9f832b",
|
'f1e88ffc2b1de2aa4827002f06943ce5468735f7433f960bf01e75885b9f832b',
|
||||||
"19247d017e002fb9143d1a89eb921222a94f8a3d0faaf2e05b0f594989edc4c4",
|
'19247d017e002fb9143d1a89eb921222a94f8a3d0faaf2e05b0f594989edc4c4',
|
||||||
"13f714ff62ee7d26b6d69ca980c141ebc54e9f71d2697083fe6c5efc1b02bd0f",
|
'13f714ff62ee7d26b6d69ca980c141ebc54e9f71d2697083fe6c5efc1b02bd0f',
|
||||||
"0c78cbb8246572f015fbdc53dc9798fa54d1119ec77c1f07ac310bcbcc40dbf8",
|
'0c78cbb8246572f015fbdc53dc9798fa54d1119ec77c1f07ac310bcbcc40dbf8',
|
||||||
"4bcde0ef92a6d24a2be7be50ac5e5299d776df2e6229ba5d475c2491da94f255",
|
'4bcde0ef92a6d24a2be7be50ac5e5299d776df2e6229ba5d475c2491da94f255',
|
||||||
"0cfd7d1058502730cf0b2ffa880c78ef534651e06832b5d87c0d7eb84eac5b0c",
|
'0cfd7d1058502730cf0b2ffa880c78ef534651e06832b5d87c0d7eb84eac5b0c',
|
||||||
"3a168f794d6e0c614429ad874317cc4cd67a8177214880ff6ea1704d29228c2f",
|
'3a168f794d6e0c614429ad874317cc4cd67a8177214880ff6ea1704d29228c2f',
|
||||||
"f9a555d817334397b402518d6fd959dc73d981ee7f5fe67969b63974ebbef127",
|
'f9a555d817334397b402518d6fd959dc73d981ee7f5fe67969b63974ebbef127',
|
||||||
"24b52691f66eaed4ce391a473902e309018257c98b9f02aaa33b399c9e6f3168",
|
'24b52691f66eaed4ce391a473902e309018257c98b9f02aaa33b399c9e6f3168',
|
||||||
"a37b5e623dc26a180d9e2c9510d06885b014e86e533adb63ec40511e10b55046",
|
'a37b5e623dc26a180d9e2c9510d06885b014e86e533adb63ec40511e10b55046',
|
||||||
"9dbaeb485e51d9e25a5621dc46e0bc0aaf51fb26be5acc4e370b96f62c469b80",
|
'9dbaeb485e51d9e25a5621dc46e0bc0aaf51fb26be5acc4e370b96f62c469b80',
|
||||||
"a6431d3d39f6c38c5df48405090752cab03bfdf5c77cf881b18a946807fba74a",
|
'a6431d3d39f6c38c5df48405090752cab03bfdf5c77cf881b18a946807fba74a',
|
||||||
"faa77e309f125373acf19855dd496fffe2f74962e545420844557a3adc7ebc11",
|
'faa77e309f125373acf19855dd496fffe2f74962e545420844557a3adc7ebc11',
|
||||||
"3523f52543ecfea2f78486dc91550fad0e6467d46d9d9c82ca63b2e0230bfa71",
|
'3523f52543ecfea2f78486dc91550fad0e6467d46d9d9c82ca63b2e0230bfa71',
|
||||||
"a0583e358e42d77d18d1fd0533ff0a65615fc3b3112061ef92f168a00bf640c1",
|
'a0583e358e42d77d18d1fd0533ff0a65615fc3b3112061ef92f168a00bf640c1',
|
||||||
"42ae900888d5e5dde59c8e3d06e13db9e84ef05d27726d4b67fd00c50cd9406a",
|
'42ae900888d5e5dde59c8e3d06e13db9e84ef05d27726d4b67fd00c50cd9406a',
|
||||||
"154940777d3ff78f592ef02790131a59263c36b4958bbc836f9a767ea1a9f178",
|
'154940777d3ff78f592ef02790131a59263c36b4958bbc836f9a767ea1a9f178',
|
||||||
"6a0337de6ac75eecf748306e8ebc5bfe5c811a1481ae50f6956a9e7f26a679f5",
|
'6a0337de6ac75eecf748306e8ebc5bfe5c811a1481ae50f6956a9e7f26a679f5',
|
||||||
"c99530c2148e09688d0b88795625943371183bf1f5d56c7446c6ed51ea133589",
|
'c99530c2148e09688d0b88795625943371183bf1f5d56c7446c6ed51ea133589',
|
||||||
"626421dbe8ad6a0fd0d622d5dd3308a1cdc00b98575a41a91fe01a439e6f40bd",
|
'626421dbe8ad6a0fd0d622d5dd3308a1cdc00b98575a41a91fe01a439e6f40bd',
|
||||||
"b2f3a559f605a158cc395126c3cf394a7e92a53b7514c75157e1dc43a6c7f93e",
|
'b2f3a559f605a158cc395126c3cf394a7e92a53b7514c75157e1dc43a6c7f93e',
|
||||||
"dffe06d1bea81f2a01c76786404bb867258f9e68013bf25454097ce935090738",
|
'dffe06d1bea81f2a01c76786404bb867258f9e68013bf25454097ce935090738',
|
||||||
"0860159ec7a2a51ce107c182a988c40b4bc2057a734354a1219b6c65e72640ed",
|
'0860159ec7a2a51ce107c182a988c40b4bc2057a734354a1219b6c65e72640ed',
|
||||||
"a405ff1bb51846b1867acc0b0da17f6f9616e592a0a7ff5ef3297c1ecfd60911",
|
'a405ff1bb51846b1867acc0b0da17f6f9616e592a0a7ff5ef3297c1ecfd60911',
|
||||||
"a7d451924263284765f6343bca8a21b79b89ebfe611c7355dd88e0ec1c29e232",
|
'a7d451924263284765f6343bca8a21b79b89ebfe611c7355dd88e0ec1c29e232',
|
||||||
"41c758d08a4d3fe4d90645711589b832a2cd54dd25bd5b66e463e5d389a53aff",
|
'41c758d08a4d3fe4d90645711589b832a2cd54dd25bd5b66e463e5d389a53aff',
|
||||||
"a05c1a93a521fa5dbc1790cfbb808893453a428a65f2c6b2d51249fbb12db309",
|
'a05c1a93a521fa5dbc1790cfbb808893453a428a65f2c6b2d51249fbb12db309',
|
||||||
"90997920aa9786e10f513cfdd14e294feee6739cee1ab61b3fb1e3f42e7a915d",
|
'90997920aa9786e10f513cfdd14e294feee6739cee1ab61b3fb1e3f42e7a915d',
|
||||||
"99fcb9cb62c20a3135484a70bd3f73983f8f3b7b26266dad34f3993958a7642c",
|
'99fcb9cb62c20a3135484a70bd3f73983f8f3b7b26266dad34f3993958a7642c',
|
||||||
"e05f9a668b37e5f78bd3b9d047f29f92b33a87f11dd48390410006f858188b7b",
|
'e05f9a668b37e5f78bd3b9d047f29f92b33a87f11dd48390410006f858188b7b',
|
||||||
"56dbc65895f7992da4a6985e7edba4d1c00879f1b28442c644c8a07658ceab27",
|
'56dbc65895f7992da4a6985e7edba4d1c00879f1b28442c644c8a07658ceab27',
|
||||||
"5e9004fe262b829563d0804656ba68b1de1690401f08a1915273230d8c902fc0",
|
'5e9004fe262b829563d0804656ba68b1de1690401f08a1915273230d8c902fc0',
|
||||||
"1ea9ed3717523c5e304b7a7ac8058a87fb4f3fed8c6004769f226c9bb67e79c5",
|
'1ea9ed3717523c5e304b7a7ac8058a87fb4f3fed8c6004769f226c9bb67e79c5',
|
||||||
"f0f1a4c009b3f1b2729e89898e2f5c0fcdc312edea5df884a9c897cb90e4c566",
|
'f0f1a4c009b3f1b2729e89898e2f5c0fcdc312edea5df884a9c897cb90e4c566',
|
||||||
"b5bb4ddf04863e6a60f33cb96c20dac8175d3bae55f335781503143c97a50e43",
|
'b5bb4ddf04863e6a60f33cb96c20dac8175d3bae55f335781503143c97a50e43',
|
||||||
"f14cc97a20c6f627b4b78301352ae35463bc359362589cd178a06c0fa90850b7",
|
'f14cc97a20c6f627b4b78301352ae35463bc359362589cd178a06c0fa90850b7',
|
||||||
"628801c8f614015c0fa0ccb2768cccc3e7b9d41ceed06071ce2534d31f7236d6",
|
'628801c8f614015c0fa0ccb2768cccc3e7b9d41ceed06071ce2534d31f7236d6',
|
||||||
"3be1013c8f8da150e2195408093153b55b08b037fd92db8bb5e803f4c2538aae",
|
'3be1013c8f8da150e2195408093153b55b08b037fd92db8bb5e803f4c2538aae',
|
||||||
"c9e1f8777685f54ba65c4e02915fd649ee1edcbf9c77ddf584b943d27efb86c3",
|
'c9e1f8777685f54ba65c4e02915fd649ee1edcbf9c77ddf584b943d27efb86c3',
|
||||||
"4274e92ed3bd02eb101baa5fb8ff7b96236830762d08273749fbb5166db8ab0b",
|
'4274e92ed3bd02eb101baa5fb8ff7b96236830762d08273749fbb5166db8ab0b',
|
||||||
"aa84c955bea04c7cee8f5bbbec97d25930fcaca363eed1b8cad37b931556d3e3",
|
'aa84c955bea04c7cee8f5bbbec97d25930fcaca363eed1b8cad37b931556d3e3',
|
||||||
"d6a29c948677fb1f71aaf16debc3d071a4dd349458eb9e056dce3a000ff853da",
|
'd6a29c948677fb1f71aaf16debc3d071a4dd349458eb9e056dce3a000ff853da',
|
||||||
"ba84bdb3d78367ca365016ac4bff9269576eb010f874c2967af73e0de5638de0",
|
'ba84bdb3d78367ca365016ac4bff9269576eb010f874c2967af73e0de5638de0',
|
||||||
"1546c79951e3b541bc64d1957b565b7a2850fc87192c7b374aee6cfc69b9805e",
|
'1546c79951e3b541bc64d1957b565b7a2850fc87192c7b374aee6cfc69b9805e',
|
||||||
"f119227d492ebe27fe9aae321980802454dfa64b2691efbe796c5075d5b07f62",
|
'f119227d492ebe27fe9aae321980802454dfa64b2691efbe796c5075d5b07f62',
|
||||||
"b8cf13d64818b32f96bbb585998b1bc9505f6a94055488e5a71fee9479c6f2a9",
|
'b8cf13d64818b32f96bbb585998b1bc9505f6a94055488e5a71fee9479c6f2a9',
|
||||||
"1aaf459705b6afef2d7b83e3f181f1af55be0813daf55edce104cc59abc28ed7",
|
'1aaf459705b6afef2d7b83e3f181f1af55be0813daf55edce104cc59abc28ed7',
|
||||||
"61ac185c8f520b5e3134953dc52ff292a40e1e96b088dab259558a9d240ec02f",
|
'61ac185c8f520b5e3134953dc52ff292a40e1e96b088dab259558a9d240ec02f',
|
||||||
"2da96e3154d7ec2329f787b73cb8a436b92d64cf3cc28e920d073279ea73b5f8",
|
'2da96e3154d7ec2329f787b73cb8a436b92d64cf3cc28e920d073279ea73b5f8',
|
||||||
"1c4d72ce733b971b9ec4e24f37d733355f6f2ea635cc67ffb3e22748484df446",
|
'1c4d72ce733b971b9ec4e24f37d733355f6f2ea635cc67ffb3e22748484df446',
|
||||||
"2a6f89769f3272ac8c7a36a42a57627eca6b260ab2c76d8046a27d44d4034893",
|
'2a6f89769f3272ac8c7a36a42a57627eca6b260ab2c76d8046a27d44d4034893',
|
||||||
"f8d11df51a2cc113698ebf39a958fe81179d7d973d2044322771c0fe63f4d7c9",
|
'f8d11df51a2cc113698ebf39a958fe81179d7d973d2044322771c0fe63f4d7c9',
|
||||||
"f2287f17a4fa232dca5715c24a92f7112402a8101b9a7b276fb8c8f617376b90",
|
'f2287f17a4fa232dca5715c24a92f7112402a8101b9a7b276fb8c8f617376b90',
|
||||||
"bb5ee510a4fda29cae30c97e7eee80569d3ec3598465f2d7e0674c395e0256e9",
|
'bb5ee510a4fda29cae30c97e7eee80569d3ec3598465f2d7e0674c395e0256e9',
|
||||||
"647ab8c84365620d60f2523505d14bd230b5e650c96dee48be47770063ee7461",
|
'647ab8c84365620d60f2523505d14bd230b5e650c96dee48be47770063ee7461',
|
||||||
"34b06018fcc33ba6ebb01198d785b0629fbdc5d1948f688059158f053093f08b",
|
'34b06018fcc33ba6ebb01198d785b0629fbdc5d1948f688059158f053093f08b',
|
||||||
"ff58b258dab0d7f36a2908e6c75229ce308d34806289c912a1a5f39a5aa71f9f",
|
'ff58b258dab0d7f36a2908e6c75229ce308d34806289c912a1a5f39a5aa71f9f',
|
||||||
"232fc124803668a9f23b1c3bcb1134274303f5c0e1b0e27c9b6c7db59f0e2a4d",
|
'232fc124803668a9f23b1c3bcb1134274303f5c0e1b0e27c9b6c7db59f0e2a4d',
|
||||||
"27a0797cc5b042ba4c11e72a9555d13a67f00161550b32ede0511718b22dbc2c",
|
'27a0797cc5b042ba4c11e72a9555d13a67f00161550b32ede0511718b22dbc2c'
|
||||||
]
|
]
|
||||||
|
|
||||||
const leaves = txHashes.map(x => Buffer.from(x, 'hex'))
|
const leaves = txHashes.map(x => Buffer.from(x, 'hex'))
|
||||||
|
|
||||||
const tree = new MerkleTree(leaves, sha256, {isBitcoinTree: true})
|
const tree = new MerkleTree(leaves, sha256, { isBitcoinTree: true })
|
||||||
const root = Buffer.from('871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a', 'hex')
|
const root = Buffer.from('871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a', 'hex')
|
||||||
t.equal(tree.getRoot().toString('hex'), root.toString('hex'))
|
t.equal(tree.getRoot().toString('hex'), root.toString('hex'))
|
||||||
|
|
||||||
|
@ -422,6 +424,18 @@ test.skip('sha256 - 1,000,000 leaves', t => {
|
||||||
t.equal(tree.getRoot().toString('hex'), root)
|
t.equal(tree.getRoot().toString('hex'), root)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('sha256 getHexLeaves', t => {
|
||||||
|
t.plan(1)
|
||||||
|
|
||||||
|
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
||||||
|
const tree = new MerkleTree(leaves, sha256)
|
||||||
|
t.deepEqual(tree.getHexLeaves(), [
|
||||||
|
'0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb',
|
||||||
|
'0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510',
|
||||||
|
'0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2'
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
test('crypto-js SHA3 leaves SHA256 hash algo', t => {
|
test('crypto-js SHA3 leaves SHA256 hash algo', t => {
|
||||||
t.plan(2)
|
t.plan(2)
|
||||||
|
|
||||||
|
@ -455,12 +469,12 @@ test('sha1', t => {
|
||||||
'd89f84d948796605a413e196f40bce1d6294175d',
|
'd89f84d948796605a413e196f40bce1d6294175d',
|
||||||
'32f04c7f572bf75a266268c6f4d8c92731dc3b7f',
|
'32f04c7f572bf75a266268c6f4d8c92731dc3b7f',
|
||||||
'b80b52d80f5fe940ac2c987044bc439e4218ac94',
|
'b80b52d80f5fe940ac2c987044bc439e4218ac94',
|
||||||
'1553c75a1d637961827f4904a0955e57915d8310',
|
'1553c75a1d637961827f4904a0955e57915d8310'
|
||||||
]
|
]
|
||||||
|
|
||||||
const tree = new MerkleTree(leaves, sha1, {
|
const tree = new MerkleTree(leaves, sha1, {
|
||||||
sortLeaves: true,
|
sortLeaves: true,
|
||||||
sortPairs: true,
|
sortPairs: true
|
||||||
})
|
})
|
||||||
|
|
||||||
const root = tree.getHexRoot()
|
const root = tree.getHexRoot()
|
||||||
|
@ -470,12 +484,36 @@ test('sha1', t => {
|
||||||
t.equal(tree.verify(proof, leaf, root), true)
|
t.equal(tree.verify(proof, leaf, root), true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('sha56 getHexLayers', t => {
|
||||||
|
t.plan(1)
|
||||||
|
|
||||||
|
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
||||||
|
const tree = new MerkleTree(leaves, sha256)
|
||||||
|
const layers = tree.getHexLayers()
|
||||||
|
|
||||||
|
t.deepEqual(layers, [
|
||||||
|
[
|
||||||
|
'0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb',
|
||||||
|
'0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510',
|
||||||
|
'0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1',
|
||||||
|
'0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x311d2e46f49b15fff8b746b74ad57f2cc9e0d9939fda94387141a2d3fdf187ae'
|
||||||
|
]
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
test('getLayersAsObject', t => {
|
test('getLayersAsObject', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
||||||
const tree = new MerkleTree(leaves, sha256)
|
const tree = new MerkleTree(leaves, sha256)
|
||||||
const obj = tree.getLayersAsObject()
|
const obj = tree.getLayersAsObject()
|
||||||
|
|
||||||
t.deepEqual(obj, {
|
t.deepEqual(obj, {
|
||||||
'311d2e46f49b15fff8b746b74ad57f2cc9e0d9939fda94387141a2d3fdf187ae': {
|
'311d2e46f49b15fff8b746b74ad57f2cc9e0d9939fda94387141a2d3fdf187ae': {
|
||||||
'0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2': {
|
'0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2': {
|
||||||
|
@ -483,7 +521,7 @@ test('getLayersAsObject', t => {
|
||||||
},
|
},
|
||||||
'176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1': {
|
'176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1': {
|
||||||
'3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb': null,
|
'3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb': null,
|
||||||
'b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510': null
|
b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -493,22 +531,38 @@ test('getLayersAsObject with duplicate odd option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
||||||
const tree = new MerkleTree(leaves, sha256, {duplicateOdd: true})
|
const tree = new MerkleTree(leaves, sha256, { duplicateOdd: true })
|
||||||
const obj = tree.getLayersAsObject()
|
const obj = tree.getLayersAsObject()
|
||||||
|
|
||||||
t.deepEqual(obj, {
|
t.deepEqual(obj, {
|
||||||
"bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1": {
|
bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1: {
|
||||||
"176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1": {
|
'176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1': {
|
||||||
"3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb": null,
|
'3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb': null,
|
||||||
"b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510": null
|
b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510: null
|
||||||
},
|
},
|
||||||
"43e061172b1177f25d0f156b2d2ed11728006fade8e167ff3d1b9dbc979a3358": {
|
'43e061172b1177f25d0f156b2d2ed11728006fade8e167ff3d1b9dbc979a3358': {
|
||||||
"0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2": null
|
'0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2': null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.skip('sha256 getHexLayersFlat', t => {
|
||||||
|
t.plan(1)
|
||||||
|
|
||||||
|
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
||||||
|
const tree = new MerkleTree(leaves, sha256)
|
||||||
|
const layers = tree.getLayersFlat()
|
||||||
|
t.deepEqual(layers, [
|
||||||
|
'3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb',
|
||||||
|
'b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510',
|
||||||
|
'0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2',
|
||||||
|
'176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1',
|
||||||
|
'0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2',
|
||||||
|
'311d2e46f49b15fff8b746b74ad57f2cc9e0d9939fda94387141a2d3fdf187ae'
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
test('print', t => {
|
test('print', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
|
@ -529,7 +583,7 @@ test('print with duplicate odd option', t => {
|
||||||
t.plan(1)
|
t.plan(1)
|
||||||
|
|
||||||
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
const leaves = ['a', 'b', 'c'].map(x => keccak(x))
|
||||||
const tree = new MerkleTree(leaves, sha256, {duplicateOdd: true})
|
const tree = new MerkleTree(leaves, sha256, { duplicateOdd: true })
|
||||||
|
|
||||||
t.equal(tree.toString(),
|
t.equal(tree.toString(),
|
||||||
`└─ bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1
|
`└─ bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1
|
||||||
|
@ -540,3 +594,179 @@ test('print with duplicate odd option', t => {
|
||||||
└─ 0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2
|
└─ 0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('sha256 getMultiProof', t => {
|
||||||
|
t.plan(3)
|
||||||
|
|
||||||
|
const leaves = Array(16).fill(0).map((x, i) => {
|
||||||
|
const b = Buffer.alloc(32)
|
||||||
|
b.writeUIntLE(i, 31, 1)
|
||||||
|
return b
|
||||||
|
})
|
||||||
|
|
||||||
|
const tree = new MerkleTree(leaves, sha256)
|
||||||
|
|
||||||
|
const root = tree.getHexRoot()
|
||||||
|
t.equal(root, '0xc1ebc5b83154907160d73863bdae7eb86fe1888495a83cb8daadb1603b8aeaf5')
|
||||||
|
|
||||||
|
const i = 100
|
||||||
|
const indices = Array(16).fill(0).map((x, j) => j).filter(j => (i >> j) % 2 === 1)
|
||||||
|
|
||||||
|
const proof = tree.getMultiProof(indices)
|
||||||
|
|
||||||
|
t.deepEqual(proof.map(x => x.toString('hex')), [
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000007',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000004',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000003',
|
||||||
|
'90f4b39548df55ad6187a1d20d731ecee78c545b94afd16f42ef7592d99cd365',
|
||||||
|
'6b41dc65d0df2bb4b8c2f1fe48e85b7ba8f9b876113ec410fe612bae61a19eaf'
|
||||||
|
])
|
||||||
|
|
||||||
|
const depth = tree.getDepth()
|
||||||
|
|
||||||
|
const tLeaves = indices.map(i => leaves[i])
|
||||||
|
t.equal(tree.verifyMultiProof(root, indices, tLeaves, depth, proof), true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('sha256 getMultiProof using tree array', t => {
|
||||||
|
t.plan(4)
|
||||||
|
|
||||||
|
const leaves = Array(16).fill(0).map((x, i) => {
|
||||||
|
const b = Buffer.alloc(32)
|
||||||
|
b.writeUIntLE(i, 31, 1)
|
||||||
|
return b
|
||||||
|
})
|
||||||
|
|
||||||
|
const tree = new MerkleTree(leaves, sha256)
|
||||||
|
|
||||||
|
const root = tree.getHexRoot()
|
||||||
|
t.equal(root, '0xc1ebc5b83154907160d73863bdae7eb86fe1888495a83cb8daadb1603b8aeaf5')
|
||||||
|
|
||||||
|
const treeFlat = tree.getLayersFlat()
|
||||||
|
|
||||||
|
const i = 100
|
||||||
|
const indices = Array(16).fill(0).map((x, j) => j).filter(j => (i >> j) % 2 === 1)
|
||||||
|
|
||||||
|
t.deepEqual(treeFlat.map(x => x.toString('hex')), [
|
||||||
|
'00',
|
||||||
|
'c1ebc5b83154907160d73863bdae7eb86fe1888495a83cb8daadb1603b8aeaf5',
|
||||||
|
'32e0fe0539aeca5782542f7232d32185eebec2e6dc258177456dcdebbbf18f8b',
|
||||||
|
'6b41dc65d0df2bb4b8c2f1fe48e85b7ba8f9b876113ec410fe612bae61a19eaf',
|
||||||
|
'd1d79b01cacccaa40e6a3825db8ff96f38858092ea71689ce31dc6df3cf31df2',
|
||||||
|
'564ad5882547f199bbe82f40f4e5ddbd3836859c2e5caf3ff5b0989caf614af0',
|
||||||
|
'5ef82769b7a6ea72894663fd3ed0736e7084beb596edeac229d4ae598d19aa2b',
|
||||||
|
'446c09a38396a1f51846548cc4a87bddd73bdcce9cf86c65f9ac6c153a92a562',
|
||||||
|
'90f4b39548df55ad6187a1d20d731ecee78c545b94afd16f42ef7592d99cd365',
|
||||||
|
'a774c351cf3882b36b2c541586b0b59c6dfd119ae831ef3c6b2e269f7a6be220',
|
||||||
|
'b023893dc819559da606f81778f566532801f428570db5fd3d3088491bb292c2',
|
||||||
|
'277752261a13c9c81c37ea894efe5dc3af8b7ffecbedce75d0ec09862e74a2cb',
|
||||||
|
'4c1f763d4f27669f41e511f15268e6396a66f7750c6128d68210a1dfbeb0cbc3',
|
||||||
|
'856b7aa12bb3a7fb591ac8cfb8051bb89c4b57cb7faf852fdc5d226076423d04',
|
||||||
|
'719cf2e4468492fde20372b704eca4a67397bba60d7513bb08f199887636027f',
|
||||||
|
'cec448f4862209fa531a43a65ec850a6327f02f518ef55f4d347510a5cf5d1cf',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000001',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000002',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000003',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000004',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000005',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000006',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000007',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000008',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000009',
|
||||||
|
'000000000000000000000000000000000000000000000000000000000000000a',
|
||||||
|
'000000000000000000000000000000000000000000000000000000000000000b',
|
||||||
|
'000000000000000000000000000000000000000000000000000000000000000c',
|
||||||
|
'000000000000000000000000000000000000000000000000000000000000000d',
|
||||||
|
'000000000000000000000000000000000000000000000000000000000000000e',
|
||||||
|
'000000000000000000000000000000000000000000000000000000000000000f'
|
||||||
|
])
|
||||||
|
|
||||||
|
const proof = tree.getMultiProof(treeFlat, indices)
|
||||||
|
|
||||||
|
t.deepEqual(proof.map(x => x.toString('hex')), [
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000007',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000004',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000003',
|
||||||
|
'90f4b39548df55ad6187a1d20d731ecee78c545b94afd16f42ef7592d99cd365',
|
||||||
|
'6b41dc65d0df2bb4b8c2f1fe48e85b7ba8f9b876113ec410fe612bae61a19eaf'
|
||||||
|
])
|
||||||
|
|
||||||
|
const depth = tree.getDepth()
|
||||||
|
|
||||||
|
const tRoot = treeFlat[1]
|
||||||
|
const tLeaves = indices.map(i => leaves[i])
|
||||||
|
t.equal(tree.verifyMultiProof(tRoot, indices, tLeaves, depth, proof), true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('sha256 getMultiProof', t => {
|
||||||
|
t.plan(3)
|
||||||
|
|
||||||
|
const leaves = Array(16).fill(0).map((x, i) => {
|
||||||
|
const b = Buffer.alloc(32)
|
||||||
|
b.writeUIntLE(i, 31, 1)
|
||||||
|
return b
|
||||||
|
})
|
||||||
|
|
||||||
|
const tree = new MerkleTree(leaves, sha256, { sortPairs: true, singleOdd: true })
|
||||||
|
|
||||||
|
const root = tree.getHexRoot()
|
||||||
|
t.equal(root, '0xc336787ec1a678cbbb3391bd0146e0c11e62dac39f84fe9a404a882094c00273')
|
||||||
|
|
||||||
|
const treeFlat = tree.getLayersFlat()
|
||||||
|
|
||||||
|
const indices = [0, 1]
|
||||||
|
const proof = tree.getMultiProof(treeFlat, indices)
|
||||||
|
const tLeaves = indices.map(i => leaves[i])
|
||||||
|
|
||||||
|
t.deepEqual(proof.map(x => x.toString('hex')), [
|
||||||
|
'a774c351cf3882b36b2c541586b0b59c6dfd119ae831ef3c6b2e269f7a6be220',
|
||||||
|
'de44f8fbe261b8ed52c606670f9c73e428a997908df7452806d6123829d50adc',
|
||||||
|
'44f76d663f391971ba8bd22469061c70aa63c176bc29b651336db7587b6f5a6c'
|
||||||
|
])
|
||||||
|
|
||||||
|
const proofFlags = tree.getProofFlags(tLeaves, proof)
|
||||||
|
t.deepEqual(proofFlags, [
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('sha256 getMultiProof - statusim', t => {
|
||||||
|
t.plan(5)
|
||||||
|
|
||||||
|
const elements = ['a', 'b', 'c', 'd', 'e', 'f']
|
||||||
|
|
||||||
|
const tree = new MerkleTree(elements, keccak, {
|
||||||
|
hashLeaves: true,
|
||||||
|
sortLeaves: true,
|
||||||
|
sortPairs: true
|
||||||
|
})
|
||||||
|
|
||||||
|
t.equal(tree.getHexRoot(), '0x1b404f199ea828ec5771fb30139c222d8417a82175fefad5cd42bc3a189bd8d5')
|
||||||
|
t.deepEqual(tree.getHexLeaves(), [
|
||||||
|
'0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2',
|
||||||
|
'0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb',
|
||||||
|
'0xa8982c89d80987fb9a510e25981ee9170206be21af3c8e0eb312ef1d3382e761',
|
||||||
|
'0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510',
|
||||||
|
'0xd1e8aeb79500496ef3dc2e57ba746a8315d048b7a664a2bf948db4fa91960483',
|
||||||
|
'0xf1918e8562236eb17adc8502332f4c9c82bc14e19bfc0aa10ab674ff75b3d2f3'
|
||||||
|
])
|
||||||
|
|
||||||
|
const leaves = tree.getLeaves(['d', 'a'])
|
||||||
|
const proof = tree.getMultiProof(leaves)
|
||||||
|
const proofFlags = tree.getProofFlags(leaves, proof)
|
||||||
|
|
||||||
|
t.deepEqual(leaves.map(x => x.toString('hex')), [
|
||||||
|
'3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb',
|
||||||
|
'f1918e8562236eb17adc8502332f4c9c82bc14e19bfc0aa10ab674ff75b3d2f3'
|
||||||
|
])
|
||||||
|
t.deepEqual(tree.getHexMultiProof(leaves), [
|
||||||
|
'0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2',
|
||||||
|
'0xd1e8aeb79500496ef3dc2e57ba746a8315d048b7a664a2bf948db4fa91960483',
|
||||||
|
'0x434d51cfeb80272378f4c3a8fd2824561c2cad9fce556ea600d46f20550976a6'
|
||||||
|
])
|
||||||
|
t.deepEqual(proofFlags, [false, false, false, true])
|
||||||
|
})
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
"downlevelIteration": true,
|
||||||
"types": ["node"],
|
"types": ["node"],
|
||||||
"baseUrl": "./",
|
"baseUrl": "./",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
|
Loading…
Reference in New Issue