forked from byteball/ocore
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmerkle.js
149 lines (129 loc) · 4.12 KB
/
merkle.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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*jslint node: true */
"use strict";
var crypto = require('crypto');
function hash(str){
return crypto.createHash("sha256").update(str, "utf8").digest("base64");
}
function getMerkleRoot(arrElements){
var arrHashes = arrElements.map(hash);
while (arrHashes.length > 1){
var arrOverHashes = []; // hashes over hashes
for (var i=0; i<arrHashes.length; i+=2){
var hash2_index = (i+1 < arrHashes.length) ? (i+1) : i; // for odd number of hashes
arrOverHashes.push(hash(arrHashes[i] + arrHashes[hash2_index]));
}
arrHashes = arrOverHashes;
}
return arrHashes[0];
}
function getMerkleProof(arrElements, element_index){
if (index < 0 || index >= arrElements.length)
throw Error("invalid index");
var arrHashes = arrElements.map(hash);
var index = element_index;
var arrSiblings = [];
while (arrHashes.length > 1){
var arrOverHashes = []; // hashes over hashes
var overIndex = null;
for (var i=0; i<arrHashes.length; i+=2){
var hash2_index = (i+1 < arrHashes.length) ? (i+1) : i; // for odd number of hashes
if (i === index){
arrSiblings.push(arrHashes[hash2_index]);
overIndex = i/2;
}
else if (hash2_index === index){
arrSiblings.push(arrHashes[i]);
overIndex = i/2;
}
arrOverHashes.push(hash(arrHashes[i] + arrHashes[hash2_index]));
}
arrHashes = arrOverHashes;
if (overIndex === null)
throw Error("overIndex not defined");
index = overIndex;
}
// add merkle root
//arrSiblings.push(arrHashes[0]);
return {
root: arrHashes[0],
siblings: arrSiblings,
index: element_index
};
}
/*function getSerializedMerkleProof(arrElements, element_index){
var proof = getMerkleProof(arrElements, element_index);
var serialized_proof = element_index;//+"-"+hash(arrElements[element_index]);
if (arrElements.length > 1)
serialized_proof += "-"+proof.siblings.join("-");
serialized_proof += "-"+proof.root;
return serialized_proof;
}*/
// returns a string element_index-siblings_joined_by_dash-root
function serializeMerkleProof(proof){
var serialized_proof = proof.index;
if (proof.siblings.length > 0)
serialized_proof += "-"+proof.siblings.join("-");
serialized_proof += "-"+proof.root;
return serialized_proof;
}
function deserializeMerkleProof(serialized_proof){
var arr = serialized_proof.split("-");
var proof = {};
proof.root = arr.pop();
proof.index = arr.shift();
proof.siblings = arr;
return proof;
}
function verifyMerkleProof(element, proof){
var index = proof.index;
var the_other_sibling = hash(element);
for (var i=0; i<proof.siblings.length; i++){
// this also works for duplicated trailing nodes
if (index % 2 === 0)
the_other_sibling = hash(the_other_sibling + proof.siblings[i]);
else
the_other_sibling = hash(proof.siblings[i] + the_other_sibling);
index = Math.floor(index/2);
}
return (the_other_sibling === proof.root);
}
exports.getMerkleRoot = getMerkleRoot;
exports.getMerkleProof = getMerkleProof;
exports.verifyMerkleProof = verifyMerkleProof;
exports.serializeMerkleProof = serializeMerkleProof;
exports.deserializeMerkleProof = deserializeMerkleProof;
// ------- TESTING ------
/*
function getRandomString(){
return crypto.randomBytes(12).toString("base64");
}
function testProofs(){
for (var len=1; len<100; len++){
var arrElements = [];
for (var i=0; i<len; i++)
arrElements.push(getRandomString());
for (var i=0; i<len; i++){
var proof = getMerkleProof(arrElements, i);
var serialized_proof = serializeMerkleProof(proof);
proof = deserializeMerkleProof(serialized_proof);
var res = verifyMerkleProof(hash(arrElements[i]), proof);
if (!res)
throw "proof failed len="+len+", i="+i;
}
}
console.log("proofs ok");
}
function testRoot(){
var arrElements = [getRandomString(), getRandomString()];
var root = hash(hash(arrElements[0]) + hash(arrElements[1]));
if (root !== getMerkleRoot(arrElements))
throw "2-element root failed";
arrElements.push(getRandomString());
var root = hash( hash( hash(arrElements[0]) + hash(arrElements[1]) ) + hash( hash(arrElements[2]) + hash(arrElements[2]) ) );
if (root !== getMerkleRoot(arrElements))
throw "3-element root failed";
console.log("root ok");
}
testProofs();
testRoot();
*/