-
Notifications
You must be signed in to change notification settings - Fork 7
/
MerkleTree.hpp
304 lines (250 loc) · 8.28 KB
/
MerkleTree.hpp
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#pragma once
#include <memory>
#include <cstdint>
#include <stdexcept>
#include <vector>
#include <string>
#include <iostream>
#include <iomanip>
#include <stack>
#include <map>
//=============== types ===========================
template<typename T>
class merkle_tree_node
{
public:
std::shared_ptr<merkle_tree_node> m_parent;
std::shared_ptr<merkle_tree_node> m_left;
std::shared_ptr<merkle_tree_node> m_right;
public:
//used for propagating sector index
std::uint32_t m_index;
//useful for aggregating nodes by level
std::uint32_t m_depth;
public:
//used to store any other user context linked to this node
T m_context;
public:
merkle_tree_node()
: m_parent(nullptr),
m_left(nullptr),
m_right(nullptr),
m_index(0)
{
}
bool isLeaf()
{
return !m_left && !m_right;
}
};
template<typename T>
class merkle_tree
{
public:
//number of nodes is used to make iterating through the tree easier
std::uint32_t nNodes;
//number of leaves in the tree - can be usefull
std::uint32_t nLeaves;
//root of the merkle tree
std::shared_ptr<merkle_tree_node<T> > root;
};
//================ generator ==========================
//this generates an empty merkle tree with specified number of leaves
//merkle tree is always a full tree
template<typename T>
std::shared_ptr<merkle_tree<T> > generate_merkle_tree(std::uint32_t nSectors)
{
// number of nodes is N = 2 * L - 1 if L is number of leaves
// in case of merkle trees - leaves are sector hashes
// I am not sure but merkle trees are probably always full trees
// meaning that each node has 2 children
std::uint32_t nNodesMax = nSectors * 2 - 1;
std::uint32_t nNodes = 1;
std::uint32_t depth = 0;
std::vector<std::shared_ptr<merkle_tree_node<T> > > children;
std::vector<std::shared_ptr<merkle_tree_node<T> > > children_temp;
std::shared_ptr<merkle_tree_node<T> > root = std::make_shared<merkle_tree_node<T> >();
root->m_depth = depth++;
children.push_back(root);
//this is a non recoursive algorithm that iterates through merkle tree
//level by level going from left to right, from top to bottom
while(nNodes != nNodesMax)
{
for(auto c : children)
{
c->m_left = std::make_shared<merkle_tree_node<T> >();
c->m_left->m_parent = c;
c->m_left->m_depth = depth;
children_temp.push_back(c->m_left);
nNodes++;
if(nNodes == nNodesMax)
throw std::runtime_error("Not a full binary tree");
c->m_right = std::make_shared<merkle_tree_node<T> >();
c->m_right->m_parent = c;
c->m_right->m_depth = depth;
children_temp.push_back(c->m_right);
nNodes++;
if(nNodes == nNodesMax)
break;
//if algo exits here it means we have unbalanced tree (full tree with last level not completely full)
//this is not important, just for note
}
//move deeper one level
children.assign(children_temp.begin(), children_temp.end());
children_temp.clear();
depth++;
}
//return a merkle tree
std::shared_ptr<merkle_tree<T> > mkt = std::make_shared<merkle_tree<T> >();
mkt->nNodes = nNodesMax;
mkt->nLeaves = nSectors;
mkt->root = root;
return mkt;
}
//================= walkers =========================
template<typename T>
struct merkle_node_walker
{
typedef int (type)(std::shared_ptr<merkle_tree_node<T> > node, void* ctx);
};
//this functions walks through tree nodes from top to bottom from left to right
//this is non recoursive walk that goes level by level in depth
template<typename T>
int walk_tree(std::shared_ptr<merkle_tree<T> > mkt, typename merkle_node_walker<T>::type* wlk, void* ctx)
{
std::uint32_t nNodes = 1;
std::vector<std::shared_ptr<merkle_tree_node<T> > > children;
std::vector<std::shared_ptr<merkle_tree_node<T> > > children_temp;
children.push_back(mkt->root);
if(wlk(mkt->root, ctx) < 0)
return 0;
//this is a non recoursive algorithm that iterates through merkle tree
//level by level going from left to right, from top to bottom
while(nNodes != mkt->nNodes)
{
for(auto c : children)
{
if(wlk(c->m_left, ctx) < 0)
return 0;
children_temp.push_back(c->m_left);
nNodes++;
if(nNodes == mkt->nNodes)
throw std::runtime_error("Not a full binary tree");
if(wlk(c->m_right, ctx) < 0)
return 0;
children_temp.push_back(c->m_right);
nNodes++;
if(nNodes == mkt->nNodes)
break;
//if algo exits here it means we have unbalanced tree (full tree with last level not completely full)
//this is not important, just for note
}
//move deeper one level
children.assign(children_temp.begin(), children_temp.end());
children_temp.clear();
}
return 0;
}
//walks from top to bottom from left to right in recoursive manner (in depth)
template<typename T>
int walk_tree_recoursive_forward(const merkle_tree<T>& mkt, typename merkle_node_walker<T>::type* wlk, void* ctx)
{
std::shared_ptr<merkle_tree_node<T> > currentNode = mkt.root;
std::stack<std::shared_ptr<merkle_tree_node<T> > > nodeStack;
nodeStack.push(currentNode);
do
{
do
{
while(true)
{
wlk(currentNode, ctx);
if(currentNode->isLeaf())
break;
nodeStack.push(currentNode);
currentNode = currentNode->m_left;
}
currentNode = nodeStack.top()->m_right;
nodeStack.pop();
}
while(!nodeStack.empty());
}
while(!nodeStack.empty());
return 0;
}
//================ index tree ==========================
//this is a tree walk indexing function that propagates index from top to bottom, from left to right
template<typename T>
int tree_indexer(std::shared_ptr<merkle_tree_node<T> > node, void* ctx)
{
if(node->isLeaf())
return 0;
int* idx = (int*)ctx;
//propagate index to left node
node->m_left->m_index = node->m_index;
//select next index into right node
node->m_right->m_index = (*idx)++;
return 0;
}
//this is a tree_indexer wrapper that keeps state locally
template<typename T>
int index_merkle_tree(std::shared_ptr<merkle_tree<T> > mkt)
{
int index = 1;
walk_tree(mkt, tree_indexer, &index);
return 0;
}
//================= depth slice tree =========================
template<typename T>
struct depth_mapper_context
{
typedef std::map<std::uint32_t, std::vector<std::shared_ptr<merkle_tree_node<T> > > > type;
typedef std::uint32_t key_type;
typedef std::vector<std::shared_ptr<merkle_tree_node<T> > > value_type;
};
template<typename T>
int depth_mapper(std::shared_ptr<merkle_tree_node<T> > node, void* ctx)
{
typename depth_mapper_context<T>::type* nodeDepthMap = (typename depth_mapper_context<T>::type*)ctx;
auto depthEntryIt = nodeDepthMap->find(node->m_depth);
if(depthEntryIt == nodeDepthMap->end())
{
auto insRes = nodeDepthMap->insert(std::make_pair(node->m_depth, typename depth_mapper_context<T>::value_type()));
depthEntryIt = insRes.first;
}
depthEntryIt->second.push_back(node);
return 0;
}
template<typename T>
int map_by_depth(std::shared_ptr<merkle_tree<T> > mkt, typename depth_mapper_context<T>::type& nodeDepthMap)
{
walk_tree(mkt, depth_mapper, &nodeDepthMap);
return 0;
}
//================ bottom top combiner ==========================
template<typename T>
struct node_combiner
{
typedef int(type)(std::shared_ptr<merkle_tree_node<T> > result, std::shared_ptr<merkle_tree_node<T> > left, std::shared_ptr<merkle_tree_node<T> > right, void* ctx);
};
template<typename T>
int bottom_top_walk_combine(std::shared_ptr<merkle_tree<T> > mkt, typename node_combiner<T>::type* wlk, void* ctx)
{
//build depth slice
typename depth_mapper_context<T>::type nodeDepthMap;
map_by_depth(mkt, nodeDepthMap);
//walk from bottom to top
for(typename depth_mapper_context<T>::type::const_reverse_iterator it = nodeDepthMap.rbegin(); it != nodeDepthMap.rend(); ++it)
{
//walk through each node
for(auto item : it->second)
{
//skip leaves
if(item->isLeaf())
continue;
//call walker
wlk(item, item->m_left, item->m_right, ctx);
}
}
return 0;
}