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