Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
indexed_tree.test.cpp
Go to the documentation of this file.
1#include "indexed_tree.hpp"
2#include "../array_store.hpp"
3#include "../hash.hpp"
4#include "../nullifier_tree/nullifier_memory_tree.hpp"
8#include "leaves_cache.hpp"
9
10using namespace bb;
11using namespace bb::crypto::merkle_tree;
12
14
15namespace {
18} // namespace
19
20const size_t NUM_VALUES = 1024;
21static std::vector<fr> VALUES = []() {
22 std::vector<fr> values(NUM_VALUES);
23 for (size_t i = 0; i < NUM_VALUES; ++i) {
24 values[i] = fr(random_engine.get_random_uint256());
25 }
26 return values;
27}();
28
29TEST(stdlib_indexed_tree, can_create)
30{
31 ArrayStore store(10);
32 IndexedTree<ArrayStore, LeavesCache, HashPolicy> tree = IndexedTree<ArrayStore, LeavesCache, HashPolicy>(store, 10);
33 EXPECT_EQ(tree.size(), 1ULL);
34
36 EXPECT_EQ(memdb.root(), tree.root());
37}
38
39TEST(stdlib_indexed_tree, test_size)
40{
41 ArrayStore store(32);
42 auto db = IndexedTree<ArrayStore, LeavesCache, HashPolicy>(store, 32);
43
44 // We assume that the first leaf is already filled with (0, 0, 0).
45 EXPECT_EQ(db.size(), 1ULL);
46
47 // Add a new non-zero leaf at index 1.
48 db.add_value(30);
49 EXPECT_EQ(db.size(), 2ULL);
50
51 // Add third.
52 db.add_value(10);
53 EXPECT_EQ(db.size(), 3ULL);
54
55 // Add forth.
56 db.add_value(20);
57 EXPECT_EQ(db.size(), 4ULL);
58}
59
60TEST(stdlib_indexed_tree, test_get_hash_path)
61{
63
64 ArrayStore store(10);
65 auto db = IndexedTree<ArrayStore, LeavesCache, HashPolicy>(store, 10);
66
67 EXPECT_EQ(memdb.root(), db.root());
68
69 EXPECT_EQ(memdb.get_hash_path(0), db.get_hash_path(0));
70
71 memdb.update_element(VALUES[512]);
72 db.add_value(VALUES[512]);
73
74 EXPECT_EQ(db.get_hash_path(0), memdb.get_hash_path(0));
75
76 for (size_t i = 0; i < 512; ++i) {
77 memdb.update_element(VALUES[i]);
78 db.add_value(VALUES[i]);
79 }
80
81 EXPECT_EQ(db.get_hash_path(512), memdb.get_hash_path(512));
82}
83
84TEST(stdlib_indexed_tree, test_batch_insert)
85{
86 const size_t batch_size = 16;
87 const size_t num_batches = 16;
88 size_t depth = 10;
89 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
90
91 ArrayStore store1(depth);
92 IndexedTree<ArrayStore, LeavesCache, HashPolicy> tree1 =
93 IndexedTree<ArrayStore, LeavesCache, HashPolicy>(store1, depth, batch_size);
94
95 ArrayStore store2(depth);
96 IndexedTree<ArrayStore, LeavesCache, HashPolicy> tree2 =
97 IndexedTree<ArrayStore, LeavesCache, HashPolicy>(store2, depth, batch_size);
98
99 EXPECT_EQ(memdb.root(), tree1.root());
100 EXPECT_EQ(tree1.root(), tree2.root());
101
102 EXPECT_EQ(memdb.get_hash_path(0), tree1.get_hash_path(0));
103 EXPECT_EQ(tree1.get_hash_path(0), tree2.get_hash_path(0));
104
105 EXPECT_EQ(memdb.get_hash_path(512), tree1.get_hash_path(512));
106 EXPECT_EQ(tree1.get_hash_path(512), tree2.get_hash_path(512));
107
108 for (size_t i = 0; i < num_batches; i++) {
109 std::vector<fr> batch;
110 std::vector<fr_hash_path> memory_tree_hash_paths;
111 for (size_t j = 0; j < batch_size; j++) {
112 batch.push_back(fr(random_engine.get_random_uint256()));
113 fr_hash_path path = memdb.update_element(batch[j]);
114 memory_tree_hash_paths.push_back(path);
115 }
116 std::vector<fr_hash_path> tree1_hash_paths = tree1.add_or_update_values(batch, true);
117 std::vector<fr_hash_path> tree2_hash_paths = tree2.add_or_update_values(batch);
118 EXPECT_EQ(memdb.root(), tree1.root());
119 EXPECT_EQ(tree1.root(), tree2.root());
120
121 EXPECT_EQ(memdb.get_hash_path(0), tree1.get_hash_path(0));
122 EXPECT_EQ(tree1.get_hash_path(0), tree2.get_hash_path(0));
123
124 EXPECT_EQ(memdb.get_hash_path(512), tree1.get_hash_path(512));
125 EXPECT_EQ(tree1.get_hash_path(512), tree2.get_hash_path(512));
126
127 for (size_t j = 0; j < batch_size; j++) {
128 EXPECT_EQ(tree1_hash_paths[j], tree2_hash_paths[j]);
129 // EXPECT_EQ(tree1_hash_paths[j], memory_tree_hash_paths[j]);
130 }
131 }
132}
133
134fr hash_leaf(const indexed_leaf& leaf)
135{
136 return HashPolicy::hash(leaf.get_hash_inputs());
137}
138
139bool check_hash_path(const fr& root, const fr_hash_path& path, const indexed_leaf& leaf_value, const size_t idx)
140{
141 auto current = hash_leaf(leaf_value);
142 size_t depth_ = path.size();
143 size_t index = idx;
144 for (size_t i = 0; i < depth_; ++i) {
145 fr left = (index & 1) ? path[i].first : current;
146 fr right = (index & 1) ? current : path[i].second;
147 current = HashPolicy::hash_pair(left, right);
148 index >>= 1;
149 }
150 return current == root;
151}
152
153TEST(stdlib_indexed_tree, test_indexed_memory)
154{
155 // Create a depth-3 indexed merkle tree
156 constexpr size_t depth = 3;
157 ArrayStore store(depth);
158 IndexedTree<ArrayStore, LeavesCache, HashPolicy> tree =
159 IndexedTree<ArrayStore, LeavesCache, HashPolicy>(store, depth, 1);
160
170 indexed_leaf zero_leaf = { 0, 0, 0 };
171 EXPECT_EQ(tree.size(), 1);
172 EXPECT_EQ(tree.get_leaf(0), zero_leaf);
173
183 tree.add_value(30);
184 EXPECT_EQ(tree.size(), 2);
185 EXPECT_EQ(hash_leaf(tree.get_leaf(0)), hash_leaf({ 0, 1, 30 }));
186 EXPECT_EQ(hash_leaf(tree.get_leaf(1)), hash_leaf({ 30, 0, 0 }));
187
197 tree.add_value(10);
198 EXPECT_EQ(tree.size(), 3);
199 EXPECT_EQ(hash_leaf(tree.get_leaf(0)), hash_leaf({ 0, 2, 10 }));
200 EXPECT_EQ(hash_leaf(tree.get_leaf(1)), hash_leaf({ 30, 0, 0 }));
201 EXPECT_EQ(hash_leaf(tree.get_leaf(2)), hash_leaf({ 10, 1, 30 }));
202
212 tree.add_value(20);
213 EXPECT_EQ(tree.size(), 4);
214 EXPECT_EQ(hash_leaf(tree.get_leaf(0)), hash_leaf({ 0, 2, 10 }));
215 EXPECT_EQ(hash_leaf(tree.get_leaf(1)), hash_leaf({ 30, 0, 0 }));
216 EXPECT_EQ(hash_leaf(tree.get_leaf(2)), hash_leaf({ 10, 3, 20 }));
217 EXPECT_EQ(hash_leaf(tree.get_leaf(3)), hash_leaf({ 20, 1, 30 }));
218
219 // Adding the same value must not affect anything
220 // tree.update_element(20);
221 // EXPECT_EQ(tree.get_leaves().size(), 4);
222 // EXPECT_EQ(tree.get_leaves()[0], hash_leaf({ 0, 2, 10 }));
223 // EXPECT_EQ(tree.get_leaves()[1], hash_leaf({ 30, 0, 0 }));
224 // EXPECT_EQ(tree.get_leaves()[2], hash_leaf({ 10, 3, 20 }));
225 // EXPECT_EQ(tree.get_leaves()[3], hash_leaf({ 20, 1, 30 }));
226
236 tree.add_value(50);
237 EXPECT_EQ(tree.size(), 5);
238 EXPECT_EQ(hash_leaf(tree.get_leaf(0)), hash_leaf({ 0, 2, 10 }));
239 EXPECT_EQ(hash_leaf(tree.get_leaf(1)), hash_leaf({ 30, 4, 50 }));
240 EXPECT_EQ(hash_leaf(tree.get_leaf(2)), hash_leaf({ 10, 3, 20 }));
241 EXPECT_EQ(hash_leaf(tree.get_leaf(3)), hash_leaf({ 20, 1, 30 }));
242 EXPECT_EQ(hash_leaf(tree.get_leaf(4)), hash_leaf({ 50, 0, 0 }));
243
244 // Manually compute the node values
245 auto e000 = hash_leaf(tree.get_leaf(0));
246 auto e001 = hash_leaf(tree.get_leaf(1));
247 auto e010 = hash_leaf(tree.get_leaf(2));
248 auto e011 = hash_leaf(tree.get_leaf(3));
249 auto e100 = hash_leaf(tree.get_leaf(4));
250 auto e101 = hash_leaf({ 0, 0, 0 });
251 auto e110 = hash_leaf({ 0, 0, 0 });
252 auto e111 = hash_leaf({ 0, 0, 0 });
253
254 auto e00 = HashPolicy::hash_pair(e000, e001);
255 auto e01 = HashPolicy::hash_pair(e010, e011);
256 auto e10 = HashPolicy::hash_pair(e100, e101);
257 auto e11 = HashPolicy::hash_pair(e110, e111);
258
259 auto e0 = HashPolicy::hash_pair(e00, e01);
260 auto e1 = HashPolicy::hash_pair(e10, e11);
261 auto root = HashPolicy::hash_pair(e0, e1);
262
263 // Check the hash path at index 2 and 3
264 // Note: This merkle proof would also serve as a non-membership proof of values in (10, 20) and (20, 30)
265 fr_hash_path expected = {
266 std::make_pair(e000, e001),
267 std::make_pair(e00, e01),
268 std::make_pair(e0, e1),
269 };
270 EXPECT_EQ(tree.get_hash_path(0), expected);
271 EXPECT_EQ(tree.get_hash_path(1), expected);
272 fr_hash_path expected2 = {
273 std::make_pair(e010, e011),
274 std::make_pair(e00, e01),
275 std::make_pair(e0, e1),
276 };
277 EXPECT_EQ(tree.get_hash_path(2), expected2);
278 EXPECT_EQ(tree.get_hash_path(3), expected2);
279 EXPECT_EQ(tree.root(), root);
280
281 // Check the hash path at index 6 and 7
282 expected = {
283 std::make_pair(e110, e111),
284 std::make_pair(e10, e11),
285 std::make_pair(e0, e1),
286 };
287 EXPECT_EQ(tree.get_hash_path(6), expected);
288 EXPECT_EQ(tree.get_hash_path(7), expected);
289}
290
291TEST(stdlib_indexed_tree, test_indexed_tree)
292{
293 // Create a depth-8 indexed merkle tree
294 constexpr size_t depth = 8;
295 ArrayStore store(depth);
296 IndexedTree<ArrayStore, LeavesCache, HashPolicy> tree =
297 IndexedTree<ArrayStore, LeavesCache, HashPolicy>(store, depth, 1);
298
299 indexed_leaf zero_leaf = { 0, 0, 0 };
300 EXPECT_EQ(tree.size(), 1);
301 EXPECT_EQ(hash_leaf(tree.get_leaf(0)), hash_leaf(zero_leaf));
302
303 // Add 20 random values to the tree
304 for (size_t i = 0; i < 20; i++) {
305 auto value = fr::random_element();
306 tree.add_value(value);
307 }
308
309 auto abs_diff = [](uint256_t a, uint256_t b) {
310 if (a > b) {
311 return (a - b);
312 } else {
313 return (b - a);
314 }
315 };
316
317 // Check if a new random value is not a member of this tree.
318 fr new_member = fr::random_element();
319 std::vector<uint256_t> differences;
320 for (size_t i = 0; i < size_t(tree.size()); i++) {
321 uint256_t diff_hi = abs_diff(uint256_t(new_member), uint256_t(tree.get_leaf(i).value));
322 uint256_t diff_lo = abs_diff(uint256_t(new_member), uint256_t(tree.get_leaf(i).value));
323 differences.push_back(diff_hi + diff_lo);
324 }
325 auto it = std::min_element(differences.begin(), differences.end());
326 auto index = static_cast<size_t>(it - differences.begin());
327
328 // Merkle proof at `index` proves non-membership of `new_member`
329 auto hash_path = tree.get_hash_path(index);
330 EXPECT_TRUE(check_hash_path(tree.root(), hash_path, tree.get_leaf(index), index));
331}
A very basic 2-d array for use as a backing store for merkle trees. Can store up to 'indices' nodes p...
fr_hash_path get_hash_path(size_t index) const
FF a
FF b
void test_batch_insert(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
bool check_hash_path(const fr &root, const fr_hash_path &path, const indexed_leaf &leaf_value, const size_t idx)
fr hash_leaf(const indexed_leaf &leaf)
const uint32_t NUM_VALUES
Definition fixtures.hpp:21
std::vector< std::pair< bb::stdlib::field_t< Ctx >, bb::stdlib::field_t< Ctx > > > hash_path
Definition hash_path.hpp:17
std::vector< std::pair< fr, fr > > fr_hash_path
Definition hash_path.hpp:15
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
RNG & get_randomness()
Definition engine.cpp:203
Entry point for Barretenberg command-line interface.
TEST(MegaCircuitBuilder, CopyConstructor)
field< Bn254FrParams > fr
Definition fr.hpp:174
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static fr hash_pair(const fr &lhs, const fr &rhs)
Definition hash.hpp:35
static fr hash(const std::vector< fr > &inputs)
Definition hash.hpp:30
static field random_element(numeric::RNG *engine=nullptr) noexcept