Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
content_addressed_indexed_tree.test.cpp
Go to the documentation of this file.
2#include "../fixtures.hpp"
3#include "../hash.hpp"
4#include "../node_store/array_store.hpp"
5#include "../nullifier_tree/nullifier_memory_tree.hpp"
6#include "../test_fixtures.hpp"
7#include "./fixtures.hpp"
18#include <algorithm>
19#include <cstdint>
20#include <filesystem>
21#include <future>
22#include <memory>
23#include <optional>
24#include <stdexcept>
25#include <vector>
26
27using namespace bb;
28using namespace bb::crypto::merkle_tree;
29
31
34
37
40
41class PersistedContentAddressedIndexedTreeTest : public testing::Test {
42 protected:
43 void SetUp() override
44 {
46 _mapSize = 1024 * 1024;
47 _maxReaders = 16;
48 std::filesystem::create_directories(_directory);
49 }
50
51 void TearDown() override { std::filesystem::remove_all(_directory); }
52
53 static std::string _directory;
54 static uint64_t _maxReaders;
55 static uint64_t _mapSize;
56};
57
61
62std::unique_ptr<TreeType> create_tree(const std::string& rootDirectory,
63 uint64_t mapSize,
64 uint64_t maxReaders,
65 uint32_t depth,
66 uint32_t batchSize,
67 ThreadPoolPtr workers)
68{
69 std::string name = random_string();
70 std::filesystem::path directory = rootDirectory;
71 directory.append(name);
72 std::filesystem::create_directories(directory);
73 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(directory, name, mapSize, maxReaders);
74 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
75 return std::make_unique<TreeType>(std::move(store), workers, batchSize);
76}
77
78template <typename TypeOfTree> void check_size(TypeOfTree& tree, index_t expected_size, bool includeUncommitted = true)
79{
80 Signal signal;
81 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
82 EXPECT_EQ(response.success, true);
83 EXPECT_EQ(response.inner.meta.size, expected_size);
84 signal.signal_level();
85 };
86 tree.get_meta_data(includeUncommitted, completion);
87 signal.wait_for_level();
88}
89
90template <typename TypeOfTree> fr get_root(TypeOfTree& tree, bool includeUncommitted = true)
91{
92 fr r;
93 Signal signal;
94 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
95 r = response.inner.meta.root;
96 signal.signal_level();
97 };
98 tree.get_meta_data(includeUncommitted, completion);
99 signal.wait_for_level();
100 return r;
101}
102
103template <typename TypeOfTree> void check_root(TypeOfTree& tree, fr expected_root, bool includeUncommitted = true)
104{
105 fr root = get_root(tree, includeUncommitted);
106 EXPECT_EQ(root, expected_root);
107}
108
109template <typename TypeOfTree>
111 block_number_t blockNumber,
112 index_t index,
113 bool includeUncommitted = true,
114 bool expected_success = true)
115{
117 Signal signal;
118 auto completion = [&](const TypedResponse<GetSiblingPathResponse>& response) -> void {
119 EXPECT_EQ(response.success, expected_success);
120 if (response.success) {
121 h = response.inner.path;
122 }
123 signal.signal_level();
124 };
125 tree.get_sibling_path(index, blockNumber, completion, includeUncommitted);
126 signal.wait_for_level();
127 return h;
128}
129
130template <typename LeafValueType, typename TypeOfTree>
132 index_t index,
133 bool includeUncommitted = true,
134 bool expected_success = true)
135{
137 Signal signal;
138 auto completion = [&](const TypedResponse<GetIndexedLeafResponse<LeafValueType>>& leaf) -> void {
139 EXPECT_EQ(leaf.success, expected_success);
140 if (leaf.success) {
141 l = leaf.inner.indexed_leaf;
142 }
143 signal.signal_level();
144 };
145 tree.get_leaf(index, includeUncommitted, completion);
146 signal.wait_for_level();
147 return l.has_value() ? l.value() : IndexedLeaf<LeafValueType>();
148}
149
150template <typename LeafValueType, typename TypeOfTree>
151GetLowIndexedLeafResponse get_low_leaf(TypeOfTree& tree, const LeafValueType& leaf, bool includeUncommitted = true)
152{
153 GetLowIndexedLeafResponse low_leaf_info;
154 Signal signal;
155 auto completion = [&](const auto& leaf) -> void {
156 low_leaf_info = leaf.inner;
157 signal.signal_level();
158 };
159 tree.find_low_leaf(leaf.get_key(), includeUncommitted, completion);
160 signal.wait_for_level();
161 return low_leaf_info;
162}
163
164template <typename LeafValueType, typename TypeOfTree>
166 block_number_t blockNumber,
167 const LeafValueType& leaf,
168 bool includeUncommitted = true)
169{
170 GetLowIndexedLeafResponse low_leaf_info;
171 Signal signal;
172 auto completion = [&](const auto& leaf) -> void {
173 low_leaf_info = leaf.inner;
174 signal.signal_level();
175 };
176 tree.find_low_leaf(leaf.get_key(), blockNumber, includeUncommitted, completion);
177 signal.wait_for_level();
178 return low_leaf_info;
179}
180
181template <typename LeafValueType, typename TypeOfTree>
182void check_historic_leaf(TypeOfTree& tree,
183 const LeafValueType& leaf,
184 index_t expected_index,
185 block_number_t blockNumber,
186 bool expected_success,
187 bool includeUncommitted = true)
188{
189 Signal signal;
190 auto completion = [&](const TypedResponse<GetIndexedLeafResponse<LeafValueType>>& response) -> void {
191 EXPECT_EQ(response.success, expected_success);
192 if (response.success) {
193 EXPECT_EQ(response.inner.indexed_leaf.value().leaf, leaf);
194 }
195 signal.signal_level();
196 };
197
198 tree.get_leaf(expected_index, blockNumber, includeUncommitted, completion);
199 signal.wait_for_level();
200}
201
202template <typename TypeOfTree>
203void check_historic_sibling_path(TypeOfTree& tree,
204 index_t index,
205 block_number_t blockNumber,
206 const fr_sibling_path& expected_sibling_path,
207 bool includeUncommitted = true,
208 bool expected_success = true)
209{
210 fr_sibling_path path = get_historic_sibling_path(tree, blockNumber, index, includeUncommitted, expected_success);
211 if (expected_success) {
212 EXPECT_EQ(path, expected_sibling_path);
213 }
214}
215
216template <typename TypeOfTree>
217void check_sibling_path(TypeOfTree& tree,
218 index_t index,
219 const fr_sibling_path& expected_sibling_path,
220 bool includeUncommitted = true,
221 bool expected_success = true)
222{
223 fr_sibling_path path = get_sibling_path(tree, index, includeUncommitted, expected_success);
224 EXPECT_EQ(path, expected_sibling_path);
225}
226
227template <typename TypeOfTree> void check_unfinalized_block_height(TypeOfTree& tree, index_t expected_block_height)
228{
229 Signal signal;
230 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
231 EXPECT_EQ(response.success, true);
232 EXPECT_EQ(response.inner.meta.unfinalizedBlockHeight, expected_block_height);
233 signal.signal_level();
234 };
235 tree.get_meta_data(true, completion);
236 signal.wait_for_level();
237}
238
239template <typename TypeOfTree> void commit_tree(TypeOfTree& tree, bool expectedSuccess = true)
240{
241 Signal signal;
242 auto completion = [&](const TypedResponse<CommitResponse>& response) -> void {
243 EXPECT_EQ(response.success, expectedSuccess);
244 signal.signal_level();
245 };
246 tree.commit(completion);
247 signal.wait_for_level();
248}
249
250template <typename LeafValueType, typename TypeOfTree>
251void add_value(TypeOfTree& tree, const LeafValueType& value, bool expectedSuccess = true)
252{
253 Signal signal;
254 auto completion = [&](const TypedResponse<AddIndexedDataResponse<LeafValueType>>& response) -> void {
255 EXPECT_EQ(response.success, expectedSuccess);
256 signal.signal_level();
257 };
258
259 tree.add_or_update_value(value, completion);
260 signal.wait_for_level();
261}
262
263template <typename LeafValueType, typename TypeOfTree>
264void add_value_sequentially(TypeOfTree& tree, const LeafValueType& value, bool expectedSuccess = true)
265{
267 Signal signal;
268 auto completion = [&](const TypedResponse<AddIndexedDataSequentiallyResponse<LeafValueType>>& response) -> void {
269 EXPECT_EQ(response.success, expectedSuccess);
270 signal.signal_level();
271 };
272
273 tree.add_or_update_values_sequentially(values, completion);
274 signal.wait_for_level();
275}
276
277template <typename LeafValueType, typename TypeOfTree>
278void add_values(TypeOfTree& tree, const std::vector<LeafValueType>& values, bool expectedSuccess = true)
279{
280 Signal signal;
281 auto completion = [&](const TypedResponse<AddIndexedDataResponse<LeafValueType>>& response) -> void {
282 EXPECT_EQ(response.success, expectedSuccess);
283 signal.signal_level();
284 };
285
286 tree.add_or_update_values(values, completion);
287 signal.wait_for_level();
288}
289
290template <typename LeafValueType, typename TypeOfTree>
291void add_values_sequentially(TypeOfTree& tree, const std::vector<LeafValueType>& values, bool expectedSuccess = true)
292{
293 Signal signal;
294 auto completion = [&](const TypedResponse<AddIndexedDataSequentiallyResponse<LeafValueType>>& response) -> void {
295 EXPECT_EQ(response.success, expectedSuccess);
296 signal.signal_level();
297 };
298
299 tree.add_or_update_values_sequentially(values, completion);
300 signal.wait_for_level();
301}
302
303template <typename LeafValueType, typename TypeOfTree>
304void block_sync_values(TypeOfTree& tree, const std::vector<LeafValueType>& values, bool expectedSuccess = true)
305{
306 Signal signal;
307 auto completion = [&](const TypedResponse<AddDataResponse>& response) -> void {
308 EXPECT_EQ(response.success, expectedSuccess);
309 signal.signal_level();
310 };
311
312 tree.add_or_update_values(values, completion);
313 signal.wait_for_level();
314}
315
316template <typename LeafValueType, typename TypeOfTree>
317void block_sync_values_sequential(TypeOfTree& tree,
318 const std::vector<LeafValueType>& values,
319 bool expectedSuccess = true)
320{
321 Signal signal;
322 auto completion = [&](const TypedResponse<AddDataResponse>& response) -> void {
323 EXPECT_EQ(response.success, expectedSuccess);
324 signal.signal_level();
325 };
326
327 tree.add_or_update_values_sequentially(values, completion);
328 signal.wait_for_level();
329}
330
331template <typename TypeOfTree>
332void remove_historic_block(TypeOfTree& tree, const block_number_t& blockNumber, bool expected_success = true)
333{
334 Signal signal;
335 auto completion = [&](const TypedResponse<RemoveHistoricResponse>& response) -> void {
336 EXPECT_EQ(response.success, expected_success);
337 signal.signal_level();
338 };
339 tree.remove_historic_block(blockNumber, completion);
340 signal.wait_for_level();
341}
342
343template <typename TypeOfTree>
344void finalize_block(TypeOfTree& tree, const block_number_t& blockNumber, bool expected_success = true)
345{
346 Signal signal;
347 auto completion = [&](const Response& response) -> void {
348 EXPECT_EQ(response.success, expected_success);
349 signal.signal_level();
350 };
351 tree.finalize_block(blockNumber, completion);
352 signal.wait_for_level();
353}
354
355template <typename TypeOfTree>
356void unwind_block(TypeOfTree& tree, const block_number_t& blockNumber, bool expected_success = true)
357{
358 Signal signal;
359 auto completion = [&](const TypedResponse<UnwindResponse>& response) -> void {
360 EXPECT_EQ(response.success, expected_success);
361 signal.signal_level();
362 };
363 tree.unwind_block(blockNumber, completion);
364 signal.wait_for_level();
365}
366
367template <typename TypeOfTree> void check_block_height(TypeOfTree& tree, index_t expected_block_height)
368{
369 Signal signal;
370 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
371 EXPECT_EQ(response.success, true);
372 EXPECT_EQ(response.inner.meta.unfinalizedBlockHeight, expected_block_height);
373 signal.signal_level();
374 };
375 tree.get_meta_data(true, completion);
376 signal.wait_for_level();
377}
378
380{
381 constexpr size_t depth = 10;
382 std::string name = random_string();
383 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
384 EXPECT_NO_THROW(std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db));
385 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
386 ThreadPoolPtr workers = make_thread_pool(1);
387 TreeType tree = TreeType(std::move(store), workers, 2);
388 check_size(tree, 2);
389
391 check_root(tree, memdb.root());
392}
393
394TEST_F(PersistedContentAddressedIndexedTreeTest, can_only_recreate_with_same_name_and_depth)
395{
396 constexpr size_t depth = 10;
397 std::string name = random_string();
398 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
399 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
400
401 EXPECT_ANY_THROW(Store("Wrong name", depth, db));
402 EXPECT_ANY_THROW(Store(name, depth + 1, db));
403}
404
406{
407 index_t current_size = 2;
408 ThreadPoolPtr workers = make_thread_pool(1);
409 constexpr size_t depth = 10;
410 std::string name = random_string();
411 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
412 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
413 auto tree = TreeType(std::move(store), workers, current_size);
414
415 check_size(tree, current_size);
416
417 // We assume that the first leaf is already filled with (0, 0, 0).
418 for (uint32_t i = 0; i < 4; i++) {
419 add_value(tree, NullifierLeafValue(VALUES[i]));
420 check_size(tree, ++current_size);
421 }
422}
423
424TEST_F(PersistedContentAddressedIndexedTreeTest, indexed_tree_must_have_at_least_2_initial_size)
425{
426 index_t current_size = 1;
427 ThreadPoolPtr workers = make_thread_pool(1);
428 ;
429 constexpr size_t depth = 10;
430 std::string name = random_string();
431 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
432 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
433 EXPECT_THROW(TreeType(std::move(store), workers, current_size), std::runtime_error);
434}
435
436TEST_F(PersistedContentAddressedIndexedTreeTest, reports_an_error_if_tree_is_overfilled)
437{
438 index_t current_size = 2;
439 ThreadPoolPtr workers = make_thread_pool(1);
440 ;
441 constexpr size_t depth = 4;
442 std::string name = random_string();
443 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
444 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
445 auto tree = TreeType(std::move(store), workers, current_size);
446
448 for (uint32_t i = 0; i < 14; i++) {
449 values.emplace_back(VALUES[i]);
450 }
451 add_values(tree, values);
452
453 std::stringstream ss;
454 ss << "Unable to insert values into tree " << name << " new size: 17 max size: 16";
455
456 Signal signal;
457 auto add_completion = [&](const TypedResponse<AddIndexedDataResponse<NullifierLeafValue>>& response) {
458 EXPECT_EQ(response.success, false);
459 EXPECT_EQ(response.message, ss.str());
460 signal.signal_level();
461 };
462 tree.add_or_update_value(NullifierLeafValue(VALUES[16]), add_completion);
463 signal.wait_for_level();
464}
465
467{
468 constexpr size_t depth = 10;
469 index_t current_size = 2;
470 NullifierMemoryTree<HashPolicy> memdb(depth, current_size);
471
472 ThreadPoolPtr workers = make_thread_pool(1);
473
474 std::string name = random_string();
475 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
476 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
477 auto tree = TreeType(std::move(store), workers, current_size);
478
479 check_size(tree, current_size);
480 check_root(tree, memdb.root());
481 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
482
483 memdb.update_element(VALUES[1000]);
484 add_value(tree, NullifierLeafValue(VALUES[1000]));
485
486 check_size(tree, ++current_size);
487 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
488 check_sibling_path(tree, 1, memdb.get_sibling_path(1));
489
490 memdb.update_element(VALUES[1001]);
491 add_value(tree, NullifierLeafValue(VALUES[1001]));
492
493 check_size(tree, ++current_size);
494 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
495 check_sibling_path(tree, 1, memdb.get_sibling_path(1));
496
497 uint32_t num_to_append = 512;
498
499 for (uint32_t i = 0; i < num_to_append; i += 2) {
500 memdb.update_element(VALUES[i]);
501 memdb.update_element(VALUES[i + 1]);
502 add_values<NullifierLeafValue>(tree, { NullifierLeafValue(VALUES[i]), NullifierLeafValue(VALUES[i + 1]) });
503 }
504 check_size(tree, num_to_append + current_size);
505 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
506 check_sibling_path(tree, 512, memdb.get_sibling_path(512));
507}
508
510{
511 index_t initial_size = 2;
512 ThreadPoolPtr workers = make_thread_pool(1);
513 ;
514 constexpr size_t depth = 10;
515 std::string name = random_string();
516 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
517 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
518 auto tree = TreeType(std::move(store), workers, initial_size);
519
520 add_value(tree, NullifierLeafValue(30));
521 add_value(tree, NullifierLeafValue(10));
522 add_value(tree, NullifierLeafValue(20));
523 add_value(tree, NullifierLeafValue(40));
524
525 // check the committed state and that the uncommitted state is empty
526 check_find_leaf_index(tree, NullifierLeafValue(10), 1 + initial_size, true, true);
527 check_find_leaf_index<NullifierLeafValue, TreeType>(
528 tree, { NullifierLeafValue(10) }, { std::nullopt }, true, false);
529
530 check_find_leaf_index<NullifierLeafValue, TreeType>(tree, { NullifierLeafValue(15) }, { std::nullopt }, true, true);
531 check_find_leaf_index<NullifierLeafValue, TreeType>(
532 tree, { NullifierLeafValue(15) }, { std::nullopt }, true, false);
533
534 check_find_leaf_index(tree, NullifierLeafValue(40), 3 + initial_size, true, true);
535 check_find_leaf_index(tree, NullifierLeafValue(30), 0 + initial_size, true, true);
536 check_find_leaf_index(tree, NullifierLeafValue(20), 2 + initial_size, true, true);
537
538 check_find_leaf_index<NullifierLeafValue, TreeType>(
539 tree, { NullifierLeafValue(40) }, { std::nullopt }, true, false);
540 check_find_leaf_index<NullifierLeafValue, TreeType>(
541 tree, { NullifierLeafValue(30) }, { std::nullopt }, true, false);
542 check_find_leaf_index<NullifierLeafValue, TreeType>(
543 tree, { NullifierLeafValue(20) }, { std::nullopt }, true, false);
544
545 commit_tree(tree);
546
551 NullifierLeafValue(48) };
552 add_values(tree, values);
553
554 // check the now committed state
555 check_find_leaf_index(tree, NullifierLeafValue(40), 3 + initial_size, true, false);
556 check_find_leaf_index(tree, NullifierLeafValue(30), 0 + initial_size, true, false);
557 check_find_leaf_index(tree, NullifierLeafValue(20), 2 + initial_size, true, false);
558
559 // check the new uncommitted state
560 check_find_leaf_index(tree, NullifierLeafValue(18), 5 + initial_size, true, true);
561 check_find_leaf_index<NullifierLeafValue, TreeType>(
562 tree, { NullifierLeafValue(18) }, { std::nullopt }, true, false);
563
564 commit_tree(tree);
565
567 add_values(tree, values);
568
569 // we now have duplicate leaf 18, one committed the other not
570 check_find_leaf_index(tree, NullifierLeafValue(18), 5 + initial_size, true, true);
571 check_find_leaf_index(tree, NullifierLeafValue(18), 5 + initial_size, true, false);
572}
573
575{
577 index_t initial_size = 2;
578 index_t current_size = initial_size;
579 ThreadPoolPtr workers = make_thread_pool(1);
580 ;
581 constexpr size_t depth = 10;
582 std::string name = random_string();
583
584 {
585 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
586 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
587 auto tree = TreeType(std::move(store), workers, initial_size);
588
589 check_size(tree, current_size);
590 check_root(tree, memdb.root());
591 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
592
593 add_value(tree, NullifierLeafValue(VALUES[512]));
594
595 // Committed data should not have changed
596 check_size(tree, current_size, false);
597 check_root(tree, memdb.root(), false);
598 check_sibling_path(tree, 0, memdb.get_sibling_path(0), false);
599 check_sibling_path(tree, 1, memdb.get_sibling_path(1), false);
600
601 memdb.update_element(VALUES[512]);
602
603 // Uncommitted data should have changed
604 check_size(tree, current_size + 1, true);
605 check_root(tree, memdb.root(), true);
606 check_sibling_path(tree, 0, memdb.get_sibling_path(0), true);
607 check_sibling_path(tree, 1, memdb.get_sibling_path(1), true);
608
609 // Now commit
610 commit_tree(tree);
611
612 // Now committed data should have changed
613 check_size(tree, ++current_size, false);
614 check_root(tree, memdb.root(), false);
615 check_sibling_path(tree, 0, memdb.get_sibling_path(0), false);
616 check_sibling_path(tree, 1, memdb.get_sibling_path(1), false);
617 }
618
619 // Now restore and it should continue from where we left off
620 {
621 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
622 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
623 auto tree = TreeType(std::move(store), workers, initial_size);
624
625 // check uncommitted state
626 check_size(tree, current_size);
627 check_root(tree, memdb.root());
628 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
629
630 // check committed state
631 check_size(tree, current_size, false);
632 check_root(tree, memdb.root(), false);
633 check_sibling_path(tree, 0, memdb.get_sibling_path(0), false);
634 }
635}
636
637void test_batch_insert(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
638{
640 const uint32_t batch_size = batchSize;
641 const uint32_t num_batches = 16;
642 uint32_t depth = 10;
643 ThreadPoolPtr workers = make_thread_pool(1);
644 ThreadPoolPtr multi_workers = make_thread_pool(8);
645 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
646
647 auto tree1 = create_tree(directory, mapSize, maxReaders, depth, batch_size, workers);
648 auto tree2 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
649 auto tree3 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
650
651 for (uint32_t i = 0; i < num_batches; i++) {
652
653 check_root(*tree1, memdb.root());
654 check_root(*tree2, memdb.root());
655 check_root(*tree3, memdb.root());
656 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
657 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
658 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
659
660 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
661 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
662 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
663
665 std::vector<fr_sibling_path> memory_tree_sibling_paths;
666 for (uint32_t j = 0; j < batch_size; j++) {
667 batch.emplace_back(random_engine.get_random_uint256());
668 fr_sibling_path path = memdb.update_element(batch[j].nullifier);
669 memory_tree_sibling_paths.push_back(path);
670 }
673 {
674 Signal signal;
675 CompletionCallback completion =
677 tree1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
678 signal.signal_level();
679 };
680 tree1->add_or_update_values(batch, completion);
681 signal.wait_for_level();
682 }
683
684 {
685 Signal signal;
686 CompletionCallback completion =
688 tree2_low_leaf_witness_data = response.inner.low_leaf_witness_data;
689 signal.signal_level();
690 };
691 tree2->add_or_update_values(batch, completion);
692 signal.wait_for_level();
693 }
694
695 {
696 Signal signal;
697 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
698 tree3->add_or_update_values(batch, completion);
699 signal.wait_for_level();
700 }
701 check_root(*tree1, memdb.root());
702 check_root(*tree2, memdb.root());
703 check_root(*tree3, memdb.root());
704
705 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
706 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
707 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
708
709 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
710 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
711 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
712
713 for (uint32_t j = 0; j < batch_size; j++) {
714 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).leaf, tree2_low_leaf_witness_data->at(j).leaf);
715 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).index, tree2_low_leaf_witness_data->at(j).index);
716 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).path, tree2_low_leaf_witness_data->at(j).path);
717 }
718 }
719}
720
722 std::string directory,
723 uint64_t mapSize,
724 uint64_t maxReaders)
725{
727 const uint32_t batch_size = batchSize;
728 const uint32_t num_batches = 16;
729 uint32_t depth = 10;
730 ThreadPoolPtr workers = make_thread_pool(1);
731 ThreadPoolPtr multi_workers = make_thread_pool(8);
732 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
733
734 for (uint32_t i = 0; i < num_batches; i++) {
735
736 auto tree1 = create_tree(directory, mapSize, maxReaders, depth, batch_size, workers);
737 auto tree2 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
738 auto tree3 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
739
740 check_root(*tree1, memdb.root());
741 check_root(*tree2, memdb.root());
742 check_root(*tree3, memdb.root());
743 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
744 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
745 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
746
747 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
748 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
749 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
750
752 std::vector<fr_sibling_path> memory_tree_sibling_paths;
753 for (uint32_t j = 0; j < batch_size; j++) {
754 batch.emplace_back(random_engine.get_random_uint256());
755 fr_sibling_path path = memdb.update_element(batch[j].nullifier);
756 memory_tree_sibling_paths.push_back(path);
757 }
760 {
761 Signal signal;
762 CompletionCallback completion =
764 tree1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
765 signal.signal_level();
766 };
767 tree1->add_or_update_values(batch, completion);
768 signal.wait_for_level();
769 }
770
771 {
772 Signal signal;
773 CompletionCallback completion =
775 tree2_low_leaf_witness_data = response.inner.low_leaf_witness_data;
776 signal.signal_level();
777 };
778 tree2->add_or_update_values(batch, completion);
779 signal.wait_for_level();
780 }
781
782 {
783 Signal signal;
784 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
785 tree3->add_or_update_values(batch, completion);
786 signal.wait_for_level();
787 }
788 check_root(*tree1, memdb.root());
789 check_root(*tree2, memdb.root());
790 check_root(*tree3, memdb.root());
791
792 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
793 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
794 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
795
796 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
797 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
798 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
799
800 for (uint32_t j = 0; j < batch_size; j++) {
801 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).leaf, tree2_low_leaf_witness_data->at(j).leaf);
802 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).index, tree2_low_leaf_witness_data->at(j).index);
803 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).path, tree2_low_leaf_witness_data->at(j).path);
804 }
805
806 commit_tree(*tree1);
807 commit_tree(*tree2);
808 commit_tree(*tree3);
809 }
810}
811
813{
814 uint32_t batchSize = 2;
815 while (batchSize <= 2) {
816 test_batch_insert(batchSize, _directory, _mapSize, _maxReaders);
817 batchSize <<= 1;
818 }
819}
820
822{
823 uint32_t batchSize = 2;
824 while (batchSize <= 32) {
825 test_batch_insert(batchSize, _directory, _mapSize, _maxReaders);
826 batchSize <<= 1;
827 }
828}
829
830TEST_F(PersistedContentAddressedIndexedTreeTest, test_compare_batch_inserts_different_sized_thread_pools)
831{
832 const uint32_t batch_size = 128;
833 uint32_t depth = 20;
834 ThreadPoolPtr workers = make_thread_pool(1);
835 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
836
837 auto tree1 = create_tree(_directory, _mapSize, _maxReaders, depth, batch_size, workers);
838 auto tree2 = create_tree(_directory, _mapSize, _maxReaders, depth, batch_size, workers);
839
841 for (uint32_t i = 1; i <= 12; i++) {
842 ThreadPoolPtr multiWorkers = make_thread_pool(i);
843 auto tree = create_tree(_directory, _mapSize, _maxReaders, depth, batch_size, multiWorkers);
844 trees.emplace_back(std::move(tree));
845 }
846
847 std::vector<fr> tree1Roots;
848 std::vector<fr> tree2Roots;
849
850 for (uint32_t round = 0; round < 10; round++) {
851 std::vector<fr> frValues1 = create_values(3);
852 std::vector<fr> frValues2 = create_values(3);
854 for (uint32_t i = 0; i < 3; i++) {
855 leaves[i] = frValues1[i];
856 leaves[i + 64] = frValues2[i];
857 }
858
859 std::vector<NullifierLeafValue> first(leaves.begin(), leaves.begin() + 64);
860 std::vector<NullifierLeafValue> second(leaves.begin() + 64, leaves.end());
861
862 add_values(*tree1, first);
863 add_values(*tree1, second);
864
865 block_sync_values(*tree2, leaves);
866
867 tree1Roots.push_back(get_root(*tree1));
868 tree2Roots.push_back(get_root(*tree2, true));
869 EXPECT_EQ(tree1Roots[round], tree2Roots[round]);
870
871 for (const auto& tree : trees) {
872 block_sync_values(*tree, leaves);
873 const fr treeRoot = get_root(*tree, true);
874 EXPECT_EQ(treeRoot, tree1Roots[round]);
875 }
876 }
877}
878
879TEST_F(PersistedContentAddressedIndexedTreeTest, reports_an_error_if_batch_contains_duplicate)
880{
881 index_t current_size = 2;
882 ThreadPoolPtr workers = make_thread_pool(1);
883 ;
884 constexpr size_t depth = 10;
885 std::string name = random_string();
886 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
887 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
888 auto tree = TreeType(std::move(store), workers, current_size);
889
891 for (uint32_t i = 0; i < 16; i++) {
892 values.emplace_back(VALUES[i]);
893 }
894 values[8] = values[0];
895
896 std::stringstream ss;
897 ss << "Duplicate key not allowed in same batch, key value: " << values[0].nullifier << ", tree: " << name;
898
899 Signal signal;
900 auto add_completion = [&](const TypedResponse<AddIndexedDataResponse<NullifierLeafValue>>& response) {
901 EXPECT_EQ(response.success, false);
902 EXPECT_EQ(response.message, ss.str());
903 signal.signal_level();
904 };
905 tree.add_or_update_values(values, add_completion);
906 signal.wait_for_level();
907}
908
909void test_sequential_insert_vs_batch(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
910{
912 const uint32_t batch_size = batchSize;
913 const uint32_t num_batches = 16;
914 uint32_t depth = 10;
915 ThreadPoolPtr workers = make_thread_pool(1);
916 ThreadPoolPtr multi_workers = make_thread_pool(8);
917 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
918
919 auto sequential_tree_1 = create_tree(directory, mapSize, maxReaders, depth, batch_size, workers);
920 auto sequential_tree_2 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
921 auto sequential_tree_3 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
922 auto batch_tree = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
923
924 for (uint32_t i = 0; i < num_batches; i++) {
925
926 check_root(*sequential_tree_1, memdb.root());
927 check_root(*sequential_tree_2, memdb.root());
928 check_root(*sequential_tree_3, memdb.root());
929 check_root(*batch_tree, memdb.root());
930 check_sibling_path(*sequential_tree_1, 0, memdb.get_sibling_path(0));
931 check_sibling_path(*sequential_tree_2, 0, memdb.get_sibling_path(0));
932 check_sibling_path(*sequential_tree_3, 0, memdb.get_sibling_path(0));
933 check_sibling_path(*batch_tree, 0, memdb.get_sibling_path(0));
934
935 check_sibling_path(*sequential_tree_1, 512, memdb.get_sibling_path(512));
936 check_sibling_path(*sequential_tree_2, 512, memdb.get_sibling_path(512));
937 check_sibling_path(*sequential_tree_3, 512, memdb.get_sibling_path(512));
938 check_sibling_path(*batch_tree, 512, memdb.get_sibling_path(512));
939
941 std::vector<fr_sibling_path> memory_tree_sibling_paths;
942 for (uint32_t j = 0; j < batch_size; j++) {
943 batch.emplace_back(random_engine.get_random_uint256());
944 fr_sibling_path path = memdb.update_element(batch[j].nullifier);
945 memory_tree_sibling_paths.push_back(path);
946 }
947 std::shared_ptr<std::vector<LeafUpdateWitnessData<NullifierLeafValue>>> sequential_tree_1_low_leaf_witness_data;
949 sequential_tree_1_insertion_witness_data;
950 std::shared_ptr<std::vector<LeafUpdateWitnessData<NullifierLeafValue>>> sequential_tree_2_low_leaf_witness_data;
952 sequential_tree_2_insertion_witness_data;
953
954 {
955 Signal signal;
958 sequential_tree_1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
959 sequential_tree_1_insertion_witness_data = response.inner.insertion_witness_data;
960 signal.signal_level();
961 };
962 sequential_tree_1->add_or_update_values_sequentially(batch, completion);
963 signal.wait_for_level();
964 }
965
966 {
967 Signal signal;
970 sequential_tree_2_low_leaf_witness_data = response.inner.low_leaf_witness_data;
971 sequential_tree_2_insertion_witness_data = response.inner.insertion_witness_data;
972 signal.signal_level();
973 };
974 sequential_tree_2->add_or_update_values_sequentially(batch, completion);
975 signal.wait_for_level();
976 }
977
978 {
979 Signal signal;
980 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
981 sequential_tree_3->add_or_update_values_sequentially(batch, completion);
982 signal.wait_for_level();
983 }
984
985 {
986 Signal signal;
987 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
988 batch_tree->add_or_update_values(batch, completion);
989 signal.wait_for_level();
990 }
991 check_root(*sequential_tree_1, memdb.root());
992 check_root(*sequential_tree_2, memdb.root());
993 check_root(*sequential_tree_3, memdb.root());
994 check_root(*batch_tree, memdb.root());
995
996 check_sibling_path(*sequential_tree_1, 0, memdb.get_sibling_path(0));
997 check_sibling_path(*sequential_tree_2, 0, memdb.get_sibling_path(0));
998 check_sibling_path(*sequential_tree_3, 0, memdb.get_sibling_path(0));
999 check_sibling_path(*batch_tree, 0, memdb.get_sibling_path(0));
1000
1001 check_sibling_path(*sequential_tree_1, 512, memdb.get_sibling_path(512));
1002 check_sibling_path(*sequential_tree_2, 512, memdb.get_sibling_path(512));
1003 check_sibling_path(*sequential_tree_3, 512, memdb.get_sibling_path(512));
1004 check_sibling_path(*batch_tree, 512, memdb.get_sibling_path(512));
1005
1006 for (uint32_t j = 0; j < batch_size; j++) {
1007 EXPECT_EQ(sequential_tree_1_low_leaf_witness_data->at(j).leaf,
1008 sequential_tree_2_low_leaf_witness_data->at(j).leaf);
1009 EXPECT_EQ(sequential_tree_1_low_leaf_witness_data->at(j).index,
1010 sequential_tree_2_low_leaf_witness_data->at(j).index);
1011 EXPECT_EQ(sequential_tree_1_low_leaf_witness_data->at(j).path,
1012 sequential_tree_2_low_leaf_witness_data->at(j).path);
1013
1014 EXPECT_EQ(sequential_tree_1_insertion_witness_data->at(j).leaf,
1015 sequential_tree_2_insertion_witness_data->at(j).leaf);
1016 EXPECT_EQ(sequential_tree_1_insertion_witness_data->at(j).index,
1017 sequential_tree_2_insertion_witness_data->at(j).index);
1018 EXPECT_EQ(sequential_tree_1_insertion_witness_data->at(j).path,
1019 sequential_tree_2_insertion_witness_data->at(j).path);
1020 }
1021 }
1022}
1023
1025{
1026 uint32_t batchSize = 2;
1027 while (batchSize <= 2) {
1028 test_sequential_insert_vs_batch(batchSize, _directory, _mapSize, _maxReaders);
1029 batchSize <<= 1;
1030 }
1031}
1032
1033TEST_F(PersistedContentAddressedIndexedTreeTest, sequential_insert_allows_multiple_inserts_to_the_same_key)
1034{
1035 index_t current_size = 2;
1036 ThreadPoolPtr workers = make_thread_pool(8);
1037 // Create a depth-3 indexed merkle tree
1038 constexpr size_t depth = 3;
1039 std::string name = random_string();
1040 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1044 std::move(store), workers, current_size);
1045
1047 add_values_sequentially(tree, values);
1048
1049 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2).leaf.value, values[1].value);
1050 check_size(tree, 3);
1051}
1052
1053template <typename LeafValueType> fr hash_leaf(const IndexedLeaf<LeafValueType>& leaf)
1054{
1055 return HashPolicy::hash(leaf.get_hash_inputs());
1056}
1057
1058bool verify_sibling_path(TreeType& tree, const IndexedNullifierLeafType& leaf_value, const uint32_t idx)
1059{
1060 fr root = get_root(tree, true);
1061 fr_sibling_path path = get_sibling_path(tree, idx, true);
1062 auto current = hash_leaf(leaf_value);
1063 uint32_t depth_ = static_cast<uint32_t>(path.size());
1064 uint32_t index = idx;
1065 for (uint32_t i = 0; i < depth_; ++i) {
1066 fr left = (index & 1) ? path[i] : current;
1067 fr right = (index & 1) ? current : path[i];
1068 current = HashPolicy::hash_pair(left, right);
1069 index >>= 1;
1070 }
1071 return current == root;
1072}
1073
1075{
1076 index_t current_size = 2;
1077 ThreadPoolPtr workers = make_thread_pool(8);
1078 // Create a depth-3 indexed merkle tree
1079 constexpr size_t depth = 3;
1080 std::string name = random_string();
1081 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1082 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1083 auto tree = TreeType(std::move(store), workers, current_size);
1084
1094 IndexedNullifierLeafType zero_leaf(NullifierLeafValue(0), 1, 1);
1096 check_size(tree, current_size);
1097 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), zero_leaf);
1098 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), one_leaf);
1099
1109 add_value(tree, NullifierLeafValue(30));
1110 check_size(tree, ++current_size);
1111 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1112 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 2, 30));
1113 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 0, 0));
1114
1124 add_value(tree, NullifierLeafValue(10));
1125 check_size(tree, ++current_size);
1126 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1127 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 3, 10));
1128 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 0, 0));
1129 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 3), create_indexed_nullifier_leaf(10, 2, 30));
1130
1140 add_value(tree, NullifierLeafValue(20));
1141 check_size(tree, ++current_size);
1142 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1143 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 3, 10));
1144 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 0, 0));
1145 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 3), create_indexed_nullifier_leaf(10, 4, 20));
1146 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 4), create_indexed_nullifier_leaf(20, 2, 30));
1147
1148 // Adding the same value must not affect anything
1149 // tree.update_element(20);
1150 // EXPECT_EQ(tree.get_leaves().size(), 4);
1151 // EXPECT_EQ(tree.get_leaves()[0], hash_leaf({ 0, 2, 10 }));
1152 // EXPECT_EQ(tree.get_leaves()[1], hash_leaf({ 30, 0, 0 }));
1153 // EXPECT_EQ(tree.get_leaves()[2], hash_leaf({ 10, 3, 20 }));
1154 // EXPECT_EQ(tree.get_leaves()[3], hash_leaf({ 20, 1, 30 }));
1155
1165 add_value(tree, NullifierLeafValue(50));
1166 check_size(tree, ++current_size);
1167 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1168 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 3, 10));
1169 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 5, 50));
1170 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 3), create_indexed_nullifier_leaf(10, 4, 20));
1171 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 4), create_indexed_nullifier_leaf(20, 2, 30));
1172 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 5), create_indexed_nullifier_leaf(50, 0, 0));
1173
1174 // Manually compute the node values
1175 auto e000 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 0));
1176 auto e001 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 1));
1177 auto e010 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 2));
1178 auto e011 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 3));
1179 auto e100 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 4));
1180 auto e101 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 5));
1181 auto e110 = fr::zero();
1182 auto e111 = fr::zero();
1183
1184 auto e00 = HashPolicy::hash_pair(e000, e001);
1185 auto e01 = HashPolicy::hash_pair(e010, e011);
1186 auto e10 = HashPolicy::hash_pair(e100, e101);
1187 auto e11 = HashPolicy::hash_pair(e110, e111);
1188
1189 auto e0 = HashPolicy::hash_pair(e00, e01);
1190 auto e1 = HashPolicy::hash_pair(e10, e11);
1191 auto root = HashPolicy::hash_pair(e0, e1);
1192
1193 // Check the hash path at index 2 and 3
1194 // Note: This merkle proof would also serve as a non-membership proof of values in (10, 20) and (20, 30)
1195 fr_sibling_path expected = {
1196 e001,
1197 e01,
1198 e1,
1199 };
1200 check_sibling_path(tree, 0, expected);
1201 expected = {
1202 e000,
1203 e01,
1204 e1,
1205 };
1206 check_sibling_path(tree, 1, expected);
1207 expected = {
1208 e011,
1209 e00,
1210 e1,
1211 };
1212 check_sibling_path(tree, 2, expected);
1213 expected = {
1214 e010,
1215 e00,
1216 e1,
1217 };
1218 check_sibling_path(tree, 3, expected);
1219 check_root(tree, root);
1220
1221 // Check the hash path at index 6 and 7
1222 expected = {
1223 e111,
1224 e10,
1225 e0,
1226 };
1227 check_sibling_path(tree, 6, expected);
1228 expected = {
1229 e110,
1230 e10,
1231 e0,
1232 };
1233 check_sibling_path(tree, 7, expected);
1234}
1235
1237{
1238 index_t current_size = 2;
1239 ThreadPoolPtr workers = make_thread_pool(1);
1240 ;
1241 // Create a depth-8 indexed merkle tree
1242 constexpr uint32_t depth = 8;
1243 std::string name = random_string();
1244 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1245 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1246 auto tree = TreeType(std::move(store), workers, current_size);
1247
1249 check_size(tree, current_size);
1250 EXPECT_EQ(hash_leaf(get_leaf<NullifierLeafValue>(tree, 0)), hash_leaf(zero_leaf));
1251
1252 // Add 20 random values to the tree
1253 for (uint32_t i = 0; i < 20; i++) {
1254 auto value = fr::random_element();
1256 ++current_size;
1257 }
1258
1259 auto abs_diff = [](uint256_t a, uint256_t b) {
1260 if (a > b) {
1261 return (a - b);
1262 } else {
1263 return (b - a);
1264 }
1265 };
1266
1267 check_size(tree, current_size);
1268
1269 // Check if a new random value is not a member of this tree.
1270 fr new_member = fr::random_element();
1271 std::vector<uint256_t> differences;
1272 for (uint32_t i = 0; i < uint32_t(21); i++) {
1273 uint256_t diff_hi =
1274 abs_diff(uint256_t(new_member), uint256_t(get_leaf<NullifierLeafValue>(tree, i).leaf.get_key()));
1275 uint256_t diff_lo =
1276 abs_diff(uint256_t(new_member), uint256_t(get_leaf<NullifierLeafValue>(tree, i).leaf.get_key()));
1277 differences.push_back(diff_hi + diff_lo);
1278 }
1279 auto it = std::min_element(differences.begin(), differences.end());
1280 auto index = static_cast<uint32_t>(it - differences.begin());
1281
1282 // Merkle proof at `index` proves non-membership of `new_member`
1283 EXPECT_TRUE(verify_sibling_path(tree, get_leaf<NullifierLeafValue>(tree, index), index));
1284}
1285
1287{
1288 constexpr size_t depth = 10;
1290 fr_sibling_path initial_path = memdb.get_sibling_path(0);
1291 memdb.update_element(VALUES[0]);
1292 fr_sibling_path final_sibling_path = memdb.get_sibling_path(0);
1293
1294 uint32_t num_reads = 16 * 1024;
1295 std::vector<fr_sibling_path> paths(num_reads);
1296
1297 {
1298 std::string name = random_string();
1299 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1300 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1302 TreeType tree(std::move(store), pool, 2);
1303
1304 check_size(tree, 2);
1305
1306 Signal signal(1 + num_reads);
1307
1308 auto add_completion = [&](const TypedResponse<AddIndexedDataResponse<NullifierLeafValue>>&) {
1309 auto commit_completion = [&](const TypedResponse<CommitResponse>&) { signal.signal_decrement(); };
1310 tree.commit(commit_completion);
1311 };
1312 tree.add_or_update_value(VALUES[0], add_completion);
1313
1314 for (size_t i = 0; i < num_reads; i++) {
1315 auto completion = [&, i](const TypedResponse<GetSiblingPathResponse>& response) {
1316 paths[i] = response.inner.path;
1317 signal.signal_decrement();
1318 };
1319 tree.get_sibling_path(0, completion, false);
1320 }
1321 signal.wait_for_level();
1322 }
1323}
1324
1325TEST_F(PersistedContentAddressedIndexedTreeTest, test_indexed_memory_with_public_data_writes)
1326{
1327 index_t current_size = 2;
1328 ThreadPoolPtr workers = make_thread_pool(8);
1329 // Create a depth-3 indexed merkle tree
1330 constexpr size_t depth = 3;
1331 std::string name = random_string();
1332 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1336 std::move(store), workers, current_size);
1337
1350 check_size(tree, current_size);
1351 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
1352 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
1353
1364 add_value(tree, PublicDataLeafValue(30, 5));
1365 check_size(tree, ++current_size);
1366 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1367 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
1368 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1369
1380 add_value(tree, PublicDataLeafValue(10, 20));
1381 check_size(tree, ++current_size);
1382 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1383 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1384 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1385 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1386
1397 add_value(tree, PublicDataLeafValue(30, 6));
1398 // The size still increases as we pad with an empty leaf
1399 check_size(tree, ++current_size);
1400
1401 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1402 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1403 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
1404 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1405 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(0, 0, 0, 0));
1406
1417 add_value(tree, PublicDataLeafValue(50, 8));
1418 check_size(tree, ++current_size);
1419 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1420 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1421 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 5, 50));
1422 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1423 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(0, 0, 0, 0));
1424 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 5), create_indexed_public_data_leaf(50, 8, 0, 0));
1425
1426 // Manually compute the node values
1427 auto e000 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 0));
1428 auto e001 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 1));
1429 auto e010 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 2));
1430 auto e011 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 3));
1431 auto e100 = fr::zero(); // tree doesn't hash 0 leaves!
1432 auto e101 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 5));
1433 auto e110 = fr::zero();
1434 auto e111 = fr::zero();
1435
1436 auto e00 = HashPolicy::hash_pair(e000, e001);
1437 auto e01 = HashPolicy::hash_pair(e010, e011);
1438 auto e10 = HashPolicy::hash_pair(e100, e101);
1439 auto e11 = HashPolicy::hash_pair(e110, e111);
1440
1441 auto e0 = HashPolicy::hash_pair(e00, e01);
1442 auto e1 = HashPolicy::hash_pair(e10, e11);
1443 auto root = HashPolicy::hash_pair(e0, e1);
1444
1445 fr_sibling_path expected = {
1446 e001,
1447 e01,
1448 e1,
1449 };
1450 check_sibling_path(tree, 0, expected);
1451 expected = {
1452 e000,
1453 e01,
1454 e1,
1455 };
1456 check_sibling_path(tree, 1, expected);
1457 expected = {
1458 e011,
1459 e00,
1460 e1,
1461 };
1462 check_sibling_path(tree, 2, expected);
1463 expected = {
1464 e010,
1465 e00,
1466 e1,
1467 };
1468 check_sibling_path(tree, 3, expected);
1469 check_root(tree, root);
1470
1471 // Check the hash path at index 6 and 7
1472 expected = {
1473 e111,
1474 e10,
1475 e0,
1476 };
1477 check_sibling_path(tree, 6, expected);
1478 expected = {
1479 e110,
1480 e10,
1481 e0,
1482 };
1483 check_sibling_path(tree, 7, expected);
1484}
1485
1486TEST_F(PersistedContentAddressedIndexedTreeTest, test_indexed_memory_with_sequential_public_data_writes)
1487{
1488 index_t current_size = 2;
1489 ThreadPoolPtr workers = make_thread_pool(8);
1490 // Create a depth-3 indexed merkle tree
1491 constexpr size_t depth = 3;
1492 std::string name = random_string();
1493 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1497 std::move(store), workers, current_size);
1498
1511 check_size(tree, current_size);
1512 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
1513 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
1514
1526 check_size(tree, ++current_size);
1527 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1528 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
1529 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1530
1542 check_size(tree, ++current_size);
1543 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1544 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1545 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1546 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1547
1559 // The size does not increase since sequential insertion doesn't pad
1560 check_size(tree, current_size);
1561
1562 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1563 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1564 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
1565 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1566
1578 check_size(tree, ++current_size);
1579 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1580 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1581 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
1582 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1583 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
1584
1585 // Manually compute the node values
1586 auto e000 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 0));
1587 auto e001 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 1));
1588 auto e010 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 2));
1589 auto e011 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 3));
1590 auto e100 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 4));
1591 auto e101 = fr::zero();
1592 auto e110 = fr::zero();
1593 auto e111 = fr::zero();
1594
1595 auto e00 = HashPolicy::hash_pair(e000, e001);
1596 auto e01 = HashPolicy::hash_pair(e010, e011);
1597 auto e10 = HashPolicy::hash_pair(e100, e101);
1598 auto e11 = HashPolicy::hash_pair(e110, e111);
1599
1600 auto e0 = HashPolicy::hash_pair(e00, e01);
1601 auto e1 = HashPolicy::hash_pair(e10, e11);
1602 auto root = HashPolicy::hash_pair(e0, e1);
1603
1604 fr_sibling_path expected = {
1605 e001,
1606 e01,
1607 e1,
1608 };
1609 check_sibling_path(tree, 0, expected);
1610 expected = {
1611 e000,
1612 e01,
1613 e1,
1614 };
1615 check_sibling_path(tree, 1, expected);
1616 expected = {
1617 e011,
1618 e00,
1619 e1,
1620 };
1621 check_sibling_path(tree, 2, expected);
1622 expected = {
1623 e010,
1624 e00,
1625 e1,
1626 };
1627 check_sibling_path(tree, 3, expected);
1628 check_root(tree, root);
1629
1630 // Check the hash path at index 6 and 7
1631 expected = {
1632 e111,
1633 e10,
1634 e0,
1635 };
1636 check_sibling_path(tree, 6, expected);
1637 expected = {
1638 e110,
1639 e10,
1640 e0,
1641 };
1642 check_sibling_path(tree, 7, expected);
1643}
1644
1646{
1647 // Create a depth-8 indexed merkle tree
1648 constexpr uint32_t depth = 8;
1649
1650 ThreadPoolPtr workers = make_thread_pool(1);
1651 std::string name = random_string();
1652 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1653 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1654 auto tree = TreeType(std::move(store), workers, 2);
1655
1656 auto predecessor = get_low_leaf(tree, NullifierLeafValue(42));
1657
1658 EXPECT_EQ(predecessor.is_already_present, false);
1659 EXPECT_EQ(predecessor.index, 1);
1660
1661 add_value(tree, NullifierLeafValue(42));
1662
1663 predecessor = get_low_leaf(tree, NullifierLeafValue(42));
1664 // returns the current leaf since it exists already. Inserting 42 again would modify the existing leaf
1665 EXPECT_EQ(predecessor.is_already_present, true);
1666 EXPECT_EQ(predecessor.index, 2);
1667}
1668
1670{
1671 // Create a depth-8 indexed merkle tree
1672 constexpr uint32_t depth = 8;
1673
1674 ThreadPoolPtr workers = make_thread_pool(1);
1675 ;
1676 std::string name = random_string();
1677 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1678 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1679 auto tree = TreeType(std::move(store), workers, 2);
1680
1681 add_value(tree, NullifierLeafValue(42));
1682 check_size(tree, 3);
1683
1684 commit_tree(tree);
1685
1686 add_value(tree, NullifierLeafValue(42), false);
1687 // expect this to fail as no data is present
1688 commit_tree(tree);
1689 check_size(tree, 3);
1690}
1691
1692TEST_F(PersistedContentAddressedIndexedTreeTest, test_historic_sibling_path_retrieval)
1693{
1695 const uint32_t batch_size = 16;
1696 const uint32_t num_batches = 8;
1697 std::string name1 = random_string();
1698 std::string name2 = random_string();
1699 uint32_t depth = 10;
1700 ThreadPoolPtr multi_workers = make_thread_pool(8);
1701 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1702
1703 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1704 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1705 auto tree1 = TreeType(std::move(store1), multi_workers, batch_size);
1706
1707 std::vector<fr_sibling_path> memory_tree_sibling_paths_index_0;
1708
1709 auto check = [&]() {
1710 check_root(tree1, memdb.root());
1711 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1712 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1713
1714 for (uint32_t i = 0; i < memory_tree_sibling_paths_index_0.size(); i++) {
1715 check_historic_sibling_path(tree1, 0, i + 1, memory_tree_sibling_paths_index_0[i]);
1716 }
1717 };
1718
1719 for (uint32_t i = 0; i < num_batches; i++) {
1720
1721 check_root(tree1, memdb.root());
1722 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1723 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1724
1726
1727 for (uint32_t j = 0; j < batch_size; j++) {
1728 batch.emplace_back(random_engine.get_random_uint256());
1729 memdb.update_element(batch[j].get_key());
1730 }
1731 memory_tree_sibling_paths_index_0.push_back(memdb.get_sibling_path(0));
1734 {
1735 Signal signal;
1736 CompletionCallback completion =
1738 tree1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
1739 signal.signal_level();
1740 };
1741 tree1.add_or_update_values(batch, completion);
1742 signal.wait_for_level();
1743 }
1744 check_root(tree1, memdb.root());
1745 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1746 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1747 commit_tree(tree1);
1748 check();
1749 }
1750}
1751
1753{
1754 index_t current_size = 2;
1755 ThreadPoolPtr workers = make_thread_pool(8);
1756 // Create a depth-3 indexed merkle tree
1757 constexpr size_t depth = 3;
1758 std::string name = random_string();
1759 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1762 using LocalTreeType =
1764 auto tree = LocalTreeType(std::move(store), workers, current_size);
1765
1778 check_size(tree, current_size);
1779 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
1780 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
1781
1793 commit_tree(tree);
1794 check_size(tree, ++current_size);
1795 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1796 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
1797 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1798
1799 auto leaf1AtBlock1 = PublicDataLeafValue(1, 0);
1800
1812 check_size(tree, ++current_size);
1813 commit_tree(tree);
1814 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1815 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1816 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1817 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1818
1819 auto leaf2AtBlock2 = PublicDataLeafValue(30, 5);
1820 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, true);
1821
1822 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
1823 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
1824 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
1825
1837 // The size does not increase since sequential insertion doesn't pad
1838 check_size(tree, current_size);
1839 commit_tree(tree);
1840 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1841 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1842 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
1843 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1844
1845 auto leaf2AtBlock3 = PublicDataLeafValue(30, 6);
1846 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
1847
1848 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
1849 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
1850 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
1851
1863 check_size(tree, ++current_size);
1864 commit_tree(tree);
1865 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1866 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1867 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
1868 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1869 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
1870
1871 check_historic_leaf(tree, leaf2AtBlock3, 2, 3, true);
1872
1873 // should not be found at block 1
1874 check_historic_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
1875 tree, { PublicDataLeafValue(10, 20) }, 1, 0, { std::nullopt }, true);
1876 // should be found at block
1877 check_historic_find_leaf_index_from(tree, PublicDataLeafValue(10, 20), 2, 0, 3, true);
1878
1880 EXPECT_EQ(lowLeaf.index, 1);
1881
1882 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(20, 0));
1883 EXPECT_EQ(lowLeaf.index, 3);
1884
1885 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(60, 0));
1886 EXPECT_EQ(lowLeaf.index, 2);
1887}
1888
1889TEST_F(PersistedContentAddressedIndexedTreeTest, test_inserting_a_duplicate_committed_nullifier_should_fail)
1890{
1891 const uint32_t batch_size = 16;
1892 uint32_t depth = 10;
1893 ThreadPoolPtr multi_workers = make_thread_pool(1);
1894 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1895
1896 std::string name1 = random_string();
1897 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1898 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1899 auto tree = TreeType(std::move(store1), multi_workers, batch_size);
1900
1901 std::vector<fr> values = create_values(batch_size);
1902 std::vector<NullifierLeafValue> nullifierValues(batch_size);
1903 std::transform(
1904 values.begin(), values.end(), nullifierValues.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1905
1906 add_values(tree, nullifierValues);
1907 commit_tree(tree);
1908
1909 // create a new set of values
1910 std::vector<fr> values2 = create_values(batch_size);
1911
1912 // copy one of the previous values into the middle of the batch
1913 values2[batch_size / 2] = values[0];
1914 std::vector<NullifierLeafValue> nullifierValues2(batch_size);
1915 std::transform(
1916 values2.begin(), values2.end(), nullifierValues2.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1917 add_values(tree, nullifierValues2, false);
1918}
1919
1920TEST_F(PersistedContentAddressedIndexedTreeTest, test_inserting_a_duplicate_uncommitted_nullifier_should_fail)
1921{
1922 const uint32_t batch_size = 16;
1923 uint32_t depth = 10;
1924 ThreadPoolPtr multi_workers = make_thread_pool(1);
1925 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1926
1927 std::string name1 = random_string();
1928 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1929 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1930 auto tree = TreeType(std::move(store1), multi_workers, batch_size);
1931
1932 std::vector<fr> values = create_values(batch_size);
1933 std::vector<NullifierLeafValue> nullifierValues(batch_size);
1934 std::transform(
1935 values.begin(), values.end(), nullifierValues.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1936
1937 add_values(tree, nullifierValues);
1938
1939 // create a new set of values
1940 std::vector<fr> values2 = create_values(batch_size);
1941
1942 // copy one of the previous values into the middle of the batch
1943 values2[batch_size / 2] = values[0];
1944 std::vector<NullifierLeafValue> nullifierValues2(batch_size);
1945 std::transform(
1946 values2.begin(), values2.end(), nullifierValues2.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1947 add_values(tree, nullifierValues2, false);
1948}
1949
1950TEST_F(PersistedContentAddressedIndexedTreeTest, test_can_create_forks_at_historic_blocks)
1951{
1953 const uint32_t batch_size = 16;
1954 uint32_t depth = 10;
1955 ThreadPoolPtr multi_workers = make_thread_pool(8);
1956 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1957
1958 std::string name1 = random_string();
1959 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1960 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1961 auto tree1 = TreeType(std::move(store1), multi_workers, batch_size);
1962
1963 check_root(tree1, memdb.root());
1964 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1965
1966 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1967
1969 for (uint32_t j = 0; j < batch_size; j++) {
1970 batch1.emplace_back(random_engine.get_random_uint256());
1971 memdb.update_element(batch1[j].nullifier);
1972 }
1973
1974 fr_sibling_path block1SiblingPathIndex3 = memdb.get_sibling_path(3 + batch_size);
1975
1976 add_values(tree1, batch1);
1977 commit_tree(tree1, true);
1978
1980 for (uint32_t j = 0; j < batch_size; j++) {
1981 batch2.emplace_back(random_engine.get_random_uint256());
1982 memdb.update_element(batch2[j].nullifier);
1983 }
1984
1985 add_values(tree1, batch2);
1986 commit_tree(tree1, true);
1987
1988 fr block2Root = memdb.root();
1989
1990 fr_sibling_path block2SiblingPathIndex19 = memdb.get_sibling_path(19 + batch_size);
1991 fr_sibling_path block2SiblingPathIndex3 = memdb.get_sibling_path(3 + batch_size);
1992
1994 for (uint32_t j = 0; j < batch_size; j++) {
1995 batch3.emplace_back(random_engine.get_random_uint256());
1996 memdb.update_element(batch3[j].nullifier);
1997 }
1998
1999 add_values(tree1, batch3);
2000 commit_tree(tree1, true);
2001
2002 fr_sibling_path block3SiblingPathIndex35 = memdb.get_sibling_path(35 + batch_size);
2003 fr_sibling_path block3SiblingPathIndex19 = memdb.get_sibling_path(19 + batch_size);
2004 fr_sibling_path block3SiblingPathIndex3 = memdb.get_sibling_path(3 + batch_size);
2005
2006 std::unique_ptr<Store> storeAtBlock2 = std::make_unique<Store>(name1, depth, 2, db1);
2007 auto treeAtBlock2 = TreeType(std::move(storeAtBlock2), multi_workers, batch_size);
2008
2009 check_root(treeAtBlock2, block2Root);
2010 check_sibling_path(treeAtBlock2, 3 + batch_size, block2SiblingPathIndex3, false, true);
2011 auto block2TreeLeaf10 = get_leaf<NullifierLeafValue>(treeAtBlock2, 7 + batch_size);
2012 EXPECT_EQ(block2TreeLeaf10.leaf.nullifier, batch1[7].nullifier);
2013
2014 check_find_leaf_index(treeAtBlock2, batch1[5], 5 + batch_size, true);
2015 check_find_leaf_index_from(treeAtBlock2, batch1[5], 0, 5 + batch_size, true);
2016
2017 // should not exist in our image
2018 get_leaf<NullifierLeafValue>(treeAtBlock2, 35 + batch_size, false, false);
2019 check_find_leaf_index<NullifierLeafValue, TreeType>(treeAtBlock2, { batch3[4] }, { std::nullopt }, true);
2020
2021 // now add the same values to our image
2022 add_values(treeAtBlock2, batch3);
2023
2024 // the state of our image should match the original tree
2025 check_sibling_path(tree1, 3 + batch_size, block3SiblingPathIndex3, false, true);
2026 check_sibling_path(tree1, 19 + batch_size, block3SiblingPathIndex19, false, true);
2027 check_sibling_path(tree1, 35 + batch_size, block3SiblingPathIndex35, false, true);
2028
2029 // needs to use uncommitted for this check
2030 check_sibling_path(treeAtBlock2, 3 + batch_size, block3SiblingPathIndex3, true, true);
2031 check_sibling_path(treeAtBlock2, 19 + batch_size, block3SiblingPathIndex19, true, true);
2032 check_sibling_path(treeAtBlock2, 35 + batch_size, block3SiblingPathIndex35, true, true);
2033
2034 // now check historic data
2035 auto historicSiblingPath = get_historic_sibling_path(treeAtBlock2, 1, 3 + batch_size);
2036 EXPECT_EQ(historicSiblingPath, block1SiblingPathIndex3);
2037 check_historic_find_leaf_index(treeAtBlock2, batch1[3], 1, 3 + batch_size, true);
2038 check_historic_find_leaf_index(treeAtBlock2, batch3[3], 2, 35 + batch_size, true, true);
2039 check_historic_find_leaf_index<NullifierLeafValue, TreeType>(
2040 treeAtBlock2, { batch3[3] }, 2, { std::nullopt }, true, false);
2041
2042 check_historic_find_leaf_index_from(treeAtBlock2, batch1[3], 2, 0, 3 + batch_size, true, false);
2043 check_historic_find_leaf_index_from<NullifierLeafValue, TreeType>(
2044 treeAtBlock2, { batch3[3] }, 2, 20 + batch_size, { std::nullopt }, true, false);
2045 check_historic_find_leaf_index_from(treeAtBlock2, batch3[3], 2, 20 + batch_size, 35 + batch_size, true, true);
2046
2047 check_unfinalized_block_height(treeAtBlock2, 2);
2048
2049 // It should be impossible to commit using the image
2050 commit_tree(treeAtBlock2, false);
2051}
2052
2054{
2055 index_t current_size = 2;
2056 ThreadPoolPtr workers = make_thread_pool(8);
2057 // Create a depth-3 indexed merkle tree
2058 constexpr size_t depth = 3;
2059 std::string name = random_string();
2060 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2063 using LocalTreeType =
2065 auto tree = LocalTreeType(std::move(store), workers, current_size);
2066
2079 check_size(tree, current_size);
2080 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
2081 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
2082
2094 commit_tree(tree);
2095 check_size(tree, ++current_size);
2096 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2097 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2098 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2099
2100 auto leaf1AtBlock1 = PublicDataLeafValue(1, 0);
2101 check_block_and_size_data(db, 1, current_size, true);
2102
2114 check_size(tree, ++current_size);
2115 commit_tree(tree);
2116 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2117 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2118 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2119 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2120
2121 check_block_and_size_data(db, 2, current_size, true);
2122
2123 auto leaf2AtBlock2 = PublicDataLeafValue(30, 5);
2124 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, true);
2125
2126 // shoudl find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2127 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2128 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2129
2141 check_size(tree, current_size);
2142 commit_tree(tree);
2143 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2144 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2145 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2146 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2147
2148 check_block_and_size_data(db, 3, current_size, true);
2149
2150 auto leaf2AtBlock3 = PublicDataLeafValue(30, 6);
2151 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
2152
2153 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2154 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2155 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2156
2168 check_size(tree, ++current_size);
2169 commit_tree(tree);
2170 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2171 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2172 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2173 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2174 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
2175
2176 check_block_and_size_data(db, 4, current_size, true);
2177
2178 check_historic_leaf(tree, leaf2AtBlock3, 2, 3, true);
2179
2180 // should not be found at block 1
2181 check_historic_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
2182 tree, { PublicDataLeafValue(10, 20) }, 1, 0, { std::nullopt }, true);
2183 // should be found at block
2184 check_historic_find_leaf_index_from(tree, PublicDataLeafValue(10, 20), 2, 0, 3, true);
2185
2187 EXPECT_EQ(lowLeaf.index, 1);
2188
2189 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(20, 0));
2190 EXPECT_EQ(lowLeaf.index, 3);
2191
2192 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(60, 0));
2193 EXPECT_EQ(lowLeaf.index, 2);
2194
2195 finalize_block(tree, 3);
2196
2197 // remove historical block 1
2198 remove_historic_block(tree, 1);
2199
2200 // Historic queries against block 1 should no longer work
2201 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, false);
2202 check_historic_find_leaf_index<PublicDataLeafValue, LocalTreeType>(
2203 tree, { leaf1AtBlock1 }, 1, { std::nullopt }, false);
2204
2205 // Queries against block 2 should work
2206 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
2207 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2208
2209 // now remove block 2 and queries against it should no longer work
2210 remove_historic_block(tree, 2);
2211 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, false);
2212
2213 // size doesn't matter, should fail to find the data
2214 check_block_and_size_data(db, 1, current_size, false);
2215}
2216
2218{
2219 index_t current_size = 2;
2220 ThreadPoolPtr workers = make_thread_pool(8);
2221 // Create a depth-3 indexed merkle tree
2222 constexpr size_t depth = 3;
2223 std::string name = random_string();
2224 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2227 using LocalTreeType =
2229 auto tree = LocalTreeType(std::move(store), workers, current_size);
2230
2243 check_size(tree, current_size);
2244 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
2245 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
2246
2247 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2248 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2249
2250 check_indices_data(db, 0, 0, true, true);
2251 check_indices_data(db, 1, 1, true, true);
2252
2264 commit_tree(tree);
2265 check_size(tree, ++current_size);
2266
2267 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2268 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2269 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2270
2271 check_block_and_size_data(db, 1, current_size, true);
2272
2273 // All historical pre-images should be present
2274 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2275 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2276 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2277 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2278
2279 check_indices_data(db, 30, 2, true, true);
2280
2281 auto leaf1AtBlock1 = PublicDataLeafValue(1, 0);
2282
2294 check_size(tree, ++current_size);
2295 commit_tree(tree);
2296 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2297 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2298 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2299 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2300
2301 // All historical pre-images should be present
2302 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2303 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2304 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2305 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2306 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2307
2308 check_indices_data(db, 10, 3, true, true);
2309
2310 check_block_and_size_data(db, 2, current_size, true);
2311
2312 auto leaf2AtBlock2 = PublicDataLeafValue(30, 5);
2313 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, true);
2314
2315 // shoudl find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2316 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2317 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2318
2330 check_size(tree, current_size);
2331 commit_tree(tree);
2332 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2333 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2334 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2335 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2336
2337 // All historical pre-images should be present
2338 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2339 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2340 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2341 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2342 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2343 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2344 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 0, 0), true);
2345
2346 // Zero leaves should not have their indices added
2347 check_indices_data(db, 0, 4, true, false);
2348
2349 check_block_and_size_data(db, 3, current_size, true);
2350
2351 auto leaf2AtBlock3 = PublicDataLeafValue(30, 6);
2352 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
2353
2354 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2355 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2356 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2357
2369 check_size(tree, ++current_size);
2370 commit_tree(tree);
2371 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2372 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2373 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2374 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2375 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
2376
2377 check_indices_data(db, 50, 4, true, true);
2378 // All historical pre-images should be present
2379 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2380 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2381 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2382 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2383 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2384 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 0, 0), true);
2385 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(50, 8, 0, 0), true);
2386 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 4, 50), true);
2387
2388 check_block_and_size_data(db, 4, current_size, true);
2389
2390 check_historic_leaf(tree, leaf2AtBlock3, 2, 3, true);
2391
2392 // should not be found at block 1
2393 check_historic_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
2394 tree, { PublicDataLeafValue(10, 20) }, 1, 0, { std::nullopt }, true);
2395 // should be found at block
2396 check_historic_find_leaf_index_from(tree, PublicDataLeafValue(10, 20), 2, 0, 3, true);
2397
2399 EXPECT_EQ(lowLeaf.index, 1);
2400
2401 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(20, 0));
2402 EXPECT_EQ(lowLeaf.index, 3);
2403
2404 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(60, 0));
2405 EXPECT_EQ(lowLeaf.index, 2);
2406
2407 unwind_block(tree, 4);
2408
2409 // Index 4 should be removed
2410 check_indices_data(db, 50, 4, false, false);
2411 // The pre-images created before block 4 should be present
2412 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2413 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2414 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2415 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2416 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2417 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2418 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 0, 0), true);
2419
2420 // The pre-images created in block 4 should be gone
2421 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(50, 8, 0, 0), false);
2422 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 4, 50), false);
2423
2424 check_size(tree, --current_size);
2425
2426 // should fail to find block 4
2427 check_block_and_size_data(db, 4, current_size, false);
2428
2429 // block 3 should work
2430 check_block_and_size_data(db, 3, current_size, true);
2431
2432 // should fail to find the leaf at index 4
2433 check_find_leaf_index<PublicDataLeafValue, LocalTreeType>(
2434 tree, { PublicDataLeafValue(50, 8) }, { std::nullopt }, true);
2435 check_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
2436 tree, { PublicDataLeafValue(50, 8) }, 0, { std::nullopt }, true);
2437
2438 // the leaf at index 2 should no longer be as it was after block 5
2439 EXPECT_NE(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2440
2441 // it should be as it was after block 4
2442 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2443
2444 unwind_block(tree, 3);
2445
2446 // The pre-images created before block 3 should be present
2447 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2448 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2449 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2450 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2451 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2452
2453 check_size(tree, current_size);
2454
2455 // the leaf at index 2 should no longer be as it was after block 4
2456 EXPECT_NE(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2457
2458 // it should be as it was after block 3
2459 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2460}
2461
2463{
2464 index_t current_size = 2;
2465 ThreadPoolPtr workers = make_thread_pool(8);
2466 // Create a depth-3 indexed merkle tree
2467 constexpr size_t depth = 3;
2468 std::string name = random_string();
2469 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2473 std::move(store), workers, current_size);
2474
2487 check_size(tree, current_size);
2488 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
2489 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
2490
2502 commit_tree(tree);
2503 check_size(tree, ++current_size);
2504 fr rootAfterBlock1 = get_root(tree, false);
2505 fr_sibling_path pathAfterBlock1 = get_sibling_path(tree, 0, false);
2506 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2507 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2508 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2509
2510 check_block_and_size_data(db, 1, current_size, true);
2511
2512 // All historical pre-images should be present
2513 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2514 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2515 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2516 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2517
2518 check_indices_data(db, 30, 2, true, true);
2519
2531 commit_tree(tree);
2532 check_size(tree, current_size);
2533 fr rootAfterBlock2 = get_root(tree, false);
2534 fr_sibling_path pathAfterBlock2 = get_sibling_path(tree, 0, false);
2535 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2536 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2537 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 8, 0, 0));
2538
2539 // All historical pre-images should be present
2540 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2541 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2542 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2543 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2544 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), true);
2545
2546 check_indices_data(db, 30, 2, true, true);
2547
2559 commit_tree(tree);
2560 check_size(tree, current_size);
2561 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2562 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2563 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2564
2565 // All historical pre-images should be present
2566 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2567 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2568 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2569 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2570 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), true);
2571
2572 check_indices_data(db, 30, 2, true, true);
2573
2574 // Unwind block 3 and the state should be reverted back to block 2
2575 unwind_block(tree, 3);
2576
2577 check_root(tree, rootAfterBlock2);
2578 check_sibling_path(tree, 0, pathAfterBlock2, false);
2579 check_size(tree, current_size);
2580 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2581 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2582 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 8, 0, 0));
2583
2584 // All historical pre-images should be present
2585 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2586 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2587 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2588 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2589 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), true);
2590
2591 check_indices_data(db, 30, 2, true, true);
2592
2593 // Unwind block 2 and the state should be reverted back to block 1
2594 unwind_block(tree, 2);
2595
2596 check_root(tree, rootAfterBlock1);
2597 check_sibling_path(tree, 0, pathAfterBlock1, false);
2598 check_size(tree, current_size);
2599 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2600 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2601 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2602
2603 // All historical pre-images should be present
2604 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2605 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2606 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2607 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2608
2609 // Check the pre-image was removed
2610 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), false);
2611
2612 check_indices_data(db, 30, 2, true, true);
2613
2614 // Now apply block 2 again and it should be moved forward back tot where it was
2615 add_value(tree, PublicDataLeafValue(30, 8));
2616 commit_tree(tree);
2617 check_size(tree, ++current_size);
2618 check_root(tree, rootAfterBlock2);
2619 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2620 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2621 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 8, 0, 0));
2622}
2623
2624void test_nullifier_tree_unwind(std::string directory,
2625 std::string name,
2626 uint64_t mapSize,
2627 uint64_t maxReaders,
2628 uint32_t depth,
2629 uint32_t blockSize,
2630 uint32_t numBlocks,
2631 uint32_t numBlocksToUnwind,
2632 std::vector<fr> values)
2633{
2634 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(directory, name, mapSize, maxReaders);
2635 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
2637 TreeType tree(std::move(store), pool, blockSize);
2638 NullifierMemoryTree<Poseidon2HashPolicy> memdb(depth, blockSize);
2639
2640 auto it = std::find_if(values.begin(), values.end(), [&](const fr& v) { return v != fr::zero(); });
2641 bool emptyBlocks = it == values.end();
2642
2643 uint32_t batchSize = blockSize;
2644
2645 std::vector<fr_sibling_path> historicPathsZeroIndex;
2646 std::vector<fr_sibling_path> historicPathsMaxIndex;
2647 std::vector<fr> roots;
2648
2649 fr initialRoot = memdb.root();
2650 fr_sibling_path initialPath = memdb.get_sibling_path(0);
2651
2653 leafValues.reserve(values.size());
2654 for (const fr& v : values) {
2655 leafValues.emplace_back(v);
2656 }
2657
2658 for (uint32_t i = 0; i < numBlocks; i++) {
2660
2661 for (size_t j = 0; j < batchSize; ++j) {
2662 size_t ind = i * batchSize + j;
2663 memdb.update_element(values[ind]);
2664 to_add.push_back(leafValues[ind]);
2665 }
2666 // Indexed trees have an initial 'batch' inserted at startup
2667 index_t expected_size = (i + 2) * batchSize;
2668 add_values(tree, to_add);
2669 commit_tree(tree);
2670
2671 historicPathsZeroIndex.push_back(memdb.get_sibling_path(0));
2672 historicPathsMaxIndex.push_back(memdb.get_sibling_path(expected_size - 1));
2673 roots.push_back(memdb.root());
2674 check_root(tree, memdb.root());
2675 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
2676 check_sibling_path(tree, expected_size - 1, memdb.get_sibling_path(expected_size - 1));
2677 check_size(tree, expected_size);
2678 check_block_and_size_data(db, i + 1, expected_size, true);
2679 check_block_and_root_data(db, i + 1, memdb.root(), true);
2680 }
2681
2682 const uint32_t blocksToRemove = numBlocksToUnwind;
2683 for (uint32_t i = 0; i < blocksToRemove; i++) {
2684 const block_number_t blockNumber = numBlocks - i;
2685
2686 check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], true);
2687 unwind_block(tree, blockNumber);
2688 if (emptyBlocks) {
2689 // with empty blocks, we should not find the block data but we do find the root
2690 check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], false, true);
2691 } else {
2692 // if blocks are not empty, this query should fail
2693 check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], false);
2694 }
2695
2696 const index_t previousValidBlock = blockNumber - 1;
2697 // Indexed trees have an initial 'batch' inserted at startup
2698 index_t deletedBlockStartIndex = (1 + previousValidBlock) * batchSize;
2699 index_t deletedBlockStartIndexIntoLocalValues = previousValidBlock * batchSize;
2700
2701 check_block_height(tree, previousValidBlock);
2702 check_size(tree, deletedBlockStartIndex);
2703 check_root(tree, previousValidBlock == 0 ? initialRoot : roots[previousValidBlock - 1]);
2704
2705 // The zero index sibling path should be as it was at the previous block
2706 check_sibling_path(tree,
2707 0,
2708 previousValidBlock == 0 ? initialPath : historicPathsZeroIndex[previousValidBlock - 1],
2709 false,
2710 true);
2711
2712 if (!emptyBlocks) {
2713 // Trying to find leaves appended in the block that was removed should fail
2714 get_leaf<NullifierLeafValue>(tree, 1 + deletedBlockStartIndex, false, false);
2715
2716 check_find_leaf_index<NullifierLeafValue, TreeType>(
2717 tree, { leafValues[1 + deletedBlockStartIndexIntoLocalValues] }, { std::nullopt }, true);
2718 }
2719
2720 for (index_t j = 0; j < numBlocks; j++) {
2721 block_number_t historicBlockNumber = static_cast<block_number_t>(j + 1);
2722 bool expectedSuccess = historicBlockNumber <= previousValidBlock;
2724 tree, 0, historicBlockNumber, historicPathsZeroIndex[j], false, expectedSuccess);
2725 index_t maxSizeAtBlock = ((j + 2) * batchSize) - 1;
2727 tree, maxSizeAtBlock, historicBlockNumber, historicPathsMaxIndex[j], false, expectedSuccess);
2728
2729 if (emptyBlocks) {
2730 continue;
2731 }
2732 const index_t leafIndex = 1;
2733 const index_t expectedIndexInTree = leafIndex + batchSize;
2735 tree, leafValues[leafIndex], expectedIndexInTree, historicBlockNumber, expectedSuccess, false);
2736
2737 std::vector<std::optional<index_t>> expectedResults;
2738 if (expectedSuccess) {
2739 expectedResults.emplace_back(std::make_optional(expectedIndexInTree));
2740 }
2741 check_historic_find_leaf_index<NullifierLeafValue, TreeType>(
2742 tree, { leafValues[leafIndex] }, historicBlockNumber, expectedResults, expectedSuccess, true);
2743 check_historic_find_leaf_index_from<NullifierLeafValue, TreeType>(
2744 tree, { leafValues[leafIndex] }, historicBlockNumber, 0, expectedResults, expectedSuccess, true);
2745 }
2746 }
2747}
2748
2750{
2751
2752 constexpr uint32_t numBlocks = 8;
2753 constexpr uint32_t numBlocksToUnwind = 4;
2754 std::vector<uint32_t> blockSizes = { 2, 4, 8, 16, 32 };
2755 for (const uint32_t& size : blockSizes) {
2756 uint32_t actualSize = size;
2757 std::vector<fr> values = create_values(actualSize * numBlocks);
2758 std::stringstream ss;
2759 ss << "DB " << actualSize;
2761 _directory, ss.str(), _mapSize, _maxReaders, 20, actualSize, numBlocks, numBlocksToUnwind, values);
2762 }
2763}
2764
2765TEST_F(PersistedContentAddressedIndexedTreeTest, can_sync_and_unwind_empty_blocks)
2766{
2767
2768 constexpr uint32_t numBlocks = 8;
2769 constexpr uint32_t numBlocksToUnwind = 4;
2770 std::vector<uint32_t> blockSizes = { 2, 4, 8, 16, 32 };
2771 for (const uint32_t& size : blockSizes) {
2772 uint32_t actualSize = size;
2773 std::vector<fr> values = std::vector<fr>(actualSize * numBlocks, fr::zero());
2774 std::stringstream ss;
2775 ss << "DB " << actualSize;
2777 _directory, ss.str(), _mapSize, _maxReaders, 20, actualSize, numBlocks, numBlocksToUnwind, values);
2778 }
2779}
2780
2782{
2783 ThreadPoolPtr workers = make_thread_pool(1);
2784 constexpr size_t depth = 3;
2785 std::string name = random_string();
2786 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2788
2789 index_t initial_size = 4;
2791 auto tree = PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values);
2792
2807 check_size(tree, initial_size);
2808 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), leaf_0);
2809 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), leaf_1);
2810 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), leaf_2);
2811 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), leaf_3);
2812}
2813
2814TEST_F(PersistedContentAddressedIndexedTreeTest, test_full_prefilled_public_data)
2815{
2816 ThreadPoolPtr workers = make_thread_pool(1);
2817 constexpr size_t depth = 3;
2818 std::string name = random_string();
2819 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2821
2822 index_t initial_size = 4;
2823 std::vector<PublicDataLeafValue> prefilled_values = {
2825 };
2826 auto tree = PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values);
2827
2842 check_size(tree, initial_size);
2843 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), leaf_0);
2844 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), leaf_1);
2845 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), leaf_2);
2846 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), leaf_3);
2847}
2848
2849TEST_F(PersistedContentAddressedIndexedTreeTest, test_prefilled_unsorted_public_data_should_fail)
2850{
2851 ThreadPoolPtr workers = make_thread_pool(1);
2852 constexpr size_t depth = 3;
2853 std::string name = random_string();
2854 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2856
2857 index_t initial_size = 4;
2858 // The prefilled values are not sorted: 5 > 3.
2860 EXPECT_THROW(PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values), std::runtime_error);
2861}
2862
2863TEST_F(PersistedContentAddressedIndexedTreeTest, test_prefilled_default_public_data_should_fail)
2864{
2865 ThreadPoolPtr workers = make_thread_pool(1);
2866 constexpr size_t depth = 3;
2867 std::string name = random_string();
2868 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2870
2871 index_t initial_size = 4;
2872 // The first prefilled value is the same as one of the default values (1).
2874 EXPECT_THROW(PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values), std::runtime_error);
2875}
2876
2877TEST_F(PersistedContentAddressedIndexedTreeTest, test_can_commit_and_revert_checkpoints)
2878{
2879 index_t initial_size = 2;
2880 index_t current_size = initial_size;
2881 ThreadPoolPtr workers = make_thread_pool(8);
2882 // Create a depth-3 indexed merkle tree
2883 constexpr size_t depth = 3;
2884 std::string name = random_string();
2885 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2889 std::move(store), workers, current_size);
2890
2913 check_size(tree, ++current_size);
2914
2926 check_size(tree, ++current_size);
2927
2939 // The size does not increase since sequential insertion doesn't pad
2940 check_size(tree, current_size);
2941 commit_tree(tree);
2942
2943 {
2944 index_t fork_size = current_size;
2947 auto forkTree =
2949 std::move(forkStore), workers, initial_size);
2950
2951 // Find the low leaf of slot 60
2952 auto predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
2953
2954 // It should be at index 2
2955 EXPECT_EQ(predecessor.is_already_present, false);
2956 EXPECT_EQ(predecessor.index, 2);
2957
2958 // checkpoint the fork
2959 checkpoint_tree(forkTree);
2960
2972 check_size(forkTree, ++fork_size);
2973 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2974 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2975 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2976 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2977 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
2978
2979 // Find the low leaf of slot 60
2980 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
2981
2982 // It should be at index 4
2983 EXPECT_EQ(predecessor.is_already_present, false);
2984 EXPECT_EQ(predecessor.index, 4);
2985
2986 // Now revert the fork and see that it is rolled back to the checkpoint
2987 revert_checkpoint_tree(forkTree);
2988 check_size(forkTree, --fork_size);
2989 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2990 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2991 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2992 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2993
2994 // Find the low leaf of slot 60
2995 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
2996
2997 // It should be back at index 2
2998 EXPECT_EQ(predecessor.is_already_present, false);
2999 EXPECT_EQ(predecessor.index, 2);
3000
3001 // checkpoint the fork again
3002 checkpoint_tree(forkTree);
3003
3004 // We now advance the fork again by a few checkpoints
3005
3017 // Make the same change again, commit the checkpoint and see that the changes remain
3019 check_size(forkTree, ++fork_size);
3020 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3021 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3022 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
3023 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3024 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3025
3026 // Find the low leaf of slot 60
3027 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3028
3029 // It should be back at index 4
3030 EXPECT_EQ(predecessor.is_already_present, false);
3031 EXPECT_EQ(predecessor.index, 4);
3032
3033 // Checkpoint again
3034 checkpoint_tree(forkTree);
3035
3047 check_size(forkTree, fork_size);
3048 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3049 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3050 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 12, 4, 50));
3051 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3052 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3053
3054 // Find the low leaf of slot 60
3055 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3056
3057 // It should be back at index 4
3058 EXPECT_EQ(predecessor.is_already_present, false);
3059 EXPECT_EQ(predecessor.index, 4);
3060
3061 // Checkpoint again
3062 checkpoint_tree(forkTree);
3063
3075
3076 check_size(forkTree, ++fork_size);
3077 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3078 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3079 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 12, 5, 45));
3080 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3081 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3082 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 5), create_indexed_public_data_leaf(45, 15, 4, 50));
3083
3084 // Find the low leaf of slot 60
3085 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3086
3087 // It should be back at index 4
3088 EXPECT_EQ(predecessor.is_already_present, false);
3089 EXPECT_EQ(predecessor.index, 4);
3090
3091 // Find the low leaf of slot 46
3092 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(46, 5));
3093
3094 // It should be back at index 4
3095 EXPECT_EQ(predecessor.is_already_present, false);
3096 EXPECT_EQ(predecessor.index, 5);
3097
3098 // Now commit the last checkpoint
3099 commit_checkpoint_tree(forkTree);
3100
3101 // The state should be identical
3102 check_size(forkTree, fork_size);
3103 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3104 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3105 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 12, 5, 45));
3106 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3107 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3108 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 5), create_indexed_public_data_leaf(45, 15, 4, 50));
3109
3110 // Find the low leaf of slot 60
3111 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3112
3113 // It should be back at index 4
3114 EXPECT_EQ(predecessor.is_already_present, false);
3115 EXPECT_EQ(predecessor.index, 4);
3116
3117 // Find the low leaf of slot 46
3118 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(46, 5));
3119
3120 // It should be back at index 4
3121 EXPECT_EQ(predecessor.is_already_present, false);
3122 EXPECT_EQ(predecessor.index, 5);
3123
3124 // Now revert the fork and we should remove both the new slot 45 and the update to slot 30
3125
3137 revert_checkpoint_tree(forkTree);
3138
3139 check_size(forkTree, --fork_size);
3140 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3141 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3142 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
3143 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3144 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3145
3146 // Find the low leaf of slot 60
3147 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3148
3149 // It should be back at index 4
3150 EXPECT_EQ(predecessor.is_already_present, false);
3151 EXPECT_EQ(predecessor.index, 4);
3152
3153 // Find the low leaf of slot 46
3154 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(46, 5));
3155
3156 // It should be back at index 4
3157 EXPECT_EQ(predecessor.is_already_present, false);
3158 EXPECT_EQ(predecessor.index, 2);
3159 }
3160}
3161
3162void advance_state(TreeType& fork, uint32_t size)
3163{
3164 std::vector<fr> values = create_values(size);
3166 for (uint32_t j = 0; j < size; j++) {
3167 leaves.emplace_back(values[j]);
3168 }
3169 add_values(fork, leaves);
3170}
3171
3172TEST_F(PersistedContentAddressedIndexedTreeTest, nullifiers_can_be_inserted_after_revert)
3173{
3174 index_t current_size = 2;
3175 ThreadPoolPtr workers = make_thread_pool(1);
3176 constexpr size_t depth = 10;
3177 std::string name = "Nullifier Tree";
3178 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
3179 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
3180 auto tree = TreeType(std::move(store), workers, current_size);
3181
3182 {
3183 std::unique_ptr<Store> forkStore = std::make_unique<Store>(name, depth, db);
3184 auto forkTree = TreeType(std::move(forkStore), workers, current_size);
3185
3186 check_size(tree, current_size);
3187
3188 uint32_t size_to_insert = 8;
3189 uint32_t num_insertions = 5;
3190
3191 for (uint32_t i = 0; i < num_insertions - 1; i++) {
3192 advance_state(forkTree, size_to_insert);
3193 current_size += size_to_insert;
3194 check_size(forkTree, current_size);
3195 checkpoint_tree(forkTree);
3196 }
3197
3198 advance_state(forkTree, size_to_insert);
3199 current_size += size_to_insert;
3200 check_size(forkTree, current_size);
3201 revert_checkpoint_tree(forkTree);
3202
3203 current_size -= size_to_insert;
3204 check_size(forkTree, current_size);
3205
3206 commit_checkpoint_tree(forkTree);
3207
3208 check_size(forkTree, current_size);
3209
3210 advance_state(forkTree, size_to_insert);
3211
3212 current_size += size_to_insert;
3213 check_size(forkTree, current_size);
3214 }
3215}
Serves as a key-value node store for merkle trees. Caches all changes in memory before persisting the...
Implements a parallelized batch insertion indexed tree Accepts template argument of the type of store...
std::function< void(TypedResponse< AddIndexedDataSequentiallyResponse< LeafValueType > > &)> AddSequentiallyCompletionCallbackWithWitness
std::function< void(TypedResponse< AddIndexedDataResponse< LeafValueType > > &)> AddCompletionCallbackWithWitness
std::shared_ptr< LMDBTreeStore > SharedPtr
fr_sibling_path get_sibling_path(size_t index) const
fr_sibling_path get_sibling_path(index_t index)
Used in parallel insertions in the the IndexedTree. Workers signal to other following workes as they ...
Definition signal.hpp:17
void signal_level(uint32_t level=0)
Signals that the given level has been passed.
Definition signal.hpp:54
void signal_decrement(uint32_t delta=1)
Definition signal.hpp:60
void wait_for_level(uint32_t level=0)
Causes the thread to wait until the required level has been signalled.
Definition signal.hpp:40
FF a
FF b
void commit_tree(TypeOfTree &tree, bool expectedSuccess=true)
void test_batch_insert(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
ContentAddressedCachedTreeStore< NullifierLeafValue > Store
fr get_root(TypeOfTree &tree, bool includeUncommitted=true)
void advance_state(TreeType &fork, uint32_t size)
void add_value_sequentially(TypeOfTree &tree, const LeafValueType &value, bool expectedSuccess=true)
void add_values(TypeOfTree &tree, const std::vector< LeafValueType > &values, bool expectedSuccess=true)
void add_values_sequentially(TypeOfTree &tree, const std::vector< LeafValueType > &values, bool expectedSuccess=true)
void check_sibling_path(TypeOfTree &tree, index_t index, const fr_sibling_path &expected_sibling_path, bool includeUncommitted=true, bool expected_success=true)
TreeType::AddCompletionCallbackWithWitness CompletionCallback
void check_historic_sibling_path(TypeOfTree &tree, index_t index, block_number_t blockNumber, const fr_sibling_path &expected_sibling_path, bool includeUncommitted=true, bool expected_success=true)
void test_batch_insert_with_commit_restore(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
ContentAddressedIndexedTree< PublicDataStore, Poseidon2HashPolicy > PublicDataTreeType
void add_value(TypeOfTree &tree, const LeafValueType &value, bool expectedSuccess=true)
std::unique_ptr< TreeType > create_tree(const std::string &rootDirectory, uint64_t mapSize, uint64_t maxReaders, uint32_t depth, uint32_t batchSize, ThreadPoolPtr workers)
fr_sibling_path get_historic_sibling_path(TypeOfTree &tree, block_number_t blockNumber, index_t index, bool includeUncommitted=true, bool expected_success=true)
GetLowIndexedLeafResponse get_historic_low_leaf(TypeOfTree &tree, block_number_t blockNumber, const LeafValueType &leaf, bool includeUncommitted=true)
void test_sequential_insert_vs_batch(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
void check_block_height(TypeOfTree &tree, index_t expected_block_height)
IndexedLeaf< LeafValueType > get_leaf(TypeOfTree &tree, index_t index, bool includeUncommitted=true, bool expected_success=true)
void check_root(TypeOfTree &tree, fr expected_root, bool includeUncommitted=true)
GetLowIndexedLeafResponse get_low_leaf(TypeOfTree &tree, const LeafValueType &leaf, bool includeUncommitted=true)
void check_historic_leaf(TypeOfTree &tree, const LeafValueType &leaf, index_t expected_index, block_number_t blockNumber, bool expected_success, bool includeUncommitted=true)
void block_sync_values_sequential(TypeOfTree &tree, const std::vector< LeafValueType > &values, bool expectedSuccess=true)
void check_unfinalized_block_height(TypeOfTree &tree, index_t expected_block_height)
void test_nullifier_tree_unwind(std::string directory, std::string name, uint64_t mapSize, uint64_t maxReaders, uint32_t depth, uint32_t blockSize, uint32_t numBlocks, uint32_t numBlocksToUnwind, std::vector< fr > values)
void check_size(TypeOfTree &tree, index_t expected_size, bool includeUncommitted=true)
ContentAddressedIndexedTree< Store, HashPolicy > TreeType
void remove_historic_block(TypeOfTree &tree, const block_number_t &blockNumber, bool expected_success=true)
void block_sync_values(TypeOfTree &tree, const std::vector< LeafValueType > &values, bool expectedSuccess=true)
fr hash_leaf(const IndexedLeaf< LeafValueType > &leaf)
void finalize_block(TypeOfTree &tree, const block_number_t &blockNumber, bool expected_success=true)
void unwind_block(TypeOfTree &tree, const block_number_t &blockNumber, bool expected_success=true)
TreeType::AddSequentiallyCompletionCallbackWithWitness SequentialCompletionCallback
bool verify_sibling_path(TreeType &tree, const IndexedNullifierLeafType &leaf_value, const uint32_t idx)
void checkpoint_tree(TreeType &tree)
ThreadPoolPtr make_thread_pool(uint64_t numThreads)
Definition fixtures.hpp:65
IndexedNullifierLeafType create_indexed_nullifier_leaf(const fr &value, index_t nextIndex, const fr &nextValue)
Definition fixtures.hpp:16
IndexedPublicDataLeafType create_indexed_public_data_leaf(const fr &slot, const fr &value, index_t nextIndex, const fr &nextValue)
Definition fixtures.hpp:21
void check_indices_data(LMDBTreeStore::SharedPtr db, fr leaf, index_t index, bool entryShouldBePresent, bool indexShouldBePresent)
void check_historic_find_leaf_index_from(TypeOfTree &tree, const std::vector< LeafValueType > &leaves, block_number_t blockNumber, index_t start_index, const std::vector< std::optional< index_t > > &expected_indices, bool expected_success, bool includeUncommitted=true)
std::string random_temp_directory()
Definition fixtures.hpp:42
uint32_t block_number_t
Definition types.hpp:19
void check_find_leaf_index(TypeOfTree &tree, const std::vector< LeafValueType > &leaves, const std::vector< std::optional< index_t > > &expected_indices, bool expected_success, bool includeUncommitted=true)
void check_block_and_root_data(LMDBTreeStore::SharedPtr db, block_number_t blockNumber, fr root, bool expectedSuccess)
void check_find_leaf_index_from(TypeOfTree &tree, const std::vector< LeafValueType > &leaves, index_t start_index, const std::vector< std::optional< index_t > > &expected_indices, bool expected_success, bool includeUncommitted=true)
std::string random_string()
Definition fixtures.hpp:35
std::shared_ptr< ThreadPool > ThreadPoolPtr
Definition fixtures.hpp:63
void check_block_and_size_data(LMDBTreeStore::SharedPtr db, block_number_t blockNumber, index_t expectedSize, bool expectedSuccess)
void commit_checkpoint_tree(TreeType &tree, bool expected_success=true)
std::vector< fr > fr_sibling_path
Definition hash_path.hpp:16
void revert_checkpoint_tree(TreeType &tree, bool expected_success=true)
void check_historic_find_leaf_index(TypeOfTree &tree, const std::vector< LeafValueType > &leaves, block_number_t blockNumber, const std::vector< std::optional< index_t > > &expected_indices, bool expected_success, bool includeUncommitted=true)
fr_sibling_path get_sibling_path(TypeOfTree &tree, index_t index, bool includeUncommitted=true, bool expected_success=true)
Key get_key(int64_t keyCount)
Definition fixtures.hpp:30
RNG & get_randomness()
Definition engine.cpp:203
Entry point for Barretenberg command-line interface.
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:123
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< fr > get_hash_inputs() const
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
static constexpr field zero()