Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
content_addressed_cache.test.cpp
Go to the documentation of this file.
7#include <cstdint>
8#include <vector>
9
10using namespace bb;
11using namespace bb::crypto::merkle_tree;
12
16
17class ContentAddressedCacheTest : public testing::Test {
18 protected:
19 void SetUp() override {}
20 void TearDown() override {};
21};
22
23uint64_t get_index(uint64_t max_index = 0)
24{
25 uint64_t result = random_engine.get_random_uint64();
26 return max_index == 0 ? 0 : result % max_index;
27}
28
30 CacheType& cache, index_t leaf_offset, uint64_t num_leaves, uint64_t num_nodes, uint64_t max_index = 0)
31{
32 for (uint64_t i = 0; i < num_leaves; i++) {
35 index_t next_index = get_index(max_index);
36 fr next_value = fr::random_element();
37 IndexedLeafType leaf_preimage = IndexedLeafType(LeafValueType(slot, value), next_index, next_value);
38 fr leaf_hash = fr::random_element();
39 cache.put_leaf_by_index(i + leaf_offset, leaf_preimage);
40 cache.put_leaf_preimage_by_hash(leaf_hash, leaf_preimage);
41 cache.update_leaf_key_index(i + leaf_offset, leaf_preimage.leaf.get_key());
42 }
43
44 for (uint64_t i = 0; i < num_nodes; i++) {
45 fr node_hash = fr::random_element();
47 cache.put_node(node_hash, node);
48
49 uint32_t level = uint32_t(i % uint64_t(cache.get_meta().depth));
50 index_t max_index_at_level = 1;
51 max_index_at_level <<= level;
52 max_index_at_level--;
53 index_t index = get_index(max_index_at_level);
54 cache.put_node_by_index(level, index, node_hash);
55 }
56
57 TreeMeta meta = cache.get_meta();
58 meta.size += num_leaves;
59 cache.put_meta(meta);
60}
61
62CacheType create_cache(uint32_t depth)
63{
64 TreeMeta meta;
65 meta.depth = depth;
66 meta.size = 0;
67 CacheType cache(depth);
68 cache.put_meta(meta);
69 return cache;
70}
71
73{
74 constexpr uint32_t depth = 10;
75 EXPECT_NO_THROW(CacheType cache(depth));
76}
77
78TEST_F(ContentAddressedCacheTest, can_checkpoint_cache)
79{
80 CacheType cache = create_cache(10);
81 add_to_cache(cache, 0, 10, 100);
82 EXPECT_NO_THROW(cache.checkpoint());
83}
84
85TEST_F(ContentAddressedCacheTest, can_not_revert_cache_without_checkpoint)
86{
87 CacheType cache = create_cache(10);
88 EXPECT_THROW(cache.revert(), std::runtime_error);
89}
90
91TEST_F(ContentAddressedCacheTest, can_not_commit_cache_without_checkpoint)
92{
93 CacheType cache = create_cache(10);
94 EXPECT_THROW(cache.commit(), std::runtime_error);
95}
96
97// Adds 4 node hashes by the given level and index
98// Returns the 4 hashes
99std::vector<fr> setup_nodes_test(uint32_t level, uint64_t index, CacheType& cache)
100{
101 // Now add a new node
102 fr node_hash_1 = fr::random_element();
103 cache.put_node_by_index(level, index, node_hash_1);
104
105 // Now add a new value at the same location
106 fr node_hash_2 = fr::random_element();
107 cache.put_node_by_index(level, index, node_hash_2);
108
109 // Checkpoint again
110 cache.checkpoint();
111 fr node_hash_3 = fr::random_element();
112 cache.put_node_by_index(level, index, node_hash_3);
113
114 // Now add a new value at the same location
115 fr node_hash_4 = fr::random_element();
116 cache.put_node_by_index(level, index, node_hash_4);
117 return { node_hash_1, node_hash_2, node_hash_3, node_hash_4 };
118}
119
120TEST_F(ContentAddressedCacheTest, commit_then_revert_nodes)
121{
122 CacheType cache = create_cache(10);
123 cache.checkpoint();
124 uint32_t level = 5;
125 uint64_t index = 15;
126
127 std::vector<fr> hashes = setup_nodes_test(level, index, cache);
128 fr node_hash_4 = hashes[3];
129 // Check current node value
130 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_4);
131
132 // Commit the last checkpoint
133 cache.commit();
134 // Check current node value
135 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_4);
136
137 // Revert the next checkpoint, there should be no node at this location
138 cache.revert();
139 EXPECT_FALSE(cache.get_node_by_index(level, index).has_value());
140}
141
142TEST_F(ContentAddressedCacheTest, commit_then_commit_nodes)
143{
144 CacheType cache = create_cache(10);
145 cache.checkpoint();
146 uint32_t level = 5;
147 uint64_t index = 15;
148 std::vector<fr> hashes = setup_nodes_test(level, index, cache);
149 fr node_hash_4 = hashes[3];
150
151 // Check current node value
152 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_4);
153
154 // Commit the last checkpoint
155 cache.commit();
156 // Check current node value
157 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_4);
158
159 // Commit again and we should still have the same node
160 cache.commit();
161 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_4);
162}
163
164TEST_F(ContentAddressedCacheTest, revert_then_commit_nodes)
165{
166 CacheType cache = create_cache(10);
167 cache.checkpoint();
168 uint32_t level = 5;
169 uint64_t index = 15;
170 std::vector<fr> hashes = setup_nodes_test(level, index, cache);
171 fr node_hash_4 = hashes[3];
172 fr node_hash_2 = hashes[1];
173
174 // Check current node value
175 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_4);
176
177 // Revert the last checkpoint
178 cache.revert();
179 // Check current node value
180 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_2);
181
182 // Commit the next checkpoint
183 cache.commit();
184 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_2);
185}
186
187TEST_F(ContentAddressedCacheTest, revert_then_revert_nodes)
188{
189 CacheType cache = create_cache(10);
190 cache.checkpoint();
191 uint32_t level = 5;
192 uint64_t index = 15;
193 std::vector<fr> hashes = setup_nodes_test(level, index, cache);
194 fr node_hash_4 = hashes[3];
195 fr node_hash_2 = hashes[1];
196
197 // Check current node value
198 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_4);
199
200 // Revert the last checkpoint
201 cache.revert();
202 // Check current node value
203 EXPECT_EQ(cache.get_node_by_index(level, index).value(), node_hash_2);
204
205 // Revert the next checkpoint, should be no node at this location
206 cache.revert();
207 EXPECT_FALSE(cache.get_node_by_index(level, index).has_value());
208}
209
211{
212 IndexedLeafType leaf;
213 if (cache.get_leaf_by_index(index, leaf)) {
214 return leaf;
215 }
216 return std::nullopt;
217}
218
219// Adds 4 leaf values at the given index
220// Return all 4 leaves
222{
224 fr value1 = fr::random_element();
225 index_t next_index = 15;
226 fr next_value = fr::random_element();
227 // Now add a new node
228 IndexedLeafType leaf_preimage1 = IndexedLeafType(LeafValueType(slot, value1), next_index, next_value);
229 cache.put_leaf_by_index(index, leaf_preimage1);
230 cache.update_leaf_key_index(index, leaf_preimage1.leaf.get_key());
231
232 // Now add a new value at the same location
233 fr value2 = fr::random_element();
234 IndexedLeafType leaf_preimage2 = IndexedLeafType(LeafValueType(slot, value2), next_index, next_value);
235 cache.put_leaf_by_index(index, leaf_preimage2);
236 cache.update_leaf_key_index(index, leaf_preimage2.leaf.get_key());
237
238 // Checkpoint again
239 cache.checkpoint();
240 fr value3 = fr::random_element();
241 IndexedLeafType leaf_preimage3 = IndexedLeafType(LeafValueType(slot, value3), next_index, next_value);
242 cache.put_leaf_by_index(index, leaf_preimage3);
243 cache.update_leaf_key_index(index, leaf_preimage3.leaf.get_key());
244
245 // Now add a new value at the same location
246 fr value4 = fr::random_element();
247 IndexedLeafType leaf_preimage4 = IndexedLeafType(LeafValueType(slot, value4), next_index, next_value);
248 cache.put_leaf_by_index(index, leaf_preimage4);
249 cache.update_leaf_key_index(index, leaf_preimage4.leaf.get_key());
250 return { leaf_preimage1, leaf_preimage2, leaf_preimage3, leaf_preimage4 };
251}
252
253TEST_F(ContentAddressedCacheTest, commit_then_revert_leaves)
254{
255 CacheType cache = create_cache(10);
256 cache.checkpoint();
257
258 uint32_t index = 67;
260 IndexedLeafType leaf_preimage4 = leaves[3];
261
262 // Check current leaf value
263 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
264 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage4);
265
266 // Verify the indices store
267 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).has_value());
268 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).value(), index);
269
270 // Commit the last checkpoint
271 cache.commit();
272 // Check current leaf value
273 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
274 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage4);
275
276 // Verify the indices store
277 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).has_value());
278 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).value(), index);
279
280 // Revert the next checkpoint, there should be no leaf at this location
281 cache.revert();
282 EXPECT_FALSE(get_leaf_by_index(cache, index).has_value());
283 EXPECT_FALSE(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).has_value());
284}
285
286TEST_F(ContentAddressedCacheTest, commit_then_commit_leaves)
287{
288 CacheType cache = create_cache(10);
289 cache.checkpoint();
290
291 uint32_t index = 67;
293 IndexedLeafType leaf_preimage4 = leaves[3];
294
295 // Check current leaf value
296 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
297 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage4);
298
299 // Verify the indices store
300 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).has_value());
301 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).value(), index);
302
303 // Commit the last checkpoint
304 cache.commit();
305 // Check current leaf value
306 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
307 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage4);
308
309 // Verify the indices store
310 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).has_value());
311 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).value(), index);
312
313 // Commit the next checkpoint, should still have the same leaf
314 cache.commit();
315 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
316 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage4);
317
318 // Verify the indices store
319 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).has_value());
320 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).value(), index);
321}
322
323TEST_F(ContentAddressedCacheTest, revert_then_commit_leaves)
324{
325 CacheType cache = create_cache(10);
326 cache.checkpoint();
327
328 uint32_t index = 67;
330 IndexedLeafType leaf_preimage4 = leaves[3];
331 IndexedLeafType leaf_preimage2 = leaves[1];
332
333 // Check current leaf value
334 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
335 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage4);
336
337 // Verify the indices store
338 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).has_value());
339 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).value(), index);
340
341 // Revert the last checkpoint
342 cache.revert();
343 // Check current leaf value
344 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
345 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage2);
346
347 // Verify the indices store still has the key at the same index
348 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage2.leaf.get_key()).has_value());
349 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage2.leaf.get_key()).value(), index);
350
351 // Commit the next checkpoint, should still have the same leaf
352 cache.commit();
353 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
354 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage2);
355
356 // Verify the indices store
357 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage2.leaf.get_key()).has_value());
358 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage2.leaf.get_key()).value(), index);
359}
360
361TEST_F(ContentAddressedCacheTest, revert_then_revert_leaves)
362{
363 CacheType cache = create_cache(10);
364 cache.checkpoint();
365
366 uint32_t index = 67;
368 IndexedLeafType leaf_preimage4 = leaves[3];
369 IndexedLeafType leaf_preimage2 = leaves[1];
370
371 // Check current leaf value
372 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
373 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage4);
374
375 // Verify the indices store
376 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).has_value());
377 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage4.leaf.get_key()).value(), index);
378
379 // Revert the last checkpoint
380 cache.revert();
381 // Check current leaf value
382 EXPECT_TRUE(get_leaf_by_index(cache, index).has_value());
383 EXPECT_EQ(get_leaf_by_index(cache, index).value(), leaf_preimage2);
384
385 // Verify the indices store still has the key at the same index
386 EXPECT_TRUE(cache.get_leaf_key_index(leaf_preimage2.leaf.get_key()).has_value());
387 EXPECT_EQ(cache.get_leaf_key_index(leaf_preimage2.leaf.get_key()).value(), index);
388
389 // Revert the next checkpoint, there should be no leaf at this location
390 cache.revert();
391 EXPECT_FALSE(get_leaf_by_index(cache, index).has_value());
392 EXPECT_FALSE(cache.get_leaf_key_index(leaf_preimage2.leaf.get_key()).has_value());
393}
394
396{
397 CacheType cache = create_cache(40);
398 add_to_cache(cache, 0, 1000, 10000);
399 CacheType cache_copy = cache;
400 cache.checkpoint();
401 add_to_cache(cache, 1000, 1000, 10000);
402 EXPECT_NO_THROW(cache.revert());
403}
404
406{
407 CacheType cache = create_cache(40);
408 add_to_cache(cache, 0, 1000, 10000);
409 CacheType cache_copy = cache;
410 cache.checkpoint();
411 add_to_cache(cache, 1000, 1000, 10000);
412 CacheType cache_copy_2 = cache;
413 cache.checkpoint();
414 add_to_cache(cache, 2000, 1000, 10000);
415 EXPECT_NO_THROW(cache.revert());
416 EXPECT_TRUE(cache_copy_2.is_equivalent_to(cache));
417 cache.commit();
418 EXPECT_TRUE(cache_copy_2.is_equivalent_to(cache));
419}
420
422{
423 CacheType cache = create_cache(40);
424 cache.checkpoint();
425 add_to_cache(cache, 0, 1000, 10000);
426 cache.checkpoint();
427 add_to_cache(cache, 1000, 1000, 10000);
428 cache.checkpoint();
429 add_to_cache(cache, 2000, 1000, 10000);
430 CacheType final_cache = cache;
431
432 cache.commit_all();
433
434 // Should no longer be able to revert, no checkpoints left
435 // Should no longer be able to revert or commit
436 EXPECT_THROW(cache.commit(), std::runtime_error);
437 EXPECT_THROW(cache.revert(), std::runtime_error);
438
439 EXPECT_TRUE(final_cache.is_equivalent_to(cache));
440}
441
443{
444 CacheType cache = create_cache(40);
445 add_to_cache(cache, 0, 1000, 10000);
446 CacheType original_cache = cache;
447 cache.checkpoint();
448 add_to_cache(cache, 1000, 1000, 10000);
449 cache.checkpoint();
450 add_to_cache(cache, 2000, 1000, 10000);
451
452 // Revert all checkpoints
453 cache.revert_all();
454
455 // Should no longer be able to revert or commit
456 EXPECT_THROW(cache.commit(), std::runtime_error);
457 EXPECT_THROW(cache.revert(), std::runtime_error);
458
459 EXPECT_TRUE(original_cache.is_equivalent_to(cache));
460}
461
462TEST_F(ContentAddressedCacheTest, can_revert_through_multiple_levels)
463{
464 uint64_t num_levels = 10;
465 CacheType cache = create_cache(40);
466 add_to_cache(cache, 0, 1000, 10000);
467
469
470 for (uint64_t i = 0; i < num_levels; i++) {
471 copies.push_back(cache);
472 cache.checkpoint();
473 add_to_cache(cache, (i + 1) * 1000, 1000, 10000);
474 }
475
476 for (uint64_t i = 0; i < num_levels; i++) {
477 cache.revert();
478 EXPECT_TRUE(copies[num_levels - i - 1].is_equivalent_to(cache));
479 }
480}
481
482TEST_F(ContentAddressedCacheTest, can_commit_through_multiple_levels)
483{
484 uint64_t num_levels = 10;
485 CacheType cache = create_cache(40);
486 add_to_cache(cache, 0, 1000, 10000);
487
488 for (uint64_t i = 0; i < num_levels; i++) {
489 cache.checkpoint();
490 add_to_cache(cache, (i + 1) * 1000, 1000, 10000);
491 }
492
493 CacheType cache_copy = cache;
494
495 for (uint64_t i = 0; i < num_levels; i++) {
496 cache.commit();
497 }
498
499 EXPECT_TRUE(cache_copy.is_equivalent_to(cache));
500}
501
502void test_reverts_remove_all_deeper_commits(uint64_t max_index, uint32_t depth, uint64_t num_levels)
503{
504 CacheType cache = create_cache(depth);
505 add_to_cache(cache, 0, 1000, 10000, max_index);
506
507 CacheType base_cache = cache;
508
509 cache.checkpoint();
510 add_to_cache(cache, 1000, 1000, 10000, max_index);
511 CacheType first_commit_cache = cache;
512
513 // make lots more checkpoints and changes
514 for (uint64_t i = 1; i < num_levels; i++) {
515 cache.checkpoint();
516 add_to_cache(cache, (i + 1) * 1000, 1000, 10000, max_index);
517 }
518
519 CacheType final_cache = cache;
520
521 // commit everything except the the first checkpoint
522 for (uint64_t i = 1; i < num_levels; i++) {
523 cache.commit();
524 }
525
526 // we should still be equivalent to the final commit cache
527 EXPECT_TRUE(final_cache.is_equivalent_to(cache));
528
529 // reverting this final checkpoint reverts eveything else
530 cache.revert();
531 EXPECT_TRUE(base_cache.is_equivalent_to(cache));
532}
533
534TEST_F(ContentAddressedCacheTest, reverts_remove_all_deeper_commits)
535{
536 // We execute this test using 2 different values for max index to produce slightly different behaviour
537 // A lower value will encourage more updates to existing nodes
538 // A higher value will mean more new nodes are added
539 uint32_t depth = 40;
540 std::array<uint64_t, 2> max_indices = { 100, 1000000 };
541 uint64_t num_levels = 10;
542
543 for (uint64_t max_index : max_indices) {
544 test_reverts_remove_all_deeper_commits(max_index, depth, num_levels);
545 }
546}
547
548void reverts_remove_all_deeper_commits_2(uint64_t max_index, uint32_t depth, uint64_t num_levels)
549{
550 CacheType cache = create_cache(depth);
551 add_to_cache(cache, 0, 1000, 10000, max_index);
552
553 CacheType base_cache = cache;
554
555 cache.checkpoint();
556 add_to_cache(cache, 1000, 1000, 10000, max_index);
557 CacheType first_commit_cache = cache;
558
559 // make lots more checkpoints and changes
560 for (uint64_t i = 1; i < num_levels; i++) {
561 cache.checkpoint();
562 add_to_cache(cache, (i + 1) * 1000, 1000, 10000, max_index);
563 }
564
565 for (uint64_t i = 1; i < num_levels; i++) {
566 if (i % 2 != 0) {
567 cache.revert();
568 } else {
569 cache.commit();
570 }
571 }
572
573 // we should still be equivalent to the first commit cache
574 EXPECT_TRUE(first_commit_cache.is_equivalent_to(cache));
575
576 // reverting this final checkpoint reverts eveything else
577 cache.revert();
578 EXPECT_TRUE(base_cache.is_equivalent_to(cache));
579}
580
582{
583 // We execute this test using 2 different values for max index to produce slightly different behaviour
584 // A lower value will encourage more updates to existing nodes
585 // A higher value will mean more new nodes are added
586 uint64_t num_levels = 10;
587 uint32_t depth = 40;
588 std::array<uint64_t, 2> max_indices = { 100, 1000000 };
589 for (uint64_t max_index : max_indices) {
590 reverts_remove_all_deeper_commits_2(max_index, depth, num_levels);
591 }
592}
void put_leaf_by_index(const index_t &index, const IndexedLeafValueType &leaf_pre_image)
bool get_leaf_by_index(const index_t &index, IndexedLeafValueType &leaf_pre_image) const
void update_leaf_key_index(const index_t &index, const fr &leaf_key)
void put_leaf_preimage_by_hash(const fr &leaf_hash, const IndexedLeafValueType &leaf_pre_image)
std::optional< fr > get_node_by_index(uint32_t level, const index_t &index) const
std::optional< index_t > get_leaf_key_index(const fr &leaf_key) const
void put_node(const fr &node_hash, const NodePayload &node)
bool is_equivalent_to(const ContentAddressedCache &other) const
void put_node_by_index(uint32_t level, const index_t &index, const fr &node)
void test_reverts_remove_all_deeper_commits(uint64_t max_index, uint32_t depth, uint64_t num_levels)
uint64_t get_index(uint64_t max_index=0)
PublicDataLeafValue LeafValueType
CacheType create_cache(uint32_t depth)
std::vector< fr > setup_nodes_test(uint32_t level, uint64_t index, CacheType &cache)
void add_to_cache(CacheType &cache, index_t leaf_offset, uint64_t num_leaves, uint64_t num_nodes, uint64_t max_index=0)
IndexedLeaf< PublicDataLeafValue > IndexedLeafType
std::optional< IndexedLeafType > get_leaf_by_index(CacheType &cache, index_t index)
void reverts_remove_all_deeper_commits_2(uint64_t max_index, uint32_t depth, uint64_t num_levels)
std::vector< IndexedLeafType > setup_leaves_tests(uint32_t index, CacheType &cache)
Entry point for Barretenberg command-line interface.
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:123
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static field random_element(numeric::RNG *engine=nullptr) noexcept