-
Notifications
You must be signed in to change notification settings - Fork 0
/
merkletree.js
83 lines (62 loc) · 1.88 KB
/
merkletree.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
const ethers = require('ethers');
exports.merkleRoot = (items) => {
if (items.length === 0) throw ('can\'t build merkle tree with empty items');
let level = items.map(leafHash);
while (level.length > 1) {
level = hashLevel(level);
}
return level[0];
};
exports.merkleProof = (items, item) => {
let index = items.findIndex((i) => i === item);
if (index === -1) throw ('item not found in items: ' + item);
let path = [];
let witnesses = [];
let level = items.map(leafHash);
while (level.length > 1) {
// Get proof for this level
let nextIndex = Math.floor(index / 2);
if (nextIndex * 2 === index) { // left side
if (index < level.length - 1) { // only if we're not the last in a level with odd number of nodes
path.push(0);
witnesses.push(level[index + 1]);
}
} else { // right side
path.push(1);
witnesses.push(level[index - 1]);
}
index = nextIndex;
level = hashLevel(level);
}
return {
path: path.reduceRight((a, b) => (a << 1) | b, 0),
witnesses,
};
};
exports.merkleVerification = (root, address, path, witnesses) => {
let node = leafHash(address);
for (let i = 0; i < witnesses.length; i++) {
if ((path & '0x01') === 1) {
node = nodeHash(witnesses[i], node);
} else {
node = nodeHash(node, witnesses[i]);
}
path /= 2;
}
return node === root;
};
// internal utility functions
function hashLevel(level) {
let nextLevel = [];
for (let i = 0; i < level.length; i += 2) {
if (i === level.length - 1) nextLevel.push(level[i]); // odd number of nodes at this level
else nextLevel.push(nodeHash(level[i], level[i + 1]));
}
return nextLevel;
}
function leafHash(leaf) {
return ethers.utils.keccak256(ethers.utils.concat(['0x00', leaf]));
}
function nodeHash(left, right) {
return ethers.utils.keccak256(ethers.utils.concat(['0x01', left, right]));
}