@osmura/merkletreejs 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1326 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MerkleTree = void 0;
7
+ const buffer_1 = require("buffer");
8
+ const buffer_reverse_1 = __importDefault(require("buffer-reverse"));
9
+ const sha256_1 = __importDefault(require("crypto-js/sha256"));
10
+ const treeify_1 = __importDefault(require("treeify"));
11
+ const Base_1 = __importDefault(require("./Base"));
12
+ /**
13
+ * Class reprensenting a Merkle Tree
14
+ * @namespace MerkleTree
15
+ */
16
+ class MerkleTree extends Base_1.default {
17
+ /**
18
+ * @desc Constructs a Merkle Tree.
19
+ * All nodes and leaves are stored as Buffers.
20
+ * Lonely leaf nodes are promoted to the next level up without being hashed again.
21
+ * @param {Buffer[]} leaves - Array of hashed leaves. Each leaf must be a Buffer.
22
+ * @param {Function} hashFunction - Hash function to use for hashing leaves and nodes
23
+ * @param {Object} options - Additional options
24
+ * @example
25
+ *```js
26
+ *const MerkleTree = require('merkletreejs')
27
+ *const crypto = require('crypto')
28
+ *
29
+ *function sha256(data) {
30
+ * // returns Buffer
31
+ * return crypto.createHash('sha256').update(data).digest()
32
+ *}
33
+ *
34
+ *const leaves = ['a', 'b', 'c'].map(value => keccak(value))
35
+ *
36
+ *const tree = new MerkleTree(leaves, sha256)
37
+ *```
38
+ */
39
+ constructor(leaves, hashFn = sha256_1.default, options = {}) {
40
+ super();
41
+ this.duplicateOdd = false;
42
+ this.hashLeaves = false;
43
+ this.isBitcoinTree = false;
44
+ this.leaves = [];
45
+ this.layers = [];
46
+ this.sortLeaves = false;
47
+ this.sortPairs = false;
48
+ this.sort = false;
49
+ this.fillDefaultHash = null;
50
+ this.complete = false;
51
+ if (options.complete) {
52
+ if (options.isBitcoinTree) {
53
+ throw new Error('option "complete" is incompatible with "isBitcoinTree"');
54
+ }
55
+ if (options.duplicateOdd) {
56
+ throw new Error('option "complete" is incompatible with "duplicateOdd"');
57
+ }
58
+ }
59
+ this.isBitcoinTree = !!options.isBitcoinTree;
60
+ this.hashLeaves = !!options.hashLeaves;
61
+ this.sortLeaves = !!options.sortLeaves;
62
+ this.sortPairs = !!options.sortPairs;
63
+ this.complete = !!options.complete;
64
+ if (options.fillDefaultHash) {
65
+ if (typeof options.fillDefaultHash === 'function') {
66
+ this.fillDefaultHash = options.fillDefaultHash;
67
+ }
68
+ else if (buffer_1.Buffer.isBuffer(options.fillDefaultHash) || typeof options.fillDefaultHash === 'string') {
69
+ this.fillDefaultHash = (idx, hashFn) => options.fillDefaultHash;
70
+ }
71
+ else {
72
+ throw new Error('method "fillDefaultHash" must be a function, Buffer, or string');
73
+ }
74
+ }
75
+ this.sort = !!options.sort;
76
+ if (this.sort) {
77
+ this.sortLeaves = true;
78
+ this.sortPairs = true;
79
+ }
80
+ this.duplicateOdd = !!options.duplicateOdd;
81
+ if (options.concatenator) {
82
+ this.concatenator = options.concatenator;
83
+ }
84
+ else {
85
+ this.concatenator = buffer_1.Buffer.concat;
86
+ }
87
+ if (typeof hashFn !== 'function') {
88
+ throw new Error('hashFn must be a function');
89
+ }
90
+ this.hashFn = this.bufferifyFn(hashFn);
91
+ this.processLeaves(leaves);
92
+ }
93
+ getOptions() {
94
+ var _a, _b;
95
+ return {
96
+ complete: this.complete,
97
+ isBitcoinTree: this.isBitcoinTree,
98
+ hashLeaves: this.hashLeaves,
99
+ sortLeaves: this.sortLeaves,
100
+ sortPairs: this.sortPairs,
101
+ sort: this.sort,
102
+ fillDefaultHash: (_b = (_a = this.fillDefaultHash) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : null,
103
+ duplicateOdd: this.duplicateOdd
104
+ };
105
+ }
106
+ processLeaves(leaves) {
107
+ if (this.hashLeaves) {
108
+ leaves = leaves.map(this.hashFn);
109
+ }
110
+ this.leaves = leaves.map(this.bufferify);
111
+ if (this.sortLeaves) {
112
+ this.leaves = this.leaves.sort(buffer_1.Buffer.compare);
113
+ }
114
+ if (this.fillDefaultHash) {
115
+ for (let i = this.leaves.length; i < Math.pow(2, Math.ceil(Math.log2(this.leaves.length))); i++) {
116
+ this.leaves.push(this.bufferify(this.fillDefaultHash(i, this.hashFn)));
117
+ }
118
+ }
119
+ this.createHashes(this.leaves);
120
+ }
121
+ createHashes(nodes) {
122
+ this.layers = [nodes];
123
+ while (nodes.length > 1) {
124
+ const layerIndex = this.layers.length;
125
+ this.layers.push([]);
126
+ const layerLimit = this.complete && layerIndex === 1 && !Number.isInteger(Math.log2(nodes.length))
127
+ ? (2 * nodes.length) - (Math.pow(2, Math.ceil(Math.log2(nodes.length))))
128
+ : nodes.length;
129
+ for (let i = 0; i < nodes.length; i += 2) {
130
+ if (i >= layerLimit) {
131
+ this.layers[layerIndex].push(...nodes.slice(layerLimit));
132
+ break;
133
+ }
134
+ else if (i + 1 === nodes.length) {
135
+ if (nodes.length % 2 === 1) {
136
+ const data = nodes[nodes.length - 1];
137
+ let hash = data;
138
+ // is bitcoin tree
139
+ if (this.isBitcoinTree) {
140
+ // Bitcoin method of duplicating the odd ending nodes
141
+ hash = this.hashFn(this.concatenator([buffer_reverse_1.default(data), buffer_reverse_1.default(data)]));
142
+ hash = buffer_reverse_1.default(this.hashFn(hash));
143
+ this.layers[layerIndex].push(hash);
144
+ continue;
145
+ }
146
+ else {
147
+ if (this.duplicateOdd) {
148
+ // continue with creating layer
149
+ }
150
+ else {
151
+ // push copy of hash and continue iteration
152
+ this.layers[layerIndex].push(nodes[i]);
153
+ continue;
154
+ }
155
+ }
156
+ }
157
+ }
158
+ const left = nodes[i];
159
+ const right = i + 1 === nodes.length ? left : nodes[i + 1];
160
+ let combined = null;
161
+ if (this.isBitcoinTree) {
162
+ combined = [buffer_reverse_1.default(left), buffer_reverse_1.default(right)];
163
+ }
164
+ else {
165
+ combined = [left, right];
166
+ }
167
+ if (this.sortPairs) {
168
+ combined.sort(buffer_1.Buffer.compare);
169
+ }
170
+ let hash = this.hashFn(this.concatenator(combined));
171
+ // double hash if bitcoin tree
172
+ if (this.isBitcoinTree) {
173
+ hash = buffer_reverse_1.default(this.hashFn(hash));
174
+ }
175
+ this.layers[layerIndex].push(hash);
176
+ }
177
+ nodes = this.layers[layerIndex];
178
+ }
179
+ }
180
+ /**
181
+ * addLeaf
182
+ * @desc Adds a leaf to the tree and re-calculates layers.
183
+ * @param {String|Buffer} - Leaf
184
+ * @param {Boolean} - Set to true if the leaf should be hashed before being added to tree.
185
+ * @example
186
+ *```js
187
+ *tree.addLeaf(newLeaf)
188
+ *```
189
+ */
190
+ addLeaf(leaf, shouldHash = false) {
191
+ if (shouldHash) {
192
+ leaf = this.hashFn(leaf);
193
+ }
194
+ this.processLeaves(this.leaves.concat(leaf));
195
+ }
196
+ /**
197
+ * addLeaves
198
+ * @desc Adds multiple leaves to the tree and re-calculates layers.
199
+ * @param {String[]|Buffer[]} - Array of leaves
200
+ * @param {Boolean} - Set to true if the leaves should be hashed before being added to tree.
201
+ * @example
202
+ *```js
203
+ *tree.addLeaves(newLeaves)
204
+ *```
205
+ */
206
+ addLeaves(leaves, shouldHash = false) {
207
+ if (shouldHash) {
208
+ leaves = leaves.map(this.hashFn);
209
+ }
210
+ this.processLeaves(this.leaves.concat(leaves));
211
+ }
212
+ /**
213
+ * getLeaves
214
+ * @desc Returns array of leaves of Merkle Tree.
215
+ * @return {Buffer[]}
216
+ * @example
217
+ *```js
218
+ *const leaves = tree.getLeaves()
219
+ *```
220
+ */
221
+ getLeaves(values) {
222
+ if (Array.isArray(values)) {
223
+ if (this.hashLeaves) {
224
+ values = values.map(this.hashFn);
225
+ if (this.sortLeaves) {
226
+ values = values.sort(buffer_1.Buffer.compare);
227
+ }
228
+ }
229
+ return this.leaves.filter(leaf => this.bufferIndexOf(values, leaf, this.sortLeaves) !== -1);
230
+ }
231
+ return this.leaves;
232
+ }
233
+ removeLeaf(index) {
234
+ if (!this.isValidLeafIndex(index)) {
235
+ throw new Error(`"${index}" is not a valid leaf index. Expected to be [0, ${this.getLeafCount() - 1}]`);
236
+ }
237
+ const result = this.leaves.splice(index, 1);
238
+ this.processLeaves(this.leaves);
239
+ return result[0];
240
+ }
241
+ updateLeaf(index, value, shouldHash = false) {
242
+ if (!this.isValidLeafIndex(index)) {
243
+ throw new Error(`"${index}" is not a valid leaf index. Expected to be [0, ${this.getLeafCount() - 1}]`);
244
+ }
245
+ if (shouldHash)
246
+ value = this.hashFn(value);
247
+ this.leaves[index] = value;
248
+ this.processLeaves(this.leaves);
249
+ }
250
+ /**
251
+ * getLeaf
252
+ * @desc Returns the leaf at the given index.
253
+ * @param {Number} - Index number
254
+ * @return {Buffer}
255
+ * @example
256
+ *```js
257
+ *const leaf = tree.getLeaf(1)
258
+ *```
259
+ */
260
+ getLeaf(index) {
261
+ if (index < 0 || index > this.leaves.length - 1) {
262
+ return buffer_1.Buffer.from([]);
263
+ }
264
+ return this.leaves[index];
265
+ }
266
+ /**
267
+ * getHexLeaf
268
+ * @desc Returns the leaf at the given index as a hex string.
269
+ * @param {Number} - Index number
270
+ * @return {String}
271
+ * @example
272
+ *```js
273
+ *const leaf = tree.getHexLeaf(1)
274
+ *```
275
+ */
276
+ getHexLeaf(index) {
277
+ return this.bufferToHex(this.getLeaf(index));
278
+ }
279
+ /**
280
+ * getLeafIndex
281
+ * @desc Returns the index of the given leaf, or -1 if the leaf is not found.
282
+ * @param {String|Buffer} - Target leaf
283
+ * @return {number}
284
+ * @example
285
+ *```js
286
+ *const leaf = Buffer.from('abc')
287
+ *const index = tree.getLeafIndex(leaf)
288
+ *```
289
+ */
290
+ getLeafIndex(target) {
291
+ target = this.bufferify(target);
292
+ const leaves = this.getLeaves();
293
+ for (let i = 0; i < leaves.length; i++) {
294
+ const leaf = leaves[i];
295
+ if (leaf.equals(target)) {
296
+ return i;
297
+ }
298
+ }
299
+ return -1;
300
+ }
301
+ /**
302
+ * getLeafCount
303
+ * @desc Returns the total number of leaves.
304
+ * @return {number}
305
+ * @example
306
+ *```js
307
+ *const count = tree.getLeafCount()
308
+ *```
309
+ */
310
+ getLeafCount() {
311
+ return this.leaves.length;
312
+ }
313
+ /**
314
+ * getHexLeaves
315
+ * @desc Returns array of leaves of Merkle Tree as hex strings.
316
+ * @return {String[]}
317
+ * @example
318
+ *```js
319
+ *const leaves = tree.getHexLeaves()
320
+ *```
321
+ */
322
+ getHexLeaves() {
323
+ return this.leaves.map(leaf => this.bufferToHex(leaf));
324
+ }
325
+ /**
326
+ * marshalLeaves
327
+ * @desc Returns array of leaves of Merkle Tree as a JSON string.
328
+ * @param {String[]|Buffer[]} - Merkle tree leaves
329
+ * @return {String} - List of leaves as JSON string
330
+ * @example
331
+ *```js
332
+ *const jsonStr = MerkleTree.marshalLeaves(leaves)
333
+ *```
334
+ */
335
+ static marshalLeaves(leaves) {
336
+ return JSON.stringify(leaves.map(leaf => MerkleTree.bufferToHex(leaf)), null, 2);
337
+ }
338
+ /**
339
+ * unmarshalLeaves
340
+ * @desc Returns array of leaves of Merkle Tree as a Buffers.
341
+ * @param {String|Object} - JSON stringified leaves
342
+ * @return {Buffer[]} - Unmarshalled list of leaves
343
+ * @example
344
+ *```js
345
+ *const leaves = MerkleTree.unmarshalLeaves(jsonStr)
346
+ *```
347
+ */
348
+ static unmarshalLeaves(jsonStr) {
349
+ let parsed = null;
350
+ if (typeof jsonStr === 'string') {
351
+ parsed = JSON.parse(jsonStr);
352
+ }
353
+ else if (jsonStr instanceof Object) {
354
+ parsed = jsonStr;
355
+ }
356
+ else {
357
+ throw new Error('Expected type of string or object');
358
+ }
359
+ if (!parsed) {
360
+ return [];
361
+ }
362
+ if (!Array.isArray(parsed)) {
363
+ throw new Error('Expected JSON string to be array');
364
+ }
365
+ return parsed.map(MerkleTree.bufferify);
366
+ }
367
+ /**
368
+ * getLayers
369
+ * @desc Returns multi-dimensional array of all layers of Merkle Tree, including leaves and root.
370
+ * @return {Buffer[][]}
371
+ * @example
372
+ *```js
373
+ *const layers = tree.getLayers()
374
+ *```
375
+ */
376
+ getLayers() {
377
+ return this.layers;
378
+ }
379
+ /**
380
+ * getHexLayers
381
+ * @desc Returns multi-dimensional array of all layers of Merkle Tree, including leaves and root as hex strings.
382
+ * @return {String[][]}
383
+ * @example
384
+ *```js
385
+ *const layers = tree.getHexLayers()
386
+ *```
387
+ */
388
+ getHexLayers() {
389
+ return this.layers.reduce((acc, item) => {
390
+ if (Array.isArray(item)) {
391
+ acc.push(item.map(layer => this.bufferToHex(layer)));
392
+ }
393
+ else {
394
+ acc.push(item);
395
+ }
396
+ return acc;
397
+ }, []);
398
+ }
399
+ /**
400
+ * getLayersFlat
401
+ * @desc Returns single flat array of all layers of Merkle Tree, including leaves and root.
402
+ * @return {Buffer[]}
403
+ * @example
404
+ *```js
405
+ *const layers = tree.getLayersFlat()
406
+ *```
407
+ */
408
+ getLayersFlat() {
409
+ const layers = this.layers.reduce((acc, item) => {
410
+ if (Array.isArray(item)) {
411
+ acc.unshift(...item);
412
+ }
413
+ else {
414
+ acc.unshift(item);
415
+ }
416
+ return acc;
417
+ }, []);
418
+ layers.unshift(buffer_1.Buffer.from([0]));
419
+ return layers;
420
+ }
421
+ /**
422
+ * getHexLayersFlat
423
+ * @desc Returns single flat array of all layers of Merkle Tree, including leaves and root as hex string.
424
+ * @return {String[]}
425
+ * @example
426
+ *```js
427
+ *const layers = tree.getHexLayersFlat()
428
+ *```
429
+ */
430
+ getHexLayersFlat() {
431
+ return this.getLayersFlat().map(layer => this.bufferToHex(layer));
432
+ }
433
+ /**
434
+ * getLayerCount
435
+ * @desc Returns the total number of layers.
436
+ * @return {number}
437
+ * @example
438
+ *```js
439
+ *const count = tree.getLayerCount()
440
+ *```
441
+ */
442
+ getLayerCount() {
443
+ return this.getLayers().length;
444
+ }
445
+ /**
446
+ * getRoot
447
+ * @desc Returns the Merkle root hash as a Buffer.
448
+ * @return {Buffer}
449
+ * @example
450
+ *```js
451
+ *const root = tree.getRoot()
452
+ *```
453
+ */
454
+ getRoot() {
455
+ if (this.layers.length === 0) {
456
+ return buffer_1.Buffer.from([]);
457
+ }
458
+ return this.layers[this.layers.length - 1][0] || buffer_1.Buffer.from([]);
459
+ }
460
+ /**
461
+ * getHexRoot
462
+ * @desc Returns the Merkle root hash as a hex string.
463
+ * @return {String}
464
+ * @example
465
+ *```js
466
+ *const root = tree.getHexRoot()
467
+ *```
468
+ */
469
+ getHexRoot() {
470
+ return this.bufferToHex(this.getRoot());
471
+ }
472
+ /**
473
+ * getProof
474
+ * @desc Returns the proof for a target leaf.
475
+ * @param {Buffer} leaf - Target leaf
476
+ * @param {Number} [index] - Target leaf index in leaves array.
477
+ * Use if there are leaves containing duplicate data in order to distinguish it.
478
+ * @return {Object[]} - Array of objects containing a position property of type string
479
+ * with values of 'left' or 'right' and a data property of type Buffer.
480
+ * @example
481
+ * ```js
482
+ *const proof = tree.getProof(leaves[2])
483
+ *```
484
+ *
485
+ * @example
486
+ *```js
487
+ *const leaves = ['a', 'b', 'a'].map(value => keccak(value))
488
+ *const tree = new MerkleTree(leaves, keccak)
489
+ *const proof = tree.getProof(leaves[2], 2)
490
+ *```
491
+ */
492
+ getProof(leaf, index) {
493
+ if (typeof leaf === 'undefined') {
494
+ throw new Error('leaf is required');
495
+ }
496
+ leaf = this.bufferify(leaf);
497
+ const proof = [];
498
+ if (!Number.isInteger(index)) {
499
+ index = -1;
500
+ for (let i = 0; i < this.leaves.length; i++) {
501
+ if (buffer_1.Buffer.compare(leaf, this.leaves[i]) === 0) {
502
+ index = i;
503
+ }
504
+ }
505
+ }
506
+ if (index <= -1) {
507
+ return [];
508
+ }
509
+ for (let i = 0; i < this.layers.length; i++) {
510
+ const layer = this.layers[i];
511
+ const isRightNode = index % 2;
512
+ const pairIndex = (isRightNode ? index - 1
513
+ : this.isBitcoinTree && index === layer.length - 1 && i < this.layers.length - 1
514
+ // Proof Generation for Bitcoin Trees
515
+ ? index
516
+ // Proof Generation for Non-Bitcoin Trees
517
+ : index + 1);
518
+ if (pairIndex < layer.length) {
519
+ proof.push({
520
+ position: isRightNode ? 'left' : 'right',
521
+ data: layer[pairIndex]
522
+ });
523
+ }
524
+ // set index to parent index
525
+ index = (index / 2) | 0;
526
+ }
527
+ return proof;
528
+ }
529
+ /**
530
+ * getHexProof
531
+ * @desc Returns the proof for a target leaf as hex strings.
532
+ * @param {Buffer} leaf - Target leaf
533
+ * @param {Number} [index] - Target leaf index in leaves array.
534
+ * Use if there are leaves containing duplicate data in order to distinguish it.
535
+ * @return {String[]} - Proof array as hex strings.
536
+ * @example
537
+ * ```js
538
+ *const proof = tree.getHexProof(leaves[2])
539
+ *```
540
+ */
541
+ getHexProof(leaf, index) {
542
+ return this.getProof(leaf, index).map(item => this.bufferToHex(item.data));
543
+ }
544
+ /**
545
+ * getProofs
546
+ * @desc Returns the proofs for all leaves.
547
+ * @return {Object[]} - Array of objects containing a position property of type string
548
+ * with values of 'left' or 'right' and a data property of type Buffer for all leaves.
549
+ * @example
550
+ * ```js
551
+ *const proofs = tree.getProofs()
552
+ *```
553
+ *
554
+ * @example
555
+ *```js
556
+ *const leaves = ['a', 'b', 'a'].map(value => keccak(value))
557
+ *const tree = new MerkleTree(leaves, keccak)
558
+ *const proofs = tree.getProofs()
559
+ *```
560
+ */
561
+ getProofs() {
562
+ const proof = [];
563
+ const proofs = [];
564
+ this.getProofsDFS(this.layers.length - 1, 0, proof, proofs);
565
+ return proofs;
566
+ }
567
+ /**
568
+ * getProofsDFS
569
+ * @desc Get all proofs through single traverse
570
+ * @param {Number} currentLayer - Current layer index in traverse.
571
+ * @param {Number} index - Current tarvese node index in traverse.
572
+ * @param {Object[]} proof - Proof chain for single leaf.
573
+ * @param {Object[]} proofs - Proofs for all leaves
574
+ * @example
575
+ * ```js
576
+ *const layers = tree.getLayers()
577
+ *const index = 0;
578
+ *let proof = [];
579
+ *let proofs = [];
580
+ *const proof = tree.getProofsDFS(layers, index, proof, proofs)
581
+ *```
582
+ */
583
+ getProofsDFS(currentLayer, index, proof, proofs) {
584
+ const isRightNode = index % 2;
585
+ if (currentLayer === -1) {
586
+ if (!isRightNode)
587
+ proofs.push([...proof].reverse());
588
+ return;
589
+ }
590
+ if (index >= this.layers[currentLayer].length)
591
+ return;
592
+ const layer = this.layers[currentLayer];
593
+ const pairIndex = isRightNode ? index - 1 : index + 1;
594
+ let pushed = false;
595
+ if (pairIndex < layer.length) {
596
+ pushed = true;
597
+ proof.push({
598
+ position: isRightNode ? 'left' : 'right',
599
+ data: layer[pairIndex]
600
+ });
601
+ }
602
+ const leftchildIndex = index * 2;
603
+ const rightchildIndex = index * 2 + 1;
604
+ this.getProofsDFS(currentLayer - 1, leftchildIndex, proof, proofs);
605
+ this.getProofsDFS(currentLayer - 1, rightchildIndex, proof, proofs);
606
+ if (pushed)
607
+ proof.splice(proof.length - 1, 1);
608
+ }
609
+ /**
610
+ * getHexProofs
611
+ * @desc Returns the proofs for all leaves as hex strings.
612
+ * @return {String[]} - Proofs array as hex strings.
613
+ * @example
614
+ * ```js
615
+ *const proofs = tree.getHexProofs()
616
+ *```
617
+ */
618
+ getHexProofs() {
619
+ return this.getProofs().map(item => this.bufferToHex(item.data));
620
+ }
621
+ /**
622
+ * getPositionalHexProof
623
+ * @desc Returns the proof for a target leaf as hex strings and the position in binary (left == 0).
624
+ * @param {Buffer} leaf - Target leaf
625
+ * @param {Number} [index] - Target leaf index in leaves array.
626
+ * Use if there are leaves containing duplicate data in order to distinguish it.
627
+ * @return {(string | number)[][]} - Proof array as hex strings. position at index 0
628
+ * @example
629
+ * ```js
630
+ *const proof = tree.getPositionalHexProof(leaves[2])
631
+ *```
632
+ */
633
+ getPositionalHexProof(leaf, index) {
634
+ return this.getProof(leaf, index).map(item => {
635
+ return [
636
+ item.position === 'left' ? 0 : 1,
637
+ this.bufferToHex(item.data)
638
+ ];
639
+ });
640
+ }
641
+ /**
642
+ * marshalProof
643
+ * @desc Returns proof array as JSON string.
644
+ * @param {String[]|Object[]} proof - Merkle tree proof array
645
+ * @return {String} - Proof array as JSON string.
646
+ * @example
647
+ * ```js
648
+ *const jsonStr = MerkleTree.marshalProof(proof)
649
+ *```
650
+ */
651
+ static marshalProof(proof) {
652
+ const json = proof.map(item => {
653
+ if (typeof item === 'string') {
654
+ return item;
655
+ }
656
+ if (buffer_1.Buffer.isBuffer(item)) {
657
+ return MerkleTree.bufferToHex(item);
658
+ }
659
+ return {
660
+ position: item.position,
661
+ data: MerkleTree.bufferToHex(item.data)
662
+ };
663
+ });
664
+ return JSON.stringify(json, null, 2);
665
+ }
666
+ /**
667
+ * unmarshalProof
668
+ * @desc Returns the proof for a target leaf as a list of Buffers.
669
+ * @param {String|Object} - Merkle tree leaves
670
+ * @return {String|Object} - Marshalled proof
671
+ * @example
672
+ * ```js
673
+ *const proof = MerkleTree.unmarshalProof(jsonStr)
674
+ *```
675
+ */
676
+ static unmarshalProof(jsonStr) {
677
+ let parsed = null;
678
+ if (typeof jsonStr === 'string') {
679
+ parsed = JSON.parse(jsonStr);
680
+ }
681
+ else if (jsonStr instanceof Object) {
682
+ parsed = jsonStr;
683
+ }
684
+ else {
685
+ throw new Error('Expected type of string or object');
686
+ }
687
+ if (!parsed) {
688
+ return [];
689
+ }
690
+ if (!Array.isArray(parsed)) {
691
+ throw new Error('Expected JSON string to be array');
692
+ }
693
+ return parsed.map(item => {
694
+ if (typeof item === 'string') {
695
+ return MerkleTree.bufferify(item);
696
+ }
697
+ else if (item instanceof Object) {
698
+ return {
699
+ position: item.position,
700
+ data: MerkleTree.bufferify(item.data)
701
+ };
702
+ }
703
+ else {
704
+ throw new Error('Expected item to be of type string or object');
705
+ }
706
+ });
707
+ }
708
+ static marshalTree(tree) {
709
+ const root = tree.getHexRoot();
710
+ const leaves = tree.leaves.map(leaf => MerkleTree.bufferToHex(leaf));
711
+ const layers = tree.getHexLayers();
712
+ const options = tree.getOptions();
713
+ return JSON.stringify({
714
+ options,
715
+ root,
716
+ layers,
717
+ leaves
718
+ }, null, 2);
719
+ }
720
+ static unmarshalTree(jsonStr, hashFn = sha256_1.default, options = {}) {
721
+ let parsed = null;
722
+ if (typeof jsonStr === 'string') {
723
+ parsed = JSON.parse(jsonStr);
724
+ }
725
+ else if (jsonStr instanceof Object) {
726
+ parsed = jsonStr;
727
+ }
728
+ else {
729
+ throw new Error('Expected type of string or object');
730
+ }
731
+ if (!parsed) {
732
+ throw new Error('could not parse json');
733
+ }
734
+ options = Object.assign({}, parsed.options || {}, options);
735
+ return new MerkleTree(parsed.leaves, hashFn, options);
736
+ }
737
+ /**
738
+ * getProofIndices
739
+ * @desc Returns the proof indices for given tree indices.
740
+ * @param {Number[]} treeIndices - Tree indices
741
+ * @param {Number} depth - Tree depth; number of layers.
742
+ * @return {Number[]} - Proof indices
743
+ * @example
744
+ * ```js
745
+ *const proofIndices = tree.getProofIndices([2,5,6], 4)
746
+ *console.log(proofIndices) // [ 23, 20, 19, 8, 3 ]
747
+ *```
748
+ */
749
+ getProofIndices(treeIndices, depth) {
750
+ const leafCount = Math.pow(2, depth);
751
+ let maximalIndices = new Set();
752
+ for (const index of treeIndices) {
753
+ let x = leafCount + index;
754
+ while (x > 1) {
755
+ maximalIndices.add(x ^ 1);
756
+ x = (x / 2) | 0;
757
+ }
758
+ }
759
+ const a = treeIndices.map(index => leafCount + index);
760
+ const b = Array.from(maximalIndices).sort((a, b) => a - b).reverse();
761
+ maximalIndices = a.concat(b);
762
+ const redundantIndices = new Set();
763
+ const proof = [];
764
+ for (let index of maximalIndices) {
765
+ if (!redundantIndices.has(index)) {
766
+ proof.push(index);
767
+ while (index > 1) {
768
+ redundantIndices.add(index);
769
+ if (!redundantIndices.has(index ^ 1))
770
+ break;
771
+ index = (index / 2) | 0;
772
+ }
773
+ }
774
+ }
775
+ return proof.filter(index => {
776
+ return !treeIndices.includes(index - leafCount);
777
+ });
778
+ }
779
+ getProofIndicesForUnevenTree(sortedLeafIndices, leavesCount) {
780
+ const depth = Math.ceil(Math.log2(leavesCount));
781
+ const unevenLayers = [];
782
+ for (let index = 0; index < depth; index++) {
783
+ const unevenLayer = leavesCount % 2 !== 0;
784
+ if (unevenLayer) {
785
+ unevenLayers.push({ index, leavesCount });
786
+ }
787
+ leavesCount = Math.ceil(leavesCount / 2);
788
+ }
789
+ const proofIndices = [];
790
+ let layerNodes = sortedLeafIndices;
791
+ for (let layerIndex = 0; layerIndex < depth; layerIndex++) {
792
+ const siblingIndices = layerNodes.map((index) => {
793
+ if (index % 2 === 0) {
794
+ return index + 1;
795
+ }
796
+ return index - 1;
797
+ });
798
+ let proofNodeIndices = siblingIndices.filter((index) => !layerNodes.includes(index));
799
+ const unevenLayer = unevenLayers.find(({ index }) => index === layerIndex);
800
+ if (unevenLayer && layerNodes.includes(unevenLayer.leavesCount - 1)) {
801
+ proofNodeIndices = proofNodeIndices.slice(0, -1);
802
+ }
803
+ proofIndices.push(proofNodeIndices);
804
+ layerNodes = [...new Set(layerNodes.map((index) => {
805
+ if (index % 2 === 0) {
806
+ return index / 2;
807
+ }
808
+ if (index % 2 === 0) {
809
+ return (index + 1) / 2;
810
+ }
811
+ return (index - 1) / 2;
812
+ }))];
813
+ }
814
+ return proofIndices;
815
+ }
816
+ /**
817
+ * getMultiProof
818
+ * @desc Returns the multiproof for given tree indices.
819
+ * @param {Number[]} indices - Tree indices.
820
+ * @return {Buffer[]} - Multiproofs
821
+ * @example
822
+ * ```js
823
+ *const indices = [2, 5, 6]
824
+ *const proof = tree.getMultiProof(indices)
825
+ *```
826
+ */
827
+ getMultiProof(tree, indices) {
828
+ if (!this.complete) {
829
+ console.warn('Warning: For correct multiProofs it\'s strongly recommended to set complete: true');
830
+ }
831
+ if (!indices) {
832
+ indices = tree;
833
+ tree = this.getLayersFlat();
834
+ }
835
+ const isUneven = this.isUnevenTree();
836
+ if (isUneven) {
837
+ if (indices.every(Number.isInteger)) {
838
+ return this.getMultiProofForUnevenTree(indices);
839
+ }
840
+ }
841
+ if (!indices.every(Number.isInteger)) {
842
+ let els = indices;
843
+ if (this.sortPairs) {
844
+ els = els.sort(buffer_1.Buffer.compare);
845
+ }
846
+ let ids = els.map((el) => this.bufferIndexOf(this.leaves, el, this.sortLeaves)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1);
847
+ if (!ids.every((idx) => idx !== -1)) {
848
+ throw new Error('Element does not exist in Merkle tree');
849
+ }
850
+ const hashes = [];
851
+ const proof = [];
852
+ let nextIds = [];
853
+ for (let i = 0; i < this.layers.length; i++) {
854
+ const layer = this.layers[i];
855
+ for (let j = 0; j < ids.length; j++) {
856
+ const idx = ids[j];
857
+ const pairElement = this.getPairNode(layer, idx);
858
+ hashes.push(layer[idx]);
859
+ if (pairElement) {
860
+ proof.push(pairElement);
861
+ }
862
+ nextIds.push((idx / 2) | 0);
863
+ }
864
+ ids = nextIds.filter((value, i, self) => self.indexOf(value) === i);
865
+ nextIds = [];
866
+ }
867
+ return proof.filter((value) => !hashes.includes(value));
868
+ }
869
+ return this.getProofIndices(indices, Math.log2((tree.length / 2) | 0)).map(index => tree[index]);
870
+ }
871
+ getMultiProofForUnevenTree(tree, indices) {
872
+ if (!indices) {
873
+ indices = tree;
874
+ tree = this.getLayers();
875
+ }
876
+ let proofHashes = [];
877
+ let currentLayerIndices = indices;
878
+ for (const treeLayer of tree) {
879
+ const siblings = [];
880
+ for (const index of currentLayerIndices) {
881
+ if (index % 2 === 0) {
882
+ const idx = index + 1;
883
+ if (!currentLayerIndices.includes(idx)) {
884
+ if (treeLayer[idx]) {
885
+ siblings.push(treeLayer[idx]);
886
+ continue;
887
+ }
888
+ }
889
+ }
890
+ const idx = index - 1;
891
+ if (!currentLayerIndices.includes(idx)) {
892
+ if (treeLayer[idx]) {
893
+ siblings.push(treeLayer[idx]);
894
+ continue;
895
+ }
896
+ }
897
+ }
898
+ proofHashes = proofHashes.concat(siblings);
899
+ const uniqueIndices = new Set();
900
+ for (const index of currentLayerIndices) {
901
+ if (index % 2 === 0) {
902
+ uniqueIndices.add(index / 2);
903
+ continue;
904
+ }
905
+ if (index % 2 === 0) {
906
+ uniqueIndices.add((index + 1) / 2);
907
+ continue;
908
+ }
909
+ uniqueIndices.add((index - 1) / 2);
910
+ }
911
+ currentLayerIndices = Array.from(uniqueIndices);
912
+ }
913
+ return proofHashes;
914
+ }
915
+ /**
916
+ * getHexMultiProof
917
+ * @desc Returns the multiproof for given tree indices as hex strings.
918
+ * @param {Number[]} indices - Tree indices.
919
+ * @return {String[]} - Multiproofs as hex strings.
920
+ * @example
921
+ * ```js
922
+ *const indices = [2, 5, 6]
923
+ *const proof = tree.getHexMultiProof(indices)
924
+ *```
925
+ */
926
+ getHexMultiProof(tree, indices) {
927
+ return this.getMultiProof(tree, indices).map((x) => this.bufferToHex(x));
928
+ }
929
+ /**
930
+ * getProofFlags
931
+ * @desc Returns list of booleans where proofs should be used instead of hashing.
932
+ * Proof flags are used in the Solidity multiproof verifiers.
933
+ * @param {Number[]|Buffer[]} leaves
934
+ * @param {Buffer[]} proofs
935
+ * @return {Boolean[]} - Boolean flags
936
+ * @example
937
+ * ```js
938
+ *const indices = [2, 5, 6]
939
+ *const proof = tree.getMultiProof(indices)
940
+ *const proofFlags = tree.getProofFlags(leaves, proof)
941
+ *```
942
+ */
943
+ getProofFlags(leaves, proofs) {
944
+ if (!Array.isArray(leaves) || leaves.length <= 0) {
945
+ throw new Error('Invalid Inputs!');
946
+ }
947
+ let ids;
948
+ if (leaves.every(Number.isInteger)) {
949
+ ids = [...leaves].sort((a, b) => a === b ? 0 : a > b ? 1 : -1); // Indices where passed
950
+ }
951
+ else {
952
+ ids = leaves.map((el) => this.bufferIndexOf(this.leaves, el, this.sortLeaves)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1);
953
+ }
954
+ if (!ids.every((idx) => idx !== -1)) {
955
+ throw new Error('Element does not exist in Merkle tree');
956
+ }
957
+ const proofBufs = proofs.map(item => this.bufferify(item));
958
+ const tested = [];
959
+ const flags = [];
960
+ for (let index = 0; index < this.layers.length; index++) {
961
+ const layer = this.layers[index];
962
+ ids = ids.reduce((ids, idx) => {
963
+ const skipped = tested.includes(layer[idx]);
964
+ if (!skipped) {
965
+ const pairElement = this.getPairNode(layer, idx);
966
+ const proofUsed = this.bufferArrayIncludes(proofBufs, layer[idx]) || this.bufferArrayIncludes(proofBufs, pairElement);
967
+ pairElement && flags.push(!proofUsed);
968
+ tested.push(layer[idx]);
969
+ tested.push(pairElement);
970
+ }
971
+ ids.push((idx / 2) | 0);
972
+ return ids;
973
+ }, []);
974
+ }
975
+ return flags;
976
+ }
977
+ /**
978
+ * verify
979
+ * @desc Returns true if the proof path (array of hashes) can connect the target node
980
+ * to the Merkle root.
981
+ * @param {Object[]} proof - Array of proof objects that should connect
982
+ * target node to Merkle root.
983
+ * @param {Buffer} targetNode - Target node Buffer
984
+ * @param {Buffer} root - Merkle root Buffer
985
+ * @return {Boolean}
986
+ * @example
987
+ *```js
988
+ *const root = tree.getRoot()
989
+ *const proof = tree.getProof(leaves[2])
990
+ *const verified = tree.verify(proof, leaves[2], root)
991
+ *```
992
+ */
993
+ verify(proof, targetNode, root) {
994
+ let hash = this.bufferify(targetNode);
995
+ root = this.bufferify(root);
996
+ if (!Array.isArray(proof) ||
997
+ !targetNode ||
998
+ !root) {
999
+ return false;
1000
+ }
1001
+ for (let i = 0; i < proof.length; i++) {
1002
+ const node = proof[i];
1003
+ let data = null;
1004
+ let isLeftNode = null;
1005
+ // case for when proof is hex values only
1006
+ if (typeof node === 'string') {
1007
+ data = this.bufferify(node);
1008
+ isLeftNode = true;
1009
+ }
1010
+ else if (Array.isArray(node)) {
1011
+ isLeftNode = (node[0] === 0);
1012
+ data = this.bufferify(node[1]);
1013
+ }
1014
+ else if (buffer_1.Buffer.isBuffer(node)) {
1015
+ data = node;
1016
+ isLeftNode = true;
1017
+ }
1018
+ else if (node instanceof Object) {
1019
+ data = this.bufferify(node.data);
1020
+ isLeftNode = (node.position === 'left');
1021
+ }
1022
+ else {
1023
+ throw new Error('Expected node to be of type string or object');
1024
+ }
1025
+ const buffers = [];
1026
+ if (this.isBitcoinTree) {
1027
+ buffers.push(buffer_reverse_1.default(hash));
1028
+ buffers[isLeftNode ? 'unshift' : 'push'](buffer_reverse_1.default(data));
1029
+ hash = this.hashFn(this.concatenator(buffers));
1030
+ hash = buffer_reverse_1.default(this.hashFn(hash));
1031
+ }
1032
+ else {
1033
+ if (this.sortPairs) {
1034
+ if (buffer_1.Buffer.compare(hash, data) === -1) {
1035
+ buffers.push(hash, data);
1036
+ hash = this.hashFn(this.concatenator(buffers));
1037
+ }
1038
+ else {
1039
+ buffers.push(data, hash);
1040
+ hash = this.hashFn(this.concatenator(buffers));
1041
+ }
1042
+ }
1043
+ else {
1044
+ buffers.push(hash);
1045
+ buffers[isLeftNode ? 'unshift' : 'push'](data);
1046
+ hash = this.hashFn(this.concatenator(buffers));
1047
+ }
1048
+ }
1049
+ }
1050
+ return buffer_1.Buffer.compare(hash, root) === 0;
1051
+ }
1052
+ /**
1053
+ * verifyMultiProof
1054
+ * @desc Returns true if the multiproofs can connect the leaves to the Merkle root.
1055
+ * @param {Buffer} root - Merkle tree root
1056
+ * @param {Number[]} proofIndices - Leave indices for proof
1057
+ * @param {Buffer[]} proofLeaves - Leaf values at indices for proof
1058
+ * @param {Number} leavesCount - Count of original leaves
1059
+ * @param {Buffer[]} proof - Multiproofs given indices
1060
+ * @return {Boolean}
1061
+ * @example
1062
+ *```js
1063
+ *const leaves = tree.getLeaves()
1064
+ *const root = tree.getRoot()
1065
+ *const treeFlat = tree.getLayersFlat()
1066
+ *const leavesCount = leaves.length
1067
+ *const proofIndices = [2, 5, 6]
1068
+ *const proofLeaves = proofIndices.map(i => leaves[i])
1069
+ *const proof = tree.getMultiProof(treeFlat, indices)
1070
+ *const verified = tree.verifyMultiProof(root, proofIndices, proofLeaves, leavesCount, proof)
1071
+ *```
1072
+ */
1073
+ verifyMultiProof(root, proofIndices, proofLeaves, leavesCount, proof) {
1074
+ const isUneven = this.isUnevenTree();
1075
+ if (isUneven) {
1076
+ // TODO: combine these functions and simplify
1077
+ return this.verifyMultiProofForUnevenTree(root, proofIndices, proofLeaves, leavesCount, proof);
1078
+ }
1079
+ const depth = Math.ceil(Math.log2(leavesCount));
1080
+ root = this.bufferify(root);
1081
+ proofLeaves = proofLeaves.map(leaf => this.bufferify(leaf));
1082
+ proof = proof.map(leaf => this.bufferify(leaf));
1083
+ const tree = {};
1084
+ for (const [index, leaf] of this.zip(proofIndices, proofLeaves)) {
1085
+ tree[(Math.pow(2, depth)) + index] = leaf;
1086
+ }
1087
+ for (const [index, proofitem] of this.zip(this.getProofIndices(proofIndices, depth), proof)) {
1088
+ tree[index] = proofitem;
1089
+ }
1090
+ let indexqueue = Object.keys(tree).map(value => +value).sort((a, b) => a - b);
1091
+ indexqueue = indexqueue.slice(0, indexqueue.length - 1);
1092
+ let i = 0;
1093
+ while (i < indexqueue.length) {
1094
+ const index = indexqueue[i];
1095
+ if (index >= 2 && ({}).hasOwnProperty.call(tree, index ^ 1)) {
1096
+ let pair = [tree[index - (index % 2)], tree[index - (index % 2) + 1]];
1097
+ if (this.sortPairs) {
1098
+ pair = pair.sort(buffer_1.Buffer.compare);
1099
+ }
1100
+ const hash = pair[1] ? this.hashFn(this.concatenator(pair)) : pair[0];
1101
+ tree[(index / 2) | 0] = hash;
1102
+ indexqueue.push((index / 2) | 0);
1103
+ }
1104
+ i += 1;
1105
+ }
1106
+ return !proofIndices.length || (({}).hasOwnProperty.call(tree, 1) && tree[1].equals(root));
1107
+ }
1108
+ verifyMultiProofWithFlags(root, leaves, proofs, proofFlag) {
1109
+ root = this.bufferify(root);
1110
+ leaves = leaves.map(this.bufferify);
1111
+ proofs = proofs.map(this.bufferify);
1112
+ const leavesLen = leaves.length;
1113
+ const totalHashes = proofFlag.length;
1114
+ const hashes = [];
1115
+ let leafPos = 0;
1116
+ let hashPos = 0;
1117
+ let proofPos = 0;
1118
+ for (let i = 0; i < totalHashes; i++) {
1119
+ const bufA = proofFlag[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proofs[proofPos++];
1120
+ const bufB = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
1121
+ const buffers = [bufA, bufB].sort(buffer_1.Buffer.compare);
1122
+ hashes[i] = this.hashFn(this.concatenator(buffers));
1123
+ }
1124
+ return buffer_1.Buffer.compare(hashes[totalHashes - 1], root) === 0;
1125
+ }
1126
+ verifyMultiProofForUnevenTree(root, indices, leaves, leavesCount, proof) {
1127
+ root = this.bufferify(root);
1128
+ leaves = leaves.map(leaf => this.bufferify(leaf));
1129
+ proof = proof.map(leaf => this.bufferify(leaf));
1130
+ const computedRoot = this.calculateRootForUnevenTree(indices, leaves, leavesCount, proof);
1131
+ return root.equals(computedRoot);
1132
+ }
1133
+ /**
1134
+ * getDepth
1135
+ * @desc Returns the tree depth (number of layers)
1136
+ * @return {Number}
1137
+ * @example
1138
+ *```js
1139
+ *const depth = tree.getDepth()
1140
+ *```
1141
+ */
1142
+ getDepth() {
1143
+ return this.getLayers().length - 1;
1144
+ }
1145
+ /**
1146
+ * getLayersAsObject
1147
+ * @desc Returns the layers as nested objects instead of an array.
1148
+ * @example
1149
+ *```js
1150
+ *const layersObj = tree.getLayersAsObject()
1151
+ *```
1152
+ */
1153
+ getLayersAsObject() {
1154
+ const layers = this.getLayers().map((layer) => layer.map((value) => this.bufferToHex(value, false)));
1155
+ const objs = [];
1156
+ for (let i = 0; i < layers.length; i++) {
1157
+ const arr = [];
1158
+ for (let j = 0; j < layers[i].length; j++) {
1159
+ const obj = { [layers[i][j]]: null };
1160
+ if (objs.length) {
1161
+ obj[layers[i][j]] = {};
1162
+ const a = objs.shift();
1163
+ const akey = Object.keys(a)[0];
1164
+ obj[layers[i][j]][akey] = a[akey];
1165
+ if (objs.length) {
1166
+ const b = objs.shift();
1167
+ const bkey = Object.keys(b)[0];
1168
+ obj[layers[i][j]][bkey] = b[bkey];
1169
+ }
1170
+ }
1171
+ arr.push(obj);
1172
+ }
1173
+ objs.push(...arr);
1174
+ }
1175
+ return objs[0];
1176
+ }
1177
+ /**
1178
+ * verify
1179
+ * @desc Returns true if the proof path (array of hashes) can connect the target node
1180
+ * to the Merkle root.
1181
+ * @param {Object[]} proof - Array of proof objects that should connect
1182
+ * target node to Merkle root.
1183
+ * @param {Buffer} targetNode - Target node Buffer
1184
+ * @param {Buffer} root - Merkle root Buffer
1185
+ * @param {Function} hashFunction - Hash function for hashing leaves and nodes
1186
+ * @param {Object} options - Additional options
1187
+ * @return {Boolean}
1188
+ * @example
1189
+ *```js
1190
+ *const verified = MerkleTree.verify(proof, leaf, root, sha256, options)
1191
+ *```
1192
+ */
1193
+ static verify(proof, targetNode, root, hashFn = sha256_1.default, options = {}) {
1194
+ const tree = new MerkleTree([], hashFn, options);
1195
+ return tree.verify(proof, targetNode, root);
1196
+ }
1197
+ /**
1198
+ * getMultiProof
1199
+ * @desc Returns the multiproof for given tree indices.
1200
+ * @param {Buffer[]} tree - Tree as a flat array.
1201
+ * @param {Number[]} indices - Tree indices.
1202
+ * @return {Buffer[]} - Multiproofs
1203
+ *
1204
+ *@example
1205
+ * ```js
1206
+ *const flatTree = tree.getLayersFlat()
1207
+ *const indices = [2, 5, 6]
1208
+ *const proof = MerkleTree.getMultiProof(flatTree, indices)
1209
+ *```
1210
+ */
1211
+ static getMultiProof(tree, indices) {
1212
+ const t = new MerkleTree([]);
1213
+ return t.getMultiProof(tree, indices);
1214
+ }
1215
+ /**
1216
+ * resetTree
1217
+ * @desc Resets the tree by clearing the leaves and layers.
1218
+ * @example
1219
+ *```js
1220
+ *tree.resetTree()
1221
+ *```
1222
+ */
1223
+ resetTree() {
1224
+ this.leaves = [];
1225
+ this.layers = [];
1226
+ }
1227
+ /**
1228
+ * getPairNode
1229
+ * @desc Returns the node at the index for given layer.
1230
+ * @param {Buffer[]} layer - Tree layer
1231
+ * @param {Number} index - Index at layer.
1232
+ * @return {Buffer} - Node
1233
+ *
1234
+ *@example
1235
+ * ```js
1236
+ *const node = tree.getPairNode(layer, index)
1237
+ *```
1238
+ */
1239
+ getPairNode(layer, idx) {
1240
+ const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1;
1241
+ if (pairIdx < layer.length) {
1242
+ return layer[pairIdx];
1243
+ }
1244
+ else {
1245
+ return null;
1246
+ }
1247
+ }
1248
+ /**
1249
+ * toTreeString
1250
+ * @desc Returns a visual representation of the merkle tree as a string.
1251
+ * @return {String}
1252
+ * @example
1253
+ *```js
1254
+ *console.log(tree.toTreeString())
1255
+ *```
1256
+ */
1257
+ toTreeString() {
1258
+ const obj = this.getLayersAsObject();
1259
+ return treeify_1.default.asTree(obj, true);
1260
+ }
1261
+ /**
1262
+ * toString
1263
+ * @desc Returns a visual representation of the merkle tree as a string.
1264
+ * @example
1265
+ *```js
1266
+ *console.log(tree.toString())
1267
+ *```
1268
+ */
1269
+ toString() {
1270
+ return this.toTreeString();
1271
+ }
1272
+ isUnevenTree(treeLayers) {
1273
+ const depth = (treeLayers === null || treeLayers === void 0 ? void 0 : treeLayers.length) || this.getDepth();
1274
+ return !this.isPowOf2(depth);
1275
+ }
1276
+ isPowOf2(v) {
1277
+ return v && !(v & (v - 1));
1278
+ }
1279
+ isValidLeafIndex(idx) {
1280
+ return idx >= 0 && idx < this.getLeafCount();
1281
+ }
1282
+ calculateRootForUnevenTree(leafIndices, leafHashes, totalLeavesCount, proofHashes) {
1283
+ const leafTuples = this.zip(leafIndices, leafHashes).sort(([indexA], [indexB]) => indexA - indexB);
1284
+ const leafTupleIndices = leafTuples.map(([index]) => index);
1285
+ const proofIndices = this.getProofIndicesForUnevenTree(leafTupleIndices, totalLeavesCount);
1286
+ let nextSliceStart = 0;
1287
+ const proofTuplesByLayers = [];
1288
+ for (let i = 0; i < proofIndices.length; i++) {
1289
+ const indices = proofIndices[i];
1290
+ const sliceStart = nextSliceStart;
1291
+ nextSliceStart += indices.length;
1292
+ proofTuplesByLayers[i] = this.zip(indices, proofHashes.slice(sliceStart, nextSliceStart));
1293
+ }
1294
+ const tree = [leafTuples];
1295
+ for (let layerIndex = 0; layerIndex < proofTuplesByLayers.length; layerIndex++) {
1296
+ const currentLayer = proofTuplesByLayers[layerIndex].concat(tree[layerIndex]).sort(([indexA], [indexB]) => indexA - indexB)
1297
+ .map(([, hash]) => hash);
1298
+ const s = tree[layerIndex].map(([layerIndex]) => layerIndex);
1299
+ const parentIndices = [...new Set(s.map((index) => {
1300
+ if (index % 2 === 0) {
1301
+ return index / 2;
1302
+ }
1303
+ if (index % 2 === 0) {
1304
+ return (index + 1) / 2;
1305
+ }
1306
+ return (index - 1) / 2;
1307
+ }))];
1308
+ const parentLayer = [];
1309
+ for (let i = 0; i < parentIndices.length; i++) {
1310
+ const parentNodeTreeIndex = parentIndices[i];
1311
+ const bufA = currentLayer[i * 2];
1312
+ const bufB = currentLayer[i * 2 + 1];
1313
+ const hash = bufB ? this.hashFn(this.concatenator([bufA, bufB])) : bufA;
1314
+ parentLayer.push([parentNodeTreeIndex, hash]);
1315
+ }
1316
+ tree.push(parentLayer);
1317
+ }
1318
+ return tree[tree.length - 1][0][1];
1319
+ }
1320
+ }
1321
+ exports.MerkleTree = MerkleTree;
1322
+ if (typeof window !== 'undefined') {
1323
+ ;
1324
+ window.MerkleTree = MerkleTree;
1325
+ }
1326
+ exports.default = MerkleTree;