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"
17#include <algorithm>
18#include <cstdint>
19#include <filesystem>
20#include <future>
21#include <memory>
22#include <optional>
23#include <stdexcept>
24#include <vector>
25
26using namespace bb;
27using namespace bb::crypto::merkle_tree;
28
30
33
36
39
42
43inline IndexedNullifierLeafType create_indexed_nullifier_leaf(const fr& value, index_t nextIndex, const fr& nextValue)
44{
45 return IndexedNullifierLeafType{ NullifierLeafValue(value), nextIndex, nextValue };
46}
47
49 const fr& value,
50 index_t nextIndex,
51 const fr& nextValue)
52{
53 return IndexedPublicDataLeafType{ PublicDataLeafValue(slot, value), nextIndex, nextValue };
54}
55
56class PersistedContentAddressedIndexedTreeTest : public testing::Test {
57 protected:
58 void SetUp() override
59 {
61 _mapSize = 1024 * 1024;
62 _maxReaders = 16;
63 std::filesystem::create_directories(_directory);
64 }
65
66 void TearDown() override { std::filesystem::remove_all(_directory); }
67
68 static std::string _directory;
69 static uint64_t _maxReaders;
70 static uint64_t _mapSize;
71};
72
76
77std::unique_ptr<TreeType> create_tree(const std::string& rootDirectory,
78 uint64_t mapSize,
79 uint64_t maxReaders,
80 uint32_t depth,
81 uint32_t batchSize,
82 ThreadPoolPtr workers)
83{
84 std::string name = random_string();
85 std::filesystem::path directory = rootDirectory;
86 directory.append(name);
87 std::filesystem::create_directories(directory);
88 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(directory, name, mapSize, maxReaders);
89 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
90 return std::make_unique<TreeType>(std::move(store), workers, batchSize);
91}
92
93template <typename TypeOfTree> void check_size(TypeOfTree& tree, index_t expected_size, bool includeUncommitted = true)
94{
95 Signal signal;
96 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
97 EXPECT_EQ(response.success, true);
98 EXPECT_EQ(response.inner.meta.size, expected_size);
99 signal.signal_level();
100 };
101 tree.get_meta_data(includeUncommitted, completion);
102 signal.wait_for_level();
103}
104
105template <typename TypeOfTree> fr get_root(TypeOfTree& tree, bool includeUncommitted = true)
106{
107 fr r;
108 Signal signal;
109 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
110 r = response.inner.meta.root;
111 signal.signal_level();
112 };
113 tree.get_meta_data(includeUncommitted, completion);
114 signal.wait_for_level();
115 return r;
116}
117
118template <typename TypeOfTree> void check_root(TypeOfTree& tree, fr expected_root, bool includeUncommitted = true)
119{
120 fr root = get_root(tree, includeUncommitted);
121 EXPECT_EQ(root, expected_root);
122}
123
124template <typename TypeOfTree>
126 block_number_t blockNumber,
128 bool includeUncommitted = true,
129 bool expected_success = true)
130{
132 Signal signal;
133 auto completion = [&](const TypedResponse<GetSiblingPathResponse>& response) -> void {
134 EXPECT_EQ(response.success, expected_success);
135 if (response.success) {
136 h = response.inner.path;
137 }
138 signal.signal_level();
139 };
140 tree.get_sibling_path(index, blockNumber, completion, includeUncommitted);
141 signal.wait_for_level();
142 return h;
143}
144
145template <typename LeafValueType, typename TypeOfTree>
148 bool includeUncommitted = true,
149 bool expected_success = true)
150{
152 Signal signal;
153 auto completion = [&](const TypedResponse<GetIndexedLeafResponse<LeafValueType>>& leaf) -> void {
154 EXPECT_EQ(leaf.success, expected_success);
155 if (leaf.success) {
156 l = leaf.inner.indexed_leaf;
157 }
158 signal.signal_level();
159 };
160 tree.get_leaf(index, includeUncommitted, completion);
161 signal.wait_for_level();
162 return l.has_value() ? l.value() : IndexedLeaf<LeafValueType>();
163}
164
165template <typename LeafValueType, typename TypeOfTree>
166GetLowIndexedLeafResponse get_low_leaf(TypeOfTree& tree, const LeafValueType& leaf, bool includeUncommitted = true)
167{
168 GetLowIndexedLeafResponse low_leaf_info;
169 Signal signal;
170 auto completion = [&](const auto& leaf) -> void {
171 low_leaf_info = leaf.inner;
172 signal.signal_level();
173 };
174 tree.find_low_leaf(leaf.get_key(), includeUncommitted, completion);
175 signal.wait_for_level();
176 return low_leaf_info;
177}
178
179template <typename LeafValueType, typename TypeOfTree>
181 block_number_t blockNumber,
182 const LeafValueType& leaf,
183 bool includeUncommitted = true)
184{
185 GetLowIndexedLeafResponse low_leaf_info;
186 Signal signal;
187 auto completion = [&](const auto& leaf) -> void {
188 low_leaf_info = leaf.inner;
189 signal.signal_level();
190 };
191 tree.find_low_leaf(leaf.get_key(), blockNumber, includeUncommitted, completion);
192 signal.wait_for_level();
193 return low_leaf_info;
194}
195
196template <typename LeafValueType, typename TypeOfTree>
197void check_historic_leaf(TypeOfTree& tree,
198 const LeafValueType& leaf,
199 index_t expected_index,
200 block_number_t blockNumber,
201 bool expected_success,
202 bool includeUncommitted = true)
203{
204 Signal signal;
205 auto completion = [&](const TypedResponse<GetIndexedLeafResponse<LeafValueType>>& response) -> void {
206 EXPECT_EQ(response.success, expected_success);
207 if (response.success) {
208 EXPECT_EQ(response.inner.indexed_leaf.value().leaf, leaf);
209 }
210 signal.signal_level();
211 };
212
213 tree.get_leaf(expected_index, blockNumber, includeUncommitted, completion);
214 signal.wait_for_level();
215}
216
217template <typename TypeOfTree>
218void check_historic_sibling_path(TypeOfTree& tree,
220 block_number_t blockNumber,
221 const fr_sibling_path& expected_sibling_path,
222 bool includeUncommitted = true,
223 bool expected_success = true)
224{
225 fr_sibling_path path = get_historic_sibling_path(tree, blockNumber, index, includeUncommitted, expected_success);
226 if (expected_success) {
227 EXPECT_EQ(path, expected_sibling_path);
228 }
229}
230
231template <typename TypeOfTree>
232void check_sibling_path(TypeOfTree& tree,
234 const fr_sibling_path& expected_sibling_path,
235 bool includeUncommitted = true,
236 bool expected_success = true)
237{
238 fr_sibling_path path = get_sibling_path(tree, index, includeUncommitted, expected_success);
239 EXPECT_EQ(path, expected_sibling_path);
240}
241
242template <typename TypeOfTree> void check_unfinalized_block_height(TypeOfTree& tree, index_t expected_block_height)
243{
244 Signal signal;
245 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
246 EXPECT_EQ(response.success, true);
247 EXPECT_EQ(response.inner.meta.unfinalizedBlockHeight, expected_block_height);
248 signal.signal_level();
249 };
250 tree.get_meta_data(true, completion);
251 signal.wait_for_level();
252}
253
254template <typename TypeOfTree> void commit_tree(TypeOfTree& tree, bool expectedSuccess = true)
255{
256 Signal signal;
257 auto completion = [&](const TypedResponse<CommitResponse>& response) -> void {
258 EXPECT_EQ(response.success, expectedSuccess);
259 signal.signal_level();
260 };
261 tree.commit(completion);
262 signal.wait_for_level();
263}
264
265template <typename LeafValueType, typename TypeOfTree>
266void add_value(TypeOfTree& tree, const LeafValueType& value, bool expectedSuccess = true)
267{
268 Signal signal;
269 auto completion = [&](const TypedResponse<AddIndexedDataResponse<LeafValueType>>& response) -> void {
270 EXPECT_EQ(response.success, expectedSuccess);
271 signal.signal_level();
272 };
273
274 tree.add_or_update_value(value, completion);
275 signal.wait_for_level();
276}
277
278template <typename LeafValueType, typename TypeOfTree>
279void add_value_sequentially(TypeOfTree& tree, const LeafValueType& value, bool expectedSuccess = true)
280{
282 Signal signal;
283 auto completion = [&](const TypedResponse<AddIndexedDataSequentiallyResponse<LeafValueType>>& response) -> void {
284 EXPECT_EQ(response.success, expectedSuccess);
285 signal.signal_level();
286 };
287
288 tree.add_or_update_values_sequentially(values, completion);
289 signal.wait_for_level();
290}
291
292template <typename LeafValueType, typename TypeOfTree>
293void add_values(TypeOfTree& tree, const std::vector<LeafValueType>& values, bool expectedSuccess = true)
294{
295 Signal signal;
296 auto completion = [&](const TypedResponse<AddIndexedDataResponse<LeafValueType>>& response) -> void {
297 EXPECT_EQ(response.success, expectedSuccess);
298 signal.signal_level();
299 };
300
301 tree.add_or_update_values(values, completion);
302 signal.wait_for_level();
303}
304
305template <typename LeafValueType, typename TypeOfTree>
306void add_values_sequentially(TypeOfTree& tree, const std::vector<LeafValueType>& values, bool expectedSuccess = true)
307{
308 Signal signal;
309 auto completion = [&](const TypedResponse<AddIndexedDataSequentiallyResponse<LeafValueType>>& response) -> void {
310 EXPECT_EQ(response.success, expectedSuccess);
311 signal.signal_level();
312 };
313
314 tree.add_or_update_values_sequentially(values, completion);
315 signal.wait_for_level();
316}
317
318template <typename LeafValueType, typename TypeOfTree>
319void block_sync_values(TypeOfTree& tree, const std::vector<LeafValueType>& values, 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(values, completion);
328 signal.wait_for_level();
329}
330
331template <typename LeafValueType, typename TypeOfTree>
332void block_sync_values_sequential(TypeOfTree& tree,
333 const std::vector<LeafValueType>& values,
334 bool expectedSuccess = true)
335{
336 Signal signal;
337 auto completion = [&](const TypedResponse<AddDataResponse>& response) -> void {
338 EXPECT_EQ(response.success, expectedSuccess);
339 signal.signal_level();
340 };
341
342 tree.add_or_update_values_sequentially(values, completion);
343 signal.wait_for_level();
344}
345
346template <typename TypeOfTree>
347void remove_historic_block(TypeOfTree& tree, const block_number_t& blockNumber, bool expected_success = true)
348{
349 Signal signal;
350 auto completion = [&](const TypedResponse<RemoveHistoricResponse>& response) -> void {
351 EXPECT_EQ(response.success, expected_success);
352 signal.signal_level();
353 };
354 tree.remove_historic_block(blockNumber, completion);
355 signal.wait_for_level();
356}
357
358template <typename TypeOfTree>
359void finalize_block(TypeOfTree& tree, const block_number_t& blockNumber, bool expected_success = true)
360{
361 Signal signal;
362 auto completion = [&](const Response& response) -> void {
363 EXPECT_EQ(response.success, expected_success);
364 signal.signal_level();
365 };
366 tree.finalize_block(blockNumber, completion);
367 signal.wait_for_level();
368}
369
370template <typename TypeOfTree>
371void unwind_block(TypeOfTree& tree, const block_number_t& blockNumber, bool expected_success = true)
372{
373 Signal signal;
374 auto completion = [&](const TypedResponse<UnwindResponse>& response) -> void {
375 EXPECT_EQ(response.success, expected_success);
376 signal.signal_level();
377 };
378 tree.unwind_block(blockNumber, completion);
379 signal.wait_for_level();
380}
381
382template <typename TypeOfTree> void check_block_height(TypeOfTree& tree, index_t expected_block_height)
383{
384 Signal signal;
385 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
386 EXPECT_EQ(response.success, true);
387 EXPECT_EQ(response.inner.meta.unfinalizedBlockHeight, expected_block_height);
388 signal.signal_level();
389 };
390 tree.get_meta_data(true, completion);
391 signal.wait_for_level();
392}
393
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 EXPECT_NO_THROW(std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db));
400 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
401 ThreadPoolPtr workers = make_thread_pool(1);
402 TreeType tree = TreeType(std::move(store), workers, 2);
403 check_size(tree, 2);
404
406 check_root(tree, memdb.root());
407}
408
409TEST_F(PersistedContentAddressedIndexedTreeTest, can_only_recreate_with_same_name_and_depth)
410{
411 constexpr size_t depth = 10;
412 std::string name = random_string();
413 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
414 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
415
416 EXPECT_ANY_THROW(Store("Wrong name", depth, db));
417 EXPECT_ANY_THROW(Store(name, depth + 1, db));
418}
419
421{
422 index_t current_size = 2;
423 ThreadPoolPtr workers = make_thread_pool(1);
424 constexpr size_t depth = 10;
425 std::string name = random_string();
426 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
427 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
428 auto tree = TreeType(std::move(store), workers, current_size);
429
430 check_size(tree, current_size);
431
432 // We assume that the first leaf is already filled with (0, 0, 0).
433 for (uint32_t i = 0; i < 4; i++) {
435 check_size(tree, ++current_size);
436 }
437}
438
439TEST_F(PersistedContentAddressedIndexedTreeTest, indexed_tree_must_have_at_least_2_initial_size)
440{
441 index_t current_size = 1;
442 ThreadPoolPtr workers = make_thread_pool(1);
443 ;
444 constexpr size_t depth = 10;
445 std::string name = random_string();
446 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
447 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
448 EXPECT_THROW(TreeType(std::move(store), workers, current_size), std::runtime_error);
449}
450
451TEST_F(PersistedContentAddressedIndexedTreeTest, reports_an_error_if_tree_is_overfilled)
452{
453 index_t current_size = 2;
454 ThreadPoolPtr workers = make_thread_pool(1);
455 ;
456 constexpr size_t depth = 4;
457 std::string name = random_string();
458 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
459 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
460 auto tree = TreeType(std::move(store), workers, current_size);
461
463 for (uint32_t i = 0; i < 14; i++) {
464 values.emplace_back(get_value(i));
465 }
466 add_values(tree, values);
467
468 std::stringstream ss;
469 ss << "Unable to insert values into tree " << name << " new size: 17 max size: 16";
470
471 Signal signal;
472 auto add_completion = [&](const TypedResponse<AddIndexedDataResponse<NullifierLeafValue>>& response) {
473 EXPECT_EQ(response.success, false);
474 EXPECT_EQ(response.message, ss.str());
475 signal.signal_level();
476 };
477 tree.add_or_update_value(NullifierLeafValue(get_value(16)), add_completion);
478 signal.wait_for_level();
479}
480
482{
483 constexpr size_t depth = 10;
484 index_t current_size = 2;
485 NullifierMemoryTree<HashPolicy> memdb(depth, current_size);
486
487 ThreadPoolPtr workers = make_thread_pool(1);
488
489 std::string name = random_string();
490 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
491 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
492 auto tree = TreeType(std::move(store), workers, current_size);
493
494 check_size(tree, current_size);
495 check_root(tree, memdb.root());
496 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
497
498 memdb.update_element(get_value(1000));
500
501 check_size(tree, ++current_size);
502 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
503 check_sibling_path(tree, 1, memdb.get_sibling_path(1));
504
505 memdb.update_element(get_value(1001));
507
508 check_size(tree, ++current_size);
509 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
510 check_sibling_path(tree, 1, memdb.get_sibling_path(1));
511
512 uint32_t num_to_append = 512;
513
514 for (uint32_t i = 0; i < num_to_append; i += 2) {
515 memdb.update_element(get_value(i));
516 memdb.update_element(get_value(i + 1));
517 add_values<NullifierLeafValue>(tree,
519 }
520 check_size(tree, num_to_append + current_size);
521 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
522 check_sibling_path(tree, 512, memdb.get_sibling_path(512));
523}
524
526{
527 index_t initial_size = 2;
528 ThreadPoolPtr workers = make_thread_pool(1);
529 ;
530 constexpr size_t depth = 10;
531 std::string name = random_string();
532 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
533 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
534 auto tree = TreeType(std::move(store), workers, initial_size);
535
536 add_value(tree, NullifierLeafValue(30));
537 add_value(tree, NullifierLeafValue(10));
538 add_value(tree, NullifierLeafValue(20));
539 add_value(tree, NullifierLeafValue(40));
540
541 // check the committed state and that the uncommitted state is empty
542 check_find_leaf_index(tree, NullifierLeafValue(10), 1 + initial_size, true, true);
543 check_find_leaf_index<NullifierLeafValue, TreeType>(
544 tree, { NullifierLeafValue(10) }, { std::nullopt }, true, false);
545
546 check_find_leaf_index<NullifierLeafValue, TreeType>(tree, { NullifierLeafValue(15) }, { std::nullopt }, true, true);
547 check_find_leaf_index<NullifierLeafValue, TreeType>(
548 tree, { NullifierLeafValue(15) }, { std::nullopt }, true, false);
549
550 check_find_leaf_index(tree, NullifierLeafValue(40), 3 + initial_size, true, true);
551 check_find_leaf_index(tree, NullifierLeafValue(30), 0 + initial_size, true, true);
552 check_find_leaf_index(tree, NullifierLeafValue(20), 2 + initial_size, true, true);
553
554 check_find_leaf_index<NullifierLeafValue, TreeType>(
555 tree, { NullifierLeafValue(40) }, { std::nullopt }, true, false);
556 check_find_leaf_index<NullifierLeafValue, TreeType>(
557 tree, { NullifierLeafValue(30) }, { std::nullopt }, true, false);
558 check_find_leaf_index<NullifierLeafValue, TreeType>(
559 tree, { NullifierLeafValue(20) }, { std::nullopt }, true, false);
560
561 commit_tree(tree);
562
567 NullifierLeafValue(48) };
568 add_values(tree, values);
569
570 // check the now committed state
571 check_find_leaf_index(tree, NullifierLeafValue(40), 3 + initial_size, true, false);
572 check_find_leaf_index(tree, NullifierLeafValue(30), 0 + initial_size, true, false);
573 check_find_leaf_index(tree, NullifierLeafValue(20), 2 + initial_size, true, false);
574
575 // check the new uncommitted state
576 check_find_leaf_index(tree, NullifierLeafValue(18), 5 + initial_size, true, true);
577 check_find_leaf_index<NullifierLeafValue, TreeType>(
578 tree, { NullifierLeafValue(18) }, { std::nullopt }, true, false);
579
580 commit_tree(tree);
581
583 add_values(tree, values);
584
585 // we now have duplicate leaf 18, one committed the other not
586 check_find_leaf_index(tree, NullifierLeafValue(18), 5 + initial_size, true, true);
587 check_find_leaf_index(tree, NullifierLeafValue(18), 5 + initial_size, true, false);
588}
589
591{
593 index_t initial_size = 2;
594 index_t current_size = initial_size;
595 ThreadPoolPtr workers = make_thread_pool(1);
596 ;
597 constexpr size_t depth = 10;
598 std::string name = random_string();
599
600 {
601 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
602 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
603 auto tree = TreeType(std::move(store), workers, initial_size);
604
605 check_size(tree, current_size);
606 check_root(tree, memdb.root());
607 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
608
610
611 // Committed data should not have changed
612 check_size(tree, current_size, false);
613 check_root(tree, memdb.root(), false);
614 check_sibling_path(tree, 0, memdb.get_sibling_path(0), false);
615 check_sibling_path(tree, 1, memdb.get_sibling_path(1), false);
616
617 memdb.update_element(get_value(512));
618
619 // Uncommitted data should have changed
620 check_size(tree, current_size + 1, true);
621 check_root(tree, memdb.root(), true);
622 check_sibling_path(tree, 0, memdb.get_sibling_path(0), true);
623 check_sibling_path(tree, 1, memdb.get_sibling_path(1), true);
624
625 // Now commit
626 commit_tree(tree);
627
628 // Now committed data should have changed
629 check_size(tree, ++current_size, false);
630 check_root(tree, memdb.root(), false);
631 check_sibling_path(tree, 0, memdb.get_sibling_path(0), false);
632 check_sibling_path(tree, 1, memdb.get_sibling_path(1), false);
633 }
634
635 // Now restore and it should continue from where we left off
636 {
637 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
638 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
639 auto tree = TreeType(std::move(store), workers, initial_size);
640
641 // check uncommitted state
642 check_size(tree, current_size);
643 check_root(tree, memdb.root());
644 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
645
646 // check committed state
647 check_size(tree, current_size, false);
648 check_root(tree, memdb.root(), false);
649 check_sibling_path(tree, 0, memdb.get_sibling_path(0), false);
650 }
651}
652
653void test_batch_insert(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
654{
656 const uint32_t batch_size = batchSize;
657 const uint32_t num_batches = 16;
658 uint32_t depth = 10;
659 ThreadPoolPtr workers = make_thread_pool(1);
660 ThreadPoolPtr multi_workers = make_thread_pool(8);
661 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
662
663 auto tree1 = create_tree(directory, mapSize, maxReaders, depth, batch_size, workers);
664 auto tree2 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
665 auto tree3 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
666
667 for (uint32_t i = 0; i < num_batches; i++) {
668
669 check_root(*tree1, memdb.root());
670 check_root(*tree2, memdb.root());
671 check_root(*tree3, memdb.root());
672 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
673 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
674 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
675
676 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
677 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
678 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
679
681 std::vector<fr_sibling_path> memory_tree_sibling_paths;
682 for (uint32_t j = 0; j < batch_size; j++) {
683 batch.emplace_back(random_engine.get_random_uint256());
684 fr_sibling_path path = memdb.update_element(batch[j].nullifier);
685 memory_tree_sibling_paths.push_back(path);
686 }
689 {
690 Signal signal;
691 CompletionCallback completion =
693 tree1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
694 signal.signal_level();
695 };
696 tree1->add_or_update_values(batch, completion);
697 signal.wait_for_level();
698 }
699
700 {
701 Signal signal;
702 CompletionCallback completion =
704 tree2_low_leaf_witness_data = response.inner.low_leaf_witness_data;
705 signal.signal_level();
706 };
707 tree2->add_or_update_values(batch, completion);
708 signal.wait_for_level();
709 }
710
711 {
712 Signal signal;
713 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
714 tree3->add_or_update_values(batch, completion);
715 signal.wait_for_level();
716 }
717 check_root(*tree1, memdb.root());
718 check_root(*tree2, memdb.root());
719 check_root(*tree3, memdb.root());
720
721 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
722 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
723 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
724
725 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
726 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
727 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
728
729 for (uint32_t j = 0; j < batch_size; j++) {
730 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).leaf, tree2_low_leaf_witness_data->at(j).leaf);
731 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).index, tree2_low_leaf_witness_data->at(j).index);
732 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).path, tree2_low_leaf_witness_data->at(j).path);
733 }
734 }
735}
736
738 std::string directory,
739 uint64_t mapSize,
740 uint64_t maxReaders)
741{
743 const uint32_t batch_size = batchSize;
744 const uint32_t num_batches = 16;
745 uint32_t depth = 10;
746 ThreadPoolPtr workers = make_thread_pool(1);
747 ThreadPoolPtr multi_workers = make_thread_pool(8);
748 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
749
750 for (uint32_t i = 0; i < num_batches; i++) {
751
752 auto tree1 = create_tree(directory, mapSize, maxReaders, depth, batch_size, workers);
753 auto tree2 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
754 auto tree3 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
755
756 check_root(*tree1, memdb.root());
757 check_root(*tree2, memdb.root());
758 check_root(*tree3, memdb.root());
759 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
760 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
761 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
762
763 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
764 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
765 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
766
768 std::vector<fr_sibling_path> memory_tree_sibling_paths;
769 for (uint32_t j = 0; j < batch_size; j++) {
770 batch.emplace_back(random_engine.get_random_uint256());
771 fr_sibling_path path = memdb.update_element(batch[j].nullifier);
772 memory_tree_sibling_paths.push_back(path);
773 }
776 {
777 Signal signal;
778 CompletionCallback completion =
780 tree1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
781 signal.signal_level();
782 };
783 tree1->add_or_update_values(batch, completion);
784 signal.wait_for_level();
785 }
786
787 {
788 Signal signal;
789 CompletionCallback completion =
791 tree2_low_leaf_witness_data = response.inner.low_leaf_witness_data;
792 signal.signal_level();
793 };
794 tree2->add_or_update_values(batch, completion);
795 signal.wait_for_level();
796 }
797
798 {
799 Signal signal;
800 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
801 tree3->add_or_update_values(batch, completion);
802 signal.wait_for_level();
803 }
804 check_root(*tree1, memdb.root());
805 check_root(*tree2, memdb.root());
806 check_root(*tree3, memdb.root());
807
808 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
809 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
810 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
811
812 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
813 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
814 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
815
816 for (uint32_t j = 0; j < batch_size; j++) {
817 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).leaf, tree2_low_leaf_witness_data->at(j).leaf);
818 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).index, tree2_low_leaf_witness_data->at(j).index);
819 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).path, tree2_low_leaf_witness_data->at(j).path);
820 }
821
822 commit_tree(*tree1);
823 commit_tree(*tree2);
824 commit_tree(*tree3);
825 }
826}
827
829{
830 uint32_t batchSize = 2;
831 while (batchSize <= 2) {
832 test_batch_insert(batchSize, _directory, _mapSize, _maxReaders);
833 batchSize <<= 1;
834 }
835}
836
838{
839 uint32_t batchSize = 2;
840 while (batchSize <= 32) {
841 test_batch_insert(batchSize, _directory, _mapSize, _maxReaders);
842 batchSize <<= 1;
843 }
844}
845
846TEST_F(PersistedContentAddressedIndexedTreeTest, test_compare_batch_inserts_different_sized_thread_pools)
847{
848 const uint32_t batch_size = 128;
849 uint32_t depth = 20;
850 ThreadPoolPtr workers = make_thread_pool(1);
851 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
852
853 auto tree1 = create_tree(_directory, _mapSize, _maxReaders, depth, batch_size, workers);
854 auto tree2 = create_tree(_directory, _mapSize, _maxReaders, depth, batch_size, workers);
855
857 for (uint32_t i = 1; i <= 12; i++) {
858 ThreadPoolPtr multiWorkers = make_thread_pool(i);
859 auto tree = create_tree(_directory, _mapSize, _maxReaders, depth, batch_size, multiWorkers);
860 trees.emplace_back(std::move(tree));
861 }
862
863 std::vector<fr> tree1Roots;
864 std::vector<fr> tree2Roots;
865
866 for (uint32_t round = 0; round < 10; round++) {
867 std::vector<fr> frValues1 = create_values(3);
868 std::vector<fr> frValues2 = create_values(3);
870 for (uint32_t i = 0; i < 3; i++) {
871 leaves[i] = frValues1[i];
872 leaves[i + 64] = frValues2[i];
873 }
874
875 std::vector<NullifierLeafValue> first(leaves.begin(), leaves.begin() + 64);
876 std::vector<NullifierLeafValue> second(leaves.begin() + 64, leaves.end());
877
878 add_values(*tree1, first);
879 add_values(*tree1, second);
880
881 block_sync_values(*tree2, leaves);
882
883 tree1Roots.push_back(get_root(*tree1));
884 tree2Roots.push_back(get_root(*tree2, true));
885 EXPECT_EQ(tree1Roots[round], tree2Roots[round]);
886
887 for (const auto& tree : trees) {
888 block_sync_values(*tree, leaves);
889 const fr treeRoot = get_root(*tree, true);
890 EXPECT_EQ(treeRoot, tree1Roots[round]);
891 }
892 }
893}
894
895TEST_F(PersistedContentAddressedIndexedTreeTest, reports_an_error_if_batch_contains_duplicate)
896{
897 index_t current_size = 2;
898 ThreadPoolPtr workers = make_thread_pool(1);
899 ;
900 constexpr size_t depth = 10;
901 std::string name = random_string();
902 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
903 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
904 auto tree = TreeType(std::move(store), workers, current_size);
905
907 for (uint32_t i = 0; i < 16; i++) {
908 values.emplace_back(get_value(i));
909 }
910 values[8] = values[0];
911
912 std::stringstream ss;
913 ss << "Duplicate key not allowed in same batch, key value: " << values[0].nullifier << ", tree: " << name;
914
915 Signal signal;
916 auto add_completion = [&](const TypedResponse<AddIndexedDataResponse<NullifierLeafValue>>& response) {
917 EXPECT_EQ(response.success, false);
918 EXPECT_EQ(response.message, ss.str());
919 signal.signal_level();
920 };
921 tree.add_or_update_values(values, add_completion);
922 signal.wait_for_level();
923}
924
925void test_sequential_insert_vs_batch(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
926{
928 const uint32_t batch_size = batchSize;
929 const uint32_t num_batches = 16;
930 uint32_t depth = 10;
931 ThreadPoolPtr workers = make_thread_pool(1);
932 ThreadPoolPtr multi_workers = make_thread_pool(8);
933 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
934
935 auto sequential_tree_1 = create_tree(directory, mapSize, maxReaders, depth, batch_size, workers);
936 auto sequential_tree_2 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
937 auto sequential_tree_3 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
938 auto batch_tree = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
939
940 for (uint32_t i = 0; i < num_batches; i++) {
941
942 check_root(*sequential_tree_1, memdb.root());
943 check_root(*sequential_tree_2, memdb.root());
944 check_root(*sequential_tree_3, memdb.root());
945 check_root(*batch_tree, memdb.root());
946 check_sibling_path(*sequential_tree_1, 0, memdb.get_sibling_path(0));
947 check_sibling_path(*sequential_tree_2, 0, memdb.get_sibling_path(0));
948 check_sibling_path(*sequential_tree_3, 0, memdb.get_sibling_path(0));
949 check_sibling_path(*batch_tree, 0, memdb.get_sibling_path(0));
950
951 check_sibling_path(*sequential_tree_1, 512, memdb.get_sibling_path(512));
952 check_sibling_path(*sequential_tree_2, 512, memdb.get_sibling_path(512));
953 check_sibling_path(*sequential_tree_3, 512, memdb.get_sibling_path(512));
954 check_sibling_path(*batch_tree, 512, memdb.get_sibling_path(512));
955
957 std::vector<fr_sibling_path> memory_tree_sibling_paths;
958 for (uint32_t j = 0; j < batch_size; j++) {
959 batch.emplace_back(random_engine.get_random_uint256());
960 fr_sibling_path path = memdb.update_element(batch[j].nullifier);
961 memory_tree_sibling_paths.push_back(path);
962 }
963 std::shared_ptr<std::vector<LeafUpdateWitnessData<NullifierLeafValue>>> sequential_tree_1_low_leaf_witness_data;
965 sequential_tree_1_insertion_witness_data;
966 std::shared_ptr<std::vector<LeafUpdateWitnessData<NullifierLeafValue>>> sequential_tree_2_low_leaf_witness_data;
968 sequential_tree_2_insertion_witness_data;
969
970 {
971 Signal signal;
974 sequential_tree_1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
975 sequential_tree_1_insertion_witness_data = response.inner.insertion_witness_data;
976 signal.signal_level();
977 };
978 sequential_tree_1->add_or_update_values_sequentially(batch, completion);
979 signal.wait_for_level();
980 }
981
982 {
983 Signal signal;
986 sequential_tree_2_low_leaf_witness_data = response.inner.low_leaf_witness_data;
987 sequential_tree_2_insertion_witness_data = response.inner.insertion_witness_data;
988 signal.signal_level();
989 };
990 sequential_tree_2->add_or_update_values_sequentially(batch, completion);
991 signal.wait_for_level();
992 }
993
994 {
995 Signal signal;
996 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
997 sequential_tree_3->add_or_update_values_sequentially(batch, completion);
998 signal.wait_for_level();
999 }
1000
1001 {
1002 Signal signal;
1003 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
1004 batch_tree->add_or_update_values(batch, completion);
1005 signal.wait_for_level();
1006 }
1007 check_root(*sequential_tree_1, memdb.root());
1008 check_root(*sequential_tree_2, memdb.root());
1009 check_root(*sequential_tree_3, memdb.root());
1010 check_root(*batch_tree, memdb.root());
1011
1012 check_sibling_path(*sequential_tree_1, 0, memdb.get_sibling_path(0));
1013 check_sibling_path(*sequential_tree_2, 0, memdb.get_sibling_path(0));
1014 check_sibling_path(*sequential_tree_3, 0, memdb.get_sibling_path(0));
1015 check_sibling_path(*batch_tree, 0, memdb.get_sibling_path(0));
1016
1017 check_sibling_path(*sequential_tree_1, 512, memdb.get_sibling_path(512));
1018 check_sibling_path(*sequential_tree_2, 512, memdb.get_sibling_path(512));
1019 check_sibling_path(*sequential_tree_3, 512, memdb.get_sibling_path(512));
1020 check_sibling_path(*batch_tree, 512, memdb.get_sibling_path(512));
1021
1022 for (uint32_t j = 0; j < batch_size; j++) {
1023 EXPECT_EQ(sequential_tree_1_low_leaf_witness_data->at(j).leaf,
1024 sequential_tree_2_low_leaf_witness_data->at(j).leaf);
1025 EXPECT_EQ(sequential_tree_1_low_leaf_witness_data->at(j).index,
1026 sequential_tree_2_low_leaf_witness_data->at(j).index);
1027 EXPECT_EQ(sequential_tree_1_low_leaf_witness_data->at(j).path,
1028 sequential_tree_2_low_leaf_witness_data->at(j).path);
1029
1030 EXPECT_EQ(sequential_tree_1_insertion_witness_data->at(j).leaf,
1031 sequential_tree_2_insertion_witness_data->at(j).leaf);
1032 EXPECT_EQ(sequential_tree_1_insertion_witness_data->at(j).index,
1033 sequential_tree_2_insertion_witness_data->at(j).index);
1034 EXPECT_EQ(sequential_tree_1_insertion_witness_data->at(j).path,
1035 sequential_tree_2_insertion_witness_data->at(j).path);
1036 }
1037 }
1038}
1039
1041{
1042 uint32_t batchSize = 2;
1043 while (batchSize <= 2) {
1044 test_sequential_insert_vs_batch(batchSize, _directory, _mapSize, _maxReaders);
1045 batchSize <<= 1;
1046 }
1047}
1048
1049TEST_F(PersistedContentAddressedIndexedTreeTest, sequential_insert_allows_multiple_inserts_to_the_same_key)
1050{
1051 index_t current_size = 2;
1052 ThreadPoolPtr workers = make_thread_pool(8);
1053 // Create a depth-3 indexed merkle tree
1054 constexpr size_t depth = 3;
1055 std::string name = random_string();
1056 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1060 std::move(store), workers, current_size);
1061
1063 add_values_sequentially(tree, values);
1064
1065 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2).leaf.value, values[1].value);
1066 check_size(tree, 3);
1067}
1068
1069template <typename LeafValueType> fr hash_leaf(const IndexedLeaf<LeafValueType>& leaf)
1070{
1071 return HashPolicy::hash(leaf.get_hash_inputs());
1072}
1073
1074bool verify_sibling_path(TreeType& tree, const IndexedNullifierLeafType& leaf_value, const uint32_t idx)
1075{
1076 fr root = get_root(tree, true);
1077 fr_sibling_path path = get_sibling_path(tree, idx, true);
1078 auto current = hash_leaf(leaf_value);
1079 uint32_t depth_ = static_cast<uint32_t>(path.size());
1080 uint32_t index = idx;
1081 for (uint32_t i = 0; i < depth_; ++i) {
1082 fr left = (index & 1) ? path[i] : current;
1083 fr right = (index & 1) ? current : path[i];
1084 current = HashPolicy::hash_pair(left, right);
1085 index >>= 1;
1086 }
1087 return current == root;
1088}
1089
1091{
1092 index_t current_size = 2;
1093 ThreadPoolPtr workers = make_thread_pool(8);
1094 // Create a depth-3 indexed merkle tree
1095 constexpr size_t depth = 3;
1096 std::string name = random_string();
1097 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1098 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1099 auto tree = TreeType(std::move(store), workers, current_size);
1100
1110 IndexedNullifierLeafType zero_leaf(NullifierLeafValue(0), 1, 1);
1112 check_size(tree, current_size);
1113 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), zero_leaf);
1114 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), one_leaf);
1115
1125 add_value(tree, NullifierLeafValue(30));
1126 check_size(tree, ++current_size);
1127 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1128 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 2, 30));
1129 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 0, 0));
1130
1140 add_value(tree, NullifierLeafValue(10));
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, 2, 30));
1146
1156 add_value(tree, NullifierLeafValue(20));
1157 check_size(tree, ++current_size);
1158 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1159 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 3, 10));
1160 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 0, 0));
1161 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 3), create_indexed_nullifier_leaf(10, 4, 20));
1162 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 4), create_indexed_nullifier_leaf(20, 2, 30));
1163
1164 // Adding the same value must not affect anything
1165 // tree.update_element(20);
1166 // EXPECT_EQ(tree.get_leaves().size(), 4);
1167 // EXPECT_EQ(tree.get_leaves()[0], hash_leaf({ 0, 2, 10 }));
1168 // EXPECT_EQ(tree.get_leaves()[1], hash_leaf({ 30, 0, 0 }));
1169 // EXPECT_EQ(tree.get_leaves()[2], hash_leaf({ 10, 3, 20 }));
1170 // EXPECT_EQ(tree.get_leaves()[3], hash_leaf({ 20, 1, 30 }));
1171
1181 add_value(tree, NullifierLeafValue(50));
1182 check_size(tree, ++current_size);
1183 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1184 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 3, 10));
1185 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 5, 50));
1186 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 3), create_indexed_nullifier_leaf(10, 4, 20));
1187 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 4), create_indexed_nullifier_leaf(20, 2, 30));
1188 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 5), create_indexed_nullifier_leaf(50, 0, 0));
1189
1190 // Manually compute the node values
1191 auto e000 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 0));
1192 auto e001 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 1));
1193 auto e010 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 2));
1194 auto e011 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 3));
1195 auto e100 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 4));
1196 auto e101 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 5));
1197 auto e110 = fr::zero();
1198 auto e111 = fr::zero();
1199
1200 auto e00 = HashPolicy::hash_pair(e000, e001);
1201 auto e01 = HashPolicy::hash_pair(e010, e011);
1202 auto e10 = HashPolicy::hash_pair(e100, e101);
1203 auto e11 = HashPolicy::hash_pair(e110, e111);
1204
1205 auto e0 = HashPolicy::hash_pair(e00, e01);
1206 auto e1 = HashPolicy::hash_pair(e10, e11);
1207 auto root = HashPolicy::hash_pair(e0, e1);
1208
1209 // Check the hash path at index 2 and 3
1210 // Note: This merkle proof would also serve as a non-membership proof of values in (10, 20) and (20, 30)
1211 fr_sibling_path expected = {
1212 e001,
1213 e01,
1214 e1,
1215 };
1216 check_sibling_path(tree, 0, expected);
1217 expected = {
1218 e000,
1219 e01,
1220 e1,
1221 };
1222 check_sibling_path(tree, 1, expected);
1223 expected = {
1224 e011,
1225 e00,
1226 e1,
1227 };
1228 check_sibling_path(tree, 2, expected);
1229 expected = {
1230 e010,
1231 e00,
1232 e1,
1233 };
1234 check_sibling_path(tree, 3, expected);
1235 check_root(tree, root);
1236
1237 // Check the hash path at index 6 and 7
1238 expected = {
1239 e111,
1240 e10,
1241 e0,
1242 };
1243 check_sibling_path(tree, 6, expected);
1244 expected = {
1245 e110,
1246 e10,
1247 e0,
1248 };
1249 check_sibling_path(tree, 7, expected);
1250}
1251
1253{
1254 index_t current_size = 2;
1255 ThreadPoolPtr workers = make_thread_pool(1);
1256 ;
1257 // Create a depth-8 indexed merkle tree
1258 constexpr uint32_t depth = 8;
1259 std::string name = random_string();
1260 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1261 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1262 auto tree = TreeType(std::move(store), workers, current_size);
1263
1265 check_size(tree, current_size);
1266 EXPECT_EQ(hash_leaf(get_leaf<NullifierLeafValue>(tree, 0)), hash_leaf(zero_leaf));
1267
1268 // Add 20 random values to the tree
1269 for (uint32_t i = 0; i < 20; i++) {
1270 auto value = fr::random_element();
1272 ++current_size;
1273 }
1274
1275 auto abs_diff = [](uint256_t a, uint256_t b) {
1276 if (a > b) {
1277 return (a - b);
1278 } else {
1279 return (b - a);
1280 }
1281 };
1282
1283 check_size(tree, current_size);
1284
1285 // Check if a new random value is not a member of this tree.
1286 fr new_member = fr::random_element();
1287 std::vector<uint256_t> differences;
1288 for (uint32_t i = 0; i < uint32_t(21); i++) {
1289 uint256_t diff_hi =
1290 abs_diff(uint256_t(new_member), uint256_t(get_leaf<NullifierLeafValue>(tree, i).leaf.get_key()));
1291 uint256_t diff_lo =
1292 abs_diff(uint256_t(new_member), uint256_t(get_leaf<NullifierLeafValue>(tree, i).leaf.get_key()));
1293 differences.push_back(diff_hi + diff_lo);
1294 }
1295 auto it = std::min_element(differences.begin(), differences.end());
1296 auto index = static_cast<uint32_t>(it - differences.begin());
1297
1298 // Merkle proof at `index` proves non-membership of `new_member`
1299 EXPECT_TRUE(verify_sibling_path(tree, get_leaf<NullifierLeafValue>(tree, index), index));
1300}
1301
1303{
1304 constexpr size_t depth = 10;
1306 fr_sibling_path initial_path = memdb.get_sibling_path(0);
1307 memdb.update_element(get_value(0));
1308 fr_sibling_path final_sibling_path = memdb.get_sibling_path(0);
1309
1310 uint32_t num_reads = 16 * 1024;
1311 std::vector<fr_sibling_path> paths(num_reads);
1312
1313 {
1314 std::string name = random_string();
1315 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1316 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1318 TreeType tree(std::move(store), pool, 2);
1319
1320 check_size(tree, 2);
1321
1322 Signal signal(1 + num_reads);
1323
1324 auto add_completion = [&](const TypedResponse<AddIndexedDataResponse<NullifierLeafValue>>&) {
1325 auto commit_completion = [&](const TypedResponse<CommitResponse>&) { signal.signal_decrement(); };
1326 tree.commit(commit_completion);
1327 };
1328 tree.add_or_update_value(get_value(0), add_completion);
1329
1330 for (size_t i = 0; i < num_reads; i++) {
1331 auto completion = [&, i](const TypedResponse<GetSiblingPathResponse>& response) {
1332 paths[i] = response.inner.path;
1333 signal.signal_decrement();
1334 };
1335 tree.get_sibling_path(0, completion, false);
1336 }
1337 signal.wait_for_level();
1338 }
1339}
1340
1341TEST_F(PersistedContentAddressedIndexedTreeTest, test_indexed_memory_with_public_data_writes)
1342{
1343 index_t current_size = 2;
1344 ThreadPoolPtr workers = make_thread_pool(8);
1345 // Create a depth-3 indexed merkle tree
1346 constexpr size_t depth = 3;
1347 std::string name = random_string();
1348 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1352 std::move(store), workers, current_size);
1353
1366 check_size(tree, current_size);
1367 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
1368 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
1369
1380 add_value(tree, PublicDataLeafValue(30, 5));
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, 2, 30));
1384 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1385
1396 add_value(tree, PublicDataLeafValue(10, 20));
1397 check_size(tree, ++current_size);
1398 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1399 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1400 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1401 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1402
1413 add_value(tree, PublicDataLeafValue(30, 6));
1414 // The size still increases as we pad with an empty leaf
1415 check_size(tree, ++current_size);
1416
1417 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1418 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1419 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
1420 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1421 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(0, 0, 0, 0));
1422
1433 add_value(tree, PublicDataLeafValue(50, 8));
1434 check_size(tree, ++current_size);
1435 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1436 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1437 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 5, 50));
1438 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1439 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(0, 0, 0, 0));
1440 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 5), create_indexed_public_data_leaf(50, 8, 0, 0));
1441
1442 // Manually compute the node values
1443 auto e000 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 0));
1444 auto e001 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 1));
1445 auto e010 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 2));
1446 auto e011 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 3));
1447 auto e100 = fr::zero(); // tree doesn't hash 0 leaves!
1448 auto e101 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 5));
1449 auto e110 = fr::zero();
1450 auto e111 = fr::zero();
1451
1452 auto e00 = HashPolicy::hash_pair(e000, e001);
1453 auto e01 = HashPolicy::hash_pair(e010, e011);
1454 auto e10 = HashPolicy::hash_pair(e100, e101);
1455 auto e11 = HashPolicy::hash_pair(e110, e111);
1456
1457 auto e0 = HashPolicy::hash_pair(e00, e01);
1458 auto e1 = HashPolicy::hash_pair(e10, e11);
1459 auto root = HashPolicy::hash_pair(e0, e1);
1460
1461 fr_sibling_path expected = {
1462 e001,
1463 e01,
1464 e1,
1465 };
1466 check_sibling_path(tree, 0, expected);
1467 expected = {
1468 e000,
1469 e01,
1470 e1,
1471 };
1472 check_sibling_path(tree, 1, expected);
1473 expected = {
1474 e011,
1475 e00,
1476 e1,
1477 };
1478 check_sibling_path(tree, 2, expected);
1479 expected = {
1480 e010,
1481 e00,
1482 e1,
1483 };
1484 check_sibling_path(tree, 3, expected);
1485 check_root(tree, root);
1486
1487 // Check the hash path at index 6 and 7
1488 expected = {
1489 e111,
1490 e10,
1491 e0,
1492 };
1493 check_sibling_path(tree, 6, expected);
1494 expected = {
1495 e110,
1496 e10,
1497 e0,
1498 };
1499 check_sibling_path(tree, 7, expected);
1500}
1501
1502TEST_F(PersistedContentAddressedIndexedTreeTest, test_indexed_memory_with_sequential_public_data_writes)
1503{
1504 index_t current_size = 2;
1505 ThreadPoolPtr workers = make_thread_pool(8);
1506 // Create a depth-3 indexed merkle tree
1507 constexpr size_t depth = 3;
1508 std::string name = random_string();
1509 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1513 std::move(store), workers, current_size);
1514
1527 check_size(tree, current_size);
1528 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
1529 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
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, 2, 30));
1545 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1546
1558 check_size(tree, ++current_size);
1559 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1560 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1561 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1562 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1563
1575 // The size does not increase since sequential insertion doesn't pad
1576 check_size(tree, current_size);
1577
1578 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1579 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1580 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
1581 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1582
1594 check_size(tree, ++current_size);
1595 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1596 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1597 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
1598 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1599 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
1600
1601 // Manually compute the node values
1602 auto e000 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 0));
1603 auto e001 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 1));
1604 auto e010 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 2));
1605 auto e011 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 3));
1606 auto e100 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 4));
1607 auto e101 = fr::zero();
1608 auto e110 = fr::zero();
1609 auto e111 = fr::zero();
1610
1611 auto e00 = HashPolicy::hash_pair(e000, e001);
1612 auto e01 = HashPolicy::hash_pair(e010, e011);
1613 auto e10 = HashPolicy::hash_pair(e100, e101);
1614 auto e11 = HashPolicy::hash_pair(e110, e111);
1615
1616 auto e0 = HashPolicy::hash_pair(e00, e01);
1617 auto e1 = HashPolicy::hash_pair(e10, e11);
1618 auto root = HashPolicy::hash_pair(e0, e1);
1619
1620 fr_sibling_path expected = {
1621 e001,
1622 e01,
1623 e1,
1624 };
1625 check_sibling_path(tree, 0, expected);
1626 expected = {
1627 e000,
1628 e01,
1629 e1,
1630 };
1631 check_sibling_path(tree, 1, expected);
1632 expected = {
1633 e011,
1634 e00,
1635 e1,
1636 };
1637 check_sibling_path(tree, 2, expected);
1638 expected = {
1639 e010,
1640 e00,
1641 e1,
1642 };
1643 check_sibling_path(tree, 3, expected);
1644 check_root(tree, root);
1645
1646 // Check the hash path at index 6 and 7
1647 expected = {
1648 e111,
1649 e10,
1650 e0,
1651 };
1652 check_sibling_path(tree, 6, expected);
1653 expected = {
1654 e110,
1655 e10,
1656 e0,
1657 };
1658 check_sibling_path(tree, 7, expected);
1659}
1660
1662{
1663 // Create a depth-8 indexed merkle tree
1664 constexpr uint32_t depth = 8;
1665
1666 ThreadPoolPtr workers = make_thread_pool(1);
1667 std::string name = random_string();
1668 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1669 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1670 auto tree = TreeType(std::move(store), workers, 2);
1671
1672 auto predecessor = get_low_leaf(tree, NullifierLeafValue(42));
1673
1674 EXPECT_EQ(predecessor.is_already_present, false);
1675 EXPECT_EQ(predecessor.index, 1);
1676
1677 add_value(tree, NullifierLeafValue(42));
1678
1679 predecessor = get_low_leaf(tree, NullifierLeafValue(42));
1680 // returns the current leaf since it exists already. Inserting 42 again would modify the existing leaf
1681 EXPECT_EQ(predecessor.is_already_present, true);
1682 EXPECT_EQ(predecessor.index, 2);
1683}
1684
1686{
1687 // Create a depth-8 indexed merkle tree
1688 constexpr uint32_t depth = 8;
1689
1690 ThreadPoolPtr workers = make_thread_pool(1);
1691 ;
1692 std::string name = random_string();
1693 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1694 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1695 auto tree = TreeType(std::move(store), workers, 2);
1696
1697 add_value(tree, NullifierLeafValue(42));
1698 check_size(tree, 3);
1699
1700 commit_tree(tree);
1701
1702 add_value(tree, NullifierLeafValue(42), false);
1703 // expect this to fail as no data is present
1704 commit_tree(tree);
1705 check_size(tree, 3);
1706}
1707
1708TEST_F(PersistedContentAddressedIndexedTreeTest, test_historic_sibling_path_retrieval)
1709{
1711 const uint32_t batch_size = 16;
1712 const uint32_t num_batches = 8;
1713 std::string name1 = random_string();
1714 std::string name2 = random_string();
1715 uint32_t depth = 10;
1716 ThreadPoolPtr multi_workers = make_thread_pool(8);
1717 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1718
1719 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1720 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1721 auto tree1 = TreeType(std::move(store1), multi_workers, batch_size);
1722
1723 std::vector<fr_sibling_path> memory_tree_sibling_paths_index_0;
1724
1725 auto check = [&]() {
1726 check_root(tree1, memdb.root());
1727 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1728 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1729
1730 for (uint32_t i = 0; i < memory_tree_sibling_paths_index_0.size(); i++) {
1731 check_historic_sibling_path(tree1, 0, i + 1, memory_tree_sibling_paths_index_0[i]);
1732 }
1733 };
1734
1735 for (uint32_t i = 0; i < num_batches; i++) {
1736
1737 check_root(tree1, memdb.root());
1738 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1739 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1740
1742
1743 for (uint32_t j = 0; j < batch_size; j++) {
1744 batch.emplace_back(random_engine.get_random_uint256());
1745 memdb.update_element(batch[j].get_key());
1746 }
1747 memory_tree_sibling_paths_index_0.push_back(memdb.get_sibling_path(0));
1750 {
1751 Signal signal;
1752 CompletionCallback completion =
1754 tree1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
1755 signal.signal_level();
1756 };
1757 tree1.add_or_update_values(batch, completion);
1758 signal.wait_for_level();
1759 }
1760 check_root(tree1, memdb.root());
1761 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1762 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1763 commit_tree(tree1);
1764 check();
1765 }
1766}
1767
1769{
1770 index_t current_size = 2;
1771 ThreadPoolPtr workers = make_thread_pool(8);
1772 // Create a depth-3 indexed merkle tree
1773 constexpr size_t depth = 3;
1774 std::string name = random_string();
1775 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1778 using LocalTreeType =
1780 auto tree = LocalTreeType(std::move(store), workers, current_size);
1781
1794 check_size(tree, current_size);
1795 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
1796 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
1797
1809 commit_tree(tree);
1810 check_size(tree, ++current_size);
1811 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1812 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
1813 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1814
1815 auto leaf1AtBlock1 = PublicDataLeafValue(1, 0);
1816
1828 check_size(tree, ++current_size);
1829 commit_tree(tree);
1830 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1831 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1832 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1833 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1834
1835 auto leaf2AtBlock2 = PublicDataLeafValue(30, 5);
1836 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, true);
1837
1838 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
1839 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
1840 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
1841
1853 // The size does not increase since sequential insertion doesn't pad
1854 check_size(tree, current_size);
1855 commit_tree(tree);
1856 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1857 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1858 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
1859 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1860
1861 auto leaf2AtBlock3 = PublicDataLeafValue(30, 6);
1862 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
1863
1864 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
1865 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
1866 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
1867
1879 check_size(tree, ++current_size);
1880 commit_tree(tree);
1881 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1882 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1883 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
1884 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1885 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
1886
1887 check_historic_leaf(tree, leaf2AtBlock3, 2, 3, true);
1888
1889 // should not be found at block 1
1890 check_historic_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
1891 tree, { PublicDataLeafValue(10, 20) }, 1, 0, { std::nullopt }, true);
1892 // should be found at block
1893 check_historic_find_leaf_index_from(tree, PublicDataLeafValue(10, 20), 2, 0, 3, true);
1894
1896 EXPECT_EQ(lowLeaf.index, 1);
1897
1898 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(20, 0));
1899 EXPECT_EQ(lowLeaf.index, 3);
1900
1901 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(60, 0));
1902 EXPECT_EQ(lowLeaf.index, 2);
1903}
1904
1905TEST_F(PersistedContentAddressedIndexedTreeTest, test_inserting_a_duplicate_committed_nullifier_should_fail)
1906{
1907 const uint32_t batch_size = 16;
1908 uint32_t depth = 10;
1909 ThreadPoolPtr multi_workers = make_thread_pool(1);
1910 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1911
1912 std::string name1 = random_string();
1913 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1914 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1915 auto tree = TreeType(std::move(store1), multi_workers, batch_size);
1916
1917 std::vector<fr> values = create_values(batch_size);
1918 std::vector<NullifierLeafValue> nullifierValues(batch_size);
1919 std::transform(
1920 values.begin(), values.end(), nullifierValues.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1921
1922 add_values(tree, nullifierValues);
1923 commit_tree(tree);
1924
1925 // create a new set of values
1926 std::vector<fr> values2 = create_values(batch_size);
1927
1928 // copy one of the previous values into the middle of the batch
1929 values2[batch_size / 2] = values[0];
1930 std::vector<NullifierLeafValue> nullifierValues2(batch_size);
1931 std::transform(
1932 values2.begin(), values2.end(), nullifierValues2.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1933 add_values(tree, nullifierValues2, false);
1934}
1935
1936TEST_F(PersistedContentAddressedIndexedTreeTest, test_inserting_a_duplicate_uncommitted_nullifier_should_fail)
1937{
1938 const uint32_t batch_size = 16;
1939 uint32_t depth = 10;
1940 ThreadPoolPtr multi_workers = make_thread_pool(1);
1941 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1942
1943 std::string name1 = random_string();
1944 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1945 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1946 auto tree = TreeType(std::move(store1), multi_workers, batch_size);
1947
1948 std::vector<fr> values = create_values(batch_size);
1949 std::vector<NullifierLeafValue> nullifierValues(batch_size);
1950 std::transform(
1951 values.begin(), values.end(), nullifierValues.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1952
1953 add_values(tree, nullifierValues);
1954
1955 // create a new set of values
1956 std::vector<fr> values2 = create_values(batch_size);
1957
1958 // copy one of the previous values into the middle of the batch
1959 values2[batch_size / 2] = values[0];
1960 std::vector<NullifierLeafValue> nullifierValues2(batch_size);
1961 std::transform(
1962 values2.begin(), values2.end(), nullifierValues2.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1963 add_values(tree, nullifierValues2, false);
1964}
1965
1966TEST_F(PersistedContentAddressedIndexedTreeTest, test_can_create_forks_at_historic_blocks)
1967{
1969 const uint32_t batch_size = 16;
1970 uint32_t depth = 10;
1971 ThreadPoolPtr multi_workers = make_thread_pool(8);
1972 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1973
1974 std::string name1 = random_string();
1975 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1976 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1977 auto tree1 = TreeType(std::move(store1), multi_workers, batch_size);
1978
1979 check_root(tree1, memdb.root());
1980 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1981
1982 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1983
1985 for (uint32_t j = 0; j < batch_size; j++) {
1986 batch1.emplace_back(random_engine.get_random_uint256());
1987 memdb.update_element(batch1[j].nullifier);
1988 }
1989
1990 fr_sibling_path block1SiblingPathIndex3 = memdb.get_sibling_path(3 + batch_size);
1991
1992 add_values(tree1, batch1);
1993 commit_tree(tree1, true);
1994
1996 for (uint32_t j = 0; j < batch_size; j++) {
1997 batch2.emplace_back(random_engine.get_random_uint256());
1998 memdb.update_element(batch2[j].nullifier);
1999 }
2000
2001 add_values(tree1, batch2);
2002 commit_tree(tree1, true);
2003
2004 fr block2Root = memdb.root();
2005
2006 fr_sibling_path block2SiblingPathIndex19 = memdb.get_sibling_path(19 + batch_size);
2007 fr_sibling_path block2SiblingPathIndex3 = memdb.get_sibling_path(3 + batch_size);
2008
2010 for (uint32_t j = 0; j < batch_size; j++) {
2011 batch3.emplace_back(random_engine.get_random_uint256());
2012 memdb.update_element(batch3[j].nullifier);
2013 }
2014
2015 add_values(tree1, batch3);
2016 commit_tree(tree1, true);
2017
2018 fr_sibling_path block3SiblingPathIndex35 = memdb.get_sibling_path(35 + batch_size);
2019 fr_sibling_path block3SiblingPathIndex19 = memdb.get_sibling_path(19 + batch_size);
2020 fr_sibling_path block3SiblingPathIndex3 = memdb.get_sibling_path(3 + batch_size);
2021
2022 std::unique_ptr<Store> storeAtBlock2 = std::make_unique<Store>(name1, depth, 2, db1);
2023 auto treeAtBlock2 = TreeType(std::move(storeAtBlock2), multi_workers, batch_size);
2024
2025 check_root(treeAtBlock2, block2Root);
2026 check_sibling_path(treeAtBlock2, 3 + batch_size, block2SiblingPathIndex3, false, true);
2027 auto block2TreeLeaf10 = get_leaf<NullifierLeafValue>(treeAtBlock2, 7 + batch_size);
2028 EXPECT_EQ(block2TreeLeaf10.leaf.nullifier, batch1[7].nullifier);
2029
2030 check_find_leaf_index(treeAtBlock2, batch1[5], 5 + batch_size, true);
2031 check_find_leaf_index_from(treeAtBlock2, batch1[5], 0, 5 + batch_size, true);
2032
2033 // should not exist in our image
2034 get_leaf<NullifierLeafValue>(treeAtBlock2, 35 + batch_size, false, false);
2035 check_find_leaf_index<NullifierLeafValue, TreeType>(treeAtBlock2, { batch3[4] }, { std::nullopt }, true);
2036
2037 // now add the same values to our image
2038 add_values(treeAtBlock2, batch3);
2039
2040 // the state of our image should match the original tree
2041 check_sibling_path(tree1, 3 + batch_size, block3SiblingPathIndex3, false, true);
2042 check_sibling_path(tree1, 19 + batch_size, block3SiblingPathIndex19, false, true);
2043 check_sibling_path(tree1, 35 + batch_size, block3SiblingPathIndex35, false, true);
2044
2045 // needs to use uncommitted for this check
2046 check_sibling_path(treeAtBlock2, 3 + batch_size, block3SiblingPathIndex3, true, true);
2047 check_sibling_path(treeAtBlock2, 19 + batch_size, block3SiblingPathIndex19, true, true);
2048 check_sibling_path(treeAtBlock2, 35 + batch_size, block3SiblingPathIndex35, true, true);
2049
2050 // now check historic data
2051 auto historicSiblingPath = get_historic_sibling_path(treeAtBlock2, 1, 3 + batch_size);
2052 EXPECT_EQ(historicSiblingPath, block1SiblingPathIndex3);
2053 check_historic_find_leaf_index(treeAtBlock2, batch1[3], 1, 3 + batch_size, true);
2054 check_historic_find_leaf_index(treeAtBlock2, batch3[3], 2, 35 + batch_size, true, true);
2055 check_historic_find_leaf_index<NullifierLeafValue, TreeType>(
2056 treeAtBlock2, { batch3[3] }, 2, { std::nullopt }, true, false);
2057
2058 check_historic_find_leaf_index_from(treeAtBlock2, batch1[3], 2, 0, 3 + batch_size, true, false);
2059 check_historic_find_leaf_index_from<NullifierLeafValue, TreeType>(
2060 treeAtBlock2, { batch3[3] }, 2, 20 + batch_size, { std::nullopt }, true, false);
2061 check_historic_find_leaf_index_from(treeAtBlock2, batch3[3], 2, 20 + batch_size, 35 + batch_size, true, true);
2062
2063 check_unfinalized_block_height(treeAtBlock2, 2);
2064
2065 // It should be impossible to commit using the image
2066 commit_tree(treeAtBlock2, false);
2067}
2068
2070{
2071 index_t current_size = 2;
2072 ThreadPoolPtr workers = make_thread_pool(8);
2073 // Create a depth-3 indexed merkle tree
2074 constexpr size_t depth = 3;
2075 std::string name = random_string();
2076 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2079 using LocalTreeType =
2081 auto tree = LocalTreeType(std::move(store), workers, current_size);
2082
2095 check_size(tree, current_size);
2096 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
2097 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
2098
2110 commit_tree(tree);
2111 check_size(tree, ++current_size);
2112 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2113 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2114 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2115
2116 auto leaf1AtBlock1 = PublicDataLeafValue(1, 0);
2117 check_block_and_size_data(db, 1, current_size, true);
2118
2130 check_size(tree, ++current_size);
2131 commit_tree(tree);
2132 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2133 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2134 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2135 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2136
2137 check_block_and_size_data(db, 2, current_size, true);
2138
2139 auto leaf2AtBlock2 = PublicDataLeafValue(30, 5);
2140 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, true);
2141
2142 // shoudl find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2143 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2144 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2145
2157 check_size(tree, current_size);
2158 commit_tree(tree);
2159 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2160 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2161 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2162 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2163
2164 check_block_and_size_data(db, 3, current_size, true);
2165
2166 auto leaf2AtBlock3 = PublicDataLeafValue(30, 6);
2167 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
2168
2169 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2170 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2171 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2172
2184 check_size(tree, ++current_size);
2185 commit_tree(tree);
2186 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2187 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2188 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2189 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2190 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
2191
2192 check_block_and_size_data(db, 4, current_size, true);
2193
2194 check_historic_leaf(tree, leaf2AtBlock3, 2, 3, true);
2195
2196 // should not be found at block 1
2197 check_historic_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
2198 tree, { PublicDataLeafValue(10, 20) }, 1, 0, { std::nullopt }, true);
2199 // should be found at block
2200 check_historic_find_leaf_index_from(tree, PublicDataLeafValue(10, 20), 2, 0, 3, true);
2201
2203 EXPECT_EQ(lowLeaf.index, 1);
2204
2205 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(20, 0));
2206 EXPECT_EQ(lowLeaf.index, 3);
2207
2208 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(60, 0));
2209 EXPECT_EQ(lowLeaf.index, 2);
2210
2211 finalize_block(tree, 3);
2212
2213 // remove historical block 1
2214 remove_historic_block(tree, 1);
2215
2216 // Historic queries against block 1 should no longer work
2217 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, false);
2218 check_historic_find_leaf_index<PublicDataLeafValue, LocalTreeType>(
2219 tree, { leaf1AtBlock1 }, 1, { std::nullopt }, false);
2220
2221 // Queries against block 2 should work
2222 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
2223 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2224
2225 // now remove block 2 and queries against it should no longer work
2226 remove_historic_block(tree, 2);
2227 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, false);
2228
2229 // size doesn't matter, should fail to find the data
2230 check_block_and_size_data(db, 1, current_size, false);
2231}
2232
2234{
2235 index_t current_size = 2;
2236 ThreadPoolPtr workers = make_thread_pool(8);
2237 // Create a depth-3 indexed merkle tree
2238 constexpr size_t depth = 3;
2239 std::string name = random_string();
2240 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2243 using LocalTreeType =
2245 auto tree = LocalTreeType(std::move(store), workers, current_size);
2246
2259 check_size(tree, current_size);
2260 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
2261 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
2262
2263 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2264 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2265
2266 check_indices_data(db, 0, 0, true, true);
2267 check_indices_data(db, 1, 1, true, true);
2268
2280 commit_tree(tree);
2281 check_size(tree, ++current_size);
2282
2283 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2284 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2285 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2286
2287 check_block_and_size_data(db, 1, current_size, true);
2288
2289 // All historical pre-images should be present
2290 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2291 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2292 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2293 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2294
2295 check_indices_data(db, 30, 2, true, true);
2296
2297 auto leaf1AtBlock1 = PublicDataLeafValue(1, 0);
2298
2310 check_size(tree, ++current_size);
2311 commit_tree(tree);
2312 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2313 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2314 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2315 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2316
2317 // All historical pre-images should be present
2318 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2319 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2320 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2321 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2322 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2323
2324 check_indices_data(db, 10, 3, true, true);
2325
2326 check_block_and_size_data(db, 2, current_size, true);
2327
2328 auto leaf2AtBlock2 = PublicDataLeafValue(30, 5);
2329 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, true);
2330
2331 // shoudl find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2332 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2333 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2334
2346 check_size(tree, current_size);
2347 commit_tree(tree);
2348 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2349 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2350 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2351 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2352
2353 // All historical pre-images should be present
2354 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2355 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2356 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2357 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2358 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2359 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2360 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 0, 0), true);
2361
2362 // Zero leaves should not have their indices added
2363 check_indices_data(db, 0, 4, true, false);
2364
2365 check_block_and_size_data(db, 3, current_size, true);
2366
2367 auto leaf2AtBlock3 = PublicDataLeafValue(30, 6);
2368 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
2369
2370 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2371 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2372 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2373
2385 check_size(tree, ++current_size);
2386 commit_tree(tree);
2387 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2388 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2389 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2390 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2391 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
2392
2393 check_indices_data(db, 50, 4, true, true);
2394 // All historical pre-images should be present
2395 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2396 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2397 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2398 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2399 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2400 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 0, 0), true);
2401 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(50, 8, 0, 0), true);
2402 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 4, 50), true);
2403
2404 check_block_and_size_data(db, 4, current_size, true);
2405
2406 check_historic_leaf(tree, leaf2AtBlock3, 2, 3, true);
2407
2408 // should not be found at block 1
2409 check_historic_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
2410 tree, { PublicDataLeafValue(10, 20) }, 1, 0, { std::nullopt }, true);
2411 // should be found at block
2412 check_historic_find_leaf_index_from(tree, PublicDataLeafValue(10, 20), 2, 0, 3, true);
2413
2415 EXPECT_EQ(lowLeaf.index, 1);
2416
2417 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(20, 0));
2418 EXPECT_EQ(lowLeaf.index, 3);
2419
2420 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(60, 0));
2421 EXPECT_EQ(lowLeaf.index, 2);
2422
2423 unwind_block(tree, 4);
2424
2425 // Index 4 should be removed
2426 check_indices_data(db, 50, 4, false, false);
2427 // The pre-images created before block 4 should be present
2428 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2429 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2430 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2431 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2432 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2433 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2434 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 0, 0), true);
2435
2436 // The pre-images created in block 4 should be gone
2437 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(50, 8, 0, 0), false);
2438 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 4, 50), false);
2439
2440 check_size(tree, --current_size);
2441
2442 // should fail to find block 4
2443 check_block_and_size_data(db, 4, current_size, false);
2444
2445 // block 3 should work
2446 check_block_and_size_data(db, 3, current_size, true);
2447
2448 // should fail to find the leaf at index 4
2449 check_find_leaf_index<PublicDataLeafValue, LocalTreeType>(
2450 tree, { PublicDataLeafValue(50, 8) }, { std::nullopt }, true);
2451 check_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
2452 tree, { PublicDataLeafValue(50, 8) }, 0, { std::nullopt }, true);
2453
2454 // the leaf at index 2 should no longer be as it was after block 5
2455 EXPECT_NE(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2456
2457 // it should be as it was after block 4
2458 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2459
2460 unwind_block(tree, 3);
2461
2462 // The pre-images created before block 3 should be present
2463 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2464 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2465 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2466 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2467 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2468
2469 check_size(tree, current_size);
2470
2471 // the leaf at index 2 should no longer be as it was after block 4
2472 EXPECT_NE(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2473
2474 // it should be as it was after block 3
2475 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2476}
2477
2479{
2480 index_t current_size = 2;
2481 ThreadPoolPtr workers = make_thread_pool(8);
2482 // Create a depth-3 indexed merkle tree
2483 constexpr size_t depth = 3;
2484 std::string name = random_string();
2485 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2489 std::move(store), workers, current_size);
2490
2503 check_size(tree, current_size);
2504 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
2505 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
2506
2518 commit_tree(tree);
2519 check_size(tree, ++current_size);
2520 fr rootAfterBlock1 = get_root(tree, false);
2521 fr_sibling_path pathAfterBlock1 = get_sibling_path(tree, 0, false);
2522 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2523 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2524 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2525
2526 check_block_and_size_data(db, 1, current_size, true);
2527
2528 // All historical pre-images should be present
2529 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2530 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2531 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2532 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2533
2534 check_indices_data(db, 30, 2, true, true);
2535
2547 commit_tree(tree);
2548 check_size(tree, current_size);
2549 fr rootAfterBlock2 = get_root(tree, false);
2550 fr_sibling_path pathAfterBlock2 = get_sibling_path(tree, 0, false);
2551 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2552 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2553 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 8, 0, 0));
2554
2555 // All historical pre-images should be present
2556 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2557 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2558 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2559 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2560 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), true);
2561
2562 check_indices_data(db, 30, 2, true, true);
2563
2575 commit_tree(tree);
2576 check_size(tree, current_size);
2577 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2578 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2579 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2580
2581 // All historical pre-images should be present
2582 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2583 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2584 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2585 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2586 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), true);
2587
2588 check_indices_data(db, 30, 2, true, true);
2589
2590 // Unwind block 3 and the state should be reverted back to block 2
2591 unwind_block(tree, 3);
2592
2593 check_root(tree, rootAfterBlock2);
2594 check_sibling_path(tree, 0, pathAfterBlock2, false);
2595 check_size(tree, current_size);
2596 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2597 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2598 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 8, 0, 0));
2599
2600 // All historical pre-images should be present
2601 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2602 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2603 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2604 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2605 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), true);
2606
2607 check_indices_data(db, 30, 2, true, true);
2608
2609 // Unwind block 2 and the state should be reverted back to block 1
2610 unwind_block(tree, 2);
2611
2612 check_root(tree, rootAfterBlock1);
2613 check_sibling_path(tree, 0, pathAfterBlock1, false);
2614 check_size(tree, current_size);
2615 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2616 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2617 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2618
2619 // All historical pre-images should be present
2620 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2621 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2622 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2623 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2624
2625 // Check the pre-image was removed
2626 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), false);
2627
2628 check_indices_data(db, 30, 2, true, true);
2629
2630 // Now apply block 2 again and it should be moved forward back tot where it was
2631 add_value(tree, PublicDataLeafValue(30, 8));
2632 commit_tree(tree);
2633 check_size(tree, ++current_size);
2634 check_root(tree, rootAfterBlock2);
2635 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2636 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2637 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 8, 0, 0));
2638}
2639
2640void test_nullifier_tree_unwind(std::string directory,
2641 std::string name,
2642 uint64_t mapSize,
2643 uint64_t maxReaders,
2644 uint32_t depth,
2645 uint32_t blockSize,
2646 uint32_t numBlocks,
2647 uint32_t numBlocksToUnwind,
2648 std::vector<fr> values)
2649{
2650 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(directory, name, mapSize, maxReaders);
2651 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
2653 TreeType tree(std::move(store), pool, blockSize);
2654 NullifierMemoryTree<Poseidon2HashPolicy> memdb(depth, blockSize);
2655
2656 auto it = std::find_if(values.begin(), values.end(), [&](const fr& v) { return v != fr::zero(); });
2657 bool emptyBlocks = it == values.end();
2658
2659 uint32_t batchSize = blockSize;
2660
2661 std::vector<fr_sibling_path> historicPathsZeroIndex;
2662 std::vector<fr_sibling_path> historicPathsMaxIndex;
2663 std::vector<fr> roots;
2664
2665 fr initialRoot = memdb.root();
2666 fr_sibling_path initialPath = memdb.get_sibling_path(0);
2667
2669 leafValues.reserve(values.size());
2670 for (const fr& v : values) {
2671 leafValues.emplace_back(v);
2672 }
2673
2674 for (uint32_t i = 0; i < numBlocks; i++) {
2676
2677 for (size_t j = 0; j < batchSize; ++j) {
2678 size_t ind = i * batchSize + j;
2679 memdb.update_element(values[ind]);
2680 to_add.push_back(leafValues[ind]);
2681 }
2682 // Indexed trees have an initial 'batch' inserted at startup
2683 index_t expected_size = (i + 2) * batchSize;
2684 add_values(tree, to_add);
2685 commit_tree(tree);
2686
2687 historicPathsZeroIndex.push_back(memdb.get_sibling_path(0));
2688 historicPathsMaxIndex.push_back(memdb.get_sibling_path(expected_size - 1));
2689 roots.push_back(memdb.root());
2690 check_root(tree, memdb.root());
2691 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
2692 check_sibling_path(tree, expected_size - 1, memdb.get_sibling_path(expected_size - 1));
2693 check_size(tree, expected_size);
2694 check_block_and_size_data(db, i + 1, expected_size, true);
2695 check_block_and_root_data(db, i + 1, memdb.root(), true);
2696 }
2697
2698 const uint32_t blocksToRemove = numBlocksToUnwind;
2699 for (uint32_t i = 0; i < blocksToRemove; i++) {
2700 const block_number_t blockNumber = numBlocks - i;
2701
2702 check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], true);
2703 unwind_block(tree, blockNumber);
2704 if (emptyBlocks) {
2705 // with empty blocks, we should not find the block data but we do find the root
2706 check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], false, true);
2707 } else {
2708 // if blocks are not empty, this query should fail
2709 check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], false);
2710 }
2711
2712 const index_t previousValidBlock = blockNumber - 1;
2713 // Indexed trees have an initial 'batch' inserted at startup
2714 index_t deletedBlockStartIndex = (1 + previousValidBlock) * batchSize;
2715 index_t deletedBlockStartIndexIntoLocalValues = previousValidBlock * batchSize;
2716
2717 check_block_height(tree, previousValidBlock);
2718 check_size(tree, deletedBlockStartIndex);
2719 check_root(tree, previousValidBlock == 0 ? initialRoot : roots[previousValidBlock - 1]);
2720
2721 // The zero index sibling path should be as it was at the previous block
2722 check_sibling_path(tree,
2723 0,
2724 previousValidBlock == 0 ? initialPath : historicPathsZeroIndex[previousValidBlock - 1],
2725 false,
2726 true);
2727
2728 if (!emptyBlocks) {
2729 // Trying to find leaves appended in the block that was removed should fail
2730 get_leaf<NullifierLeafValue>(tree, 1 + deletedBlockStartIndex, false, false);
2731
2732 check_find_leaf_index<NullifierLeafValue, TreeType>(
2733 tree, { leafValues[1 + deletedBlockStartIndexIntoLocalValues] }, { std::nullopt }, true);
2734 }
2735
2736 for (index_t j = 0; j < numBlocks; j++) {
2737 block_number_t historicBlockNumber = static_cast<block_number_t>(j + 1);
2738 bool expectedSuccess = historicBlockNumber <= previousValidBlock;
2740 tree, 0, historicBlockNumber, historicPathsZeroIndex[j], false, expectedSuccess);
2741 index_t maxSizeAtBlock = ((j + 2) * batchSize) - 1;
2743 tree, maxSizeAtBlock, historicBlockNumber, historicPathsMaxIndex[j], false, expectedSuccess);
2744
2745 if (emptyBlocks) {
2746 continue;
2747 }
2748 const index_t leafIndex = 1;
2749 const index_t expectedIndexInTree = leafIndex + batchSize;
2751 tree, leafValues[leafIndex], expectedIndexInTree, historicBlockNumber, expectedSuccess, false);
2752
2753 std::vector<std::optional<index_t>> expectedResults;
2754 if (expectedSuccess) {
2755 expectedResults.emplace_back(std::make_optional(expectedIndexInTree));
2756 }
2757 check_historic_find_leaf_index<NullifierLeafValue, TreeType>(
2758 tree, { leafValues[leafIndex] }, historicBlockNumber, expectedResults, expectedSuccess, true);
2759 check_historic_find_leaf_index_from<NullifierLeafValue, TreeType>(
2760 tree, { leafValues[leafIndex] }, historicBlockNumber, 0, expectedResults, expectedSuccess, true);
2761 }
2762 }
2763}
2764
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 = create_values(actualSize * numBlocks);
2774 std::stringstream ss;
2775 ss << "DB " << actualSize;
2777 _directory, ss.str(), _mapSize, _maxReaders, 20, actualSize, numBlocks, numBlocksToUnwind, values);
2778 }
2779}
2780
2781TEST_F(PersistedContentAddressedIndexedTreeTest, can_sync_and_unwind_empty_blocks)
2782{
2783
2784 constexpr uint32_t numBlocks = 8;
2785 constexpr uint32_t numBlocksToUnwind = 4;
2786 std::vector<uint32_t> blockSizes = { 2, 4, 8, 16, 32 };
2787 for (const uint32_t& size : blockSizes) {
2788 uint32_t actualSize = size;
2789 std::vector<fr> values = std::vector<fr>(actualSize * numBlocks, fr::zero());
2790 std::stringstream ss;
2791 ss << "DB " << actualSize;
2793 _directory, ss.str(), _mapSize, _maxReaders, 20, actualSize, numBlocks, numBlocksToUnwind, values);
2794 }
2795}
2796
2798{
2799 ThreadPoolPtr workers = make_thread_pool(1);
2800 constexpr size_t depth = 3;
2801 std::string name = random_string();
2802 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2804
2805 index_t initial_size = 4;
2807 auto tree = PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values);
2808
2823 check_size(tree, initial_size);
2824 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), leaf_0);
2825 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), leaf_1);
2826 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), leaf_2);
2827 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), leaf_3);
2828}
2829
2830TEST_F(PersistedContentAddressedIndexedTreeTest, test_full_prefilled_public_data)
2831{
2832 ThreadPoolPtr workers = make_thread_pool(1);
2833 constexpr size_t depth = 3;
2834 std::string name = random_string();
2835 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2837
2838 index_t initial_size = 4;
2839 std::vector<PublicDataLeafValue> prefilled_values = {
2841 };
2842 auto tree = PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values);
2843
2858 check_size(tree, initial_size);
2859 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), leaf_0);
2860 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), leaf_1);
2861 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), leaf_2);
2862 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), leaf_3);
2863}
2864
2865TEST_F(PersistedContentAddressedIndexedTreeTest, test_prefilled_unsorted_public_data_should_fail)
2866{
2867 ThreadPoolPtr workers = make_thread_pool(1);
2868 constexpr size_t depth = 3;
2869 std::string name = random_string();
2870 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2872
2873 index_t initial_size = 4;
2874 // The prefilled values are not sorted: 5 > 3.
2876 EXPECT_THROW(PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values), std::runtime_error);
2877}
2878
2879TEST_F(PersistedContentAddressedIndexedTreeTest, test_prefilled_default_public_data_should_fail)
2880{
2881 ThreadPoolPtr workers = make_thread_pool(1);
2882 constexpr size_t depth = 3;
2883 std::string name = random_string();
2884 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2886
2887 index_t initial_size = 4;
2888 // The first prefilled value is the same as one of the default values (1).
2890 EXPECT_THROW(PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values), std::runtime_error);
2891}
2892
2893TEST_F(PersistedContentAddressedIndexedTreeTest, test_can_commit_and_revert_checkpoints)
2894{
2895 index_t initial_size = 2;
2896 index_t current_size = initial_size;
2897 ThreadPoolPtr workers = make_thread_pool(8);
2898 // Create a depth-3 indexed merkle tree
2899 constexpr size_t depth = 3;
2900 std::string name = random_string();
2901 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2905 std::move(store), workers, current_size);
2906
2929 check_size(tree, ++current_size);
2930
2942 check_size(tree, ++current_size);
2943
2955 // The size does not increase since sequential insertion doesn't pad
2956 check_size(tree, current_size);
2957 commit_tree(tree);
2958
2959 {
2960 index_t fork_size = current_size;
2963 auto forkTree =
2965 std::move(forkStore), workers, initial_size);
2966
2967 // Find the low leaf of slot 60
2968 auto predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
2969
2970 // It should be at index 2
2971 EXPECT_EQ(predecessor.is_already_present, false);
2972 EXPECT_EQ(predecessor.index, 2);
2973
2974 // checkpoint the fork
2975 checkpoint_tree(forkTree);
2976
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, 4, 50));
2992 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2993 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
2994
2995 // Find the low leaf of slot 60
2996 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
2997
2998 // It should be at index 4
2999 EXPECT_EQ(predecessor.is_already_present, false);
3000 EXPECT_EQ(predecessor.index, 4);
3001
3002 // Now revert the fork and see that it is rolled back to the checkpoint
3003 revert_checkpoint_tree(forkTree);
3004 check_size(forkTree, --fork_size);
3005 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3006 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3007 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
3008 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3009
3010 // Find the low leaf of slot 60
3011 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3012
3013 // It should be back at index 2
3014 EXPECT_EQ(predecessor.is_already_present, false);
3015 EXPECT_EQ(predecessor.index, 2);
3016
3017 // checkpoint the fork again
3018 checkpoint_tree(forkTree);
3019
3020 // We now advance the fork again by a few checkpoints
3021
3033 // Make the same change again, commit the checkpoint and see that the changes remain
3035 check_size(forkTree, ++fork_size);
3036 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3037 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3038 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
3039 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3040 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3041
3042 // Find the low leaf of slot 60
3043 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3044
3045 // It should be back at index 4
3046 EXPECT_EQ(predecessor.is_already_present, false);
3047 EXPECT_EQ(predecessor.index, 4);
3048
3049 // Checkpoint again
3050 checkpoint_tree(forkTree);
3051
3063 check_size(forkTree, fork_size);
3064 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3065 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3066 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 12, 4, 50));
3067 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3068 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3069
3070 // Find the low leaf of slot 60
3071 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3072
3073 // It should be back at index 4
3074 EXPECT_EQ(predecessor.is_already_present, false);
3075 EXPECT_EQ(predecessor.index, 4);
3076
3077 // Checkpoint again
3078 checkpoint_tree(forkTree);
3079
3091
3092 check_size(forkTree, ++fork_size);
3093 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3094 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3095 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 12, 5, 45));
3096 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3097 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3098 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 5), create_indexed_public_data_leaf(45, 15, 4, 50));
3099
3100 // Find the low leaf of slot 60
3101 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3102
3103 // It should be back at index 4
3104 EXPECT_EQ(predecessor.is_already_present, false);
3105 EXPECT_EQ(predecessor.index, 4);
3106
3107 // Find the low leaf of slot 46
3108 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(46, 5));
3109
3110 // It should be back at index 4
3111 EXPECT_EQ(predecessor.is_already_present, false);
3112 EXPECT_EQ(predecessor.index, 5);
3113
3114 // Now commit the last checkpoint
3115 commit_checkpoint_tree(forkTree);
3116
3117 // The state should be identical
3118 check_size(forkTree, fork_size);
3119 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3120 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3121 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 12, 5, 45));
3122 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3123 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3124 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 5), create_indexed_public_data_leaf(45, 15, 4, 50));
3125
3126 // Find the low leaf of slot 60
3127 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3128
3129 // It should be back at index 4
3130 EXPECT_EQ(predecessor.is_already_present, false);
3131 EXPECT_EQ(predecessor.index, 4);
3132
3133 // Find the low leaf of slot 46
3134 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(46, 5));
3135
3136 // It should be back at index 4
3137 EXPECT_EQ(predecessor.is_already_present, false);
3138 EXPECT_EQ(predecessor.index, 5);
3139
3140 // Now revert the fork and we should remove both the new slot 45 and the update to slot 30
3141
3153 revert_checkpoint_tree(forkTree);
3154
3155 check_size(forkTree, --fork_size);
3156 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3157 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3158 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
3159 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3160 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3161
3162 // Find the low leaf of slot 60
3163 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3164
3165 // It should be back at index 4
3166 EXPECT_EQ(predecessor.is_already_present, false);
3167 EXPECT_EQ(predecessor.index, 4);
3168
3169 // Find the low leaf of slot 46
3170 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(46, 5));
3171
3172 // It should be back at index 4
3173 EXPECT_EQ(predecessor.is_already_present, false);
3174 EXPECT_EQ(predecessor.index, 2);
3175 }
3176}
3177
3178void advance_state(TreeType& fork, uint32_t size)
3179{
3180 std::vector<fr> values = create_values(size);
3182 for (uint32_t j = 0; j < size; j++) {
3183 leaves.emplace_back(values[j]);
3184 }
3185 add_values(fork, leaves);
3186}
3187
3188TEST_F(PersistedContentAddressedIndexedTreeTest, nullifiers_can_be_inserted_after_revert)
3189{
3190 index_t current_size = 2;
3191 ThreadPoolPtr workers = make_thread_pool(1);
3192 constexpr size_t depth = 10;
3193 std::string name = "Nullifier Tree";
3194 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
3195 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
3196 auto tree = TreeType(std::move(store), workers, current_size);
3197
3198 {
3199 std::unique_ptr<Store> forkStore = std::make_unique<Store>(name, depth, db);
3200 auto forkTree = TreeType(std::move(forkStore), workers, current_size);
3201
3202 check_size(tree, current_size);
3203
3204 uint32_t size_to_insert = 8;
3205 uint32_t num_insertions = 5;
3206
3207 for (uint32_t i = 0; i < num_insertions - 1; i++) {
3208 advance_state(forkTree, size_to_insert);
3209 current_size += size_to_insert;
3210 check_size(forkTree, current_size);
3211 checkpoint_tree(forkTree);
3212 }
3213
3214 advance_state(forkTree, size_to_insert);
3215 current_size += size_to_insert;
3216 check_size(forkTree, current_size);
3217 revert_checkpoint_tree(forkTree);
3218
3219 current_size -= size_to_insert;
3220 check_size(forkTree, current_size);
3221
3222 commit_checkpoint_tree(forkTree);
3223
3224 check_size(forkTree, current_size);
3225
3226 advance_state(forkTree, size_to_insert);
3227
3228 current_size += size_to_insert;
3229 check_size(forkTree, current_size);
3230 }
3231}
Implements a simple append-only merkle tree All methods are asynchronous unless specified as otherwis...
void get_sibling_path(const index_t &index, const HashPathCallback &on_completion, bool includeUncommitted) const
Returns the sibling path from the leaf at the given index to the root.
void commit(const CommitCallback &on_completion)
Commit the tree to the backing store.
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
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
IndexedNullifierLeafType create_indexed_nullifier_leaf(const fr &value, index_t nextIndex, const fr &nextValue)
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)
IndexedPublicDataLeafType create_indexed_public_data_leaf(const fr &slot, const fr &value, index_t nextIndex, const fr &nextValue)
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:60
const fr & get_value(size_t index)
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:37
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:30
std::shared_ptr< ThreadPool > ThreadPoolPtr
Definition fixtures.hpp:58
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:14
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:230
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:155
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:19
static fr hash(const std::vector< fr > &inputs)
Definition hash.hpp:14
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr field zero()