@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
|
@@ -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 {};
|