@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,95 @@
1
+ /// <reference types="node" />
2
+ import Base from './Base';
3
+ /**
4
+ * @desc The index of this MMR implementation starts from 1 not 0.
5
+ */
6
+ export declare class MerkleMountainRange extends Base {
7
+ root: Buffer;
8
+ size: number;
9
+ width: number;
10
+ hashes: any;
11
+ data: any;
12
+ hashLeafFn: any;
13
+ peakBaggingFn: any;
14
+ hashBranchFn: any;
15
+ private hashFn;
16
+ constructor(hashFn?: any, leaves?: any[], hashLeafFn?: any, peakBaggingFn?: any, hashBranchFn?: any);
17
+ /**
18
+ * @desc This only stores the hashed value of the leaf.
19
+ * If you need to retrieve the detail data later, use a map to store them.
20
+ */
21
+ append(data: Buffer | string): void;
22
+ /**
23
+ * @desc It returns the hash of a leaf node with hash(M | DATA )
24
+ * M is the index of the node.
25
+ */
26
+ hashLeaf(index: number, dataHash: Buffer | string): any;
27
+ /**
28
+ * @desc It returns the hash a parent node with hash(M | Left child | Right child)
29
+ * M is the index of the node.
30
+ */
31
+ hashBranch(index: number, left: any, right: any): any;
32
+ getPeaks(): any[];
33
+ getLeafIndex(width: number): number;
34
+ /**
35
+ * @desc It returns all peaks of the smallest merkle mountain range tree which includes
36
+ * the given index(size).
37
+ */
38
+ getPeakIndexes(width: number): number[];
39
+ numOfPeaks(width: number): number;
40
+ peakBagging(width: number, peaks: any[]): any;
41
+ /**
42
+ * @desc It returns the size of the tree.
43
+ */
44
+ getSize(width: number): number;
45
+ /**
46
+ * @desc It returns the root value of the tree.
47
+ */
48
+ getRoot(): any;
49
+ getHexRoot(): any;
50
+ /**
51
+ * @dev It returns the hash value of a node for the given position. Note that the index starts from 1.
52
+ */
53
+ getNode(index: number): any;
54
+ /**
55
+ * @desc It returns the height of the highest peak.
56
+ */
57
+ mountainHeight(size: number): number;
58
+ /**
59
+ * @desc It returns the height of the index.
60
+ */
61
+ heightAt(index: number): number;
62
+ /**
63
+ * @desc It returns whether the index is the leaf node or not
64
+ */
65
+ isLeaf(index: number): boolean;
66
+ /**
67
+ * @desc It returns the children when it is a parent node.
68
+ */
69
+ getChildren(index: number): number[];
70
+ /**
71
+ * @desc It returns a merkle proof for a leaf. Note that the index starts from 1.
72
+ */
73
+ getMerkleProof(index: number): {
74
+ root: Buffer;
75
+ width: number;
76
+ peakBagging: any[];
77
+ siblings: any[];
78
+ };
79
+ /**
80
+ * @desc It returns true when the given params verifies that the given value exists in the tree or reverts the transaction.
81
+ */
82
+ verify(root: any, width: number, index: number, value: Buffer | string, peaks: any[], siblings: any[]): boolean;
83
+ peaksToPeakMap(width: number, peaks: any[]): {};
84
+ peakMapToPeaks(width: number, peakMap: any): any[];
85
+ peakUpdate(width: number, prevPeakMap: any, itemHash: any): {};
86
+ rollUp(root: any, width: number, peaks: any[], itemHashes: any[]): any;
87
+ /**
88
+ * @desc It returns the hash value of the node for the index.
89
+ * If the hash already exists it simply returns the stored value. On the other hand,
90
+ * it computes hashes recursively downward.
91
+ * Only appending an item calls this function.
92
+ */
93
+ private _getOrCreateNode;
94
+ }
95
+ export default MerkleMountainRange;
@@ -0,0 +1,436 @@
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.MerkleMountainRange = void 0;
7
+ const buffer_1 = require("buffer");
8
+ const sha256_1 = __importDefault(require("crypto-js/sha256"));
9
+ const Base_1 = __importDefault(require("./Base"));
10
+ // @credit: https://github.com/wanseob/solidity-mmr
11
+ /**
12
+ * @desc The index of this MMR implementation starts from 1 not 0.
13
+ */
14
+ class MerkleMountainRange extends Base_1.default {
15
+ constructor(hashFn = sha256_1.default, leaves = [], hashLeafFn, peakBaggingFn, hashBranchFn) {
16
+ super();
17
+ this.root = buffer_1.Buffer.alloc(0);
18
+ this.size = 0;
19
+ this.width = 0;
20
+ this.hashes = {};
21
+ this.data = {};
22
+ leaves = leaves.map(this.bufferify);
23
+ this.hashFn = this.bufferifyFn(hashFn);
24
+ this.hashLeafFn = hashLeafFn;
25
+ this.peakBaggingFn = peakBaggingFn;
26
+ this.hashBranchFn = hashBranchFn;
27
+ for (const leaf of leaves) {
28
+ this.append(leaf);
29
+ }
30
+ }
31
+ /**
32
+ * @desc This only stores the hashed value of the leaf.
33
+ * If you need to retrieve the detail data later, use a map to store them.
34
+ */
35
+ append(data) {
36
+ data = this.bufferify(data);
37
+ const dataHash = this.hashFn(data);
38
+ const dataHashHex = this.bufferToHex(dataHash);
39
+ if (!this.data[dataHashHex] || this.bufferToHex(this.hashFn(this.data[dataHashHex])) !== dataHashHex) {
40
+ this.data[dataHashHex] = data;
41
+ }
42
+ const leaf = this.hashLeaf(this.size + 1, dataHash);
43
+ this.hashes[this.size + 1] = leaf;
44
+ this.width += 1;
45
+ // find peaks for enlarged tree
46
+ const peakIndexes = this.getPeakIndexes(this.width);
47
+ // the right most peak's value is the new size of the updated tree
48
+ this.size = this.getSize(this.width);
49
+ // starting from the left-most peak, get all peak hashes
50
+ const peaks = [];
51
+ for (let i = 0; i < peakIndexes.length; i++) {
52
+ peaks[i] = this._getOrCreateNode(peakIndexes[i]);
53
+ }
54
+ // update the tree root hash
55
+ this.root = this.peakBagging(this.width, peaks);
56
+ }
57
+ /**
58
+ * @desc It returns the hash of a leaf node with hash(M | DATA )
59
+ * M is the index of the node.
60
+ */
61
+ hashLeaf(index, dataHash) {
62
+ dataHash = this.bufferify(dataHash);
63
+ if (this.hashLeafFn) {
64
+ return this.bufferify(this.hashLeafFn(index, dataHash));
65
+ }
66
+ return this.hashFn(buffer_1.Buffer.concat([this.bufferify(index), dataHash]));
67
+ }
68
+ /**
69
+ * @desc It returns the hash a parent node with hash(M | Left child | Right child)
70
+ * M is the index of the node.
71
+ */
72
+ hashBranch(index, left, right) {
73
+ if (this.hashBranchFn) {
74
+ return this.bufferify(this.hashBranchFn(index, left, right));
75
+ }
76
+ return this.hashFn(buffer_1.Buffer.concat([this.bufferify(index), this.bufferify(left), this.bufferify(right)]));
77
+ }
78
+ getPeaks() {
79
+ const peakIndexes = this.getPeakIndexes(this.width);
80
+ const peaks = [];
81
+ for (let i = 0; i < peakIndexes.length; i++) {
82
+ peaks[i] = this.hashes[peakIndexes[i]];
83
+ }
84
+ return peaks;
85
+ }
86
+ getLeafIndex(width) {
87
+ if (width % 2 === 1) {
88
+ return this.getSize(width);
89
+ }
90
+ return this.getSize(width - 1) + 1;
91
+ }
92
+ /**
93
+ * @desc It returns all peaks of the smallest merkle mountain range tree which includes
94
+ * the given index(size).
95
+ */
96
+ getPeakIndexes(width) {
97
+ const numPeaks = this.numOfPeaks(width);
98
+ const peakIndexes = [];
99
+ let count = 0;
100
+ let size = 0;
101
+ for (let i = 255; i > 0; i--) {
102
+ if ((width & (1 << (i - 1))) !== 0) {
103
+ // peak exists
104
+ size = size + (1 << i) - 1;
105
+ peakIndexes[count++] = size;
106
+ if (peakIndexes.length >= numPeaks) {
107
+ break;
108
+ }
109
+ }
110
+ }
111
+ if (count !== peakIndexes.length) {
112
+ throw new Error('invalid bit calculation');
113
+ }
114
+ return peakIndexes;
115
+ }
116
+ numOfPeaks(width) {
117
+ let bits = width;
118
+ let num = 0;
119
+ while (bits > 0) {
120
+ if (bits % 2 === 1) {
121
+ num++;
122
+ }
123
+ bits = bits >> 1;
124
+ }
125
+ return num;
126
+ }
127
+ peakBagging(width, peaks) {
128
+ const size = this.getSize(width);
129
+ if (this.numOfPeaks(width) !== peaks.length) {
130
+ throw new Error('received invalid number of peaks');
131
+ }
132
+ if (width === 0 && !peaks.length) {
133
+ return buffer_1.Buffer.alloc(0);
134
+ }
135
+ if (this.peakBaggingFn) {
136
+ return this.bufferify(this.peakBaggingFn(size, peaks));
137
+ }
138
+ return this.hashFn(buffer_1.Buffer.concat([this.bufferify(size), ...peaks.map(this.bufferify)]));
139
+ }
140
+ /**
141
+ * @desc It returns the size of the tree.
142
+ */
143
+ getSize(width) {
144
+ return (width << 1) - this.numOfPeaks(width);
145
+ }
146
+ /**
147
+ * @desc It returns the root value of the tree.
148
+ */
149
+ getRoot() {
150
+ return this.root;
151
+ }
152
+ getHexRoot() {
153
+ return this.bufferToHex(this.getRoot());
154
+ }
155
+ /**
156
+ * @dev It returns the hash value of a node for the given position. Note that the index starts from 1.
157
+ */
158
+ getNode(index) {
159
+ return this.hashes[index];
160
+ }
161
+ /**
162
+ * @desc It returns the height of the highest peak.
163
+ */
164
+ mountainHeight(size) {
165
+ let height = 1;
166
+ while (1 << height <= size + height) {
167
+ height++;
168
+ }
169
+ return height - 1;
170
+ }
171
+ /**
172
+ * @desc It returns the height of the index.
173
+ */
174
+ heightAt(index) {
175
+ let reducedIndex = index;
176
+ let peakIndex = 0;
177
+ let height = 0;
178
+ // if an index has a left mountain then subtract the mountain
179
+ while (reducedIndex > peakIndex) {
180
+ reducedIndex -= (1 << height) - 1;
181
+ height = this.mountainHeight(reducedIndex);
182
+ peakIndex = (1 << height) - 1;
183
+ }
184
+ // index is on the right slope
185
+ return height - (peakIndex - reducedIndex);
186
+ }
187
+ /**
188
+ * @desc It returns whether the index is the leaf node or not
189
+ */
190
+ isLeaf(index) {
191
+ return this.heightAt(index) === 1;
192
+ }
193
+ /**
194
+ * @desc It returns the children when it is a parent node.
195
+ */
196
+ getChildren(index) {
197
+ const left = index - (1 << (this.heightAt(index) - 1));
198
+ const right = index - 1;
199
+ if (left === right) {
200
+ throw new Error('not a parent');
201
+ }
202
+ return [left, right];
203
+ }
204
+ /**
205
+ * @desc It returns a merkle proof for a leaf. Note that the index starts from 1.
206
+ */
207
+ getMerkleProof(index) {
208
+ if (index > this.size) {
209
+ throw new Error('out of range');
210
+ }
211
+ if (!this.isLeaf(index)) {
212
+ throw new Error('not a leaf');
213
+ }
214
+ const root = this.root;
215
+ const width = this.width;
216
+ // find all peaks for bagging
217
+ const peaks = this.getPeakIndexes(this.width);
218
+ const peakBagging = [];
219
+ let cursor = 0;
220
+ for (let i = 0; i < peaks.length; i++) {
221
+ // collect the hash of all peaks
222
+ peakBagging[i] = this.hashes[peaks[i]];
223
+ // find the peak which includes the target index
224
+ if (peaks[i] >= index && cursor === 0) {
225
+ cursor = peaks[i];
226
+ }
227
+ }
228
+ let left = 0;
229
+ let right = 0;
230
+ // get hashes of the siblings in the mountain which the index belgons to.
231
+ // it moves the cursor from the summit of the mountain down to the target index
232
+ let height = this.heightAt(cursor);
233
+ const siblings = [];
234
+ while (cursor !== index) {
235
+ height--;
236
+ ([left, right] = this.getChildren(cursor));
237
+ // move the cursor down to the left size or right size
238
+ cursor = index <= left ? left : right;
239
+ // remaining node is the sibling
240
+ siblings[height - 1] = this.hashes[index <= left ? right : left];
241
+ }
242
+ return {
243
+ root,
244
+ width,
245
+ peakBagging,
246
+ siblings
247
+ };
248
+ }
249
+ /**
250
+ * @desc It returns true when the given params verifies that the given value exists in the tree or reverts the transaction.
251
+ */
252
+ verify(root, width, index, value, peaks, siblings) {
253
+ value = this.bufferify(value);
254
+ const size = this.getSize(width);
255
+ if (size < index) {
256
+ throw new Error('index is out of range');
257
+ }
258
+ // check the root equals the peak bagging hash
259
+ if (!root.equals(this.peakBagging(width, peaks))) {
260
+ throw new Error('invalid root hash from the peaks');
261
+ }
262
+ // find the mountain where the target index belongs to
263
+ let cursor = 0;
264
+ let targetPeak;
265
+ const peakIndexes = this.getPeakIndexes(width);
266
+ for (let i = 0; i < peakIndexes.length; i++) {
267
+ if (peakIndexes[i] >= index) {
268
+ targetPeak = peaks[i];
269
+ cursor = peakIndexes[i];
270
+ break;
271
+ }
272
+ }
273
+ if (!targetPeak) {
274
+ throw new Error('target not found');
275
+ }
276
+ // find the path climbing down
277
+ let height = siblings.length + 1;
278
+ const path = new Array(height);
279
+ let left = 0;
280
+ let right = 0;
281
+ while (height > 0) {
282
+ // record the current cursor and climb down
283
+ path[--height] = cursor;
284
+ if (cursor === index) {
285
+ // on the leaf node. Stop climbing down
286
+ break;
287
+ }
288
+ else {
289
+ // on the parent node. Go left or right
290
+ ([left, right] = this.getChildren(cursor));
291
+ cursor = index > left ? right : left;
292
+ continue;
293
+ }
294
+ }
295
+ // calculate the summit hash climbing up again
296
+ let node;
297
+ while (height < path.length) {
298
+ // move cursor
299
+ cursor = path[height];
300
+ if (height === 0) {
301
+ // cusor is on the leaf
302
+ node = this.hashLeaf(cursor, this.hashFn(value));
303
+ }
304
+ else if (cursor - 1 === path[height - 1]) {
305
+ // cursor is on a parent and a siblings is on the left
306
+ node = this.hashBranch(cursor, siblings[height - 1], node);
307
+ }
308
+ else {
309
+ // cursor is on a parent and a siblings is on the right
310
+ node = this.hashBranch(cursor, node, siblings[height - 1]);
311
+ }
312
+ // climb up
313
+ height++;
314
+ }
315
+ // computed hash value of the summit should equal to the target peak hash
316
+ if (!node.equals(targetPeak)) {
317
+ throw new Error('hashed peak is invalid');
318
+ }
319
+ return true;
320
+ }
321
+ peaksToPeakMap(width, peaks) {
322
+ const peakMap = {};
323
+ let bitIndex = 0;
324
+ let peakRef = 0;
325
+ let count = peaks.length;
326
+ for (let height = 1; height <= 32; height++) {
327
+ // index starts from the right most bit
328
+ bitIndex = 32 - height;
329
+ peakRef = 1 << (height - 1);
330
+ if ((width & peakRef) !== 0) {
331
+ peakMap[bitIndex] = peaks[--count];
332
+ }
333
+ else {
334
+ peakMap[bitIndex] = 0;
335
+ }
336
+ }
337
+ if (count !== 0) {
338
+ throw new Error('invalid number of peaks');
339
+ }
340
+ return peakMap;
341
+ }
342
+ peakMapToPeaks(width, peakMap) {
343
+ const arrLength = this.numOfPeaks(width);
344
+ const peaks = new Array(arrLength);
345
+ let count = 0;
346
+ for (let i = 0; i < 32; i++) {
347
+ if (peakMap[i] !== 0) {
348
+ peaks[count++] = peakMap[i];
349
+ }
350
+ }
351
+ if (count !== arrLength) {
352
+ throw new Error('invalid number of peaks');
353
+ }
354
+ return peaks;
355
+ }
356
+ peakUpdate(width, prevPeakMap, itemHash) {
357
+ const nextPeakMap = {};
358
+ const newWidth = width + 1;
359
+ let cursorIndex = this.getLeafIndex(newWidth);
360
+ let cursorNode = this.hashLeaf(cursorIndex, itemHash);
361
+ let bitIndex = 0;
362
+ let peakRef = 0;
363
+ let prevPeakExist = false;
364
+ let nextPeakExist = false;
365
+ let obtained = false;
366
+ for (let height = 1; height <= 32; height++) {
367
+ // index starts from the right most bit
368
+ bitIndex = 32 - height;
369
+ if (obtained) {
370
+ nextPeakMap[bitIndex] = prevPeakMap[bitIndex];
371
+ }
372
+ else {
373
+ peakRef = 1 << (height - 1);
374
+ prevPeakExist = (width & peakRef) !== 0;
375
+ nextPeakExist = (newWidth & peakRef) !== 0;
376
+ // get new cursor node with hashing the peak and the current cursor
377
+ cursorIndex++;
378
+ if (prevPeakExist) {
379
+ cursorNode = this.hashBranch(cursorIndex, prevPeakMap[bitIndex], cursorNode);
380
+ }
381
+ // if new peak exists for the bit index
382
+ if (nextPeakExist) {
383
+ // if prev peak exists for the bit index
384
+ if (prevPeakExist) {
385
+ nextPeakMap[bitIndex] = prevPeakMap[bitIndex];
386
+ }
387
+ else {
388
+ nextPeakMap[bitIndex] = cursorNode;
389
+ }
390
+ obtained = true;
391
+ }
392
+ else {
393
+ nextPeakMap[bitIndex] = 0;
394
+ }
395
+ }
396
+ }
397
+ return nextPeakMap;
398
+ }
399
+ rollUp(root, width, peaks, itemHashes) {
400
+ // check the root equals the peak bagging hash
401
+ if (!root.equals(this.peakBagging(width, peaks))) {
402
+ throw new Error('invalid root hash from the peaks');
403
+ }
404
+ let tmpWidth = width;
405
+ let tmpPeakMap = this.peaksToPeakMap(width, peaks);
406
+ for (let i = 0; i < itemHashes.length; i++) {
407
+ tmpPeakMap = this.peakUpdate(tmpWidth, tmpPeakMap, itemHashes[i]);
408
+ tmpWidth++;
409
+ }
410
+ return this.peakBagging(tmpWidth, this.peakMapToPeaks(tmpWidth, tmpPeakMap));
411
+ }
412
+ /**
413
+ * @desc It returns the hash value of the node for the index.
414
+ * If the hash already exists it simply returns the stored value. On the other hand,
415
+ * it computes hashes recursively downward.
416
+ * Only appending an item calls this function.
417
+ */
418
+ _getOrCreateNode(index) {
419
+ if (index > this.size) {
420
+ throw new Error('out of range');
421
+ }
422
+ if (!this.hashes[index]) {
423
+ const [leftIndex, rightIndex] = this.getChildren(index);
424
+ const leftHash = this._getOrCreateNode(leftIndex);
425
+ const rightHash = this._getOrCreateNode(rightIndex);
426
+ this.hashes[index] = this.hashBranch(index, leftHash, rightHash);
427
+ }
428
+ return this.hashes[index];
429
+ }
430
+ }
431
+ exports.MerkleMountainRange = MerkleMountainRange;
432
+ if (typeof window !== 'undefined') {
433
+ ;
434
+ window.MerkleMountainRange = MerkleMountainRange;
435
+ }
436
+ exports.default = MerkleMountainRange;
@@ -0,0 +1,33 @@
1
+ /// <reference types="node" />
2
+ import Base from './Base';
3
+ declare type TValue = Buffer | BigInt | string | number | null | undefined;
4
+ declare type THashFn = (value: TValue) => Buffer;
5
+ declare type ProofItem = {
6
+ key: string;
7
+ hash: Buffer;
8
+ siblings: {
9
+ key: string;
10
+ hash: Buffer;
11
+ }[];
12
+ };
13
+ declare class MerkleRadixNode {
14
+ key: string;
15
+ value: TValue;
16
+ children: Map<string, MerkleRadixNode>;
17
+ hash: Buffer;
18
+ hashFn: THashFn;
19
+ constructor(key: string, value: any, hashFn: THashFn);
20
+ computeHash(): Buffer;
21
+ updateHash(): void;
22
+ }
23
+ export declare class MerkleRadixTree extends Base {
24
+ root: MerkleRadixNode;
25
+ hashFn: THashFn;
26
+ constructor(hashFn: THashFn);
27
+ insert(key: string, value: TValue): void;
28
+ lookup(key: string): TValue;
29
+ private commonPrefixLength;
30
+ generateProof(key: string): any[];
31
+ verifyProof(proof: ProofItem[], rootHash: Buffer): boolean;
32
+ }
33
+ export {};