Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
content_addressed_indexed_tree.test.cpp
Go to the documentation of this file.
2#include "../fixtures.hpp"
3#include "../hash.hpp"
4#include "../node_store/array_store.hpp"
5#include "../nullifier_tree/nullifier_memory_tree.hpp"
6#include "../test_fixtures.hpp"
17#include <algorithm>
18#include <cstdint>
19#include <filesystem>
20#include <future>
21#include <memory>
22#include <optional>
23#include <stdexcept>
24#include <vector>
25
27template <typename Store, typename HashingPolicy> struct ContentAddressedIndexedTreeTestAccess {
29 using LeafUpdate = typename Tree::LeafUpdate;
32
34 const index_t& highest_index,
35 std::shared_ptr<std::vector<LeafUpdate>> updates,
36 const UpdatesCompletionCallback& completion)
37 {
38 tree.perform_updates_without_witness(highest_index, std::move(updates), completion);
39 }
40};
41} // namespace bb::crypto::merkle_tree
42
43using namespace bb;
44using namespace bb::crypto::merkle_tree;
45
47
50
53
56
59
60inline IndexedNullifierLeafType create_indexed_nullifier_leaf(const fr& value, index_t nextIndex, const fr& nextValue)
61{
62 return IndexedNullifierLeafType{ NullifierLeafValue(value), nextIndex, nextValue };
63}
64
66 const fr& value,
67 index_t nextIndex,
68 const fr& nextValue)
69{
70 return IndexedPublicDataLeafType{ PublicDataLeafValue(slot, value), nextIndex, nextValue };
71}
72
73class PersistedContentAddressedIndexedTreeTest : public testing::Test {
74 protected:
75 void SetUp() override
76 {
78 _mapSize = 1024 * 1024;
79 _maxReaders = 16;
80 std::filesystem::create_directories(_directory);
81 }
82
83 void TearDown() override { std::filesystem::remove_all(_directory); }
84
85 static std::string _directory;
86 static uint64_t _maxReaders;
87 static uint64_t _mapSize;
88};
89
93
94std::unique_ptr<TreeType> create_tree(const std::string& rootDirectory,
95 uint64_t mapSize,
96 uint64_t maxReaders,
97 uint32_t depth,
98 uint32_t batchSize,
99 ThreadPoolPtr workers)
100{
101 std::string name = random_string();
102 std::filesystem::path directory = rootDirectory;
103 directory.append(name);
104 std::filesystem::create_directories(directory);
105 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(directory, name, mapSize, maxReaders);
106 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
107 return std::make_unique<TreeType>(std::move(store), workers, batchSize);
108}
109
110template <typename TypeOfTree> void check_size(TypeOfTree& tree, index_t expected_size, bool includeUncommitted = true)
111{
112 Signal signal;
113 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
114 EXPECT_EQ(response.success, true);
115 EXPECT_EQ(response.inner.meta.size, expected_size);
116 signal.signal_level();
117 };
118 tree.get_meta_data(includeUncommitted, completion);
119 signal.wait_for_level();
120}
121
122template <typename TypeOfTree> fr get_root(TypeOfTree& tree, bool includeUncommitted = true)
123{
124 fr r;
125 Signal signal;
126 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
127 r = response.inner.meta.root;
128 signal.signal_level();
129 };
130 tree.get_meta_data(includeUncommitted, completion);
131 signal.wait_for_level();
132 return r;
133}
134
135template <typename TypeOfTree> void check_root(TypeOfTree& tree, fr expected_root, bool includeUncommitted = true)
136{
137 fr root = get_root(tree, includeUncommitted);
138 EXPECT_EQ(root, expected_root);
139}
140
141template <typename TypeOfTree>
143 block_number_t blockNumber,
145 bool includeUncommitted = true,
146 bool expected_success = true)
147{
149 Signal signal;
150 auto completion = [&](const TypedResponse<GetSiblingPathResponse>& response) -> void {
151 EXPECT_EQ(response.success, expected_success);
152 if (response.success) {
153 h = response.inner.path;
154 }
155 signal.signal_level();
156 };
157 tree.get_sibling_path(index, blockNumber, completion, includeUncommitted);
158 signal.wait_for_level();
159 return h;
160}
161
162template <typename LeafValueType, typename TypeOfTree>
165 bool includeUncommitted = true,
166 bool expected_success = true)
167{
169 Signal signal;
170 auto completion = [&](const TypedResponse<GetIndexedLeafResponse<LeafValueType>>& leaf) -> void {
171 EXPECT_EQ(leaf.success, expected_success);
172 if (leaf.success) {
173 l = leaf.inner.indexed_leaf;
174 }
175 signal.signal_level();
176 };
177 tree.get_leaf(index, includeUncommitted, completion);
178 signal.wait_for_level();
179 return l.has_value() ? l.value() : IndexedLeaf<LeafValueType>();
180}
181
182template <typename LeafValueType, typename TypeOfTree>
183GetLowIndexedLeafResponse get_low_leaf(TypeOfTree& tree, const LeafValueType& leaf, bool includeUncommitted = true)
184{
185 GetLowIndexedLeafResponse low_leaf_info;
186 Signal signal;
187 auto completion = [&](const auto& leaf) -> void {
188 low_leaf_info = leaf.inner;
189 signal.signal_level();
190 };
191 tree.find_low_leaf(leaf.get_key(), includeUncommitted, completion);
192 signal.wait_for_level();
193 return low_leaf_info;
194}
195
196template <typename LeafValueType, typename TypeOfTree>
198 block_number_t blockNumber,
199 const LeafValueType& leaf,
200 bool includeUncommitted = true)
201{
202 GetLowIndexedLeafResponse low_leaf_info;
203 Signal signal;
204 auto completion = [&](const auto& leaf) -> void {
205 low_leaf_info = leaf.inner;
206 signal.signal_level();
207 };
208 tree.find_low_leaf(leaf.get_key(), blockNumber, includeUncommitted, completion);
209 signal.wait_for_level();
210 return low_leaf_info;
211}
212
213template <typename LeafValueType, typename TypeOfTree>
214void check_historic_leaf(TypeOfTree& tree,
215 const LeafValueType& leaf,
216 index_t expected_index,
217 block_number_t blockNumber,
218 bool expected_success,
219 bool includeUncommitted = true)
220{
221 Signal signal;
222 auto completion = [&](const TypedResponse<GetIndexedLeafResponse<LeafValueType>>& response) -> void {
223 EXPECT_EQ(response.success, expected_success);
224 if (response.success) {
225 EXPECT_EQ(response.inner.indexed_leaf.value().leaf, leaf);
226 }
227 signal.signal_level();
228 };
229
230 tree.get_leaf(expected_index, blockNumber, includeUncommitted, completion);
231 signal.wait_for_level();
232}
233
234template <typename TypeOfTree>
235void check_historic_sibling_path(TypeOfTree& tree,
237 block_number_t blockNumber,
238 const fr_sibling_path& expected_sibling_path,
239 bool includeUncommitted = true,
240 bool expected_success = true)
241{
242 fr_sibling_path path = get_historic_sibling_path(tree, blockNumber, index, includeUncommitted, expected_success);
243 if (expected_success) {
244 EXPECT_EQ(path, expected_sibling_path);
245 }
246}
247
248template <typename TypeOfTree>
249void check_sibling_path(TypeOfTree& tree,
251 const fr_sibling_path& expected_sibling_path,
252 bool includeUncommitted = true,
253 bool expected_success = true)
254{
255 fr_sibling_path path = get_sibling_path(tree, index, includeUncommitted, expected_success);
256 EXPECT_EQ(path, expected_sibling_path);
257}
258
259template <typename TypeOfTree> void check_unfinalized_block_height(TypeOfTree& tree, index_t expected_block_height)
260{
261 Signal signal;
262 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
263 EXPECT_EQ(response.success, true);
264 EXPECT_EQ(response.inner.meta.unfinalizedBlockHeight, expected_block_height);
265 signal.signal_level();
266 };
267 tree.get_meta_data(true, completion);
268 signal.wait_for_level();
269}
270
271template <typename TypeOfTree> void commit_tree(TypeOfTree& tree, bool expectedSuccess = true)
272{
273 Signal signal;
274 auto completion = [&](const TypedResponse<CommitResponse>& response) -> void {
275 EXPECT_EQ(response.success, expectedSuccess);
276 signal.signal_level();
277 };
278 tree.commit(completion);
279 signal.wait_for_level();
280}
281
282template <typename LeafValueType, typename TypeOfTree>
283void add_value(TypeOfTree& tree, const LeafValueType& value, bool expectedSuccess = true)
284{
285 Signal signal;
286 auto completion = [&](const TypedResponse<AddIndexedDataResponse<LeafValueType>>& response) -> void {
287 EXPECT_EQ(response.success, expectedSuccess);
288 signal.signal_level();
289 };
290
291 tree.add_or_update_value(value, completion);
292 signal.wait_for_level();
293}
294
295template <typename LeafValueType, typename TypeOfTree>
296void add_value_sequentially(TypeOfTree& tree, const LeafValueType& value, bool expectedSuccess = true)
297{
299 Signal signal;
300 auto completion = [&](const TypedResponse<AddIndexedDataSequentiallyResponse<LeafValueType>>& response) -> void {
301 EXPECT_EQ(response.success, expectedSuccess);
302 signal.signal_level();
303 };
304
305 tree.add_or_update_values_sequentially(values, completion);
306 signal.wait_for_level();
307}
308
309template <typename LeafValueType, typename TypeOfTree>
310void add_values(TypeOfTree& tree, const std::vector<LeafValueType>& values, bool expectedSuccess = true)
311{
312 Signal signal;
313 auto completion = [&](const TypedResponse<AddIndexedDataResponse<LeafValueType>>& response) -> void {
314 EXPECT_EQ(response.success, expectedSuccess);
315 signal.signal_level();
316 };
317
318 tree.add_or_update_values(values, completion);
319 signal.wait_for_level();
320}
321
322template <typename LeafValueType, typename TypeOfTree>
323void add_values_sequentially(TypeOfTree& tree, const std::vector<LeafValueType>& values, bool expectedSuccess = true)
324{
325 Signal signal;
326 auto completion = [&](const TypedResponse<AddIndexedDataSequentiallyResponse<LeafValueType>>& response) -> void {
327 EXPECT_EQ(response.success, expectedSuccess);
328 signal.signal_level();
329 };
330
331 tree.add_or_update_values_sequentially(values, completion);
332 signal.wait_for_level();
333}
334
335template <typename LeafValueType, typename TypeOfTree>
336void block_sync_values(TypeOfTree& tree, const std::vector<LeafValueType>& values, bool expectedSuccess = true)
337{
338 Signal signal;
339 auto completion = [&](const TypedResponse<AddDataResponse>& response) -> void {
340 EXPECT_EQ(response.success, expectedSuccess);
341 signal.signal_level();
342 };
343
344 tree.add_or_update_values(values, completion);
345 signal.wait_for_level();
346}
347
348template <typename LeafValueType, typename TypeOfTree>
349void block_sync_values_sequential(TypeOfTree& tree,
350 const std::vector<LeafValueType>& values,
351 bool expectedSuccess = true)
352{
353 Signal signal;
354 auto completion = [&](const TypedResponse<AddDataResponse>& response) -> void {
355 EXPECT_EQ(response.success, expectedSuccess);
356 signal.signal_level();
357 };
358
359 tree.add_or_update_values_sequentially(values, completion);
360 signal.wait_for_level();
361}
362
363template <typename TypeOfTree>
364void remove_historic_block(TypeOfTree& tree, const block_number_t& blockNumber, bool expected_success = true)
365{
366 Signal signal;
367 auto completion = [&](const TypedResponse<RemoveHistoricResponse>& response) -> void {
368 EXPECT_EQ(response.success, expected_success);
369 signal.signal_level();
370 };
371 tree.remove_historic_block(blockNumber, completion);
372 signal.wait_for_level();
373}
374
375template <typename TypeOfTree>
376void finalize_block(TypeOfTree& tree, const block_number_t& blockNumber, bool expected_success = true)
377{
378 Signal signal;
379 auto completion = [&](const Response& response) -> void {
380 EXPECT_EQ(response.success, expected_success);
381 signal.signal_level();
382 };
383 tree.finalize_block(blockNumber, completion);
384 signal.wait_for_level();
385}
386
387template <typename TypeOfTree>
388void unwind_block(TypeOfTree& tree, const block_number_t& blockNumber, bool expected_success = true)
389{
390 Signal signal;
391 auto completion = [&](const TypedResponse<UnwindResponse>& response) -> void {
392 EXPECT_EQ(response.success, expected_success);
393 signal.signal_level();
394 };
395 tree.unwind_block(blockNumber, completion);
396 signal.wait_for_level();
397}
398
399template <typename TypeOfTree> void check_block_height(TypeOfTree& tree, index_t expected_block_height)
400{
401 Signal signal;
402 auto completion = [&](const TypedResponse<TreeMetaResponse>& response) -> void {
403 EXPECT_EQ(response.success, true);
404 EXPECT_EQ(response.inner.meta.unfinalizedBlockHeight, expected_block_height);
405 signal.signal_level();
406 };
407 tree.get_meta_data(true, completion);
408 signal.wait_for_level();
409}
410
412{
413 constexpr size_t depth = 10;
414 std::string name = random_string();
415 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
416 EXPECT_NO_THROW(std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db));
417 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
418 ThreadPoolPtr workers = make_thread_pool(1);
419 TreeType tree = TreeType(std::move(store), workers, 2);
420 check_size(tree, 2);
421
423 check_root(tree, memdb.root());
424}
425
426TEST_F(PersistedContentAddressedIndexedTreeTest, can_only_recreate_with_same_name_and_depth)
427{
428 constexpr size_t depth = 10;
429 std::string name = random_string();
430 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
431 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
432
433 EXPECT_ANY_THROW(Store("Wrong name", depth, db));
434 EXPECT_ANY_THROW(Store(name, depth + 1, db));
435}
436
438{
439 index_t current_size = 2;
440 ThreadPoolPtr workers = make_thread_pool(1);
441 constexpr size_t depth = 10;
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
447 check_size(tree, current_size);
448
449 // We assume that the first leaf is already filled with (0, 0, 0).
450 for (uint32_t i = 0; i < 4; i++) {
452 check_size(tree, ++current_size);
453 }
454}
455
456TEST_F(PersistedContentAddressedIndexedTreeTest, indexed_tree_must_have_at_least_2_initial_size)
457{
458 index_t current_size = 1;
459 ThreadPoolPtr workers = make_thread_pool(1);
460 ;
461 constexpr size_t depth = 10;
462 std::string name = random_string();
463 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
464 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
465 EXPECT_THROW(TreeType(std::move(store), workers, current_size), std::runtime_error);
466}
467
468TEST_F(PersistedContentAddressedIndexedTreeTest, reports_an_error_if_tree_is_overfilled)
469{
470 index_t current_size = 2;
471 ThreadPoolPtr workers = make_thread_pool(1);
472 ;
473 constexpr size_t depth = 4;
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
480 for (uint32_t i = 0; i < 14; i++) {
481 values.emplace_back(get_value(i));
482 }
483 add_values(tree, values);
484
485 std::stringstream ss;
486 ss << "Unable to insert values into tree " << name << " new size: 17 max size: 16";
487
488 Signal signal;
489 auto add_completion = [&](const TypedResponse<AddIndexedDataResponse<NullifierLeafValue>>& response) {
490 EXPECT_EQ(response.success, false);
491 EXPECT_EQ(response.message, ss.str());
492 signal.signal_level();
493 };
494 tree.add_or_update_value(NullifierLeafValue(get_value(16)), add_completion);
495 signal.wait_for_level();
496}
497
499{
500 constexpr size_t depth = 10;
501 index_t current_size = 2;
502 NullifierMemoryTree<HashPolicy> memdb(depth, current_size);
503
504 ThreadPoolPtr workers = make_thread_pool(1);
505
506 std::string name = random_string();
507 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
508 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
509 auto tree = TreeType(std::move(store), workers, current_size);
510
511 check_size(tree, current_size);
512 check_root(tree, memdb.root());
513 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
514
515 memdb.update_element(get_value(1000));
517
518 check_size(tree, ++current_size);
519 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
520 check_sibling_path(tree, 1, memdb.get_sibling_path(1));
521
522 memdb.update_element(get_value(1001));
524
525 check_size(tree, ++current_size);
526 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
527 check_sibling_path(tree, 1, memdb.get_sibling_path(1));
528
529 uint32_t num_to_append = 512;
530
531 for (uint32_t i = 0; i < num_to_append; i += 2) {
532 memdb.update_element(get_value(i));
533 memdb.update_element(get_value(i + 1));
534 add_values<NullifierLeafValue>(tree,
536 }
537 check_size(tree, num_to_append + current_size);
538 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
539 check_sibling_path(tree, 512, memdb.get_sibling_path(512));
540}
541
543{
544 index_t initial_size = 2;
545 ThreadPoolPtr workers = make_thread_pool(1);
546 ;
547 constexpr size_t depth = 10;
548 std::string name = random_string();
549 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
550 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
551 auto tree = TreeType(std::move(store), workers, initial_size);
552
553 add_value(tree, NullifierLeafValue(30));
554 add_value(tree, NullifierLeafValue(10));
555 add_value(tree, NullifierLeafValue(20));
556 add_value(tree, NullifierLeafValue(40));
557
558 // check the committed state and that the uncommitted state is empty
559 check_find_leaf_index(tree, NullifierLeafValue(10), 1 + initial_size, true, true);
560 check_find_leaf_index<NullifierLeafValue, TreeType>(
561 tree, { NullifierLeafValue(10) }, { std::nullopt }, true, false);
562
563 check_find_leaf_index<NullifierLeafValue, TreeType>(tree, { NullifierLeafValue(15) }, { std::nullopt }, true, true);
564 check_find_leaf_index<NullifierLeafValue, TreeType>(
565 tree, { NullifierLeafValue(15) }, { std::nullopt }, true, false);
566
567 check_find_leaf_index(tree, NullifierLeafValue(40), 3 + initial_size, true, true);
568 check_find_leaf_index(tree, NullifierLeafValue(30), 0 + initial_size, true, true);
569 check_find_leaf_index(tree, NullifierLeafValue(20), 2 + initial_size, true, true);
570
571 check_find_leaf_index<NullifierLeafValue, TreeType>(
572 tree, { NullifierLeafValue(40) }, { std::nullopt }, true, false);
573 check_find_leaf_index<NullifierLeafValue, TreeType>(
574 tree, { NullifierLeafValue(30) }, { std::nullopt }, true, false);
575 check_find_leaf_index<NullifierLeafValue, TreeType>(
576 tree, { NullifierLeafValue(20) }, { std::nullopt }, true, false);
577
578 commit_tree(tree);
579
584 NullifierLeafValue(48) };
585 add_values(tree, values);
586
587 // check the now committed state
588 check_find_leaf_index(tree, NullifierLeafValue(40), 3 + initial_size, true, false);
589 check_find_leaf_index(tree, NullifierLeafValue(30), 0 + initial_size, true, false);
590 check_find_leaf_index(tree, NullifierLeafValue(20), 2 + initial_size, true, false);
591
592 // check the new uncommitted state
593 check_find_leaf_index(tree, NullifierLeafValue(18), 5 + initial_size, true, true);
594 check_find_leaf_index<NullifierLeafValue, TreeType>(
595 tree, { NullifierLeafValue(18) }, { std::nullopt }, true, false);
596
597 commit_tree(tree);
598
600 add_values(tree, values);
601
602 // we now have duplicate leaf 18, one committed the other not
603 check_find_leaf_index(tree, NullifierLeafValue(18), 5 + initial_size, true, true);
604 check_find_leaf_index(tree, NullifierLeafValue(18), 5 + initial_size, true, false);
605}
606
608{
610 index_t initial_size = 2;
611 index_t current_size = initial_size;
612 ThreadPoolPtr workers = make_thread_pool(1);
613 ;
614 constexpr size_t depth = 10;
615 std::string name = random_string();
616
617 {
618 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
619 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
620 auto tree = TreeType(std::move(store), workers, initial_size);
621
622 check_size(tree, current_size);
623 check_root(tree, memdb.root());
624 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
625
627
628 // Committed data should not have changed
629 check_size(tree, current_size, false);
630 check_root(tree, memdb.root(), false);
631 check_sibling_path(tree, 0, memdb.get_sibling_path(0), false);
632 check_sibling_path(tree, 1, memdb.get_sibling_path(1), false);
633
634 memdb.update_element(get_value(512));
635
636 // Uncommitted data should have changed
637 check_size(tree, current_size + 1, true);
638 check_root(tree, memdb.root(), true);
639 check_sibling_path(tree, 0, memdb.get_sibling_path(0), true);
640 check_sibling_path(tree, 1, memdb.get_sibling_path(1), true);
641
642 // Now commit
643 commit_tree(tree);
644
645 // Now committed data should have changed
646 check_size(tree, ++current_size, false);
647 check_root(tree, memdb.root(), false);
648 check_sibling_path(tree, 0, memdb.get_sibling_path(0), false);
649 check_sibling_path(tree, 1, memdb.get_sibling_path(1), false);
650 }
651
652 // Now restore and it should continue from where we left off
653 {
654 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
655 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
656 auto tree = TreeType(std::move(store), workers, initial_size);
657
658 // check uncommitted state
659 check_size(tree, current_size);
660 check_root(tree, memdb.root());
661 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
662
663 // check committed state
664 check_size(tree, current_size, false);
665 check_root(tree, memdb.root(), false);
666 check_sibling_path(tree, 0, memdb.get_sibling_path(0), false);
667 }
668}
669
670void test_batch_insert(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
671{
673 const uint32_t batch_size = batchSize;
674 const uint32_t num_batches = 16;
675 uint32_t depth = 10;
676 ThreadPoolPtr workers = make_thread_pool(1);
677 ThreadPoolPtr multi_workers = make_thread_pool(8);
678 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
679
680 auto tree1 = create_tree(directory, mapSize, maxReaders, depth, batch_size, workers);
681 auto tree2 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
682 auto tree3 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
683
684 for (uint32_t i = 0; i < num_batches; i++) {
685
686 check_root(*tree1, memdb.root());
687 check_root(*tree2, memdb.root());
688 check_root(*tree3, memdb.root());
689 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
690 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
691 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
692
693 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
694 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
695 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
696
698 std::vector<fr_sibling_path> memory_tree_sibling_paths;
699 for (uint32_t j = 0; j < batch_size; j++) {
700 batch.emplace_back(random_engine.get_random_uint256());
701 fr_sibling_path path = memdb.update_element(batch[j].nullifier);
702 memory_tree_sibling_paths.push_back(path);
703 }
706 {
707 Signal signal;
708 CompletionCallback completion =
710 tree1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
711 signal.signal_level();
712 };
713 tree1->add_or_update_values(batch, completion);
714 signal.wait_for_level();
715 }
716
717 {
718 Signal signal;
719 CompletionCallback completion =
721 tree2_low_leaf_witness_data = response.inner.low_leaf_witness_data;
722 signal.signal_level();
723 };
724 tree2->add_or_update_values(batch, completion);
725 signal.wait_for_level();
726 }
727
728 {
729 Signal signal;
730 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
731 tree3->add_or_update_values(batch, completion);
732 signal.wait_for_level();
733 }
734 check_root(*tree1, memdb.root());
735 check_root(*tree2, memdb.root());
736 check_root(*tree3, memdb.root());
737
738 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
739 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
740 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
741
742 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
743 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
744 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
745
746 for (uint32_t j = 0; j < batch_size; j++) {
747 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).leaf, tree2_low_leaf_witness_data->at(j).leaf);
748 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).index, tree2_low_leaf_witness_data->at(j).index);
749 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).path, tree2_low_leaf_witness_data->at(j).path);
750 }
751 }
752}
753
755 std::string directory,
756 uint64_t mapSize,
757 uint64_t maxReaders)
758{
760 const uint32_t batch_size = batchSize;
761 const uint32_t num_batches = 16;
762 uint32_t depth = 10;
763 ThreadPoolPtr workers = make_thread_pool(1);
764 ThreadPoolPtr multi_workers = make_thread_pool(8);
765 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
766
767 for (uint32_t i = 0; i < num_batches; i++) {
768
769 auto tree1 = create_tree(directory, mapSize, maxReaders, depth, batch_size, workers);
770 auto tree2 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
771 auto tree3 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
772
773 check_root(*tree1, memdb.root());
774 check_root(*tree2, memdb.root());
775 check_root(*tree3, memdb.root());
776 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
777 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
778 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
779
780 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
781 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
782 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
783
785 std::vector<fr_sibling_path> memory_tree_sibling_paths;
786 for (uint32_t j = 0; j < batch_size; j++) {
787 batch.emplace_back(random_engine.get_random_uint256());
788 fr_sibling_path path = memdb.update_element(batch[j].nullifier);
789 memory_tree_sibling_paths.push_back(path);
790 }
793 {
794 Signal signal;
795 CompletionCallback completion =
797 tree1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
798 signal.signal_level();
799 };
800 tree1->add_or_update_values(batch, completion);
801 signal.wait_for_level();
802 }
803
804 {
805 Signal signal;
806 CompletionCallback completion =
808 tree2_low_leaf_witness_data = response.inner.low_leaf_witness_data;
809 signal.signal_level();
810 };
811 tree2->add_or_update_values(batch, completion);
812 signal.wait_for_level();
813 }
814
815 {
816 Signal signal;
817 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
818 tree3->add_or_update_values(batch, completion);
819 signal.wait_for_level();
820 }
821 check_root(*tree1, memdb.root());
822 check_root(*tree2, memdb.root());
823 check_root(*tree3, memdb.root());
824
825 check_sibling_path(*tree1, 0, memdb.get_sibling_path(0));
826 check_sibling_path(*tree2, 0, memdb.get_sibling_path(0));
827 check_sibling_path(*tree3, 0, memdb.get_sibling_path(0));
828
829 check_sibling_path(*tree1, 512, memdb.get_sibling_path(512));
830 check_sibling_path(*tree2, 512, memdb.get_sibling_path(512));
831 check_sibling_path(*tree3, 512, memdb.get_sibling_path(512));
832
833 for (uint32_t j = 0; j < batch_size; j++) {
834 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).leaf, tree2_low_leaf_witness_data->at(j).leaf);
835 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).index, tree2_low_leaf_witness_data->at(j).index);
836 EXPECT_EQ(tree1_low_leaf_witness_data->at(j).path, tree2_low_leaf_witness_data->at(j).path);
837 }
838
839 commit_tree(*tree1);
840 commit_tree(*tree2);
841 commit_tree(*tree3);
842 }
843}
844
846{
847 uint32_t batchSize = 2;
848 while (batchSize <= 2) {
849 test_batch_insert(batchSize, _directory, _mapSize, _maxReaders);
850 batchSize <<= 1;
851 }
852}
853
855{
856 uint32_t batchSize = 2;
857 while (batchSize <= 32) {
858 test_batch_insert(batchSize, _directory, _mapSize, _maxReaders);
859 batchSize <<= 1;
860 }
861}
862
863TEST_F(PersistedContentAddressedIndexedTreeTest, test_compare_batch_inserts_different_sized_thread_pools)
864{
865 const uint32_t batch_size = 128;
866 uint32_t depth = 20;
867 ThreadPoolPtr workers = make_thread_pool(1);
868 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
869
870 auto tree1 = create_tree(_directory, _mapSize, _maxReaders, depth, batch_size, workers);
871 auto tree2 = create_tree(_directory, _mapSize, _maxReaders, depth, batch_size, workers);
872
874 for (uint32_t i = 1; i <= 12; i++) {
875 ThreadPoolPtr multiWorkers = make_thread_pool(i);
876 auto tree = create_tree(_directory, _mapSize, _maxReaders, depth, batch_size, multiWorkers);
877 trees.emplace_back(std::move(tree));
878 }
879
880 std::vector<fr> tree1Roots;
881 std::vector<fr> tree2Roots;
882
883 for (uint32_t round = 0; round < 10; round++) {
884 std::vector<fr> frValues1 = create_values(3);
885 std::vector<fr> frValues2 = create_values(3);
887 for (uint32_t i = 0; i < 3; i++) {
888 leaves[i] = frValues1[i];
889 leaves[i + 64] = frValues2[i];
890 }
891
892 std::vector<NullifierLeafValue> first(leaves.begin(), leaves.begin() + 64);
893 std::vector<NullifierLeafValue> second(leaves.begin() + 64, leaves.end());
894
895 add_values(*tree1, first);
896 add_values(*tree1, second);
897
898 block_sync_values(*tree2, leaves);
899
900 tree1Roots.push_back(get_root(*tree1));
901 tree2Roots.push_back(get_root(*tree2, true));
902 EXPECT_EQ(tree1Roots[round], tree2Roots[round]);
903
904 for (const auto& tree : trees) {
905 block_sync_values(*tree, leaves);
906 const fr treeRoot = get_root(*tree, true);
907 EXPECT_EQ(treeRoot, tree1Roots[round]);
908 }
909 }
910}
911
912TEST_F(PersistedContentAddressedIndexedTreeTest, reports_an_error_if_batch_contains_duplicate)
913{
914 index_t current_size = 2;
915 ThreadPoolPtr workers = make_thread_pool(1);
916 ;
917 constexpr size_t depth = 10;
918 std::string name = random_string();
919 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
920 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
921 auto tree = TreeType(std::move(store), workers, current_size);
922
924 for (uint32_t i = 0; i < 16; i++) {
925 values.emplace_back(get_value(i));
926 }
927 values[8] = values[0];
928
929 std::stringstream ss;
930 ss << "Duplicate key not allowed in same batch, key value: " << values[0].nullifier << ", tree: " << name;
931
932 Signal signal;
933 auto add_completion = [&](const TypedResponse<AddIndexedDataResponse<NullifierLeafValue>>& response) {
934 EXPECT_EQ(response.success, false);
935 EXPECT_EQ(response.message, ss.str());
936 signal.signal_level();
937 };
938 tree.add_or_update_values(values, add_completion);
939 signal.wait_for_level();
940}
941
942void test_sequential_insert_vs_batch(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
943{
945 const uint32_t batch_size = batchSize;
946 const uint32_t num_batches = 16;
947 uint32_t depth = 10;
948 ThreadPoolPtr workers = make_thread_pool(1);
949 ThreadPoolPtr multi_workers = make_thread_pool(8);
950 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
951
952 auto sequential_tree_1 = create_tree(directory, mapSize, maxReaders, depth, batch_size, workers);
953 auto sequential_tree_2 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
954 auto sequential_tree_3 = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
955 auto batch_tree = create_tree(directory, mapSize, maxReaders, depth, batch_size, multi_workers);
956
957 for (uint32_t i = 0; i < num_batches; i++) {
958
959 check_root(*sequential_tree_1, memdb.root());
960 check_root(*sequential_tree_2, memdb.root());
961 check_root(*sequential_tree_3, memdb.root());
962 check_root(*batch_tree, memdb.root());
963 check_sibling_path(*sequential_tree_1, 0, memdb.get_sibling_path(0));
964 check_sibling_path(*sequential_tree_2, 0, memdb.get_sibling_path(0));
965 check_sibling_path(*sequential_tree_3, 0, memdb.get_sibling_path(0));
966 check_sibling_path(*batch_tree, 0, memdb.get_sibling_path(0));
967
968 check_sibling_path(*sequential_tree_1, 512, memdb.get_sibling_path(512));
969 check_sibling_path(*sequential_tree_2, 512, memdb.get_sibling_path(512));
970 check_sibling_path(*sequential_tree_3, 512, memdb.get_sibling_path(512));
971 check_sibling_path(*batch_tree, 512, memdb.get_sibling_path(512));
972
974 std::vector<fr_sibling_path> memory_tree_sibling_paths;
975 for (uint32_t j = 0; j < batch_size; j++) {
976 batch.emplace_back(random_engine.get_random_uint256());
977 fr_sibling_path path = memdb.update_element(batch[j].nullifier);
978 memory_tree_sibling_paths.push_back(path);
979 }
980 std::shared_ptr<std::vector<LeafUpdateWitnessData<NullifierLeafValue>>> sequential_tree_1_low_leaf_witness_data;
982 sequential_tree_1_insertion_witness_data;
983 std::shared_ptr<std::vector<LeafUpdateWitnessData<NullifierLeafValue>>> sequential_tree_2_low_leaf_witness_data;
985 sequential_tree_2_insertion_witness_data;
986
987 {
988 Signal signal;
991 sequential_tree_1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
992 sequential_tree_1_insertion_witness_data = response.inner.insertion_witness_data;
993 signal.signal_level();
994 };
995 sequential_tree_1->add_or_update_values_sequentially(batch, completion);
996 signal.wait_for_level();
997 }
998
999 {
1000 Signal signal;
1001 SequentialCompletionCallback completion =
1003 sequential_tree_2_low_leaf_witness_data = response.inner.low_leaf_witness_data;
1004 sequential_tree_2_insertion_witness_data = response.inner.insertion_witness_data;
1005 signal.signal_level();
1006 };
1007 sequential_tree_2->add_or_update_values_sequentially(batch, completion);
1008 signal.wait_for_level();
1009 }
1010
1011 {
1012 Signal signal;
1013 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
1014 sequential_tree_3->add_or_update_values_sequentially(batch, completion);
1015 signal.wait_for_level();
1016 }
1017
1018 {
1019 Signal signal;
1020 auto completion = [&](const TypedResponse<AddDataResponse>&) { signal.signal_level(); };
1021 batch_tree->add_or_update_values(batch, completion);
1022 signal.wait_for_level();
1023 }
1024 check_root(*sequential_tree_1, memdb.root());
1025 check_root(*sequential_tree_2, memdb.root());
1026 check_root(*sequential_tree_3, memdb.root());
1027 check_root(*batch_tree, memdb.root());
1028
1029 check_sibling_path(*sequential_tree_1, 0, memdb.get_sibling_path(0));
1030 check_sibling_path(*sequential_tree_2, 0, memdb.get_sibling_path(0));
1031 check_sibling_path(*sequential_tree_3, 0, memdb.get_sibling_path(0));
1032 check_sibling_path(*batch_tree, 0, memdb.get_sibling_path(0));
1033
1034 check_sibling_path(*sequential_tree_1, 512, memdb.get_sibling_path(512));
1035 check_sibling_path(*sequential_tree_2, 512, memdb.get_sibling_path(512));
1036 check_sibling_path(*sequential_tree_3, 512, memdb.get_sibling_path(512));
1037 check_sibling_path(*batch_tree, 512, memdb.get_sibling_path(512));
1038
1039 for (uint32_t j = 0; j < batch_size; j++) {
1040 EXPECT_EQ(sequential_tree_1_low_leaf_witness_data->at(j).leaf,
1041 sequential_tree_2_low_leaf_witness_data->at(j).leaf);
1042 EXPECT_EQ(sequential_tree_1_low_leaf_witness_data->at(j).index,
1043 sequential_tree_2_low_leaf_witness_data->at(j).index);
1044 EXPECT_EQ(sequential_tree_1_low_leaf_witness_data->at(j).path,
1045 sequential_tree_2_low_leaf_witness_data->at(j).path);
1046
1047 EXPECT_EQ(sequential_tree_1_insertion_witness_data->at(j).leaf,
1048 sequential_tree_2_insertion_witness_data->at(j).leaf);
1049 EXPECT_EQ(sequential_tree_1_insertion_witness_data->at(j).index,
1050 sequential_tree_2_insertion_witness_data->at(j).index);
1051 EXPECT_EQ(sequential_tree_1_insertion_witness_data->at(j).path,
1052 sequential_tree_2_insertion_witness_data->at(j).path);
1053 }
1054 }
1055}
1056
1058{
1059 uint32_t batchSize = 2;
1060 while (batchSize <= 2) {
1061 test_sequential_insert_vs_batch(batchSize, _directory, _mapSize, _maxReaders);
1062 batchSize <<= 1;
1063 }
1064}
1065
1066TEST_F(PersistedContentAddressedIndexedTreeTest, sequential_insert_allows_multiple_inserts_to_the_same_key)
1067{
1068 index_t current_size = 2;
1069 ThreadPoolPtr workers = make_thread_pool(8);
1070 // Create a depth-3 indexed merkle tree
1071 constexpr size_t depth = 3;
1072 std::string name = random_string();
1073 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1077 std::move(store), workers, current_size);
1078
1080 add_values_sequentially(tree, values);
1081
1082 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2).leaf.value, values[1].value);
1083 check_size(tree, 3);
1084}
1085
1086template <typename LeafValueType> fr hash_leaf(const IndexedLeaf<LeafValueType>& leaf)
1087{
1088 return HashPolicy::hash(leaf.get_hash_inputs());
1089}
1090
1091bool verify_sibling_path(TreeType& tree, const IndexedNullifierLeafType& leaf_value, const uint32_t idx)
1092{
1093 fr root = get_root(tree, true);
1094 fr_sibling_path path = get_sibling_path(tree, idx, true);
1095 auto current = hash_leaf(leaf_value);
1096 uint32_t depth_ = static_cast<uint32_t>(path.size());
1097 uint32_t index = idx;
1098 for (uint32_t i = 0; i < depth_; ++i) {
1099 fr left = (index & 1) ? path[i] : current;
1100 fr right = (index & 1) ? current : path[i];
1101 current = HashPolicy::hash_pair(left, right);
1102 index >>= 1;
1103 }
1104 return current == root;
1105}
1106
1108{
1109 index_t current_size = 2;
1110 ThreadPoolPtr workers = make_thread_pool(8);
1111 // Create a depth-3 indexed merkle tree
1112 constexpr size_t depth = 3;
1113 std::string name = random_string();
1114 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1115 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1116 auto tree = TreeType(std::move(store), workers, current_size);
1117
1127 IndexedNullifierLeafType zero_leaf(NullifierLeafValue(0), 1, 1);
1129 check_size(tree, current_size);
1130 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), zero_leaf);
1131 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), one_leaf);
1132
1142 add_value(tree, NullifierLeafValue(30));
1143 check_size(tree, ++current_size);
1144 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1145 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 2, 30));
1146 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 0, 0));
1147
1157 add_value(tree, NullifierLeafValue(10));
1158 check_size(tree, ++current_size);
1159 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1160 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 3, 10));
1161 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 0, 0));
1162 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 3), create_indexed_nullifier_leaf(10, 2, 30));
1163
1173 add_value(tree, NullifierLeafValue(20));
1174 check_size(tree, ++current_size);
1175 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1176 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 3, 10));
1177 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 0, 0));
1178 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 3), create_indexed_nullifier_leaf(10, 4, 20));
1179 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 4), create_indexed_nullifier_leaf(20, 2, 30));
1180
1181 // Adding the same value must not affect anything
1182 // tree.update_element(20);
1183 // EXPECT_EQ(tree.get_leaves().size(), 4);
1184 // EXPECT_EQ(tree.get_leaves()[0], hash_leaf({ 0, 2, 10 }));
1185 // EXPECT_EQ(tree.get_leaves()[1], hash_leaf({ 30, 0, 0 }));
1186 // EXPECT_EQ(tree.get_leaves()[2], hash_leaf({ 10, 3, 20 }));
1187 // EXPECT_EQ(tree.get_leaves()[3], hash_leaf({ 20, 1, 30 }));
1188
1198 add_value(tree, NullifierLeafValue(50));
1199 check_size(tree, ++current_size);
1200 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 0), create_indexed_nullifier_leaf(0, 1, 1));
1201 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 1), create_indexed_nullifier_leaf(1, 3, 10));
1202 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 2), create_indexed_nullifier_leaf(30, 5, 50));
1203 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 3), create_indexed_nullifier_leaf(10, 4, 20));
1204 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 4), create_indexed_nullifier_leaf(20, 2, 30));
1205 EXPECT_EQ(get_leaf<NullifierLeafValue>(tree, 5), create_indexed_nullifier_leaf(50, 0, 0));
1206
1207 // Manually compute the node values
1208 auto e000 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 0));
1209 auto e001 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 1));
1210 auto e010 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 2));
1211 auto e011 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 3));
1212 auto e100 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 4));
1213 auto e101 = hash_leaf(get_leaf<NullifierLeafValue>(tree, 5));
1214 auto e110 = fr::zero();
1215 auto e111 = fr::zero();
1216
1217 auto e00 = HashPolicy::hash_pair(e000, e001);
1218 auto e01 = HashPolicy::hash_pair(e010, e011);
1219 auto e10 = HashPolicy::hash_pair(e100, e101);
1220 auto e11 = HashPolicy::hash_pair(e110, e111);
1221
1222 auto e0 = HashPolicy::hash_pair(e00, e01);
1223 auto e1 = HashPolicy::hash_pair(e10, e11);
1224 auto root = HashPolicy::hash_pair(e0, e1);
1225
1226 // Check the hash path at index 2 and 3
1227 // Note: This merkle proof would also serve as a non-membership proof of values in (10, 20) and (20, 30)
1228 fr_sibling_path expected = {
1229 e001,
1230 e01,
1231 e1,
1232 };
1233 check_sibling_path(tree, 0, expected);
1234 expected = {
1235 e000,
1236 e01,
1237 e1,
1238 };
1239 check_sibling_path(tree, 1, expected);
1240 expected = {
1241 e011,
1242 e00,
1243 e1,
1244 };
1245 check_sibling_path(tree, 2, expected);
1246 expected = {
1247 e010,
1248 e00,
1249 e1,
1250 };
1251 check_sibling_path(tree, 3, expected);
1252 check_root(tree, root);
1253
1254 // Check the hash path at index 6 and 7
1255 expected = {
1256 e111,
1257 e10,
1258 e0,
1259 };
1260 check_sibling_path(tree, 6, expected);
1261 expected = {
1262 e110,
1263 e10,
1264 e0,
1265 };
1266 check_sibling_path(tree, 7, expected);
1267}
1268
1270{
1271 index_t current_size = 2;
1272 ThreadPoolPtr workers = make_thread_pool(1);
1273 ;
1274 // Create a depth-8 indexed merkle tree
1275 constexpr uint32_t depth = 8;
1276 std::string name = random_string();
1277 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1278 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1279 auto tree = TreeType(std::move(store), workers, current_size);
1280
1282 check_size(tree, current_size);
1283 EXPECT_EQ(hash_leaf(get_leaf<NullifierLeafValue>(tree, 0)), hash_leaf(zero_leaf));
1284
1285 // Add 20 random values to the tree
1286 for (uint32_t i = 0; i < 20; i++) {
1287 auto value = fr::random_element();
1289 ++current_size;
1290 }
1291
1292 auto abs_diff = [](uint256_t a, uint256_t b) {
1293 if (a > b) {
1294 return (a - b);
1295 } else {
1296 return (b - a);
1297 }
1298 };
1299
1300 check_size(tree, current_size);
1301
1302 // Check if a new random value is not a member of this tree.
1303 fr new_member = fr::random_element();
1304 std::vector<uint256_t> differences;
1305 for (uint32_t i = 0; i < uint32_t(21); i++) {
1306 uint256_t diff_hi =
1307 abs_diff(uint256_t(new_member), uint256_t(get_leaf<NullifierLeafValue>(tree, i).leaf.get_key()));
1308 uint256_t diff_lo =
1309 abs_diff(uint256_t(new_member), uint256_t(get_leaf<NullifierLeafValue>(tree, i).leaf.get_key()));
1310 differences.push_back(diff_hi + diff_lo);
1311 }
1312 auto it = std::min_element(differences.begin(), differences.end());
1313 auto index = static_cast<uint32_t>(it - differences.begin());
1314
1315 // Merkle proof at `index` proves non-membership of `new_member`
1316 EXPECT_TRUE(verify_sibling_path(tree, get_leaf<NullifierLeafValue>(tree, index), index));
1317}
1318
1320{
1321 constexpr size_t depth = 10;
1323 fr_sibling_path initial_path = memdb.get_sibling_path(0);
1324 memdb.update_element(get_value(0));
1325 fr_sibling_path final_sibling_path = memdb.get_sibling_path(0);
1326
1327 uint32_t num_reads = 16 * 1024;
1328 std::vector<fr_sibling_path> paths(num_reads);
1329
1330 {
1331 std::string name = random_string();
1332 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1333 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1335 TreeType tree(std::move(store), pool, 2);
1336
1337 check_size(tree, 2);
1338
1339 Signal signal(1 + num_reads);
1340
1341 auto add_completion = [&](const TypedResponse<AddIndexedDataResponse<NullifierLeafValue>>&) {
1342 auto commit_completion = [&](const TypedResponse<CommitResponse>&) { signal.signal_decrement(); };
1343 tree.commit(commit_completion);
1344 };
1345 tree.add_or_update_value(get_value(0), add_completion);
1346
1347 for (size_t i = 0; i < num_reads; i++) {
1348 auto completion = [&, i](const TypedResponse<GetSiblingPathResponse>& response) {
1349 paths[i] = response.inner.path;
1350 signal.signal_decrement();
1351 };
1352 tree.get_sibling_path(0, completion, false);
1353 }
1354 signal.wait_for_level();
1355 }
1356}
1357
1358TEST_F(PersistedContentAddressedIndexedTreeTest, test_indexed_memory_with_public_data_writes)
1359{
1360 index_t current_size = 2;
1361 ThreadPoolPtr workers = make_thread_pool(8);
1362 // Create a depth-3 indexed merkle tree
1363 constexpr size_t depth = 3;
1364 std::string name = random_string();
1365 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1369 std::move(store), workers, current_size);
1370
1383 check_size(tree, current_size);
1384 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
1385 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
1386
1397 add_value(tree, PublicDataLeafValue(30, 5));
1398 check_size(tree, ++current_size);
1399 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1400 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
1401 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1402
1413 add_value(tree, PublicDataLeafValue(10, 20));
1414 check_size(tree, ++current_size);
1415 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1416 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1417 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1418 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1419
1430 add_value(tree, PublicDataLeafValue(30, 6));
1431 // The size still increases as we pad with an empty leaf
1432 check_size(tree, ++current_size);
1433
1434 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1435 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1436 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
1437 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1438 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(0, 0, 0, 0));
1439
1450 add_value(tree, PublicDataLeafValue(50, 8));
1451 check_size(tree, ++current_size);
1452 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1453 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1454 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 5, 50));
1455 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1456 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(0, 0, 0, 0));
1457 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 5), create_indexed_public_data_leaf(50, 8, 0, 0));
1458
1459 // Manually compute the node values
1460 auto e000 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 0));
1461 auto e001 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 1));
1462 auto e010 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 2));
1463 auto e011 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 3));
1464 auto e100 = fr::zero(); // tree doesn't hash 0 leaves!
1465 auto e101 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 5));
1466 auto e110 = fr::zero();
1467 auto e111 = fr::zero();
1468
1469 auto e00 = HashPolicy::hash_pair(e000, e001);
1470 auto e01 = HashPolicy::hash_pair(e010, e011);
1471 auto e10 = HashPolicy::hash_pair(e100, e101);
1472 auto e11 = HashPolicy::hash_pair(e110, e111);
1473
1474 auto e0 = HashPolicy::hash_pair(e00, e01);
1475 auto e1 = HashPolicy::hash_pair(e10, e11);
1476 auto root = HashPolicy::hash_pair(e0, e1);
1477
1478 fr_sibling_path expected = {
1479 e001,
1480 e01,
1481 e1,
1482 };
1483 check_sibling_path(tree, 0, expected);
1484 expected = {
1485 e000,
1486 e01,
1487 e1,
1488 };
1489 check_sibling_path(tree, 1, expected);
1490 expected = {
1491 e011,
1492 e00,
1493 e1,
1494 };
1495 check_sibling_path(tree, 2, expected);
1496 expected = {
1497 e010,
1498 e00,
1499 e1,
1500 };
1501 check_sibling_path(tree, 3, expected);
1502 check_root(tree, root);
1503
1504 // Check the hash path at index 6 and 7
1505 expected = {
1506 e111,
1507 e10,
1508 e0,
1509 };
1510 check_sibling_path(tree, 6, expected);
1511 expected = {
1512 e110,
1513 e10,
1514 e0,
1515 };
1516 check_sibling_path(tree, 7, expected);
1517}
1518
1519TEST_F(PersistedContentAddressedIndexedTreeTest, test_indexed_memory_with_sequential_public_data_writes)
1520{
1521 index_t current_size = 2;
1522 ThreadPoolPtr workers = make_thread_pool(8);
1523 // Create a depth-3 indexed merkle tree
1524 constexpr size_t depth = 3;
1525 std::string name = random_string();
1526 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1530 std::move(store), workers, current_size);
1531
1544 check_size(tree, current_size);
1545 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
1546 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
1547
1559 check_size(tree, ++current_size);
1560 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1561 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
1562 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1563
1575 check_size(tree, ++current_size);
1576 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1577 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1578 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1579 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1580
1592 // The size does not increase since sequential insertion doesn't pad
1593 check_size(tree, current_size);
1594
1595 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1596 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1597 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
1598 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1599
1611 check_size(tree, ++current_size);
1612 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1613 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1614 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
1615 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1616 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
1617
1618 // Manually compute the node values
1619 auto e000 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 0));
1620 auto e001 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 1));
1621 auto e010 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 2));
1622 auto e011 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 3));
1623 auto e100 = hash_leaf(get_leaf<PublicDataLeafValue>(tree, 4));
1624 auto e101 = fr::zero();
1625 auto e110 = fr::zero();
1626 auto e111 = fr::zero();
1627
1628 auto e00 = HashPolicy::hash_pair(e000, e001);
1629 auto e01 = HashPolicy::hash_pair(e010, e011);
1630 auto e10 = HashPolicy::hash_pair(e100, e101);
1631 auto e11 = HashPolicy::hash_pair(e110, e111);
1632
1633 auto e0 = HashPolicy::hash_pair(e00, e01);
1634 auto e1 = HashPolicy::hash_pair(e10, e11);
1635 auto root = HashPolicy::hash_pair(e0, e1);
1636
1637 fr_sibling_path expected = {
1638 e001,
1639 e01,
1640 e1,
1641 };
1642 check_sibling_path(tree, 0, expected);
1643 expected = {
1644 e000,
1645 e01,
1646 e1,
1647 };
1648 check_sibling_path(tree, 1, expected);
1649 expected = {
1650 e011,
1651 e00,
1652 e1,
1653 };
1654 check_sibling_path(tree, 2, expected);
1655 expected = {
1656 e010,
1657 e00,
1658 e1,
1659 };
1660 check_sibling_path(tree, 3, expected);
1661 check_root(tree, root);
1662
1663 // Check the hash path at index 6 and 7
1664 expected = {
1665 e111,
1666 e10,
1667 e0,
1668 };
1669 check_sibling_path(tree, 6, expected);
1670 expected = {
1671 e110,
1672 e10,
1673 e0,
1674 };
1675 check_sibling_path(tree, 7, expected);
1676}
1677
1679{
1680 // Create a depth-8 indexed merkle tree
1681 constexpr uint32_t depth = 8;
1682
1683 ThreadPoolPtr workers = make_thread_pool(1);
1684 std::string name = random_string();
1685 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1686 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1687 auto tree = TreeType(std::move(store), workers, 2);
1688
1689 auto predecessor = get_low_leaf(tree, NullifierLeafValue(42));
1690
1691 EXPECT_EQ(predecessor.is_already_present, false);
1692 EXPECT_EQ(predecessor.index, 1);
1693
1694 add_value(tree, NullifierLeafValue(42));
1695
1696 predecessor = get_low_leaf(tree, NullifierLeafValue(42));
1697 // returns the current leaf since it exists already. Inserting 42 again would modify the existing leaf
1698 EXPECT_EQ(predecessor.is_already_present, true);
1699 EXPECT_EQ(predecessor.index, 2);
1700}
1701
1703{
1704 // Create a depth-8 indexed merkle tree
1705 constexpr uint32_t depth = 8;
1706
1707 ThreadPoolPtr workers = make_thread_pool(1);
1708 ;
1709 std::string name = random_string();
1710 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1711 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
1712 auto tree = TreeType(std::move(store), workers, 2);
1713
1714 add_value(tree, NullifierLeafValue(42));
1715 check_size(tree, 3);
1716
1717 commit_tree(tree);
1718
1719 add_value(tree, NullifierLeafValue(42), false);
1720 // expect this to fail as no data is present
1721 commit_tree(tree);
1722 check_size(tree, 3);
1723}
1724
1725TEST_F(PersistedContentAddressedIndexedTreeTest, test_historic_sibling_path_retrieval)
1726{
1728 const uint32_t batch_size = 16;
1729 const uint32_t num_batches = 8;
1730 std::string name1 = random_string();
1731 std::string name2 = random_string();
1732 uint32_t depth = 10;
1733 ThreadPoolPtr multi_workers = make_thread_pool(8);
1734 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1735
1736 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1737 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1738 auto tree1 = TreeType(std::move(store1), multi_workers, batch_size);
1739
1740 std::vector<fr_sibling_path> memory_tree_sibling_paths_index_0;
1741
1742 auto check = [&]() {
1743 check_root(tree1, memdb.root());
1744 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1745 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1746
1747 for (uint32_t i = 0; i < memory_tree_sibling_paths_index_0.size(); i++) {
1748 check_historic_sibling_path(tree1, 0, i + 1, memory_tree_sibling_paths_index_0[i]);
1749 }
1750 };
1751
1752 for (uint32_t i = 0; i < num_batches; i++) {
1753
1754 check_root(tree1, memdb.root());
1755 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1756 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1757
1759
1760 for (uint32_t j = 0; j < batch_size; j++) {
1761 batch.emplace_back(random_engine.get_random_uint256());
1762 memdb.update_element(batch[j].get_key());
1763 }
1764 memory_tree_sibling_paths_index_0.push_back(memdb.get_sibling_path(0));
1767 {
1768 Signal signal;
1769 CompletionCallback completion =
1771 tree1_low_leaf_witness_data = response.inner.low_leaf_witness_data;
1772 signal.signal_level();
1773 };
1774 tree1.add_or_update_values(batch, completion);
1775 signal.wait_for_level();
1776 }
1777 check_root(tree1, memdb.root());
1778 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1779 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
1780 commit_tree(tree1);
1781 check();
1782 }
1783}
1784
1786{
1787 index_t current_size = 2;
1788 ThreadPoolPtr workers = make_thread_pool(8);
1789 // Create a depth-3 indexed merkle tree
1790 constexpr size_t depth = 3;
1791 std::string name = random_string();
1792 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
1795 using LocalTreeType =
1797 auto tree = LocalTreeType(std::move(store), workers, current_size);
1798
1811 check_size(tree, current_size);
1812 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
1813 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
1814
1826 commit_tree(tree);
1827 check_size(tree, ++current_size);
1828 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1829 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
1830 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1831
1832 auto leaf1AtBlock1 = PublicDataLeafValue(1, 0);
1833
1845 check_size(tree, ++current_size);
1846 commit_tree(tree);
1847 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1848 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1849 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
1850 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1851
1852 auto leaf2AtBlock2 = PublicDataLeafValue(30, 5);
1853 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, true);
1854
1855 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
1856 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
1857 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
1858
1870 // The size does not increase since sequential insertion doesn't pad
1871 check_size(tree, current_size);
1872 commit_tree(tree);
1873 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1874 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1875 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
1876 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1877
1878 auto leaf2AtBlock3 = PublicDataLeafValue(30, 6);
1879 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
1880
1881 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
1882 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
1883 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
1884
1896 check_size(tree, ++current_size);
1897 commit_tree(tree);
1898 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
1899 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
1900 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
1901 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
1902 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
1903
1904 check_historic_leaf(tree, leaf2AtBlock3, 2, 3, true);
1905
1906 // should not be found at block 1
1907 check_historic_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
1908 tree, { PublicDataLeafValue(10, 20) }, 1, 0, { std::nullopt }, true);
1909 // should be found at block
1910 check_historic_find_leaf_index_from(tree, PublicDataLeafValue(10, 20), 2, 0, 3, true);
1911
1913 EXPECT_EQ(lowLeaf.index, 1);
1914
1915 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(20, 0));
1916 EXPECT_EQ(lowLeaf.index, 3);
1917
1918 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(60, 0));
1919 EXPECT_EQ(lowLeaf.index, 2);
1920}
1921
1922TEST_F(PersistedContentAddressedIndexedTreeTest, test_inserting_a_duplicate_committed_nullifier_should_fail)
1923{
1924 const uint32_t batch_size = 16;
1925 uint32_t depth = 10;
1926 ThreadPoolPtr multi_workers = make_thread_pool(1);
1927 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1928
1929 std::string name1 = random_string();
1930 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1931 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1932 auto tree = TreeType(std::move(store1), multi_workers, batch_size);
1933
1934 std::vector<fr> values = create_values(batch_size);
1935 std::vector<NullifierLeafValue> nullifierValues(batch_size);
1936 std::transform(
1937 values.begin(), values.end(), nullifierValues.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1938
1939 add_values(tree, nullifierValues);
1940 commit_tree(tree);
1941
1942 // create a new set of values
1943 std::vector<fr> values2 = create_values(batch_size);
1944
1945 // copy one of the previous values into the middle of the batch
1946 values2[batch_size / 2] = values[0];
1947 std::vector<NullifierLeafValue> nullifierValues2(batch_size);
1948 std::transform(
1949 values2.begin(), values2.end(), nullifierValues2.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1950 add_values(tree, nullifierValues2, false);
1951}
1952
1953TEST_F(PersistedContentAddressedIndexedTreeTest, test_inserting_a_duplicate_uncommitted_nullifier_should_fail)
1954{
1955 const uint32_t batch_size = 16;
1956 uint32_t depth = 10;
1957 ThreadPoolPtr multi_workers = make_thread_pool(1);
1958 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1959
1960 std::string name1 = random_string();
1961 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1962 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1963 auto tree = TreeType(std::move(store1), multi_workers, batch_size);
1964
1965 std::vector<fr> values = create_values(batch_size);
1966 std::vector<NullifierLeafValue> nullifierValues(batch_size);
1967 std::transform(
1968 values.begin(), values.end(), nullifierValues.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1969
1970 add_values(tree, nullifierValues);
1971
1972 // create a new set of values
1973 std::vector<fr> values2 = create_values(batch_size);
1974
1975 // copy one of the previous values into the middle of the batch
1976 values2[batch_size / 2] = values[0];
1977 std::vector<NullifierLeafValue> nullifierValues2(batch_size);
1978 std::transform(
1979 values2.begin(), values2.end(), nullifierValues2.begin(), [](const fr& v) { return NullifierLeafValue(v); });
1980 add_values(tree, nullifierValues2, false);
1981}
1982
1983TEST_F(PersistedContentAddressedIndexedTreeTest, test_can_create_forks_at_historic_blocks)
1984{
1986 const uint32_t batch_size = 16;
1987 uint32_t depth = 10;
1988 ThreadPoolPtr multi_workers = make_thread_pool(8);
1989 NullifierMemoryTree<HashPolicy> memdb(depth, batch_size);
1990
1991 std::string name1 = random_string();
1992 LMDBTreeStore::SharedPtr db1 = std::make_shared<LMDBTreeStore>(_directory, name1, _mapSize, _maxReaders);
1993 std::unique_ptr<Store> store1 = std::make_unique<Store>(name1, depth, db1);
1994 auto tree1 = TreeType(std::move(store1), multi_workers, batch_size);
1995
1996 check_root(tree1, memdb.root());
1997 check_sibling_path(tree1, 0, memdb.get_sibling_path(0));
1998
1999 check_sibling_path(tree1, 512, memdb.get_sibling_path(512));
2000
2002 for (uint32_t j = 0; j < batch_size; j++) {
2003 batch1.emplace_back(random_engine.get_random_uint256());
2004 memdb.update_element(batch1[j].nullifier);
2005 }
2006
2007 fr_sibling_path block1SiblingPathIndex3 = memdb.get_sibling_path(3 + batch_size);
2008
2009 add_values(tree1, batch1);
2010 commit_tree(tree1, true);
2011
2013 for (uint32_t j = 0; j < batch_size; j++) {
2014 batch2.emplace_back(random_engine.get_random_uint256());
2015 memdb.update_element(batch2[j].nullifier);
2016 }
2017
2018 add_values(tree1, batch2);
2019 commit_tree(tree1, true);
2020
2021 fr block2Root = memdb.root();
2022
2023 fr_sibling_path block2SiblingPathIndex19 = memdb.get_sibling_path(19 + batch_size);
2024 fr_sibling_path block2SiblingPathIndex3 = memdb.get_sibling_path(3 + batch_size);
2025
2027 for (uint32_t j = 0; j < batch_size; j++) {
2028 batch3.emplace_back(random_engine.get_random_uint256());
2029 memdb.update_element(batch3[j].nullifier);
2030 }
2031
2032 add_values(tree1, batch3);
2033 commit_tree(tree1, true);
2034
2035 fr_sibling_path block3SiblingPathIndex35 = memdb.get_sibling_path(35 + batch_size);
2036 fr_sibling_path block3SiblingPathIndex19 = memdb.get_sibling_path(19 + batch_size);
2037 fr_sibling_path block3SiblingPathIndex3 = memdb.get_sibling_path(3 + batch_size);
2038
2039 std::unique_ptr<Store> storeAtBlock2 = std::make_unique<Store>(name1, depth, 2, db1);
2040 auto treeAtBlock2 = TreeType(std::move(storeAtBlock2), multi_workers, batch_size);
2041
2042 check_root(treeAtBlock2, block2Root);
2043 check_sibling_path(treeAtBlock2, 3 + batch_size, block2SiblingPathIndex3, false, true);
2044 auto block2TreeLeaf10 = get_leaf<NullifierLeafValue>(treeAtBlock2, 7 + batch_size);
2045 EXPECT_EQ(block2TreeLeaf10.leaf.nullifier, batch1[7].nullifier);
2046
2047 check_find_leaf_index(treeAtBlock2, batch1[5], 5 + batch_size, true);
2048 check_find_leaf_index_from(treeAtBlock2, batch1[5], 0, 5 + batch_size, true);
2049
2050 // should not exist in our image
2051 get_leaf<NullifierLeafValue>(treeAtBlock2, 35 + batch_size, false, false);
2052 check_find_leaf_index<NullifierLeafValue, TreeType>(treeAtBlock2, { batch3[4] }, { std::nullopt }, true);
2053
2054 // now add the same values to our image
2055 add_values(treeAtBlock2, batch3);
2056
2057 // the state of our image should match the original tree
2058 check_sibling_path(tree1, 3 + batch_size, block3SiblingPathIndex3, false, true);
2059 check_sibling_path(tree1, 19 + batch_size, block3SiblingPathIndex19, false, true);
2060 check_sibling_path(tree1, 35 + batch_size, block3SiblingPathIndex35, false, true);
2061
2062 // needs to use uncommitted for this check
2063 check_sibling_path(treeAtBlock2, 3 + batch_size, block3SiblingPathIndex3, true, true);
2064 check_sibling_path(treeAtBlock2, 19 + batch_size, block3SiblingPathIndex19, true, true);
2065 check_sibling_path(treeAtBlock2, 35 + batch_size, block3SiblingPathIndex35, true, true);
2066
2067 // now check historic data
2068 auto historicSiblingPath = get_historic_sibling_path(treeAtBlock2, 1, 3 + batch_size);
2069 EXPECT_EQ(historicSiblingPath, block1SiblingPathIndex3);
2070 check_historic_find_leaf_index(treeAtBlock2, batch1[3], 1, 3 + batch_size, true);
2071 check_historic_find_leaf_index(treeAtBlock2, batch3[3], 2, 35 + batch_size, true, true);
2072 check_historic_find_leaf_index<NullifierLeafValue, TreeType>(
2073 treeAtBlock2, { batch3[3] }, 2, { std::nullopt }, true, false);
2074
2075 check_historic_find_leaf_index_from(treeAtBlock2, batch1[3], 2, 0, 3 + batch_size, true, false);
2076 check_historic_find_leaf_index_from<NullifierLeafValue, TreeType>(
2077 treeAtBlock2, { batch3[3] }, 2, 20 + batch_size, { std::nullopt }, true, false);
2078 check_historic_find_leaf_index_from(treeAtBlock2, batch3[3], 2, 20 + batch_size, 35 + batch_size, true, true);
2079
2080 check_unfinalized_block_height(treeAtBlock2, 2);
2081
2082 // It should be impossible to commit using the image
2083 commit_tree(treeAtBlock2, false);
2084}
2085
2087{
2088 index_t current_size = 2;
2089 ThreadPoolPtr workers = make_thread_pool(8);
2090 // Create a depth-3 indexed merkle tree
2091 constexpr size_t depth = 3;
2092 std::string name = random_string();
2093 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2096 using LocalTreeType =
2098 auto tree = LocalTreeType(std::move(store), workers, current_size);
2099
2112 check_size(tree, current_size);
2113 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
2114 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
2115
2127 commit_tree(tree);
2128 check_size(tree, ++current_size);
2129 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2130 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2131 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2132
2133 auto leaf1AtBlock1 = PublicDataLeafValue(1, 0);
2134 check_block_and_size_data(db, 1, current_size, true);
2135
2147 check_size(tree, ++current_size);
2148 commit_tree(tree);
2149 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2150 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2151 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2152 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2153
2154 check_block_and_size_data(db, 2, current_size, true);
2155
2156 auto leaf2AtBlock2 = PublicDataLeafValue(30, 5);
2157 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, true);
2158
2159 // shoudl find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2160 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2161 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2162
2174 check_size(tree, current_size);
2175 commit_tree(tree);
2176 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2177 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2178 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2179 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2180
2181 check_block_and_size_data(db, 3, current_size, true);
2182
2183 auto leaf2AtBlock3 = PublicDataLeafValue(30, 6);
2184 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
2185
2186 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2187 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2188 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2189
2201 check_size(tree, ++current_size);
2202 commit_tree(tree);
2203 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2204 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2205 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2206 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2207 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
2208
2209 check_block_and_size_data(db, 4, current_size, true);
2210
2211 check_historic_leaf(tree, leaf2AtBlock3, 2, 3, true);
2212
2213 // should not be found at block 1
2214 check_historic_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
2215 tree, { PublicDataLeafValue(10, 20) }, 1, 0, { std::nullopt }, true);
2216 // should be found at block
2217 check_historic_find_leaf_index_from(tree, PublicDataLeafValue(10, 20), 2, 0, 3, true);
2218
2220 EXPECT_EQ(lowLeaf.index, 1);
2221
2222 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(20, 0));
2223 EXPECT_EQ(lowLeaf.index, 3);
2224
2225 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(60, 0));
2226 EXPECT_EQ(lowLeaf.index, 2);
2227
2228 finalize_block(tree, 3);
2229
2230 // remove historical block 1
2231 remove_historic_block(tree, 1);
2232
2233 // Historic queries against block 1 should no longer work
2234 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, false);
2235 check_historic_find_leaf_index<PublicDataLeafValue, LocalTreeType>(
2236 tree, { leaf1AtBlock1 }, 1, { std::nullopt }, false);
2237
2238 // Queries against block 2 should work
2239 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
2240 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2241
2242 // now remove block 2 and queries against it should no longer work
2243 remove_historic_block(tree, 2);
2244 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, false);
2245
2246 // size doesn't matter, should fail to find the data
2247 check_block_and_size_data(db, 1, current_size, false);
2248}
2249
2251{
2252 index_t current_size = 2;
2253 ThreadPoolPtr workers = make_thread_pool(8);
2254 // Create a depth-3 indexed merkle tree
2255 constexpr size_t depth = 3;
2256 std::string name = random_string();
2257 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2260 using LocalTreeType =
2262 auto tree = LocalTreeType(std::move(store), workers, current_size);
2263
2276 check_size(tree, current_size);
2277 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
2278 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
2279
2280 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2281 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2282
2283 check_indices_data(db, 0, 0, true, true);
2284 check_indices_data(db, 1, 1, true, true);
2285
2297 commit_tree(tree);
2298 check_size(tree, ++current_size);
2299
2300 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2301 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2302 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2303
2304 check_block_and_size_data(db, 1, current_size, true);
2305
2306 // All historical pre-images should be present
2307 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2308 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2309 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2310 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2311
2312 check_indices_data(db, 30, 2, true, true);
2313
2314 auto leaf1AtBlock1 = PublicDataLeafValue(1, 0);
2315
2327 check_size(tree, ++current_size);
2328 commit_tree(tree);
2329 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2330 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2331 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2332 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2333
2334 // All historical pre-images should be present
2335 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2336 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2337 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2338 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2339 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2340
2341 check_indices_data(db, 10, 3, true, true);
2342
2343 check_block_and_size_data(db, 2, current_size, true);
2344
2345 auto leaf2AtBlock2 = PublicDataLeafValue(30, 5);
2346 check_historic_leaf(tree, leaf1AtBlock1, 1, 1, true);
2347
2348 // shoudl find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2349 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2350 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2351
2363 check_size(tree, current_size);
2364 commit_tree(tree);
2365 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2366 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2367 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2368 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2369
2370 // All historical pre-images should be present
2371 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2372 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2373 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2374 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2375 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2376 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2377 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 0, 0), true);
2378
2379 // Zero leaves should not have their indices added
2380 check_indices_data(db, 0, 4, true, false);
2381
2382 check_block_and_size_data(db, 3, current_size, true);
2383
2384 auto leaf2AtBlock3 = PublicDataLeafValue(30, 6);
2385 check_historic_leaf(tree, leaf2AtBlock2, 2, 2, true);
2386
2387 // should find this leaf at both blocks 1 and 2 as it looks for the slot which doesn't change
2388 check_historic_find_leaf_index(tree, leaf1AtBlock1, 1, 1, true);
2389 check_historic_find_leaf_index(tree, leaf1AtBlock1, 2, 1, true);
2390
2402 check_size(tree, ++current_size);
2403 commit_tree(tree);
2404 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2405 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
2406 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2407 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
2408 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
2409
2410 check_indices_data(db, 50, 4, true, true);
2411 // All historical pre-images should be present
2412 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2413 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2414 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2415 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2416 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2417 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 0, 0), true);
2418 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(50, 8, 0, 0), true);
2419 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 4, 50), true);
2420
2421 check_block_and_size_data(db, 4, current_size, true);
2422
2423 check_historic_leaf(tree, leaf2AtBlock3, 2, 3, true);
2424
2425 // should not be found at block 1
2426 check_historic_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
2427 tree, { PublicDataLeafValue(10, 20) }, 1, 0, { std::nullopt }, true);
2428 // should be found at block
2429 check_historic_find_leaf_index_from(tree, PublicDataLeafValue(10, 20), 2, 0, 3, true);
2430
2432 EXPECT_EQ(lowLeaf.index, 1);
2433
2434 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(20, 0));
2435 EXPECT_EQ(lowLeaf.index, 3);
2436
2437 lowLeaf = get_historic_low_leaf(tree, 2, PublicDataLeafValue(60, 0));
2438 EXPECT_EQ(lowLeaf.index, 2);
2439
2440 unwind_block(tree, 4);
2441
2442 // Index 4 should be removed
2443 check_indices_data(db, 50, 4, false, false);
2444 // The pre-images created before block 4 should be present
2445 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2446 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2447 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2448 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2449 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2450 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2451 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 0, 0), true);
2452
2453 // The pre-images created in block 4 should be gone
2454 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(50, 8, 0, 0), false);
2455 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 6, 4, 50), false);
2456
2457 check_size(tree, --current_size);
2458
2459 // should fail to find block 4
2460 check_block_and_size_data(db, 4, current_size, false);
2461
2462 // block 3 should work
2463 check_block_and_size_data(db, 3, current_size, true);
2464
2465 // should fail to find the leaf at index 4
2466 check_find_leaf_index<PublicDataLeafValue, LocalTreeType>(
2467 tree, { PublicDataLeafValue(50, 8) }, { std::nullopt }, true);
2468 check_find_leaf_index_from<PublicDataLeafValue, LocalTreeType>(
2469 tree, { PublicDataLeafValue(50, 8) }, 0, { std::nullopt }, true);
2470
2471 // the leaf at index 2 should no longer be as it was after block 5
2472 EXPECT_NE(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
2473
2474 // it should be as it was after block 4
2475 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2476
2477 unwind_block(tree, 3);
2478
2479 // The pre-images created before block 3 should be present
2480 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2481 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2482 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 3, 10), true);
2483 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2484 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(10, 20, 2, 30), true);
2485
2486 check_size(tree, current_size);
2487
2488 // the leaf at index 2 should no longer be as it was after block 4
2489 EXPECT_NE(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
2490
2491 // it should be as it was after block 3
2492 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2493}
2494
2496{
2497 index_t current_size = 2;
2498 ThreadPoolPtr workers = make_thread_pool(8);
2499 // Create a depth-3 indexed merkle tree
2500 constexpr size_t depth = 3;
2501 std::string name = random_string();
2502 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2506 std::move(store), workers, current_size);
2507
2520 check_size(tree, current_size);
2521 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), zero_leaf);
2522 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), one_leaf);
2523
2535 commit_tree(tree);
2536 check_size(tree, ++current_size);
2537 fr rootAfterBlock1 = get_root(tree, false);
2538 fr_sibling_path pathAfterBlock1 = get_sibling_path(tree, 0, false);
2539 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2540 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2541 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2542
2543 check_block_and_size_data(db, 1, current_size, true);
2544
2545 // All historical pre-images should be present
2546 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2547 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2548 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2549 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2550
2551 check_indices_data(db, 30, 2, true, true);
2552
2564 commit_tree(tree);
2565 check_size(tree, current_size);
2566 fr rootAfterBlock2 = get_root(tree, false);
2567 fr_sibling_path pathAfterBlock2 = get_sibling_path(tree, 0, false);
2568 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2569 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2570 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 8, 0, 0));
2571
2572 // All historical pre-images should be present
2573 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2574 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2575 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2576 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2577 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), true);
2578
2579 check_indices_data(db, 30, 2, true, true);
2580
2592 commit_tree(tree);
2593 check_size(tree, current_size);
2594 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2595 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2596 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2597
2598 // All historical pre-images should be present
2599 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2600 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2601 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2602 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2603 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), true);
2604
2605 check_indices_data(db, 30, 2, true, true);
2606
2607 // Unwind block 3 and the state should be reverted back to block 2
2608 unwind_block(tree, 3);
2609
2610 check_root(tree, rootAfterBlock2);
2611 check_sibling_path(tree, 0, pathAfterBlock2, false);
2612 check_size(tree, current_size);
2613 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2614 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2615 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 8, 0, 0));
2616
2617 // All historical pre-images should be present
2618 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2619 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2620 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2621 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2622 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), true);
2623
2624 check_indices_data(db, 30, 2, true, true);
2625
2626 // Unwind block 2 and the state should be reverted back to block 1
2627 unwind_block(tree, 2);
2628
2629 check_root(tree, rootAfterBlock1);
2630 check_sibling_path(tree, 0, pathAfterBlock1, false);
2631 check_size(tree, current_size);
2632 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2633 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2634 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 5, 0, 0));
2635
2636 // All historical pre-images should be present
2637 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, zero_leaf, true);
2638 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, one_leaf, true);
2639 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(1, 0, 2, 30), true);
2640 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 5, 0, 0), true);
2641
2642 // Check the pre-image was removed
2643 check_leaf_by_hash<PublicDataLeafValue, HashPolicy>(db, create_indexed_public_data_leaf(30, 8, 0, 0), false);
2644
2645 check_indices_data(db, 30, 2, true, true);
2646
2647 // Now apply block 2 again and it should be moved forward back tot where it was
2648 add_value(tree, PublicDataLeafValue(30, 8));
2649 commit_tree(tree);
2650 check_size(tree, ++current_size);
2651 check_root(tree, rootAfterBlock2);
2652 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
2653 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), create_indexed_public_data_leaf(1, 0, 2, 30));
2654 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), create_indexed_public_data_leaf(30, 8, 0, 0));
2655}
2656
2657void test_nullifier_tree_unwind(std::string directory,
2658 std::string name,
2659 uint64_t mapSize,
2660 uint64_t maxReaders,
2661 uint32_t depth,
2662 uint32_t blockSize,
2663 uint32_t numBlocks,
2664 uint32_t numBlocksToUnwind,
2665 std::vector<fr> values)
2666{
2667 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(directory, name, mapSize, maxReaders);
2668 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
2670 TreeType tree(std::move(store), pool, blockSize);
2671 NullifierMemoryTree<Poseidon2HashPolicy> memdb(depth, blockSize);
2672
2673 auto it = std::find_if(values.begin(), values.end(), [&](const fr& v) { return v != fr::zero(); });
2674 bool emptyBlocks = it == values.end();
2675
2676 uint32_t batchSize = blockSize;
2677
2678 std::vector<fr_sibling_path> historicPathsZeroIndex;
2679 std::vector<fr_sibling_path> historicPathsMaxIndex;
2680 std::vector<fr> roots;
2681
2682 fr initialRoot = memdb.root();
2683 fr_sibling_path initialPath = memdb.get_sibling_path(0);
2684
2686 leafValues.reserve(values.size());
2687 for (const fr& v : values) {
2688 leafValues.emplace_back(v);
2689 }
2690
2691 for (uint32_t i = 0; i < numBlocks; i++) {
2693
2694 for (size_t j = 0; j < batchSize; ++j) {
2695 size_t ind = i * batchSize + j;
2696 memdb.update_element(values[ind]);
2697 to_add.push_back(leafValues[ind]);
2698 }
2699 // Indexed trees have an initial 'batch' inserted at startup
2700 index_t expected_size = (i + 2) * batchSize;
2701 add_values(tree, to_add);
2702 commit_tree(tree);
2703
2704 historicPathsZeroIndex.push_back(memdb.get_sibling_path(0));
2705 historicPathsMaxIndex.push_back(memdb.get_sibling_path(expected_size - 1));
2706 roots.push_back(memdb.root());
2707 check_root(tree, memdb.root());
2708 check_sibling_path(tree, 0, memdb.get_sibling_path(0));
2709 check_sibling_path(tree, expected_size - 1, memdb.get_sibling_path(expected_size - 1));
2710 check_size(tree, expected_size);
2711 check_block_and_size_data(db, i + 1, expected_size, true);
2712 check_block_and_root_data(db, i + 1, memdb.root(), true);
2713 }
2714
2715 const uint32_t blocksToRemove = numBlocksToUnwind;
2716 for (uint32_t i = 0; i < blocksToRemove; i++) {
2717 const block_number_t blockNumber = numBlocks - i;
2718
2719 check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], true);
2720 unwind_block(tree, blockNumber);
2721 if (emptyBlocks) {
2722 // with empty blocks, we should not find the block data but we do find the root
2723 check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], false, true);
2724 } else {
2725 // if blocks are not empty, this query should fail
2726 check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], false);
2727 }
2728
2729 const index_t previousValidBlock = blockNumber - 1;
2730 // Indexed trees have an initial 'batch' inserted at startup
2731 index_t deletedBlockStartIndex = (1 + previousValidBlock) * batchSize;
2732 index_t deletedBlockStartIndexIntoLocalValues = previousValidBlock * batchSize;
2733
2734 check_block_height(tree, previousValidBlock);
2735 check_size(tree, deletedBlockStartIndex);
2736 check_root(tree, previousValidBlock == 0 ? initialRoot : roots[previousValidBlock - 1]);
2737
2738 // The zero index sibling path should be as it was at the previous block
2739 check_sibling_path(tree,
2740 0,
2741 previousValidBlock == 0 ? initialPath : historicPathsZeroIndex[previousValidBlock - 1],
2742 false,
2743 true);
2744
2745 if (!emptyBlocks) {
2746 // Trying to find leaves appended in the block that was removed should fail
2747 get_leaf<NullifierLeafValue>(tree, 1 + deletedBlockStartIndex, false, false);
2748
2749 check_find_leaf_index<NullifierLeafValue, TreeType>(
2750 tree, { leafValues[1 + deletedBlockStartIndexIntoLocalValues] }, { std::nullopt }, true);
2751 }
2752
2753 for (index_t j = 0; j < numBlocks; j++) {
2754 block_number_t historicBlockNumber = static_cast<block_number_t>(j + 1);
2755 bool expectedSuccess = historicBlockNumber <= previousValidBlock;
2757 tree, 0, historicBlockNumber, historicPathsZeroIndex[j], false, expectedSuccess);
2758 index_t maxSizeAtBlock = ((j + 2) * batchSize) - 1;
2760 tree, maxSizeAtBlock, historicBlockNumber, historicPathsMaxIndex[j], false, expectedSuccess);
2761
2762 if (emptyBlocks) {
2763 continue;
2764 }
2765 const index_t leafIndex = 1;
2766 const index_t expectedIndexInTree = leafIndex + batchSize;
2768 tree, leafValues[leafIndex], expectedIndexInTree, historicBlockNumber, expectedSuccess, false);
2769
2770 std::vector<std::optional<index_t>> expectedResults;
2771 if (expectedSuccess) {
2772 expectedResults.emplace_back(std::make_optional(expectedIndexInTree));
2773 }
2774 check_historic_find_leaf_index<NullifierLeafValue, TreeType>(
2775 tree, { leafValues[leafIndex] }, historicBlockNumber, expectedResults, expectedSuccess, true);
2776 check_historic_find_leaf_index_from<NullifierLeafValue, TreeType>(
2777 tree, { leafValues[leafIndex] }, historicBlockNumber, 0, expectedResults, expectedSuccess, true);
2778 }
2779 }
2780}
2781
2783{
2784
2785 constexpr uint32_t numBlocks = 8;
2786 constexpr uint32_t numBlocksToUnwind = 4;
2787 std::vector<uint32_t> blockSizes = { 2, 4, 8, 16, 32 };
2788 for (const uint32_t& size : blockSizes) {
2789 uint32_t actualSize = size;
2790 std::vector<fr> values = create_values(actualSize * numBlocks);
2791 std::stringstream ss;
2792 ss << "DB " << actualSize;
2794 _directory, ss.str(), _mapSize, _maxReaders, 20, actualSize, numBlocks, numBlocksToUnwind, values);
2795 }
2796}
2797
2798TEST_F(PersistedContentAddressedIndexedTreeTest, can_sync_and_unwind_empty_blocks)
2799{
2800
2801 constexpr uint32_t numBlocks = 8;
2802 constexpr uint32_t numBlocksToUnwind = 4;
2803 std::vector<uint32_t> blockSizes = { 2, 4, 8, 16, 32 };
2804 for (const uint32_t& size : blockSizes) {
2805 uint32_t actualSize = size;
2806 std::vector<fr> values = std::vector<fr>(actualSize * numBlocks, fr::zero());
2807 std::stringstream ss;
2808 ss << "DB " << actualSize;
2810 _directory, ss.str(), _mapSize, _maxReaders, 20, actualSize, numBlocks, numBlocksToUnwind, values);
2811 }
2812}
2813
2815{
2816 ThreadPoolPtr workers = make_thread_pool(1);
2817 constexpr size_t depth = 3;
2818 std::string name = random_string();
2819 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2821
2822 index_t initial_size = 4;
2824 auto tree = PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values);
2825
2840 check_size(tree, initial_size);
2841 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), leaf_0);
2842 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), leaf_1);
2843 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), leaf_2);
2844 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), leaf_3);
2845}
2846
2847TEST_F(PersistedContentAddressedIndexedTreeTest, test_full_prefilled_public_data)
2848{
2849 ThreadPoolPtr workers = make_thread_pool(1);
2850 constexpr size_t depth = 3;
2851 std::string name = random_string();
2852 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2854
2855 index_t initial_size = 4;
2856 std::vector<PublicDataLeafValue> prefilled_values = {
2858 };
2859 auto tree = PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values);
2860
2875 check_size(tree, initial_size);
2876 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 0), leaf_0);
2877 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 1), leaf_1);
2878 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 2), leaf_2);
2879 EXPECT_EQ(get_leaf<PublicDataLeafValue>(tree, 3), leaf_3);
2880}
2881
2882TEST_F(PersistedContentAddressedIndexedTreeTest, test_prefilled_unsorted_public_data_should_fail)
2883{
2884 ThreadPoolPtr workers = make_thread_pool(1);
2885 constexpr size_t depth = 3;
2886 std::string name = random_string();
2887 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2889
2890 index_t initial_size = 4;
2891 // The prefilled values are not sorted: 5 > 3.
2893 EXPECT_THROW(PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values), std::runtime_error);
2894}
2895
2896TEST_F(PersistedContentAddressedIndexedTreeTest, test_prefilled_default_public_data_should_fail)
2897{
2898 ThreadPoolPtr workers = make_thread_pool(1);
2899 constexpr size_t depth = 3;
2900 std::string name = random_string();
2901 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2903
2904 index_t initial_size = 4;
2905 // The first prefilled value is the same as one of the default values (1).
2907 EXPECT_THROW(PublicDataTreeType(std::move(store), workers, initial_size, prefilled_values), std::runtime_error);
2908}
2909
2910TEST_F(PersistedContentAddressedIndexedTreeTest, test_can_commit_and_revert_checkpoints)
2911{
2912 index_t initial_size = 2;
2913 index_t current_size = initial_size;
2914 ThreadPoolPtr workers = make_thread_pool(8);
2915 // Create a depth-3 indexed merkle tree
2916 constexpr size_t depth = 3;
2917 std::string name = random_string();
2918 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
2922 std::move(store), workers, current_size);
2923
2946 check_size(tree, ++current_size);
2947
2959 check_size(tree, ++current_size);
2960
2972 // The size does not increase since sequential insertion doesn't pad
2973 check_size(tree, current_size);
2974 commit_tree(tree);
2975
2976 {
2977 index_t fork_size = current_size;
2980 auto forkTree =
2982 std::move(forkStore), workers, initial_size);
2983
2984 // Find the low leaf of slot 60
2985 auto predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
2986
2987 // It should be at index 2
2988 EXPECT_EQ(predecessor.is_already_present, false);
2989 EXPECT_EQ(predecessor.index, 2);
2990
2991 // checkpoint the fork
2992 checkpoint_tree(forkTree);
2993
3005 check_size(forkTree, ++fork_size);
3006 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3007 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3008 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
3009 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3010 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3011
3012 // Find the low leaf of slot 60
3013 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3014
3015 // It should be at index 4
3016 EXPECT_EQ(predecessor.is_already_present, false);
3017 EXPECT_EQ(predecessor.index, 4);
3018
3019 // Now revert the fork and see that it is rolled back to the checkpoint
3020 revert_checkpoint_tree(forkTree);
3021 check_size(forkTree, --fork_size);
3022 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3023 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3024 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 0, 0));
3025 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
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 2
3031 EXPECT_EQ(predecessor.is_already_present, false);
3032 EXPECT_EQ(predecessor.index, 2);
3033
3034 // checkpoint the fork again
3035 checkpoint_tree(forkTree);
3036
3037 // We now advance the fork again by a few checkpoints
3038
3050 // Make the same change again, commit the checkpoint and see that the changes remain
3052 check_size(forkTree, ++fork_size);
3053 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3054 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3055 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
3056 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3057 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3058
3059 // Find the low leaf of slot 60
3060 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3061
3062 // It should be back at index 4
3063 EXPECT_EQ(predecessor.is_already_present, false);
3064 EXPECT_EQ(predecessor.index, 4);
3065
3066 // Checkpoint again
3067 checkpoint_tree(forkTree);
3068
3080 check_size(forkTree, fork_size);
3081 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3082 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3083 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 12, 4, 50));
3084 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3085 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3086
3087 // Find the low leaf of slot 60
3088 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3089
3090 // It should be back at index 4
3091 EXPECT_EQ(predecessor.is_already_present, false);
3092 EXPECT_EQ(predecessor.index, 4);
3093
3094 // Checkpoint again
3095 checkpoint_tree(forkTree);
3096
3108
3109 check_size(forkTree, ++fork_size);
3110 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3111 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3112 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 12, 5, 45));
3113 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3114 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3115 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 5), create_indexed_public_data_leaf(45, 15, 4, 50));
3116
3117 // Find the low leaf of slot 60
3118 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3119
3120 // It should be back at index 4
3121 EXPECT_EQ(predecessor.is_already_present, false);
3122 EXPECT_EQ(predecessor.index, 4);
3123
3124 // Find the low leaf of slot 46
3125 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(46, 5));
3126
3127 // It should be back at index 4
3128 EXPECT_EQ(predecessor.is_already_present, false);
3129 EXPECT_EQ(predecessor.index, 5);
3130
3131 // Now commit the last checkpoint
3132 commit_checkpoint_tree(forkTree);
3133
3134 // The state should be identical
3135 check_size(forkTree, fork_size);
3136 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3137 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3138 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 12, 5, 45));
3139 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3140 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3141 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 5), create_indexed_public_data_leaf(45, 15, 4, 50));
3142
3143 // Find the low leaf of slot 60
3144 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3145
3146 // It should be back at index 4
3147 EXPECT_EQ(predecessor.is_already_present, false);
3148 EXPECT_EQ(predecessor.index, 4);
3149
3150 // Find the low leaf of slot 46
3151 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(46, 5));
3152
3153 // It should be back at index 4
3154 EXPECT_EQ(predecessor.is_already_present, false);
3155 EXPECT_EQ(predecessor.index, 5);
3156
3157 // Now revert the fork and we should remove both the new slot 45 and the update to slot 30
3158
3170 revert_checkpoint_tree(forkTree);
3171
3172 check_size(forkTree, --fork_size);
3173 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 0), create_indexed_public_data_leaf(0, 0, 1, 1));
3174 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 1), create_indexed_public_data_leaf(1, 0, 3, 10));
3175 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 2), create_indexed_public_data_leaf(30, 6, 4, 50));
3176 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 3), create_indexed_public_data_leaf(10, 20, 2, 30));
3177 EXPECT_EQ(get_leaf<PublicDataLeafValue>(forkTree, 4), create_indexed_public_data_leaf(50, 8, 0, 0));
3178
3179 // Find the low leaf of slot 60
3180 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(60, 5));
3181
3182 // It should be back at index 4
3183 EXPECT_EQ(predecessor.is_already_present, false);
3184 EXPECT_EQ(predecessor.index, 4);
3185
3186 // Find the low leaf of slot 46
3187 predecessor = get_low_leaf(forkTree, PublicDataLeafValue(46, 5));
3188
3189 // It should be back at index 4
3190 EXPECT_EQ(predecessor.is_already_present, false);
3191 EXPECT_EQ(predecessor.index, 2);
3192 }
3193}
3194
3195void advance_state(TreeType& fork, uint32_t size)
3196{
3197 std::vector<fr> values = create_values(size);
3199 for (uint32_t j = 0; j < size; j++) {
3200 leaves.emplace_back(values[j]);
3201 }
3202 add_values(fork, leaves);
3203}
3204
3205TEST_F(PersistedContentAddressedIndexedTreeTest, nullifiers_can_be_inserted_after_revert)
3206{
3207 index_t current_size = 2;
3208 ThreadPoolPtr workers = make_thread_pool(1);
3209 constexpr size_t depth = 10;
3210 std::string name = "Nullifier Tree";
3211 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
3212 std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
3213 auto tree = TreeType(std::move(store), workers, current_size);
3214
3215 {
3216 std::unique_ptr<Store> forkStore = std::make_unique<Store>(name, depth, db);
3217 auto forkTree = TreeType(std::move(forkStore), workers, current_size);
3218
3219 check_size(tree, current_size);
3220
3221 uint32_t size_to_insert = 8;
3222 uint32_t num_insertions = 5;
3223
3224 for (uint32_t i = 0; i < num_insertions - 1; i++) {
3225 advance_state(forkTree, size_to_insert);
3226 current_size += size_to_insert;
3227 check_size(forkTree, current_size);
3228 checkpoint_tree(forkTree);
3229 }
3230
3231 advance_state(forkTree, size_to_insert);
3232 current_size += size_to_insert;
3233 check_size(forkTree, current_size);
3234 revert_checkpoint_tree(forkTree);
3235
3236 current_size -= size_to_insert;
3237 check_size(forkTree, current_size);
3238
3239 commit_checkpoint_tree(forkTree);
3240
3241 check_size(forkTree, current_size);
3242
3243 advance_state(forkTree, size_to_insert);
3244
3245 current_size += size_to_insert;
3246 check_size(forkTree, current_size);
3247 }
3248}
3249
3250// Configured to throw an exception when put_cached_node_by_index is called, to simulate a failure in the thread during
3251// a path update.
3253 public:
3255 using Base::Base;
3256
3257 std::atomic<bool> throw_on_put_cached_node_by_index{ false };
3258 std::atomic<uint64_t> num_put_cached_node_calls{ 0 };
3259
3260 void put_cached_node_by_index(uint32_t level, index_t index, const fr& value)
3261 {
3263 num_put_cached_node_calls.fetch_add(1);
3264 throw std::runtime_error("injected failure from put_cached_node_by_index");
3265 }
3267 }
3268};
3269
3272
3273// The test checks that the perform_updates_without_witness callback has success=false
3274// and an error message, and that at least one call to put_cached_node_by_index was made (to ensure the failure was
3275// injected at the right place). This tests that the tree correctly handles exceptions thrown from the thread.
3276TEST_F(PersistedContentAddressedIndexedTreeTest, perform_updates_without_witness_when_thread_fails)
3277{
3278 constexpr size_t depth = 6;
3279 constexpr index_t initial_size = 2;
3280
3281 std::string name = random_string();
3282 LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
3283 auto store = std::make_unique<ThrowingNullifierStore>(name, depth, db);
3284 auto* raw_store = store.get();
3285
3286 ThreadPoolPtr workers = make_thread_pool(4);
3287 ThrowingTreeType tree(std::move(store), workers, initial_size);
3288
3290 updates->push_back(typename TestAccess::LeafUpdate{
3291 .leaf_index = 1,
3292 .updated_leaf = IndexedLeaf<NullifierLeafValue>(NullifierLeafValue(fr(123)), 0, fr(0)),
3293 .original_leaf = IndexedLeaf<NullifierLeafValue>::empty(),
3294 });
3295
3296 // Configure to throw an exception when put_cached_node_by_index is called, to simulate a failure in the thread
3297 // during a path update.
3298 raw_store->throw_on_put_cached_node_by_index = true;
3299
3300 Signal signal;
3301 bool callback_success = false;
3302 std::string callback_message;
3303
3305 tree,
3306 /*highest_index=*/1,
3307 updates,
3309 callback_success = response.success;
3310 callback_message = response.message;
3311 signal.signal_level();
3312 });
3313
3314 signal.wait_for_level();
3315
3316 // At least one call to put_cached_node_by_index should have been made
3317 EXPECT_GT(raw_store->num_put_cached_node_calls.load(), 0);
3318
3319 // The callback should indicate failure and contain an error message
3320 EXPECT_FALSE(callback_success);
3321 EXPECT_FALSE(callback_message.empty());
3322}
void put_cached_node_by_index(uint32_t level, index_t index, const fr &value)
Implements a simple append-only merkle tree All methods are asynchronous unless specified as otherwis...
void get_sibling_path(const index_t &index, const HashPathCallback &on_completion, bool includeUncommitted) const
Returns the sibling path from the leaf at the given index to the root.
void commit(const CommitCallback &on_completion)
Commit the tree to the backing store.
Serves as a key-value node store for merkle trees. Caches all changes in memory before persisting the...
void put_cached_node_by_index(uint32_t level, const index_t &index, const fr &data, bool overwriteIfPresent=true)
Writes the provided data at the given node coordinates. Only writes to uncommitted data.
Implements a parallelized batch insertion indexed tree Accepts template argument of the type of store...
std::function< void(TypedResponse< AddIndexedDataSequentiallyResponse< LeafValueType > > &)> AddSequentiallyCompletionCallbackWithWitness
void perform_updates_without_witness(const index_t &highest_index, std::shared_ptr< std::vector< LeafUpdate > > updates, const UpdatesCompletionCallback &completion)
std::function< void(const TypedResponse< UpdatesCompletionResponse > &)> UpdatesCompletionCallback
std::function< void(TypedResponse< AddIndexedDataResponse< LeafValueType > > &)> AddCompletionCallbackWithWitness
std::shared_ptr< LMDBTreeStore > SharedPtr
fr_sibling_path get_sibling_path(size_t index) const
Used in parallel insertions in the the IndexedTree. Workers signal to other following workes as they ...
Definition signal.hpp:17
void signal_level(uint32_t level=0)
Signals that the given level has been passed.
Definition signal.hpp:54
void signal_decrement(uint32_t delta=1)
Definition signal.hpp:60
void wait_for_level(uint32_t level=0)
Causes the thread to wait until the required level has been signalled.
Definition signal.hpp:40
FF a
FF b
void commit_tree(TypeOfTree &tree, bool expectedSuccess=true)
void test_batch_insert(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
ContentAddressedCachedTreeStore< NullifierLeafValue > Store
fr get_root(TypeOfTree &tree, bool includeUncommitted=true)
void advance_state(TreeType &fork, uint32_t size)
void add_value_sequentially(TypeOfTree &tree, const LeafValueType &value, bool expectedSuccess=true)
void add_values(TypeOfTree &tree, const std::vector< LeafValueType > &values, bool expectedSuccess=true)
void add_values_sequentially(TypeOfTree &tree, const std::vector< LeafValueType > &values, bool expectedSuccess=true)
void check_sibling_path(TypeOfTree &tree, index_t index, const fr_sibling_path &expected_sibling_path, bool includeUncommitted=true, bool expected_success=true)
TreeType::AddCompletionCallbackWithWitness CompletionCallback
void check_historic_sibling_path(TypeOfTree &tree, index_t index, block_number_t blockNumber, const fr_sibling_path &expected_sibling_path, bool includeUncommitted=true, bool expected_success=true)
void test_batch_insert_with_commit_restore(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
ContentAddressedIndexedTree< PublicDataStore, Poseidon2HashPolicy > PublicDataTreeType
IndexedNullifierLeafType create_indexed_nullifier_leaf(const fr &value, index_t nextIndex, const fr &nextValue)
void add_value(TypeOfTree &tree, const LeafValueType &value, bool expectedSuccess=true)
std::unique_ptr< TreeType > create_tree(const std::string &rootDirectory, uint64_t mapSize, uint64_t maxReaders, uint32_t depth, uint32_t batchSize, ThreadPoolPtr workers)
fr_sibling_path get_historic_sibling_path(TypeOfTree &tree, block_number_t blockNumber, index_t index, bool includeUncommitted=true, bool expected_success=true)
GetLowIndexedLeafResponse get_historic_low_leaf(TypeOfTree &tree, block_number_t blockNumber, const LeafValueType &leaf, bool includeUncommitted=true)
void test_sequential_insert_vs_batch(uint32_t batchSize, std::string directory, uint64_t mapSize, uint64_t maxReaders)
void check_block_height(TypeOfTree &tree, index_t expected_block_height)
IndexedLeaf< LeafValueType > get_leaf(TypeOfTree &tree, index_t index, bool includeUncommitted=true, bool expected_success=true)
void check_root(TypeOfTree &tree, fr expected_root, bool includeUncommitted=true)
GetLowIndexedLeafResponse get_low_leaf(TypeOfTree &tree, const LeafValueType &leaf, bool includeUncommitted=true)
void check_historic_leaf(TypeOfTree &tree, const LeafValueType &leaf, index_t expected_index, block_number_t blockNumber, bool expected_success, bool includeUncommitted=true)
void block_sync_values_sequential(TypeOfTree &tree, const std::vector< LeafValueType > &values, bool expectedSuccess=true)
IndexedPublicDataLeafType create_indexed_public_data_leaf(const fr &slot, const fr &value, index_t nextIndex, const fr &nextValue)
void check_unfinalized_block_height(TypeOfTree &tree, index_t expected_block_height)
void test_nullifier_tree_unwind(std::string directory, std::string name, uint64_t mapSize, uint64_t maxReaders, uint32_t depth, uint32_t blockSize, uint32_t numBlocks, uint32_t numBlocksToUnwind, std::vector< fr > values)
void check_size(TypeOfTree &tree, index_t expected_size, bool includeUncommitted=true)
ContentAddressedIndexedTree< Store, HashPolicy > TreeType
void remove_historic_block(TypeOfTree &tree, const block_number_t &blockNumber, bool expected_success=true)
void block_sync_values(TypeOfTree &tree, const std::vector< LeafValueType > &values, bool expectedSuccess=true)
fr hash_leaf(const IndexedLeaf< LeafValueType > &leaf)
void finalize_block(TypeOfTree &tree, const block_number_t &blockNumber, bool expected_success=true)
void unwind_block(TypeOfTree &tree, const block_number_t &blockNumber, bool expected_success=true)
TreeType::AddSequentiallyCompletionCallbackWithWitness SequentialCompletionCallback
bool verify_sibling_path(TreeType &tree, const IndexedNullifierLeafType &leaf_value, const uint32_t idx)
ThreadPoolPtr make_thread_pool(uint64_t numThreads)
Definition fixtures.hpp:60
const fr & get_value(size_t index)
void check_indices_data(LMDBTreeStore::SharedPtr db, fr leaf, index_t index, bool entryShouldBePresent, bool indexShouldBePresent)
void check_historic_find_leaf_index_from(TypeOfTree &tree, const std::vector< LeafValueType > &leaves, block_number_t blockNumber, index_t start_index, const std::vector< std::optional< index_t > > &expected_indices, bool expected_success, bool includeUncommitted=true)
std::string random_temp_directory()
Definition fixtures.hpp:37
uint32_t block_number_t
Definition types.hpp:19
uint32_t checkpoint_tree(TreeType &tree)
void check_find_leaf_index(TypeOfTree &tree, const std::vector< LeafValueType > &leaves, const std::vector< std::optional< index_t > > &expected_indices, bool expected_success, bool includeUncommitted=true)
void check_block_and_root_data(LMDBTreeStore::SharedPtr db, block_number_t blockNumber, fr root, bool expectedSuccess)
void check_find_leaf_index_from(TypeOfTree &tree, const std::vector< LeafValueType > &leaves, index_t start_index, const std::vector< std::optional< index_t > > &expected_indices, bool expected_success, bool includeUncommitted=true)
std::string random_string()
Definition fixtures.hpp:30
std::shared_ptr< ThreadPool > ThreadPoolPtr
Definition fixtures.hpp:58
void check_block_and_size_data(LMDBTreeStore::SharedPtr db, block_number_t blockNumber, index_t expectedSize, bool expectedSuccess)
void commit_checkpoint_tree(TreeType &tree, bool expected_success=true)
std::vector< fr > fr_sibling_path
Definition hash_path.hpp:14
void revert_checkpoint_tree(TreeType &tree, bool expected_success=true)
void check_historic_find_leaf_index(TypeOfTree &tree, const std::vector< LeafValueType > &leaves, block_number_t blockNumber, const std::vector< std::optional< index_t > > &expected_indices, bool expected_success, bool includeUncommitted=true)
fr_sibling_path get_sibling_path(TypeOfTree &tree, index_t index, bool includeUncommitted=true, bool expected_success=true)
Key get_key(int64_t keyCount)
Definition fixtures.hpp:30
RNG & get_randomness()
Definition engine.cpp:258
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:155
field< Bn254FrParams > fr
Definition fr.hpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static void perform_updates_without_witness(Tree &tree, const index_t &highest_index, std::shared_ptr< std::vector< LeafUpdate > > updates, const UpdatesCompletionCallback &completion)
static IndexedLeaf< LeafType > empty()
std::vector< fr > get_hash_inputs() const
static fr hash_pair(const fr &lhs, const fr &rhs)
Definition hash.hpp:19
static fr hash(const std::vector< fr > &inputs)
Definition hash.hpp:14
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr field zero()