Add duplicateOdd and sort option

This commit is contained in:
Miguel Mota 2019-06-07 00:29:42 -07:00
parent c1fcf89f2c
commit 49ef70eccd
No known key found for this signature in database
GPG Key ID: 67EC1161588A00F9
9 changed files with 1657 additions and 114 deletions

4
.gitignore vendored
View File

@ -103,7 +103,11 @@ tmp
.netrwhist
# Debug files to ignore #
#########################
example.js
# Build directories #
#####################
build
#dist
docs

343
README.md
View File

@ -380,6 +380,349 @@ const verified = tree.verify(proof, leaves[2], root)
* * *
# Class: MerkleTree
Class reprensenting a Merkle Tree
*__namespace__*: MerkleTree
## Hierarchy
**MerkleTree**
## Index
### Constructors
* [constructor](_index_.merkletree.md#constructor)
### Properties
* [_sort](_index_.merkletree.md#_sort)
* [duplicateOdd](_index_.merkletree.md#duplicateodd)
* [hashAlgo](_index_.merkletree.md#hashalgo)
* [hashLeaves](_index_.merkletree.md#hashleaves)
* [isBitcoinTree](_index_.merkletree.md#isbitcointree)
* [layers](_index_.merkletree.md#layers)
* [leaves](_index_.merkletree.md#leaves)
### Methods
* [createHashes](_index_.merkletree.md#createhashes)
* [getLayers](_index_.merkletree.md#getlayers)
* [getLayersAsObject](_index_.merkletree.md#getlayersasobject)
* [getLeaves](_index_.merkletree.md#getleaves)
* [getProof](_index_.merkletree.md#getproof)
* [getRoot](_index_.merkletree.md#getroot)
* [print](_index_.merkletree.md#print)
* [toString](_index_.merkletree.md#tostring)
* [toTreeString](_index_.merkletree.md#totreestring)
* [verify](_index_.merkletree.md#verify)
* [bufferify](_index_.merkletree.md#bufferify)
* [print](_index_.merkletree.md#print-1)
---
## Constructors
<a id="constructor"></a>
### constructor
**new MerkleTree**(leaves: *`any`*, hashAlgorithm: *`any`*, options?: *`any`*): [MerkleTree](_index_.merkletree.md)
*Defined in [index.ts:16](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L16)*
*__desc__*: Constructs a Merkle Tree. All nodes and leaves are stored as Buffers. Lonely leaf nodes are promoted to the next level up without being hashed again.
*__example__*: const MerkleTree = require('merkletreejs') const crypto = require('crypto')
function sha256(data) { // returns Buffer return crypto.createHash('sha256').update(data).digest() }
const leaves = \['a', 'b', 'c'\].map(x => sha3(x))
const tree = new MerkleTree(leaves, sha256)
**Parameters:**
| Name | Type | Default value | Description |
| ------ | ------ | ------ | ------ |
| leaves | `any` | - | Array of hashed leaves. Each leaf must be a Buffer. |
| hashAlgorithm | `any` | - | Algorithm used for hashing leaves and nodes |
| `Default value` options | `any` | {} as any | Additional options |
**Returns:** [MerkleTree](_index_.merkletree.md)
___
## Properties
<a id="_sort"></a>
### _sort
**● _sort**: *`boolean`*
*Defined in [index.ts:15](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L15)*
___
<a id="duplicateodd"></a>
### duplicateOdd
**● duplicateOdd**: *`boolean`*
*Defined in [index.ts:16](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L16)*
___
<a id="hashalgo"></a>
### hashAlgo
**● hashAlgo**: *`any`*
*Defined in [index.ts:10](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L10)*
___
<a id="hashleaves"></a>
### hashLeaves
**● hashLeaves**: *`boolean`*
*Defined in [index.ts:11](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L11)*
___
<a id="isbitcointree"></a>
### isBitcoinTree
**● isBitcoinTree**: *`boolean`*
*Defined in [index.ts:14](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L14)*
___
<a id="layers"></a>
### layers
**● layers**: *`any`*
*Defined in [index.ts:13](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L13)*
___
<a id="leaves"></a>
### leaves
**● leaves**: *`any`*
*Defined in [index.ts:12](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L12)*
___
## Methods
<a id="createhashes"></a>
### createHashes
**createHashes**(nodes: *`any`*): `void`
*Defined in [index.ts:60](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L60)*
**Parameters:**
| Name | Type |
| ------ | ------ |
| nodes | `any` |
**Returns:** `void`
___
<a id="getlayers"></a>
### getLayers
**getLayers**(): `any`
*Defined in [index.ts:145](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L145)*
getLayers
*__desc__*: Returns array of all layers of Merkle Tree, including leaves and root.
*__example__*: const layers = tree.getLayers()
**Returns:** `any`
___
<a id="getlayersasobject"></a>
### getLayersAsObject
**getLayersAsObject**(): `any`
*Defined in [index.ts:295](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L295)*
**Returns:** `any`
___
<a id="getleaves"></a>
### getLeaves
**getLeaves**(): `any`
*Defined in [index.ts:134](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L134)*
getLeaves
*__desc__*: Returns array of leaves of Merkle Tree.
*__example__*: const leaves = tree.getLeaves()
**Returns:** `any`
___
<a id="getproof"></a>
### getProof
**getProof**(leaf: *`any`*, index?: *`any`*): `any`[]
*Defined in [index.ts:176](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L176)*
getProof
*__desc__*: Returns the proof for a target leaf.
*__example__*: const proof = tree.getProof(leaves\[2\])
*__example__*: const leaves = \['a', 'b', 'a'\].map(x => sha3(x)) const tree = new MerkleTree(leaves, sha3) const proof = tree.getProof(leaves\[2\], 2)
**Parameters:**
| Name | Type | Description |
| ------ | ------ | ------ |
| leaf | `any` | Target leaf |
| `Optional` index | `any` |
**Returns:** `any`[]
* Array of objects containing a position property of type string with values of 'left' or 'right' and a data property of type Buffer.
___
<a id="getroot"></a>
### getRoot
**getRoot**(): `any`
*Defined in [index.ts:156](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L156)*
getRoot
*__desc__*: Returns the Merkle root hash as a Buffer.
*__example__*: const root = tree.getRoot()
**Returns:** `any`
___
<a id="print"></a>
### print
**print**(): `void`
*Defined in [index.ts:324](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L324)*
**Returns:** `void`
___
<a id="tostring"></a>
### toString
**toString**(): `any`
*Defined in [index.ts:335](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L335)*
**Returns:** `any`
___
<a id="totreestring"></a>
### toTreeString
**toTreeString**(): `any`
*Defined in [index.ts:329](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L329)*
**Returns:** `any`
___
<a id="verify"></a>
### verify
**verify**(proof: *`any`*, targetNode: *`any`*, root: *`any`*): `boolean`
*Defined in [index.ts:258](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L258)*
verify
*__desc__*: Returns true if the proof path (array of hashes) can connect the target node to the Merkle root.
*__example__*: const root = tree.getRoot() const proof = tree.getProof(leaves\[2\]) const verified = tree.verify(proof, leaves\[2\], root)
**Parameters:**
| Name | Type | Description |
| ------ | ------ | ------ |
| proof | `any` | Array of proof objects that should connect target node to Merkle root. |
| targetNode | `any` | Target node Buffer |
| root | `any` | Merkle root Buffer |
**Returns:** `boolean`
___
<a id="bufferify"></a>
### `<Static>` bufferify
**bufferify**(x: *`any`*): `any`
*Defined in [index.ts:340](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L340)*
**Parameters:**
| Name | Type |
| ------ | ------ |
| x | `any` |
**Returns:** `any`
___
<a id="print-1"></a>
### `<Static>` print
**print**(tree: *`any`*): `void`
*Defined in [index.ts:345](https://github.com/miguelmota/merkletreejs/blob/c1fcf89/index.ts#L345)*
**Parameters:**
| Name | Type |
| ------ | ------ |
| tree | `any` |
**Returns:** `void`
___
## Test
```bash

6
dist/index.d.ts vendored
View File

@ -4,9 +4,12 @@
*/
export declare class MerkleTree {
hashAlgo: any;
hashLeaves: boolean;
leaves: any;
layers: any;
isBitcoinTree: boolean;
_sort: boolean;
duplicateOdd: boolean;
/**
* @desc Constructs a Merkle Tree.
* All nodes and leaves are stored as Buffers.
@ -14,9 +17,12 @@ export declare class MerkleTree {
* @param {Buffer[]} leaves - Array of hashed leaves. Each leaf must be a Buffer.
* @param {Function} hashAlgorithm - Algorithm used for hashing leaves and nodes
* @param {Object} options - Additional options
* @param {Boolean} options.sort - If set to `true`, the leaves and hashing pairs will be sorted.
* @param {Boolean} options.hashLeaves - If set to `true`, the leaves will hashed using the set hashing algorithms.
* @param {Boolean} options.isBitcoinTree - 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.
* @param {Boolean} options.duplicateOdd - If set to `true`, an odd node will be duplicated and combined to make a pair to generate the layer hash.
* @example
* const MerkleTree = require('merkletreejs')
* const crypto = require('crypto')

61
dist/index.js vendored
View File

@ -15,9 +15,12 @@ var MerkleTree = /** @class */ (function () {
* @param {Buffer[]} leaves - Array of hashed leaves. Each leaf must be a Buffer.
* @param {Function} hashAlgorithm - Algorithm used for hashing leaves and nodes
* @param {Object} options - Additional options
* @param {Boolean} options.sort - If set to `true`, the leaves and hashing pairs will be sorted.
* @param {Boolean} options.hashLeaves - If set to `true`, the leaves will hashed using the set hashing algorithms.
* @param {Boolean} options.isBitcoinTree - 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.
* @param {Boolean} options.duplicateOdd - If set to `true`, an odd node will be duplicated and combined to make a pair to generate the layer hash.
* @example
* const MerkleTree = require('merkletreejs')
* const crypto = require('crypto')
@ -33,10 +36,16 @@ var MerkleTree = /** @class */ (function () {
*/
function MerkleTree(leaves, hashAlgorithm, options) {
if (options === void 0) { options = {}; }
this.isBitcoinTree = !!options.isBitcoinTree;
this.hashLeaves = !!options.hashLeaves;
this._sort = !!options.sort;
this.duplicateOdd = !!options.duplicateOdd;
this.hashAlgo = bufferifyFn(hashAlgorithm);
if (this.hashLeaves) {
leaves = leaves.map(this.hashAlgo);
}
this.leaves = leaves.map(bufferify);
this.layers = [this.leaves];
this.isBitcoinTree = !!options.isBitcoinTree;
this.createHashes(this.leaves);
}
// TODO: documentation
@ -44,15 +53,44 @@ var MerkleTree = /** @class */ (function () {
while (nodes.length > 1) {
var layerIndex = this.layers.length;
this.layers.push([]);
for (var i = 0; i < nodes.length - 1; i += 2) {
for (var i = 0; i < nodes.length; i += 2) {
if (i + 1 === nodes.length) {
if (nodes.length % 2 === 1) {
var data_1 = nodes[nodes.length - 1];
var hash_1 = data_1;
// is bitcoin tree
if (this.isBitcoinTree) {
// Bitcoin method of duplicating the odd ending nodes
data_1 = Buffer.concat([reverse(data_1), reverse(data_1)]);
hash_1 = this.hashAlgo(data_1);
hash_1 = reverse(this.hashAlgo(hash_1));
this.layers[layerIndex].push(hash_1);
continue;
}
else {
if (!this.duplicateOdd) {
this.layers[layerIndex].push(nodes[i]);
continue;
}
}
}
}
var left = nodes[i];
var right = nodes[i + 1];
var right = i + 1 == nodes.length ? left : nodes[i + 1];
var data = null;
if (this.isBitcoinTree) {
data = Buffer.concat([reverse(left), reverse(right)]);
var combined = [reverse(left), reverse(right)];
if (this._sort) {
combined.sort(Buffer.compare);
}
data = Buffer.concat(combined);
}
else {
data = Buffer.concat([left, right]);
var combined = [left, right];
if (this._sort) {
combined.sort(Buffer.compare);
}
data = Buffer.concat(combined);
}
var hash = this.hashAlgo(data);
// double hash if bitcoin tree
@ -61,19 +99,6 @@ var MerkleTree = /** @class */ (function () {
}
this.layers[layerIndex].push(hash);
}
// is odd number of nodes
if (nodes.length % 2 === 1) {
var data = nodes[nodes.length - 1];
var hash = data;
// is bitcoin tree
if (this.isBitcoinTree) {
// Bitcoin method of duplicating the odd ending nodes
data = Buffer.concat([reverse(data), reverse(data)]);
hash = this.hashAlgo(data);
hash = reverse(this.hashAlgo(hash));
}
this.layers[layerIndex].push(hash);
}
nodes = this.layers[layerIndex];
}
};

138
index.ts
View File

@ -8,9 +8,12 @@ import * as treeify from 'treeify'
*/
export class MerkleTree {
hashAlgo: any
hashLeaves: boolean
leaves: any
layers: any
isBitcoinTree: boolean
_sort: boolean
duplicateOdd: boolean
/**
* @desc Constructs a Merkle Tree.
@ -19,9 +22,12 @@ export class MerkleTree {
* @param {Buffer[]} leaves - Array of hashed leaves. Each leaf must be a Buffer.
* @param {Function} hashAlgorithm - Algorithm used for hashing leaves and nodes
* @param {Object} options - Additional options
* @param {Boolean} options.sort - If set to `true`, the leaves and hashing pairs will be sorted.
* @param {Boolean} options.hashLeaves - If set to `true`, the leaves will hashed using the set hashing algorithms.
* @param {Boolean} options.isBitcoinTree - 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.
* @param {Boolean} options.duplicateOdd - If set to `true`, an odd node will be duplicated and combined to make a pair to generate the layer hash.
* @example
* const MerkleTree = require('merkletreejs')
* const crypto = require('crypto')
@ -36,11 +42,17 @@ export class MerkleTree {
* const tree = new MerkleTree(leaves, sha256)
*/
constructor(leaves, hashAlgorithm, options={} as any) {
this.isBitcoinTree = !!options.isBitcoinTree
this.hashLeaves = !!options.hashLeaves
this._sort = !!options.sort
this.duplicateOdd = !!options.duplicateOdd
this.hashAlgo = bufferifyFn(hashAlgorithm)
if (this.hashLeaves) {
leaves = leaves.map(this.hashAlgo)
}
this.leaves = leaves.map(bufferify)
this.layers = [this.leaves]
this.isBitcoinTree = !!options.isBitcoinTree
this.createHashes(this.leaves)
}
@ -52,15 +64,50 @@ export class MerkleTree {
this.layers.push([])
for (let i = 0; i < nodes.length - 1; i += 2) {
for (let i = 0; i < nodes.length; i += 2) {
if (i+1 === nodes.length) {
if (nodes.length % 2 === 1) {
let data = nodes[nodes.length-1]
let hash = data
// is bitcoin tree
if (this.isBitcoinTree) {
// Bitcoin method of duplicating the odd ending nodes
data = Buffer.concat([reverse(data), reverse(data)])
hash = this.hashAlgo(data)
hash = reverse(this.hashAlgo(hash))
this.layers[layerIndex].push(hash)
continue
} else {
if (!this.duplicateOdd) {
this.layers[layerIndex].push(nodes[i])
continue
}
}
}
}
const left = nodes[i]
const right = nodes[i+1]
const right = i + 1 == nodes.length ? left : nodes[i + 1];
let data = null
if (this.isBitcoinTree) {
data = Buffer.concat([reverse(left), reverse(right)])
let combined = [reverse(left), reverse(right)]
if (this._sort) {
combined.sort(Buffer.compare)
}
data = Buffer.concat(combined)
} else {
data = Buffer.concat([left, right])
let combined = [left, right]
if (this._sort) {
combined.sort(Buffer.compare)
}
data = Buffer.concat(combined)
}
let hash = this.hashAlgo(data)
@ -73,25 +120,8 @@ export class MerkleTree {
this.layers[layerIndex].push(hash)
}
// is odd number of nodes
if (nodes.length % 2 === 1) {
let data = nodes[nodes.length-1]
let hash = data
// is bitcoin tree
if (this.isBitcoinTree) {
// Bitcoin method of duplicating the odd ending nodes
data = Buffer.concat([reverse(data), reverse(data)])
hash = this.hashAlgo(data)
hash = reverse(this.hashAlgo(hash))
}
this.layers[layerIndex].push(hash)
}
nodes = this.layers[layerIndex]
}
}
/**
@ -180,36 +210,36 @@ export class MerkleTree {
// set index to parent index
index = (index / 2)|0
}
return proof
} else {
// Proof Generation for Non-Bitcoin Trees
for (let i = 0; i < this.layers.length; i++) {
const layer = this.layers[i]
const isRightNode = index % 2
const pairIndex = (isRightNode ? index - 1 : index + 1)
if (pairIndex < layer.length) {
proof.push({
position: isRightNode ? 'left': 'right',
data: layer[pairIndex]
})
}
// set index to parent index
index = (index / 2)|0
}
return proof
}
}
return proof
} else {
// Proof Generation for Non-Bitcoin Trees
for (let i = 0; i < this.layers.length; i++) {
const layer = this.layers[i]
const isRightNode = index % 2
const pairIndex = (isRightNode ? index - 1 : index + 1)
if (pairIndex < layer.length) {
proof.push({
position: isRightNode ? 'left': 'right',
data: layer[pairIndex]
})
}
// set index to parent index
index = (index / 2)|0
}
return proof
}
}
/**
* verify
* @desc Returns true if the proof path (array of hashes) can connect the target node
@ -230,9 +260,9 @@ export class MerkleTree {
root = bufferify(root)
if (!Array.isArray(proof) ||
!proof.length ||
!targetNode ||
!root) {
!proof.length ||
!targetNode ||
!root) {
return false
}

View File

@ -1,9 +0,0 @@
const jsdoc2md = require('jsdoc-to-markdown')
jsdoc2md.render({
files: 'index.js',
separators: true
})
.then(data => {
console.log(data)
})

1001
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
{
"name": "merkletreejs",
"version": "0.0.25",
"version": "0.1.0",
"description": "Construct Merkle Trees and verify proofs",
"main": "dist/index.js",
"types": "typings/merkletreejs/*",
"scripts": {
"test": "tape test/*.js",
"build": "rm -rf dist/ && tsc",
"docs:md": "node jsdoc.js"
"docs:md": "typedoc --theme markdown index.ts --out docs"
},
"repository": {
"type": "git",
@ -40,8 +40,9 @@
"@types/node": "^11.12.1",
"crypto": "0.0.3",
"ethereumjs-util": "^5.1.2",
"jsdoc-to-markdown": "^3.0.0",
"tape": "^4.9.2",
"typedoc": "^0.14.2",
"typedoc-plugin-markdown": "^1.2.1",
"typescript": "^3.4.1"
},
"engines": {
@ -51,6 +52,7 @@
"buffer-reverse": "^1.0.1",
"crypto-js": "^3.1.9-1",
"is-buffer": "^2.0.3",
"merkle-lib": "^2.0.10",
"treeify": "^1.1.0"
}
}

View File

@ -7,18 +7,25 @@ const SHA3 = require('crypto-js/sha3')
const { MerkleTree } = require('../')
function sha256(data) {
return crypto.createHash('sha256').update(data).digest()
}
const sha256 = (data) => crypto.createHash('sha256').update(data).digest()
test('sha256', t => {
test('sha256 with sha3 leaves', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(x => sha3(x))
const tree = new MerkleTree(leaves, sha256)
const root = '311d2e46f49b15fff8b746b74ad57f2cc9e0d9939fda94387141a2d3fdf187ae'
t.equal(tree.getRoot().toString('hex'), root)
})
test('sha256 with sha3 leaves with duplicate odd option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(x => sha3(x))
const tree = new MerkleTree(leaves, sha256, {duplicateOdd: true})
const root = 'bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1'
t.equal(tree.getRoot().toString('hex'), root)
})
@ -26,32 +33,100 @@ test('crypto-js - sha256', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(x => sha3(x))
const tree = new MerkleTree(leaves, SHA256)
const root = '311d2e46f49b15fff8b746b74ad57f2cc9e0d9939fda94387141a2d3fdf187ae'
t.equal(tree.getRoot().toString('hex'), root)
})
test('crypto-js - SHA256 leaves', t => {
test('sha256 with sort option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(x => sha256(x))
const tree = new MerkleTree(leaves, sha256, {sort: true})
const root = 'a30ba95a1a5dc397fe45ea20105363b08d682b864a28f4940419a29349a28325'
t.equal(tree.getRoot().toString('hex'), root)
})
test('sha256 with sha256 leaves and sort option and duplicate odd option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(x => sha256(x))
const tree = new MerkleTree(leaves, sha256, {sort: true, duplicateOdd: true})
const root = 'a5260b2a7ec31584e5d5689a5628c2b3d949e2397334fd71c107478e5f887eaf'
t.equal(tree.getRoot().toString('hex'), root)
})
test('sha256 with hash leaves option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c', 'd', 'e', 'f']
const tree = new MerkleTree(leaves, sha256, {hashLeaves: true})
const root = '1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2'
t.equal(tree.getRoot().toString('hex'), root)
})
test('sha256 with hash leaves option and duplicate odd option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c', 'd', 'e', 'f']
const tree = new MerkleTree(leaves, sha256, {hashLeaves: true, duplicateOdd: true})
const root = '44205acec5156114821f1f71d87c72e0de395633cd1589def6d4444cc79f8103'
t.equal(tree.getRoot().toString('hex'), root)
})
test('crypto-js - sha256 with sha3 leaves', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(SHA256)
const tree = new MerkleTree(leaves, SHA256)
const root = '7075152d03a5cd92104887b476862778ec0c87be5c2fa1c0a90f87c49fad6eff'
t.equal(tree.getRoot().toString('hex'), root)
})
test('crypto-js - SHA3 leaves', t => {
test('crypto-js - sha256 with sha3 leaves and duplicate odd option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(SHA3)
const leaves = ['a', 'b', 'c'].map(x => sha3(x))
const tree = new MerkleTree(leaves, SHA256, {duplicateOdd: true})
const root = 'bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1'
t.equal(tree.getRoot().toString('hex'), root)
})
test('crypto-js - SHA256 with SHA256 leaves and duplicate odd option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(SHA256)
const tree = new MerkleTree(leaves, SHA256, {duplicateOdd: true})
const root = 'd31a37ef6ac14a2db1470c4316beb5592e6afd4465022339adafda76a18ffabe'
t.equal(tree.getRoot().toString('hex'), root)
})
test('crypto-js - SHA256 with SHA3 leaves', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(x => SHA3(x))
const tree = new MerkleTree(leaves, SHA256)
const root = '57e9ee696a291f8a51d224a6d64ba4a0693920a63f1e0329efe96c02a5f28849'
t.equal(tree.getRoot().toString('hex'), root)
})
test('crypto-js - SHA256 with sha3 leaves and duplicate odd option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(x => sha3(x))
const tree = new MerkleTree(leaves, SHA256, {duplicateOdd: true})
const root = 'bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1'
t.equal(tree.getRoot().toString('hex'), root)
})
@ -71,6 +146,7 @@ test('solidity sha3 [keccak-256]', t => {
const layers = tree.getLayers().slice(1) // no leaves
const layer_1 = sha3(Buffer.concat([leaves[0], leaves[1]])).toString('hex')
t.equal(layers[0][0].toString('hex'), layer_1)
t.equal(layers[0][1].toString('hex'), c_hash)
@ -103,6 +179,53 @@ test('solidity sha3 [keccak-256]', t => {
t.equal(tree.verify(proof_2, leaves[2], root), true)
})
test('solidity sha3 [keccak-256] with duplicate odd option', t => {
t.plan(20)
const leaves = ['a', 'b', 'c'].map(x => sha3(x))
const a_hash = '3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb'
const b_hash = 'b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510'
const c_hash = '0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2'
t.deepEqual(leaves.map(x => x.toString('hex')), [a_hash, b_hash, c_hash])
const tree = new MerkleTree(leaves, sha3, {duplicateOdd: true})
const layers = tree.getLayers().slice(1) // no leaves
const layer_1 = sha3(Buffer.concat([leaves[0], leaves[1]])).toString('hex')
const layer_2 = sha3(Buffer.concat([leaves[2], leaves[2]])).toString('hex')
t.equal(layers[0][0].toString('hex'), layer_1)
t.equal(layers[0][1].toString('hex'), layer_2)
const root = Buffer.from('905b17edcf8b6fb1415b32cdbab3e02c2c93f80a345de80ea2bbf9feba9f5a55', 'hex')
t.equal(tree.getRoot().toString('hex'), root.toString('hex'))
const proof_0 = tree.getProof(leaves[0])
t.equal(proof_0.length, 2)
t.equal(proof_0[0].position, 'right')
t.equal(proof_0[0].data.toString('hex'), b_hash)
t.equal(proof_0[1].position, 'right')
t.equal(proof_0[1].data.toString('hex'), layer_2)
t.equal(tree.verify(proof_0, leaves[0], root), true)
const proof_1 = tree.getProof(leaves[1])
t.equal(proof_1.length, 2)
t.equal(proof_1[0].position, 'left')
t.equal(proof_1[0].data.toString('hex'), a_hash)
t.equal(proof_1[1].position, 'right')
t.equal(proof_1[1].data.toString('hex'), layer_2)
t.equal(tree.verify(proof_1, leaves[1], root), true)
const proof_2 = tree.getProof(leaves[2])
t.equal(proof_2.length, 1)
t.equal(proof_2[0].position, 'left')
t.equal(proof_2[0].data.toString('hex'), layer_1)
t.equal(tree.verify(proof_2, layer_2, root), true)
})
test('solidity sha3 [keccak-256] with duplicate leaves', t => {
t.plan(5)
@ -126,7 +249,6 @@ test('solidity sha3 [keccak-256] with duplicate leaves', t => {
t.equal(proof_0[0].data.toString('hex'), layer_1)
})
test('sha-256 with option.isBitcoinTree', t => {
t.plan(2)
@ -235,10 +357,9 @@ test('sha-256 with option.isBitcoinTree', t => {
"27a0797cc5b042ba4c11e72a9555d13a67f00161550b32ede0511718b22dbc2c",
]
var 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 root = Buffer.from('871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a', 'hex')
t.equal(tree.getRoot().toString('hex'), root.toString('hex'))
@ -249,7 +370,7 @@ test('sha-256 with option.isBitcoinTree', t => {
test('sha3 - hex strings', t => {
t.plan(1)
let leaves = ['a', 'b', 'c'].map(x => sha3(x).toString('hex'))
const leaves = ['a', 'b', 'c'].map(x => sha3(x).toString('hex'))
const tree = new MerkleTree(leaves, SHA256)
const root = '311d2e46f49b15fff8b746b74ad57f2cc9e0d9939fda94387141a2d3fdf187ae'
t.equal(tree.getRoot().toString('hex'), root)
@ -265,19 +386,18 @@ test('sha256 - no leaves', t => {
t.equal(tree.getRoot().toString('hex'), root)
})
test.skip('sha256 - 1,000,000 leaves', t => {
test('sha256 - 1,000,000 leaves', t => {
t.plan(1)
let values = []
const values = []
for (let i = 0; i < 1e6; i++) {
values.push(`${i}`)
}
const leaves = values.map(x => sha256(x))
const tree = new MerkleTree(leaves, sha256)
const root = '101dd357df60384d254330fe118e3046871767c2748ebd62ce031c117df483da'
t.equal(tree.getRoot().toString('hex'), root)
})
@ -285,11 +405,8 @@ test('crypto-js SHA3 leaves SHA256 hash algo', t => {
t.plan(2)
const leaves = ['a', 'b', 'c', 'd'].map(SHA3)
const tree = new MerkleTree(leaves, SHA256)
t.deepEqual(tree.getLeaves(), leaves.map(MerkleTree.bufferify))
const root = tree.getRoot()
const verifications = leaves.map(leaf => {
@ -329,6 +446,26 @@ test('getLayersAsObject', t => {
})
})
test('getLayersAsObject with duplicate odd option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(x => sha3(x))
const tree = new MerkleTree(leaves, sha256, {duplicateOdd: true})
const obj = tree.getLayersAsObject()
t.deepEqual(obj, {
"bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1": {
"176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1": {
"3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb": null,
"b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510": null
},
"43e061172b1177f25d0f156b2d2ed11728006fade8e167ff3d1b9dbc979a3358": {
"0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2": null
}
}
})
})
test('print', t => {
t.plan(1)
@ -344,3 +481,19 @@ test('print', t => {
0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2
`)
})
test('print with duplicate odd option', t => {
t.plan(1)
const leaves = ['a', 'b', 'c'].map(x => sha3(x))
const tree = new MerkleTree(leaves, sha256, {duplicateOdd: true})
t.equal(tree.toString(),
`└─ bcdd0f60308db788712205115fe4273bfda49fa0925611fee765a63df9ab96a1
176f0f307632fdd5831875eb709e2f68d770b102262998b214ddeb3f04164ae1
3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb
b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510
43e061172b1177f25d0f156b2d2ed11728006fade8e167ff3d1b9dbc979a3358
0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2
`)
})