@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.
- package/LICENSE +21 -0
- package/README.md +232 -0
- package/dist/Base.d.ts +212 -0
- package/dist/Base.js +379 -0
- package/dist/IncrementalMerkleTree.d.ts +36 -0
- package/dist/IncrementalMerkleTree.js +252 -0
- package/dist/MerkleMountainRange.d.ts +95 -0
- package/dist/MerkleMountainRange.js +436 -0
- package/dist/MerkleRadixTree.d.ts +33 -0
- package/dist/MerkleRadixTree.js +152 -0
- package/dist/MerkleSumTree.d.ts +37 -0
- package/dist/MerkleSumTree.js +138 -0
- package/dist/MerkleTree.d.ts +570 -0
- package/dist/MerkleTree.js +1326 -0
- package/dist/UnifiedBinaryTree.d.ts +454 -0
- package/dist/UnifiedBinaryTree.js +829 -0
- package/dist/functional.d.ts +255 -0
- package/dist/functional.js +360 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +30 -0
- package/merkletree.js +1 -0
- package/package.json +155 -0
package/dist/Base.js
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
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.Base = void 0;
|
|
7
|
+
const buffer_1 = require("buffer");
|
|
8
|
+
const crypto_js_1 = __importDefault(require("crypto-js"));
|
|
9
|
+
class Base {
|
|
10
|
+
/**
|
|
11
|
+
* print
|
|
12
|
+
* @desc Prints out a visual representation of the merkle tree.
|
|
13
|
+
* @example
|
|
14
|
+
*```js
|
|
15
|
+
*tree.print()
|
|
16
|
+
*```
|
|
17
|
+
*/
|
|
18
|
+
print() {
|
|
19
|
+
Base.print(this);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* bufferIndexOf
|
|
23
|
+
* @desc Returns the first index of which given buffer is found in array.
|
|
24
|
+
* @param {Buffer[]} haystack - Array of buffers.
|
|
25
|
+
* @param {Buffer} needle - Buffer to find.
|
|
26
|
+
* @return {Number} - Index number
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```js
|
|
30
|
+
*const index = tree.bufferIndexOf(haystack, needle)
|
|
31
|
+
*```
|
|
32
|
+
*/
|
|
33
|
+
bufferIndexOf(array, element, isSorted = false) {
|
|
34
|
+
if (isSorted) {
|
|
35
|
+
return this.binarySearch(array, element, buffer_1.Buffer.compare);
|
|
36
|
+
}
|
|
37
|
+
const eqChecker = (buffer1, buffer2) => buffer1.equals(buffer2);
|
|
38
|
+
return this.linearSearch(array, element, eqChecker);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* binarySearch
|
|
42
|
+
* @desc Returns the first index of which given item is found in array using binary search.
|
|
43
|
+
* @param {Buffer[]} array - Array of items.
|
|
44
|
+
* @param {Buffer} element - Item to find.
|
|
45
|
+
* @param {Function} compareFunction
|
|
46
|
+
* @return {Number} - Index number
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```js
|
|
50
|
+
*const index = MerkleTree.binarySearch(array, element, Buffer.compare)
|
|
51
|
+
*```
|
|
52
|
+
*/
|
|
53
|
+
static binarySearch(array, element, compareFunction) {
|
|
54
|
+
let start = 0;
|
|
55
|
+
let end = array.length - 1;
|
|
56
|
+
// Iterate while start not meets end
|
|
57
|
+
while (start <= end) {
|
|
58
|
+
// Find the mid index
|
|
59
|
+
const mid = Math.floor((start + end) / 2);
|
|
60
|
+
// Check if the mid value is greater than, equal to, or less than search element.
|
|
61
|
+
const ordering = compareFunction(array[mid], element);
|
|
62
|
+
// If element is present at mid, start iterating for searching first appearance.
|
|
63
|
+
if (ordering === 0) {
|
|
64
|
+
// Linear reverse iteration until the first matching item index is found.
|
|
65
|
+
for (let i = mid - 1; i >= 0; i--) {
|
|
66
|
+
if (compareFunction(array[i], element) === 0)
|
|
67
|
+
continue;
|
|
68
|
+
return i + 1;
|
|
69
|
+
}
|
|
70
|
+
return 0;
|
|
71
|
+
} /* Else look in left or right half accordingly */
|
|
72
|
+
else if (ordering < 0) {
|
|
73
|
+
start = mid + 1;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
end = mid - 1;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return -1;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* binarySearch
|
|
83
|
+
* @desc Returns the first index of which given item is found in array using binary search.
|
|
84
|
+
* @param {Buffer[]} array - Array of items.
|
|
85
|
+
* @param {Buffer} element - Item to find.
|
|
86
|
+
* @param {Function} compareFunction
|
|
87
|
+
* @return {Number} - Index number
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```js
|
|
91
|
+
*const index = tree.binarySearch(array, element, Buffer.compare)
|
|
92
|
+
*```
|
|
93
|
+
*/
|
|
94
|
+
binarySearch(array, element, compareFunction) {
|
|
95
|
+
return Base.binarySearch(array, element, compareFunction);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* linearSearch
|
|
99
|
+
* @desc Returns the first index of which given item is found in array using linear search.
|
|
100
|
+
* @param {Buffer[]} array - Array of items.
|
|
101
|
+
* @param {Buffer} element - Item to find.
|
|
102
|
+
* @param {Function} eqChecker
|
|
103
|
+
* @return {Number} - Index number
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```js
|
|
107
|
+
*const index = MerkleTree.linearSearch(array, element, (a, b) => a === b)
|
|
108
|
+
*```
|
|
109
|
+
*/
|
|
110
|
+
static linearSearch(array, element, eqChecker) {
|
|
111
|
+
for (let i = 0; i < array.length; i++) {
|
|
112
|
+
if (eqChecker(array[i], element)) {
|
|
113
|
+
return i;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return -1;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* linearSearch
|
|
120
|
+
* @desc Returns the first index of which given item is found in array using linear search.
|
|
121
|
+
* @param {Buffer[]} array - Array of items.
|
|
122
|
+
* @param {Buffer} element - Item to find.
|
|
123
|
+
* @param {Function} eqChecker
|
|
124
|
+
* @return {Number} - Index number
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```js
|
|
128
|
+
*const index = tree.linearSearch(array, element, (a, b) => a === b)
|
|
129
|
+
*```
|
|
130
|
+
*/
|
|
131
|
+
linearSearch(array, element, eqChecker) {
|
|
132
|
+
return Base.linearSearch(array, element, eqChecker);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* bufferify
|
|
136
|
+
* @desc Returns a buffer type for the given value.
|
|
137
|
+
* @param {String|Number|Object|Buffer|ArrayBuffer} value
|
|
138
|
+
* @return {Buffer}
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```js
|
|
142
|
+
*const buf = MerkleTree.bufferify('0x1234')
|
|
143
|
+
*```
|
|
144
|
+
*/
|
|
145
|
+
static bufferify(value) {
|
|
146
|
+
if (!buffer_1.Buffer.isBuffer(value)) {
|
|
147
|
+
// crypto-js support
|
|
148
|
+
if (typeof value === 'object' && value.words) {
|
|
149
|
+
return buffer_1.Buffer.from(value.toString(crypto_js_1.default.enc.Hex), 'hex');
|
|
150
|
+
}
|
|
151
|
+
else if (Base.isHexString(value)) {
|
|
152
|
+
const hexString = value.replace('0x', '');
|
|
153
|
+
const paddedHexString = hexString.length % 2 ? '0' + hexString : hexString;
|
|
154
|
+
return buffer_1.Buffer.from(paddedHexString, 'hex');
|
|
155
|
+
}
|
|
156
|
+
else if (typeof value === 'string') {
|
|
157
|
+
return buffer_1.Buffer.from(value);
|
|
158
|
+
}
|
|
159
|
+
else if (typeof value === 'bigint') {
|
|
160
|
+
const hexString = value.toString(16).length % 2 ? '0' + value.toString(16) : value.toString(16);
|
|
161
|
+
return buffer_1.Buffer.from(hexString, 'hex');
|
|
162
|
+
}
|
|
163
|
+
else if (value instanceof Uint8Array) {
|
|
164
|
+
return buffer_1.Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
165
|
+
}
|
|
166
|
+
else if (typeof value === 'number') {
|
|
167
|
+
let s = value.toString();
|
|
168
|
+
if (s.length % 2) {
|
|
169
|
+
s = `0${s}`;
|
|
170
|
+
}
|
|
171
|
+
return buffer_1.Buffer.from(s, 'hex');
|
|
172
|
+
}
|
|
173
|
+
else if (ArrayBuffer.isView(value)) {
|
|
174
|
+
return buffer_1.Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* bufferifyFn
|
|
181
|
+
* @desc Returns a function that will bufferify the return value.
|
|
182
|
+
* @param {Function}
|
|
183
|
+
* @return {Function}
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```js
|
|
187
|
+
*const fn = MerkleTree.bufferifyFn((value) => sha256(value))
|
|
188
|
+
*```
|
|
189
|
+
*/
|
|
190
|
+
static bufferifyFn(f) {
|
|
191
|
+
if (typeof f !== 'function') {
|
|
192
|
+
throw new Error(`bufferifyFn expects a function, received: ${typeof f}`);
|
|
193
|
+
}
|
|
194
|
+
return (value) => {
|
|
195
|
+
const v = f(value);
|
|
196
|
+
if (buffer_1.Buffer.isBuffer(v)) {
|
|
197
|
+
return v;
|
|
198
|
+
}
|
|
199
|
+
if (Base.isHexString(v)) {
|
|
200
|
+
const hexString = v.replace('0x', '');
|
|
201
|
+
const paddedHexString = hexString.length % 2 ? '0' + hexString : hexString;
|
|
202
|
+
return buffer_1.Buffer.from(paddedHexString, 'hex');
|
|
203
|
+
}
|
|
204
|
+
if (typeof v === 'string') {
|
|
205
|
+
return buffer_1.Buffer.from(v);
|
|
206
|
+
}
|
|
207
|
+
if (typeof v === 'bigint') {
|
|
208
|
+
const hexString = v.toString(16).length % 2 ? '0' + v.toString(16) : v.toString(16);
|
|
209
|
+
return buffer_1.Buffer.from(hexString, 'hex');
|
|
210
|
+
}
|
|
211
|
+
if (ArrayBuffer.isView(v)) {
|
|
212
|
+
return buffer_1.Buffer.from(v.buffer, v.byteOffset, v.byteLength);
|
|
213
|
+
}
|
|
214
|
+
// crypto-js support
|
|
215
|
+
return buffer_1.Buffer.from(f(crypto_js_1.default.enc.Hex.parse(value.toString('hex'))).toString(crypto_js_1.default.enc.Hex), 'hex');
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
bigNumberify(value) {
|
|
219
|
+
return Base.bigNumberify(value);
|
|
220
|
+
}
|
|
221
|
+
static bigNumberify(value) {
|
|
222
|
+
if (typeof value === 'bigint') {
|
|
223
|
+
return value;
|
|
224
|
+
}
|
|
225
|
+
if (typeof value === 'string') {
|
|
226
|
+
if (value.startsWith('0x') && Base.isHexString(value)) {
|
|
227
|
+
// Remove '0x' and ensure even-length hex string
|
|
228
|
+
const hexString = value.replace('0x', '');
|
|
229
|
+
const paddedHexString = hexString.length % 2 ? '0' + hexString : (hexString || '0');
|
|
230
|
+
return BigInt('0x' + paddedHexString);
|
|
231
|
+
}
|
|
232
|
+
return BigInt(value);
|
|
233
|
+
}
|
|
234
|
+
if (buffer_1.Buffer.isBuffer(value)) {
|
|
235
|
+
// Convert buffer to hex string and ensure even-length hex string
|
|
236
|
+
const hexString = value.toString('hex');
|
|
237
|
+
const paddedHexString = hexString.length % 2 ? '0' + hexString : (hexString || '0');
|
|
238
|
+
return BigInt('0x' + paddedHexString);
|
|
239
|
+
}
|
|
240
|
+
if (value instanceof Uint8Array) {
|
|
241
|
+
// Convert Uint8Array to hex string and ensure even-length hex string
|
|
242
|
+
const hexString = buffer_1.Buffer.from(value).toString('hex');
|
|
243
|
+
const paddedHexString = hexString.length % 2 ? '0' + hexString : (hexString || '0');
|
|
244
|
+
return BigInt('0x' + paddedHexString);
|
|
245
|
+
}
|
|
246
|
+
if (typeof value === 'number') {
|
|
247
|
+
return BigInt(value);
|
|
248
|
+
}
|
|
249
|
+
throw new Error('cannot bigNumberify');
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* isHexString
|
|
253
|
+
* @desc Returns true if value is a hex string.
|
|
254
|
+
* @param {String} value
|
|
255
|
+
* @return {Boolean}
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```js
|
|
259
|
+
*console.log(MerkleTree.isHexString('0x1234'))
|
|
260
|
+
*```
|
|
261
|
+
*/
|
|
262
|
+
static isHexString(v) {
|
|
263
|
+
return typeof v === 'string' && /^(0x)?[0-9A-Fa-f]*$/.test(v);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* print
|
|
267
|
+
* @desc Prints out a visual representation of the given merkle tree.
|
|
268
|
+
* @param {Object} tree - Merkle tree instance.
|
|
269
|
+
* @return {String}
|
|
270
|
+
* @example
|
|
271
|
+
*```js
|
|
272
|
+
*MerkleTree.print(tree)
|
|
273
|
+
*```
|
|
274
|
+
*/
|
|
275
|
+
static print(tree) {
|
|
276
|
+
console.log(tree.toString());
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* bufferToHex
|
|
280
|
+
* @desc Returns a hex string with 0x prefix for given buffer.
|
|
281
|
+
* @param {Buffer} value
|
|
282
|
+
* @return {String}
|
|
283
|
+
* @example
|
|
284
|
+
*```js
|
|
285
|
+
*const hexStr = tree.bufferToHex(Buffer.from('A'))
|
|
286
|
+
*```
|
|
287
|
+
*/
|
|
288
|
+
bufferToHex(value, withPrefix = true) {
|
|
289
|
+
return Base.bufferToHex(value, withPrefix);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* bufferToHex
|
|
293
|
+
* @desc Returns a hex string with 0x prefix for given buffer.
|
|
294
|
+
* @param {Buffer} value
|
|
295
|
+
* @return {String}
|
|
296
|
+
* @example
|
|
297
|
+
*```js
|
|
298
|
+
*const hexStr = MerkleTree.bufferToHex(Buffer.from('A'))
|
|
299
|
+
*```
|
|
300
|
+
*/
|
|
301
|
+
static bufferToHex(value, withPrefix = true) {
|
|
302
|
+
return `${withPrefix ? '0x' : ''}${(value || buffer_1.Buffer.alloc(0)).toString('hex')}`;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* bufferify
|
|
306
|
+
* @desc Returns a buffer type for the given value.
|
|
307
|
+
* @param {String|Number|Object|Buffer} value
|
|
308
|
+
* @return {Buffer}
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```js
|
|
312
|
+
*const buf = tree.bufferify('0x1234')
|
|
313
|
+
*```
|
|
314
|
+
*/
|
|
315
|
+
bufferify(value) {
|
|
316
|
+
return Base.bufferify(value);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* bufferifyFn
|
|
320
|
+
* @desc Returns a function that will bufferify the return value.
|
|
321
|
+
* @param {Function}
|
|
322
|
+
* @return {Function}
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```js
|
|
326
|
+
*const fn = tree.bufferifyFn((value) => sha256(value))
|
|
327
|
+
*```
|
|
328
|
+
*/
|
|
329
|
+
bufferifyFn(f) {
|
|
330
|
+
return Base.bufferifyFn(f);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* isHexString
|
|
334
|
+
* @desc Returns true if value is a hex string.
|
|
335
|
+
* @param {String} value
|
|
336
|
+
* @return {Boolean}
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```js
|
|
340
|
+
*console.log(MerkleTree.isHexString('0x1234'))
|
|
341
|
+
*```
|
|
342
|
+
*/
|
|
343
|
+
isHexString(value) {
|
|
344
|
+
return Base.isHexString(value);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* log2
|
|
348
|
+
* @desc Returns the log2 of number.
|
|
349
|
+
* @param {Number} value
|
|
350
|
+
* @return {Number}
|
|
351
|
+
*/
|
|
352
|
+
log2(n) {
|
|
353
|
+
return n === 1 ? 0 : 1 + this.log2((n / 2) | 0);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* zip
|
|
357
|
+
* @desc Returns true if value is a hex string.
|
|
358
|
+
* @param {String[]|Number[]|Buffer[]} a - first array
|
|
359
|
+
* @param {String[]|Number[]|Buffer[]} b - second array
|
|
360
|
+
* @return {String[][]|Number[][]|Buffer[][]}
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```js
|
|
364
|
+
*const zipped = tree.zip(['a', 'b'],['A', 'B'])
|
|
365
|
+
*console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ]
|
|
366
|
+
*```
|
|
367
|
+
*/
|
|
368
|
+
zip(a, b) {
|
|
369
|
+
return a.map((e, i) => [e, b[i]]);
|
|
370
|
+
}
|
|
371
|
+
static hexZeroPad(hexStr, length) {
|
|
372
|
+
return '0x' + hexStr.replace('0x', '').padStart(length, '0');
|
|
373
|
+
}
|
|
374
|
+
bufferArrayIncludes(bufferArray, targetBuffer) {
|
|
375
|
+
return bufferArray.some(buffer => buffer.equals(targetBuffer !== null && targetBuffer !== void 0 ? targetBuffer : buffer_1.Buffer.alloc(0)));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
exports.Base = Base;
|
|
379
|
+
exports.default = Base;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import Base from './Base';
|
|
2
|
+
export interface Options {
|
|
3
|
+
depth?: number;
|
|
4
|
+
arity?: number;
|
|
5
|
+
zeroValue?: any;
|
|
6
|
+
}
|
|
7
|
+
export declare class IncrementalMerkleTree extends Base {
|
|
8
|
+
private depth?;
|
|
9
|
+
private arity?;
|
|
10
|
+
private zeroes?;
|
|
11
|
+
private root?;
|
|
12
|
+
private nodes?;
|
|
13
|
+
private hashFn;
|
|
14
|
+
private zeroValue;
|
|
15
|
+
constructor(hashFn: any, options: Options);
|
|
16
|
+
getRoot(): any;
|
|
17
|
+
getHexRoot(): string;
|
|
18
|
+
insert(leaf: any): void;
|
|
19
|
+
delete(index: number): void;
|
|
20
|
+
update(index: number, newLeaf: any): void;
|
|
21
|
+
getDepth(): number;
|
|
22
|
+
getArity(): number;
|
|
23
|
+
getMaxLeaves(): number;
|
|
24
|
+
indexOf(leaf: any): number;
|
|
25
|
+
getLeaves(): bigint[];
|
|
26
|
+
copyList(list: any[]): bigint[];
|
|
27
|
+
getLayers(): any[];
|
|
28
|
+
getHexLayers(): string[];
|
|
29
|
+
getLayersAsObject(): any;
|
|
30
|
+
computeRoot(): any;
|
|
31
|
+
getProof(index: number): any;
|
|
32
|
+
verify(proof: any): boolean;
|
|
33
|
+
toString(): string;
|
|
34
|
+
protected toTreeString(): string;
|
|
35
|
+
}
|
|
36
|
+
export default IncrementalMerkleTree;
|
|
@@ -0,0 +1,252 @@
|
|
|
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.IncrementalMerkleTree = void 0;
|
|
7
|
+
const Base_1 = __importDefault(require("./Base"));
|
|
8
|
+
const treeify_1 = __importDefault(require("treeify"));
|
|
9
|
+
class IncrementalMerkleTree extends Base_1.default {
|
|
10
|
+
constructor(hashFn, options) {
|
|
11
|
+
super();
|
|
12
|
+
this.hashFn = hashFn;
|
|
13
|
+
if (options.depth) {
|
|
14
|
+
this.depth = options.depth;
|
|
15
|
+
}
|
|
16
|
+
if (options.arity) {
|
|
17
|
+
this.arity = options.arity;
|
|
18
|
+
}
|
|
19
|
+
if (this.depth < 1) {
|
|
20
|
+
throw new Error('depth must be greater than 0');
|
|
21
|
+
}
|
|
22
|
+
if (this.arity < 1) {
|
|
23
|
+
throw new Error('arity must be greater than 0');
|
|
24
|
+
}
|
|
25
|
+
const nodes = [];
|
|
26
|
+
let zeroValue = options.zeroValue;
|
|
27
|
+
this.zeroValue = zeroValue;
|
|
28
|
+
this.zeroes = [];
|
|
29
|
+
if (this.depth) {
|
|
30
|
+
for (let i = 0; i < this.depth; i++) {
|
|
31
|
+
this.zeroes.push(zeroValue);
|
|
32
|
+
nodes[i] = [];
|
|
33
|
+
zeroValue = this.hashFn(Array(this.arity).fill(zeroValue));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
this.nodes = nodes;
|
|
37
|
+
this.root = zeroValue;
|
|
38
|
+
}
|
|
39
|
+
getRoot() {
|
|
40
|
+
return this.root;
|
|
41
|
+
}
|
|
42
|
+
getHexRoot() {
|
|
43
|
+
return this.bufferToHex(this.bufferify(this.getRoot()));
|
|
44
|
+
}
|
|
45
|
+
insert(leaf) {
|
|
46
|
+
if (this.depth && this.arity) {
|
|
47
|
+
if (this.nodes[0].length >= this.getMaxLeaves()) {
|
|
48
|
+
throw new Error('tree is full');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
let node = leaf;
|
|
52
|
+
let index = this.nodes[0].length;
|
|
53
|
+
for (let level = 0; level < this.depth; level += 1) {
|
|
54
|
+
const position = index % this.arity;
|
|
55
|
+
const levelStartIndex = index - position;
|
|
56
|
+
const levelEndIndex = levelStartIndex + this.arity;
|
|
57
|
+
const children = [];
|
|
58
|
+
this.nodes[level][index] = node;
|
|
59
|
+
for (let i = levelStartIndex; i < levelEndIndex; i += 1) {
|
|
60
|
+
if (i < this.nodes[level].length) {
|
|
61
|
+
children.push(this.nodes[level][i]);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
children.push(this.zeroes[level]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
node = this.hashFn(children);
|
|
68
|
+
index = Math.floor(index / this.arity);
|
|
69
|
+
}
|
|
70
|
+
this.root = node;
|
|
71
|
+
}
|
|
72
|
+
delete(index) {
|
|
73
|
+
this.update(index, this.zeroValue);
|
|
74
|
+
}
|
|
75
|
+
update(index, newLeaf) {
|
|
76
|
+
if (index < 0 || index >= this.nodes[0].length) {
|
|
77
|
+
throw new Error('out of bounds');
|
|
78
|
+
}
|
|
79
|
+
let node = newLeaf;
|
|
80
|
+
for (let level = 0; level < this.depth; level += 1) {
|
|
81
|
+
const position = index % this.arity;
|
|
82
|
+
const levelStartIndex = index - position;
|
|
83
|
+
const levelEndIndex = levelStartIndex + this.arity;
|
|
84
|
+
const children = [];
|
|
85
|
+
this.nodes[level][index] = node;
|
|
86
|
+
for (let i = levelStartIndex; i < levelEndIndex; i += 1) {
|
|
87
|
+
if (i < this.nodes[level].length) {
|
|
88
|
+
children.push(this.nodes[level][i]);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
children.push(this.zeroes[level]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
node = this.hashFn(children);
|
|
95
|
+
index = Math.floor(index / this.arity);
|
|
96
|
+
}
|
|
97
|
+
this.root = node;
|
|
98
|
+
}
|
|
99
|
+
getDepth() {
|
|
100
|
+
return this.depth;
|
|
101
|
+
}
|
|
102
|
+
getArity() {
|
|
103
|
+
return this.arity;
|
|
104
|
+
}
|
|
105
|
+
getMaxLeaves() {
|
|
106
|
+
return Math.pow(this.depth, this.arity);
|
|
107
|
+
}
|
|
108
|
+
indexOf(leaf) {
|
|
109
|
+
return this.nodes[0].indexOf(leaf);
|
|
110
|
+
}
|
|
111
|
+
getLeaves() {
|
|
112
|
+
const leaves = this.copyList(this.nodes[0]);
|
|
113
|
+
const index = this.nodes[0].length;
|
|
114
|
+
for (let i = index; i < this.getMaxLeaves(); i++) {
|
|
115
|
+
leaves[i] = this.zeroValue;
|
|
116
|
+
}
|
|
117
|
+
return leaves;
|
|
118
|
+
}
|
|
119
|
+
copyList(list) {
|
|
120
|
+
return list.map((x) => BigInt(x));
|
|
121
|
+
}
|
|
122
|
+
getLayers() {
|
|
123
|
+
const layers = [];
|
|
124
|
+
for (const list of this.nodes) {
|
|
125
|
+
layers.push(this.copyList(list));
|
|
126
|
+
}
|
|
127
|
+
if (layers[0].length < this.getMaxLeaves()) {
|
|
128
|
+
let index = layers[0].length;
|
|
129
|
+
for (let i = index; i < this.getMaxLeaves(); i++) {
|
|
130
|
+
layers[0][i] = this.zeroValue;
|
|
131
|
+
}
|
|
132
|
+
for (let level = 0; level < this.depth; level++) {
|
|
133
|
+
const position = index % this.arity;
|
|
134
|
+
const levelStartIndex = index - position;
|
|
135
|
+
const levelEndIndex = levelStartIndex + this.arity;
|
|
136
|
+
for (let i = levelStartIndex; i < levelEndIndex; i++) {
|
|
137
|
+
if (i >= layers[level].length) {
|
|
138
|
+
layers[level][i] = this.zeroes[level];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
index = Math.floor(index / this.arity);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
layers.push([this.root]);
|
|
145
|
+
return layers;
|
|
146
|
+
}
|
|
147
|
+
getHexLayers() {
|
|
148
|
+
return this.getLayers().reduce((acc, item) => {
|
|
149
|
+
if (Array.isArray(item)) {
|
|
150
|
+
acc.push(item.map(layer => this.bufferToHex(this.bufferify(layer))));
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
acc.push(item);
|
|
154
|
+
}
|
|
155
|
+
return acc;
|
|
156
|
+
}, []);
|
|
157
|
+
}
|
|
158
|
+
getLayersAsObject() {
|
|
159
|
+
const layers = this.getLayers().map((layer) => layer.map((value) => this.bufferToHex(this.bufferify(value), false)));
|
|
160
|
+
const objs = [];
|
|
161
|
+
for (let i = 0; i < layers.length; i++) {
|
|
162
|
+
const arr = [];
|
|
163
|
+
for (let j = 0; j < layers[i].length; j++) {
|
|
164
|
+
const obj = { [layers[i][j]]: null };
|
|
165
|
+
if (objs.length) {
|
|
166
|
+
obj[layers[i][j]] = {};
|
|
167
|
+
const a = objs.shift();
|
|
168
|
+
const akey = Object.keys(a)[0];
|
|
169
|
+
obj[layers[i][j]][akey] = a[akey];
|
|
170
|
+
if (objs.length) {
|
|
171
|
+
const b = objs.shift();
|
|
172
|
+
const bkey = Object.keys(b)[0];
|
|
173
|
+
obj[layers[i][j]][bkey] = b[bkey];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
arr.push(obj);
|
|
177
|
+
}
|
|
178
|
+
objs.push(...arr);
|
|
179
|
+
}
|
|
180
|
+
return objs[0];
|
|
181
|
+
}
|
|
182
|
+
computeRoot() {
|
|
183
|
+
let node;
|
|
184
|
+
let index = this.nodes[0].length;
|
|
185
|
+
for (let level = 0; level < this.depth; level += 1) {
|
|
186
|
+
const position = index % this.arity;
|
|
187
|
+
const levelStartIndex = index - position;
|
|
188
|
+
const levelEndIndex = levelStartIndex + this.arity;
|
|
189
|
+
const children = [];
|
|
190
|
+
for (let i = levelStartIndex; i < levelEndIndex; i += 1) {
|
|
191
|
+
if (i < this.nodes[level].length) {
|
|
192
|
+
children.push(this.nodes[level][i]);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
children.push(this.zeroes[level]);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
node = this.hashFn(children);
|
|
199
|
+
index = Math.floor(index / this.arity);
|
|
200
|
+
}
|
|
201
|
+
return node;
|
|
202
|
+
}
|
|
203
|
+
getProof(index) {
|
|
204
|
+
if (index < 0 || index >= this.nodes[0].length) {
|
|
205
|
+
throw new Error('The leaf does not exist in this tree');
|
|
206
|
+
}
|
|
207
|
+
const siblings = [];
|
|
208
|
+
const pathIndices = [];
|
|
209
|
+
const leafIndex = index;
|
|
210
|
+
for (let level = 0; level < this.depth; level += 1) {
|
|
211
|
+
const position = index % this.arity;
|
|
212
|
+
const levelStartIndex = index - position;
|
|
213
|
+
const levelEndIndex = levelStartIndex + this.arity;
|
|
214
|
+
pathIndices[level] = position;
|
|
215
|
+
siblings[level] = [];
|
|
216
|
+
for (let i = levelStartIndex; i < levelEndIndex; i += 1) {
|
|
217
|
+
if (i !== index) {
|
|
218
|
+
if (i < this.nodes[level].length) {
|
|
219
|
+
siblings[level].push(this.nodes[level][i]);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
siblings[level].push(this.zeroes[level]);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
index = Math.floor(index / this.arity);
|
|
227
|
+
}
|
|
228
|
+
return { root: this.root, leaf: this.nodes[0][leafIndex], pathIndices, siblings };
|
|
229
|
+
}
|
|
230
|
+
verify(proof) {
|
|
231
|
+
let node = proof.leaf;
|
|
232
|
+
for (let i = 0; i < proof.siblings.length; i += 1) {
|
|
233
|
+
const children = proof.siblings[i].slice();
|
|
234
|
+
children.splice(proof.pathIndices[i], 0, node);
|
|
235
|
+
node = this.hashFn(children);
|
|
236
|
+
}
|
|
237
|
+
return proof.root === node;
|
|
238
|
+
}
|
|
239
|
+
toString() {
|
|
240
|
+
return this.toTreeString();
|
|
241
|
+
}
|
|
242
|
+
toTreeString() {
|
|
243
|
+
const obj = this.getLayersAsObject();
|
|
244
|
+
return treeify_1.default.asTree(obj, true);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
exports.IncrementalMerkleTree = IncrementalMerkleTree;
|
|
248
|
+
if (typeof window !== 'undefined') {
|
|
249
|
+
;
|
|
250
|
+
window.IncrementalMerkleTree = IncrementalMerkleTree;
|
|
251
|
+
}
|
|
252
|
+
exports.default = IncrementalMerkleTree;
|