veriblock-pop-cpp
C++11 Libraries for leveraging VeriBlock Proof-Of-Proof blockchain technology.
merkle_tree.hpp
1// Copyright (c) 2019-2022 Xenios SEZC
2// https://www.veriblock.org
3// Distributed under the MIT software license, see the accompanying
4// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
5
6#ifndef VERIBLOCK_MERKLE_TREE_H_
7#define VERIBLOCK_MERKLE_TREE_H_
8
9#include <algorithm>
10#include <deque>
11#include <unordered_map>
12#include <vector>
13#include <veriblock/pop/arith_uint256.hpp>
14#include <veriblock/pop/entities/atv.hpp>
15#include <veriblock/pop/entities/merkle_path.hpp>
16
17namespace altintegration {
18
20template <typename Specific, typename TxHash>
21struct MerkleTree {
22 using hash_t = TxHash;
23
24 MerkleTree(Specific& instance, const std::vector<hash_t>& hashes)
25 : instance(instance) {
26 buildTree(hashes);
27 for (int32_t i = 0, size = static_cast<int32_t>(hashes.size()); i < size;
28 ++i) {
29 hash_indices[hashes[i]] = i;
30 }
31 }
32
33 std::vector<hash_t> getMerklePathLayers(const hash_t& hash) const {
34 auto it = hash_indices.find(hash);
35 VBK_ASSERT(it != hash_indices.end());
36 size_t index = it->second;
37 return getMerklePathLayers(index);
38 }
39
40 std::vector<hash_t> getMerklePathLayers(size_t index) const {
41 VBK_ASSERT(!layers.empty());
42 auto& leafs = layers[0];
43 if (leafs.size() == 1) {
44 VBK_ASSERT(layers.size() == 1);
45 // no layers
46 return {};
47 }
48 VBK_ASSERT(index < leafs.size());
49 std::vector<hash_t> merklePath{};
50 for (size_t i = 0, size = layers.size(); i < (size - 1); ++i) {
51 auto& layer = layers[i];
52 if (index % 2 == 0) {
53 if (layer.size() == index + 1) {
54 merklePath.push_back(layer[index]);
55 } else {
56 merklePath.push_back(layer[index + 1]);
57 }
58 } else {
59 merklePath.push_back(layer[index - 1]);
60 }
61
62 index /= 2;
63 }
64
65 return merklePath;
66 }
67
68 hash_t getMerkleRoot() const {
69 if (layers.empty()) {
70 return hash_t{};
71 }
72
73 auto& root = layers.back();
74 VBK_ASSERT(root.size() == 1);
75 return root[0];
76 }
77
78 const std::vector<std::vector<hash_t>>& getLayers() const { return layers; }
79
80 const std::unordered_map<hash_t, int32_t>& getHashIndices() const {
81 return hash_indices;
82 }
83
84 protected:
85 void buildTree(std::vector<hash_t> layer) {
86 size_t n = layer.size();
87 if (n == 0) {
88 return;
89 }
90 if (n == 1) {
91 layers = {layer};
92 return;
93 }
94
95 layers.push_back(layer);
96
97 layer.resize(n % 2 == 0 ? n : n + 1);
98 while (n > 1) {
99 if ((n % 2) != 0u) {
100 layer[n] = layer[n - 1];
101 ++n;
102 }
103 n /= 2;
104 for (size_t i = 0; i < n; i++) {
105 layer[i] = instance.hash(layer[2 * i], layer[2 * i + 1]);
106 }
107 layers.push_back({layer.begin(), layer.begin() + n});
108 }
109 }
110
111 Specific& instance;
112 std::vector<std::vector<hash_t>> layers;
113 std::unordered_map<hash_t, int32_t> hash_indices;
114};
115
117struct VbkMerkleTree {
118 using hash_t = typename MerkleTree<VbkMerkleTree, uint256>::hash_t;
119
120 enum class TreeIndex : int32_t {
121 POP = 0,
122 NORMAL = 1,
123 };
124
125 explicit VbkMerkleTree(const std::vector<hash_t>& normal_hashes,
126 const std::vector<hash_t>& pop_hashes)
127 : pop_tree(*this, pop_hashes), normal_tree(*this, normal_hashes) {}
128
129 hash_t hash(const hash_t& a, const hash_t& b) const { return sha256(a, b); }
130
131 std::vector<hash_t> finalizePath(std::vector<hash_t> path,
132 const TreeIndex treeIndex) const {
133 switch (treeIndex) {
134 case TreeIndex::POP: {
135 // opposite tree merkle root
136 if (path.empty() && normal_tree.getLayers().empty()) {
137 return path;
138 }
139
140 path.emplace_back(normal_tree.getMerkleRoot());
141 break;
142 }
143 case TreeIndex::NORMAL: {
144 // opposite tree merkle root
145 if (path.empty() && pop_tree.getLayers().empty()) {
146 return path;
147 }
148
149 path.emplace_back(pop_tree.getMerkleRoot());
150 break;
151 }
152 }
153
154 // metapackage hash
155 path.emplace_back();
156 return path;
157 }
158
159 hash_t getMerkleRoot() const {
160 if (pop_tree.getLayers().empty() && normal_tree.getLayers().empty()) {
161 return hash_t{};
162 }
163
164 if ((pop_tree.getLayers().size() == 1) && normal_tree.getLayers().empty()) {
165 // the only layer
166 VBK_ASSERT(pop_tree.getLayers()[0].size() == 1);
167 return pop_tree.getMerkleRoot();
168 }
169
170 if ((normal_tree.getLayers().size() == 1) && pop_tree.getLayers().empty()) {
171 // the only layer
172 VBK_ASSERT(normal_tree.getLayers()[0].size() == 1);
173 return normal_tree.getMerkleRoot();
174 }
175
176 auto pop_hash = pop_tree.getMerkleRoot();
177 auto normal_hash = normal_tree.getMerkleRoot();
178
179 // add metapackage hash (also all zeroes) to the left subtree
180 auto metapackageHash = hash_t();
181 return hash(metapackageHash, hash(pop_hash, normal_hash));
182 }
183
184 std::vector<hash_t> getMerklePathLayers(const size_t index,
185 const TreeIndex treeIndex) const {
186 switch (treeIndex) {
187 case TreeIndex::POP:
188 return finalizePath(pop_tree.getMerklePathLayers(index), treeIndex);
189 case TreeIndex::NORMAL:
190 return finalizePath(normal_tree.getMerklePathLayers(index), treeIndex);
191 default:
192 return {};
193 }
194 }
195
196 VbkMerklePath getMerklePath(const hash_t& hash,
197 const TreeIndex treeIndex) const {
198 VbkMerklePath merklePath;
199 merklePath.subject = hash;
200 merklePath.treeIndex = static_cast<int32_t>(treeIndex);
201 switch (treeIndex) {
202 case TreeIndex::POP: {
203 const auto it = pop_tree.getHashIndices().find(hash);
204 VBK_ASSERT(it != pop_tree.getHashIndices().end());
205 const int32_t index = it->second;
206
207 merklePath.index = index;
208 merklePath.layers = finalizePath(
209 pop_tree.getMerklePathLayers(static_cast<size_t>(index)),
210 treeIndex);
211 break;
212 }
213 case TreeIndex::NORMAL: {
214 const auto it = normal_tree.getHashIndices().find(hash);
215 VBK_ASSERT(it != normal_tree.getHashIndices().end());
216 const int32_t index = it->second;
217
218 merklePath.index = index;
219 merklePath.layers = finalizePath(
220 normal_tree.getMerklePathLayers(static_cast<size_t>(index)),
221 treeIndex);
222 break;
223 }
224 default:
225 return merklePath;
226 }
227
228 return merklePath;
229 }
230
231 private:
232 MerkleTree<VbkMerkleTree, uint256> pop_tree;
233 MerkleTree<VbkMerkleTree, uint256> normal_tree;
234};
235
236// Calculates an approximate amount of PopTxs in the POP merkle subtree
237inline uint32_t estimateNumberOfPopTxs(
238 const std::vector<VbkMerklePath>& paths) {
239 // validate that we have a continugous indexes set
240 std::set<int32_t> indexes;
241 int32_t maxIndex = -1;
242 for (const auto& path : paths) {
243 if (path.treeIndex == (int32_t)VbkMerkleTree::TreeIndex::POP &&
244 maxIndex < path.index) {
245 maxIndex = path.index;
246 }
247 }
248
249 // find the latest index and determine amount of the leaves
250 uint32_t aproximate_leaves_number = 0;
251 for (const auto& path : paths) {
252 if (path.treeIndex == (int32_t)VbkMerkleTree::TreeIndex::POP &&
253 maxIndex == path.index) {
254 if (path.layers.empty()) {
255 aproximate_leaves_number = 1;
256 break;
257 }
258 auto vec = path.equalLayerIndexes();
259 if (vec.empty()) {
260 aproximate_leaves_number = (1 << (path.layers.size() - 2));
261 break;
262 } else if (vec.front() == 0) {
263 aproximate_leaves_number = path.index + 1;
264 break;
265 } else {
266 aproximate_leaves_number = (1 << (path.layers.size() - 2));
267 for (const auto& i : vec) {
268 aproximate_leaves_number -= (1 << i);
269 }
270 break;
271 }
272 }
273 }
274
275 return aproximate_leaves_number;
276}
277
278inline bool isPopSubTreeFull(const std::vector<VbkMerklePath>& paths) {
279 return (uint32_t)paths.size() == estimateNumberOfPopTxs(paths);
280}
281
283struct BtcMerkleTree : public MerkleTree<BtcMerkleTree, uint256> {
284 using base = MerkleTree<BtcMerkleTree, uint256>;
285
286 explicit BtcMerkleTree(const std::vector<hash_t>& txes) : base(*this, txes) {}
287
288 hash_t hash(const hash_t& a, const hash_t& b) const {
289 return sha256twice(a, b);
290 }
291
292 MerklePath getMerklePath(const hash_t& hash) const {
293 auto it = hash_indices.find(hash);
294 VBK_ASSERT(it != hash_indices.end());
295 size_t index = it->second;
296
297 MerklePath merklePath;
298 merklePath.index = (int32_t)index;
299 merklePath.subject = hash;
300 merklePath.layers = getMerklePathLayers(index);
301 return merklePath;
302 }
303
304 hash_t getMerkleRoot() { return base::getMerkleRoot().reverse(); }
305};
306
308template <typename pop_t>
309struct PayloadsMerkleTree
310 : public MerkleTree<PayloadsMerkleTree<pop_t>, typename pop_t::id_t> {
311 using base = MerkleTree<PayloadsMerkleTree<pop_t>, typename pop_t::id_t>;
312 using hash_t = typename base::hash_t;
313
314 explicit PayloadsMerkleTree(const std::vector<hash_t>& hashes)
315 : base(*this, hashes) {}
316
317 hash_t hash(const hash_t& a, const hash_t& b) const {
318 return sha256twice(a, b).template trimLE<hash_t::size()>();
319 }
320
321 hash_t getMerkleRoot() { return base::getMerkleRoot().reverse(); }
322};
323
324} // namespace altintegration
325#endif // !VERIBLOCK_MERKLE_TREE_H_
Defines logging helpers.
Definition: block.hpp:14
uint256 sha256twice(Slice< const uint8_t > data)
Calculates SHA256 of the input data twice.
uint256 sha256(Slice< const uint8_t > data)
Calculates SHA256 of the input data.