Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
hinting_dbs.test.cpp
Go to the documentation of this file.
11
12#include <algorithm>
13#include <cstddef>
14#include <cstdlib>
15#include <gmock/gmock.h>
16#include <gtest/gtest.h>
17#include <vector>
18
19namespace bb::avm2::simulation {
20namespace {
21
22class HintingDBsTest : public ::testing::Test {
23 protected:
24 HintingDBsTest(const AvmProvingInputs& inputs)
25 : inputs(inputs)
26 , base_contract_db(HintedRawContractDB(inputs.hints))
27 , base_merkle_db(HintedRawMerkleDB(inputs.hints))
28 {}
29
30 template <typename Hint>
31 void compare_hints(const std::vector<Hint>& input_hints, const std::vector<Hint>& collected_hints)
32 {
33 for (const Hint& input_hint : input_hints) {
34 EXPECT_FALSE(std::ranges::find(collected_hints.begin(), collected_hints.end(), input_hint) ==
35 collected_hints.end());
36 }
37
38 for (const Hint& collected_hint : collected_hints) {
39 EXPECT_FALSE(std::ranges::find(input_hints.begin(), input_hints.end(), collected_hint) ==
40 input_hints.end());
41 }
42 }
43
44 public:
45 AvmProvingInputs inputs;
46 HintedRawContractDB base_contract_db;
47 HintedRawMerkleDB base_merkle_db;
48 HintingContractsDB hinting_contract_db = HintingContractsDB(base_contract_db);
49 HintingRawDB hinting_merkle_db = HintingRawDB(base_merkle_db);
50};
51
52class HintingDBsMinimalTest : public HintingDBsTest {
53 protected:
54 HintingDBsMinimalTest()
55 : HintingDBsTest(AvmProvingInputs::from(read_file("../src/barretenberg/vm2/testing/minimal_tx.testdata.bin")))
56 {}
57};
58
59// A helper to reset the randomly generated values in avm_inputs.testdata.bin to avoid unrelated failures:
60AvmProvingInputs fix_hint_keys(AvmProvingInputs inputs)
61{
62 auto reset_action_counters = [&]<typename H>(std::vector<H>& hints) {
63 for (auto& hint : hints) {
64 hint.hint_key = 0;
65 }
66 };
67 auto reset_tree_id = [&]<typename H>(std::vector<H>& hints) {
68 for (auto& hint : hints) {
69 // The AVM handles treeIds 0 - 3:
70 hint.tree_id = MerkleTreeId(hint.tree_id % 4);
71 hint.hint_key = get_tree_info_helper(hint.tree_id, inputs.hints.starting_tree_roots);
72 }
73 };
74 reset_action_counters(inputs.hints.contract_instances);
75 reset_action_counters(inputs.hints.contract_classes);
76 reset_action_counters(inputs.hints.bytecode_commitments);
77 reset_tree_id(inputs.hints.get_sibling_path_hints);
78
79 return inputs;
80};
81
82class HintingDBsTestInputTest : public HintingDBsTest {
83 protected:
84 HintingDBsTestInputTest()
85 : HintingDBsTest(fix_hint_keys(
86 AvmProvingInputs::from(read_file("../src/barretenberg/vm2/testing/avm_inputs.testdata.bin"))))
87 {}
88};
89
90TEST_F(HintingDBsTestInputTest, GetContractInstance)
91{
92 for (const auto& instance_hint : inputs.hints.contract_instances) {
93 auto instance = hinting_contract_db.get_contract_instance(instance_hint.address);
94 EXPECT_TRUE(instance.has_value());
95 EXPECT_EQ(instance.value(), base_contract_db.get_contract_instance(instance_hint.address).value());
96 }
97
98 ExecutionHints collected_hints;
99 hinting_contract_db.dump_hints(collected_hints);
100 compare_hints(inputs.hints.contract_instances, collected_hints.contract_instances);
101}
102
103TEST_F(HintingDBsTestInputTest, GetContractClass)
104{
105 for (const auto& class_hint : inputs.hints.contract_classes) {
106 auto klass = hinting_contract_db.get_contract_class(class_hint.class_id);
107 EXPECT_TRUE(klass.has_value());
108 EXPECT_THAT(klass.value(), base_contract_db.get_contract_class(class_hint.class_id).value());
109 }
110
111 ExecutionHints collected_hints;
112 hinting_contract_db.dump_hints(collected_hints);
113 compare_hints(inputs.hints.contract_classes, collected_hints.contract_classes);
114}
115
116TEST_F(HintingDBsTestInputTest, GetBytecodeCommitment)
117{
118 for (const auto& hint : inputs.hints.bytecode_commitments) {
119 auto commitment = hinting_contract_db.get_bytecode_commitment(hint.class_id);
120 EXPECT_TRUE(commitment.has_value());
121 EXPECT_EQ(commitment.value(), base_contract_db.get_bytecode_commitment(hint.class_id).value());
122 }
123
124 ExecutionHints collected_hints;
125 hinting_contract_db.dump_hints(collected_hints);
126 compare_hints(inputs.hints.bytecode_commitments, collected_hints.bytecode_commitments);
127}
128
129TEST_F(HintingDBsTestInputTest, GetDebugFunctionName)
130{
131 for (const auto& hint : inputs.hints.debug_function_names) {
132 auto name = hinting_contract_db.get_debug_function_name(hint.address, hint.selector);
133 EXPECT_TRUE(name.has_value());
134 EXPECT_EQ(name.value(), base_contract_db.get_debug_function_name(hint.address, hint.selector).value());
135 }
136
137 ExecutionHints collected_hints;
138 hinting_contract_db.dump_hints(collected_hints);
139 compare_hints(inputs.hints.debug_function_names, collected_hints.debug_function_names);
140}
141
142TEST_F(HintingDBsMinimalTest, ContractDBCheckpoints)
143{
144 // The minimal tx has one create and one commit. The conditionals are in case the minimal tx ever changes, bricking
145 // this test:
147 // The hinting db will cause the underlying base db to push a checkpoint onto the stack and increment the action
148 // counter:
152 }
153 }
154 ExecutionHints collected_hints;
155 hinting_contract_db.dump_hints(collected_hints);
157 collected_hints.contract_db_create_checkpoint_hints);
159 collected_hints.contract_db_commit_checkpoint_hints);
160}
161
162TEST_F(HintingDBsTestInputTest, GetSiblingPath)
163{
164 for (const auto& hint : inputs.hints.get_sibling_path_hints) {
165 auto path = hinting_merkle_db.get_sibling_path(hint.tree_id, hint.index);
166 EXPECT_EQ(path, base_merkle_db.get_sibling_path(hint.tree_id, hint.index));
167 }
168
169 ExecutionHints collected_hints;
170 hinting_merkle_db.dump_hints(collected_hints);
171 compare_hints(inputs.hints.get_sibling_path_hints, collected_hints.get_sibling_path_hints);
172}
173
174TEST_F(HintingDBsMinimalTest, MerkleDBCheckpoints)
175{
176 // The minimal tx has one create and one commit. The conditionals are in case the minimal tx ever changes, bricking
177 // this test:
178 if (inputs.hints.create_checkpoint_hints.size() == 1) {
179 // The hinting db will cause the underlying base db to push a checkpoint onto the stack and increment the action
180 // counter:
182 if (inputs.hints.commit_checkpoint_hints.size() == 1) {
184 }
185 }
186 ExecutionHints collected_hints;
187 hinting_merkle_db.dump_hints(collected_hints);
188 compare_hints(inputs.hints.create_checkpoint_hints, collected_hints.create_checkpoint_hints);
189 compare_hints(inputs.hints.commit_checkpoint_hints, collected_hints.commit_checkpoint_hints);
190}
191
192class MockedHintingDBsTest : public ::testing::Test {
193 protected:
194 MockedHintingDBsTest() { ON_CALL(base_merkle_db, get_tree_roots).WillByDefault(testing::Return(mock_tree_info)); }
195 testing::StrictMock<MockContractDB> base_contract_db;
196 testing::StrictMock<MockLowLevelMerkleDB> base_merkle_db;
197 HintingContractsDB hinting_contract_db = HintingContractsDB(base_contract_db);
198 HintingRawDB hinting_merkle_db = HintingRawDB(base_merkle_db);
199
200 TreeSnapshots mock_tree_info = {
201 { 1, 2 },
202 { 3, 2 },
203 { 5, 5 },
204 { 7, 3 },
205 };
206};
207
208TEST_F(MockedHintingDBsTest, GetLowLeaf)
209{
210 // Mock some slots:
211 std::vector<FF> update_preimage_slots = { 1, 2, 4 };
213 // get_low_indexed_leaf will call get_tree_roots and get_sibling_path (which itself will call get_tree_roots):
214 EXPECT_CALL(base_merkle_db, get_tree_roots).Times(static_cast<int>(update_preimage_slots.size() * 2));
216 .WillRepeatedly([&](world_state::MerkleTreeId, index_t) { return mock_path; });
217 EXPECT_CALL(base_merkle_db, get_low_indexed_leaf(world_state::MerkleTreeId::PUBLIC_DATA_TREE, testing::_))
218 .WillRepeatedly([&](world_state::MerkleTreeId, const FF& leaf_slot) {
219 for (size_t i = 0; i < update_preimage_slots.size(); ++i) {
220 if (leaf_slot == update_preimage_slots[i]) {
221 return GetLowIndexedLeafResponse(true, static_cast<uint64_t>(i));
222 }
223 }
224 throw std::runtime_error("Leaf not found");
225 });
226
227 // Call the db:
228 for (const auto& update_preimage_slot : update_preimage_slots) {
230 }
231 ExecutionHints collected_hints;
232 hinting_merkle_db.dump_hints(collected_hints);
233
234 // Check the collected hints:
235 EXPECT_EQ(collected_hints.get_previous_value_index_hints.size(), update_preimage_slots.size());
236 EXPECT_EQ(collected_hints.get_sibling_path_hints.size(), update_preimage_slots.size());
237 EXPECT_THAT(
238 collected_hints.get_previous_value_index_hints,
239 testing::ElementsAreArray({ GetPreviousValueIndexHint{ .hint_key = mock_tree_info.public_data_tree,
240 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
241 .value = update_preimage_slots[0],
242 .index = 0,
243 .already_present = true },
244 GetPreviousValueIndexHint{ .hint_key = mock_tree_info.public_data_tree,
245 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
246 .value = update_preimage_slots[1],
247 .index = 1,
248 .already_present = true },
249 GetPreviousValueIndexHint{ .hint_key = mock_tree_info.public_data_tree,
250 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
251 .value = update_preimage_slots[2],
252 .index = 2,
253 .already_present = true } }));
254 EXPECT_THAT(collected_hints.get_sibling_path_hints,
255 testing::ElementsAreArray({ GetSiblingPathHint{ .hint_key = mock_tree_info.public_data_tree,
256 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
257 .index = 0,
258 .path = mock_path },
259 GetSiblingPathHint{ .hint_key = mock_tree_info.public_data_tree,
260 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
261 .index = 1,
262 .path = mock_path },
263 GetSiblingPathHint{ .hint_key = mock_tree_info.public_data_tree,
264 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
265 .index = 2,
266 .path = mock_path } }));
267}
268
269TEST_F(MockedHintingDBsTest, GetLeafValue)
270{
271 // Mock some leaf values:
272 std::vector<FF> note_hash_leaf_values = { 11, 22, 44, 88 };
274 // get_leaf_value will call get_tree_roots and get_sibling_path (which itself will call get_tree_roots):
275 EXPECT_CALL(base_merkle_db, get_tree_roots).Times(static_cast<int>(note_hash_leaf_values.size() * 2));
276 EXPECT_CALL(base_merkle_db, get_sibling_path(world_state::MerkleTreeId::NOTE_HASH_TREE, testing::_))
277 .WillRepeatedly([&](world_state::MerkleTreeId, index_t) { return mock_path; });
278 EXPECT_CALL(base_merkle_db, get_leaf_value(world_state::MerkleTreeId::NOTE_HASH_TREE, testing::_))
279 .WillRepeatedly([&](world_state::MerkleTreeId, index_t index) {
280 if (index < note_hash_leaf_values.size()) {
281 return note_hash_leaf_values[index];
282 }
283 throw std::runtime_error("Leaf not found");
284 });
285
286 // Call the db:
287 for (index_t i = 0; i < note_hash_leaf_values.size(); i++) {
288 hinting_merkle_db.get_leaf_value(world_state::MerkleTreeId::NOTE_HASH_TREE, i);
289 }
290 ExecutionHints collected_hints;
291 hinting_merkle_db.dump_hints(collected_hints);
292
293 // Check the collected hints:
294 EXPECT_EQ(collected_hints.get_leaf_value_hints.size(), note_hash_leaf_values.size());
295 EXPECT_EQ(collected_hints.get_sibling_path_hints.size(), note_hash_leaf_values.size());
296 EXPECT_THAT(collected_hints.get_leaf_value_hints,
297 testing::ElementsAreArray({
298 GetLeafValueHint{ .hint_key = mock_tree_info.note_hash_tree,
299 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
300 .index = 0,
301 .value = note_hash_leaf_values[0] },
302 GetLeafValueHint{ .hint_key = mock_tree_info.note_hash_tree,
303 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
304 .index = 1,
305 .value = note_hash_leaf_values[1] },
306 GetLeafValueHint{ .hint_key = mock_tree_info.note_hash_tree,
307 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
308 .index = 2,
309 .value = note_hash_leaf_values[2] },
310 GetLeafValueHint{ .hint_key = mock_tree_info.note_hash_tree,
311 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
312 .index = 3,
313 .value = note_hash_leaf_values[3] },
314 }));
315 EXPECT_THAT(collected_hints.get_sibling_path_hints,
316 testing::ElementsAreArray({ GetSiblingPathHint{ .hint_key = mock_tree_info.note_hash_tree,
317 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
318 .index = 0,
319 .path = mock_path },
320 GetSiblingPathHint{ .hint_key = mock_tree_info.note_hash_tree,
321 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
322 .index = 1,
323 .path = mock_path },
324 GetSiblingPathHint{ .hint_key = mock_tree_info.note_hash_tree,
325 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
326 .index = 2,
327 .path = mock_path },
328 GetSiblingPathHint{ .hint_key = mock_tree_info.note_hash_tree,
329 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
330 .index = 3,
331 .path = mock_path } }));
332}
333
334TEST_F(MockedHintingDBsTest, GetLeafPreimagePublicDataTree)
335{
336 // Mock some leaf values:
337 std::vector<PublicDataLeafValue> public_leaf_values = { { 1, 3 }, { 2, 6 }, { 4, 7 } };
338 std::vector<IndexedLeaf<PublicDataLeafValue>> public_leaf_preimages = { { public_leaf_values[0], 1, 6 },
339 { public_leaf_values[1], 2, 4 },
340 { public_leaf_values[2], 0, 3 } };
342 // get_leaf_preimage_public_data_tree will call get_tree_roots and get_sibling_path (which itself will call
343 // get_tree_roots):
344 EXPECT_CALL(base_merkle_db, get_tree_roots).Times(static_cast<int>(public_leaf_preimages.size() * 2));
345 EXPECT_CALL(base_merkle_db, get_sibling_path(world_state::MerkleTreeId::PUBLIC_DATA_TREE, testing::_))
346 .WillRepeatedly([&](world_state::MerkleTreeId, index_t) { return mock_path; });
347 EXPECT_CALL(base_merkle_db, get_leaf_preimage_public_data_tree(testing::_)).WillRepeatedly([&](index_t index) {
348 if (index < public_leaf_preimages.size()) {
349 return public_leaf_preimages[index];
350 }
351 throw std::runtime_error("Leaf preimage not found");
352 });
353
354 // Call the db:
355 for (index_t i = 0; i < public_leaf_preimages.size(); i++) {
356 hinting_merkle_db.get_leaf_preimage_public_data_tree(i);
357 }
358 ExecutionHints collected_hints;
359 hinting_merkle_db.dump_hints(collected_hints);
360
361 // Check the collected hints:
362 EXPECT_EQ(collected_hints.get_leaf_preimage_hints_public_data_tree.size(), public_leaf_preimages.size());
363 EXPECT_EQ(collected_hints.get_sibling_path_hints.size(), public_leaf_preimages.size());
364 EXPECT_THAT(
365 collected_hints.get_leaf_preimage_hints_public_data_tree,
366 testing::ElementsAreArray(
367 { GetLeafPreimageHint<PublicDataTreeLeafPreimage>{
368 .hint_key = mock_tree_info.public_data_tree, .index = 0, .leaf_preimage = public_leaf_preimages[0] },
369 GetLeafPreimageHint<PublicDataTreeLeafPreimage>{
370 .hint_key = mock_tree_info.public_data_tree, .index = 1, .leaf_preimage = public_leaf_preimages[1] },
371 GetLeafPreimageHint<PublicDataTreeLeafPreimage>{ .hint_key = mock_tree_info.public_data_tree,
372 .index = 2,
373 .leaf_preimage = public_leaf_preimages[2] } }));
374 EXPECT_THAT(collected_hints.get_sibling_path_hints,
375 testing::ElementsAreArray({ GetSiblingPathHint{ .hint_key = mock_tree_info.public_data_tree,
376 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
377 .index = 0,
378 .path = mock_path },
379 GetSiblingPathHint{ .hint_key = mock_tree_info.public_data_tree,
380 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
381 .index = 1,
382 .path = mock_path },
383 GetSiblingPathHint{ .hint_key = mock_tree_info.public_data_tree,
384 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
385 .index = 2,
386 .path = mock_path } }));
387}
388
389TEST_F(MockedHintingDBsTest, GetLeafPreimageNullifierTree)
390{
391 // Mock some leaf values:
392 std::vector<NullifierLeafValue> nullifier_leaf_values = { { 1 }, { 2 }, { 4 } };
393 std::vector<IndexedLeaf<NullifierLeafValue>> nullifier_leaf_preimages = { { nullifier_leaf_values[0], 1, 6 },
394 { nullifier_leaf_values[1], 2, 4 },
395 { nullifier_leaf_values[2], 0, 3 } };
397 // get_leaf_preimage_nullifier_tree will call get_tree_roots and get_sibling_path (which itself will call
398 // get_tree_roots):
399 EXPECT_CALL(base_merkle_db, get_tree_roots).Times(static_cast<int>(nullifier_leaf_preimages.size() * 2));
400 EXPECT_CALL(base_merkle_db, get_sibling_path(world_state::MerkleTreeId::NULLIFIER_TREE, testing::_))
401 .WillRepeatedly([&](world_state::MerkleTreeId, index_t) { return mock_path; });
402 EXPECT_CALL(base_merkle_db, get_leaf_preimage_nullifier_tree(testing::_)).WillRepeatedly([&](index_t index) {
403 if (index < nullifier_leaf_preimages.size()) {
404 return nullifier_leaf_preimages[index];
405 }
406 throw std::runtime_error("Leaf preimage not found");
407 });
408
409 // Call the db:
410 for (index_t i = 0; i < nullifier_leaf_preimages.size(); i++) {
411 hinting_merkle_db.get_leaf_preimage_nullifier_tree(i);
412 }
413 ExecutionHints collected_hints;
414 hinting_merkle_db.dump_hints(collected_hints);
415
416 // Check the collected hints:
417 EXPECT_EQ(collected_hints.get_leaf_preimage_hints_nullifier_tree.size(), nullifier_leaf_preimages.size());
418 EXPECT_EQ(collected_hints.get_sibling_path_hints.size(), nullifier_leaf_preimages.size());
419 EXPECT_THAT(
420 collected_hints.get_leaf_preimage_hints_nullifier_tree,
421 testing::ElementsAreArray(
422 { GetLeafPreimageHint<NullifierTreeLeafPreimage>{
423 .hint_key = mock_tree_info.nullifier_tree, .index = 0, .leaf_preimage = nullifier_leaf_preimages[0] },
424 GetLeafPreimageHint<NullifierTreeLeafPreimage>{
425 .hint_key = mock_tree_info.nullifier_tree, .index = 1, .leaf_preimage = nullifier_leaf_preimages[1] },
426 GetLeafPreimageHint<NullifierTreeLeafPreimage>{ .hint_key = mock_tree_info.nullifier_tree,
427 .index = 2,
428 .leaf_preimage = nullifier_leaf_preimages[2] } }));
429 EXPECT_THAT(collected_hints.get_sibling_path_hints,
430 testing::ElementsAreArray({ GetSiblingPathHint{ .hint_key = mock_tree_info.nullifier_tree,
431 .tree_id = world_state::MerkleTreeId::NULLIFIER_TREE,
432 .index = 0,
433 .path = mock_path },
434 GetSiblingPathHint{ .hint_key = mock_tree_info.nullifier_tree,
435 .tree_id = world_state::MerkleTreeId::NULLIFIER_TREE,
436 .index = 1,
437 .path = mock_path },
438 GetSiblingPathHint{ .hint_key = mock_tree_info.nullifier_tree,
439 .tree_id = world_state::MerkleTreeId::NULLIFIER_TREE,
440 .index = 2,
441 .path = mock_path } }));
442}
443
444TEST_F(MockedHintingDBsTest, InsertIndexedLeavesPublicDataTree)
445{
446 AppendOnlyTreeSnapshot state_before = mock_tree_info.public_data_tree;
447 // Mock the leaf values:
448 PublicDataLeafValue public_leaf_value = { 4, 7 };
449 PublicDataLeafValue low_leaf_value = { 2, 6 };
451 AppendOnlyTreeSnapshot mock_state_after = { mock_tree_info.public_data_tree.root++,
452 mock_tree_info.public_data_tree.next_available_leaf_index++ };
453 LeafUpdateWitnessData<PublicDataLeafValue> mock_low_witness_data =
454 LeafUpdateWitnessData<PublicDataLeafValue>{ { low_leaf_value, 0, 0 }, 0, mock_path };
455 // insert_indexed_leaves_public_data_tree will call get_tree_roots and get_tree_info (which itself will call
456 // get_tree_roots):
457 EXPECT_CALL(base_merkle_db, get_tree_roots).Times(2);
458 EXPECT_CALL(base_merkle_db, insert_indexed_leaves_public_data_tree(testing::_))
459 .WillOnce([&](PublicDataLeafValue value) {
460 SequentialInsertionResult<PublicDataLeafValue> result = {
461 .low_leaf_witness_data = { mock_low_witness_data },
462 .insertion_witness_data = { { { value, 1, 6 }, 1, mock_path } }
463 };
464 mock_tree_info.public_data_tree = mock_state_after;
465 return result;
466 });
467
468 // Call the db:
469 hinting_merkle_db.insert_indexed_leaves_public_data_tree(public_leaf_value);
470 ExecutionHints collected_hints;
471 hinting_merkle_db.dump_hints(collected_hints);
472
473 // Check the collected hints:
474 EXPECT_EQ(collected_hints.sequential_insert_hints_public_data_tree.size(), 1);
475 EXPECT_THAT(collected_hints.sequential_insert_hints_public_data_tree,
476 testing::ElementsAre(SequentialInsertHint<PublicDataLeafValue>{
477 .hint_key = state_before,
478 .tree_id = world_state::MerkleTreeId::PUBLIC_DATA_TREE,
479 .leaf = public_leaf_value,
480 .low_leaves_witness_data = mock_low_witness_data,
481 .insertion_witness_data = { { public_leaf_value, 1, 6 }, 1, mock_path },
482 .state_after = mock_tree_info.public_data_tree }));
483}
484
485TEST_F(MockedHintingDBsTest, InsertIndexedLeavesNullifierTree)
486{
487 AppendOnlyTreeSnapshot state_before = mock_tree_info.nullifier_tree;
488 // Mock the leaf values:
490 NullifierLeafValue low_leaf_value = { 2 };
492 AppendOnlyTreeSnapshot mock_state_after = { mock_tree_info.nullifier_tree.root++,
493 mock_tree_info.nullifier_tree.next_available_leaf_index++ };
494 LeafUpdateWitnessData<NullifierLeafValue> mock_low_witness_data =
495 LeafUpdateWitnessData<NullifierLeafValue>{ { low_leaf_value, 0, 0 }, 0, mock_path };
496 // insert_indexed_leaves_nullifier_tree will call get_tree_roots and get_tree_info (which itself will call
497 // get_tree_roots):
498 EXPECT_CALL(base_merkle_db, get_tree_roots).Times(2);
499 EXPECT_CALL(base_merkle_db, insert_indexed_leaves_nullifier_tree(testing::_))
500 .WillOnce([&](NullifierLeafValue value) {
501 SequentialInsertionResult<NullifierLeafValue> result = { .low_leaf_witness_data = { mock_low_witness_data },
502 .insertion_witness_data = {
503 { { value, 1, 6 }, 1, mock_path } } };
504 mock_tree_info.nullifier_tree = mock_state_after;
505 return result;
506 });
507
508 // Call the db:
509 hinting_merkle_db.insert_indexed_leaves_nullifier_tree(nullifier);
510 ExecutionHints collected_hints;
511 hinting_merkle_db.dump_hints(collected_hints);
512
513 // Check the collected hints:
514 EXPECT_EQ(collected_hints.sequential_insert_hints_nullifier_tree.size(), 1);
515 EXPECT_THAT(collected_hints.sequential_insert_hints_nullifier_tree,
516 testing::ElementsAre(SequentialInsertHint<NullifierLeafValue>{
517 .hint_key = state_before,
518 .tree_id = world_state::MerkleTreeId::NULLIFIER_TREE,
519 .leaf = nullifier,
520 .low_leaves_witness_data = mock_low_witness_data,
521 .insertion_witness_data = { { nullifier, 1, 6 }, 1, mock_path },
522 .state_after = mock_tree_info.nullifier_tree }));
523}
524
525TEST_F(MockedHintingDBsTest, AppendLeaves)
526{
527 // Set initial state:
528 mock_tree_info.note_hash_tree = { 0, 0 };
529 // Mock the leaf values:
530 std::vector<FF> note_hash_leaf_values = { 11, 22, 44, 88 };
532 auto mock_append_internal = [&](world_state::MerkleTreeId, const FF&) -> AppendLeafResult {
533 auto this_path = mock_path;
534 auto this_index = mock_tree_info.note_hash_tree.next_available_leaf_index;
535 auto this_sibling =
536 this_index % 2 == 0 ? note_hash_leaf_values[this_index + 1] : note_hash_leaf_values[this_index - 1];
537 this_path[0] = this_sibling;
538 AppendLeafResult result = { mock_tree_info.note_hash_tree.root, mock_path };
539 mock_tree_info.note_hash_tree.next_available_leaf_index++;
540 mock_tree_info.note_hash_tree.root += 2;
541 return result;
542 };
543 auto int_state = mock_tree_info.note_hash_tree;
544 auto expected_end_state = mock_tree_info;
545 expected_end_state.note_hash_tree = { int_state.root + 2 * note_hash_leaf_values.size(),
546 int_state.next_available_leaf_index + note_hash_leaf_values.size() };
547 // append_leaves will call get_tree_info at the beginning and end of appending leaves:
548 EXPECT_CALL(base_merkle_db, get_tree_roots)
549 .WillOnce(testing::Return(mock_tree_info))
550 .WillOnce(testing::Return(expected_end_state));
551 EXPECT_CALL(base_merkle_db, append_leaves(world_state::MerkleTreeId::NOTE_HASH_TREE, testing::_))
552 .WillOnce([&](world_state::MerkleTreeId, std::span<const FF> leaves) {
554 for (const auto& leaf : leaves) {
555 results.push_back(mock_append_internal(world_state::MerkleTreeId::NOTE_HASH_TREE, leaf));
556 }
557 return results;
558 });
559
560 // Call the db:
561 hinting_merkle_db.append_leaves(world_state::MerkleTreeId::NOTE_HASH_TREE, note_hash_leaf_values);
562 ExecutionHints collected_hints;
563 hinting_merkle_db.dump_hints(collected_hints);
564
565 std::vector<AppendLeavesHint> expected_append_hints;
566 std::vector<GetSiblingPathHint> expected_sibling_hints;
567 for (const auto& leaf : note_hash_leaf_values) {
568 AppendOnlyTreeSnapshot state_after = { int_state.root + 2, int_state.next_available_leaf_index + 1 };
569 expected_append_hints.push_back(AppendLeavesHint{ .hint_key = int_state,
570 .state_after = state_after,
571 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
572 .leaves = { leaf } });
573 expected_sibling_hints.push_back(GetSiblingPathHint{ .hint_key = state_after,
574 .tree_id = world_state::MerkleTreeId::NOTE_HASH_TREE,
575 .index = int_state.next_available_leaf_index,
576 .path = mock_path });
577 int_state = state_after;
578 }
579
580 // Check the collected hints:
581 EXPECT_EQ(collected_hints.append_leaves_hints.size(), note_hash_leaf_values.size());
582 EXPECT_EQ(collected_hints.get_sibling_path_hints.size(), note_hash_leaf_values.size());
583 EXPECT_THAT(collected_hints.append_leaves_hints, testing::ElementsAreArray(expected_append_hints));
584 EXPECT_THAT(collected_hints.get_sibling_path_hints, testing::ElementsAreArray(expected_sibling_hints));
585}
586
587TEST_F(MockedHintingDBsTest, MerkleDBCheckpoints)
588{
589 uint32_t mock_checkpoint_id = 0;
590 EXPECT_CALL(base_merkle_db, get_checkpoint_id)
591 .WillOnce(testing::Return(mock_checkpoint_id))
592 .WillOnce(testing::Return(++mock_checkpoint_id))
593 .WillOnce(testing::Return(mock_checkpoint_id))
594 .WillOnce(testing::Return(++mock_checkpoint_id));
595 ;
596 EXPECT_CALL(base_merkle_db, create_checkpoint).Times(2);
597 // Call the db:
598 hinting_merkle_db.create_checkpoint();
599 hinting_merkle_db.create_checkpoint();
600
601 EXPECT_CALL(base_merkle_db, get_checkpoint_id)
602 .WillOnce(testing::Return(mock_checkpoint_id))
603 .WillOnce(testing::Return(--mock_checkpoint_id));
604 EXPECT_CALL(base_merkle_db, commit_checkpoint).Times(1);
605 hinting_merkle_db.commit_checkpoint();
606
607 // revert_checkpoint will call get_tree_roots before and after calling the underlying db::
608 EXPECT_CALL(base_merkle_db, get_tree_roots).Times(2);
609
610 EXPECT_CALL(base_merkle_db, get_checkpoint_id)
611 .WillOnce(testing::Return(mock_checkpoint_id))
612 .WillOnce(testing::Return(--mock_checkpoint_id));
613 EXPECT_CALL(base_merkle_db, revert_checkpoint).Times(1);
614 hinting_merkle_db.revert_checkpoint();
615 ExecutionHints collected_hints;
616 hinting_merkle_db.dump_hints(collected_hints);
617
618 // Check the collected hints:
619 EXPECT_EQ(collected_hints.create_checkpoint_hints.size(), 2);
620 EXPECT_EQ(collected_hints.commit_checkpoint_hints.size(), 1);
621 EXPECT_EQ(collected_hints.revert_checkpoint_hints.size(), 1);
622 mock_checkpoint_id = 0;
623 uint32_t mock_action_counter = 0;
624 EXPECT_THAT(collected_hints.create_checkpoint_hints,
625 testing::ElementsAreArray({ CreateCheckpointHint{
626 .action_counter = mock_action_counter++,
627 .old_checkpoint_id = mock_checkpoint_id,
628 .new_checkpoint_id = ++mock_checkpoint_id,
629 },
630 CreateCheckpointHint{
631 .action_counter = mock_action_counter++,
632 .old_checkpoint_id = mock_checkpoint_id,
633 .new_checkpoint_id = ++mock_checkpoint_id,
634 } }));
635 EXPECT_THAT(collected_hints.commit_checkpoint_hints,
636 testing::ElementsAre(CommitCheckpointHint{
637 .action_counter = mock_action_counter++,
638 .old_checkpoint_id = mock_checkpoint_id,
639 .new_checkpoint_id = --mock_checkpoint_id,
640 }));
641 EXPECT_THAT(collected_hints.revert_checkpoint_hints,
642 testing::ElementsAre(RevertCheckpointHint{
643 .action_counter = mock_action_counter++,
644 .old_checkpoint_id = mock_checkpoint_id,
645 .new_checkpoint_id = --mock_checkpoint_id,
646 .state_before = mock_tree_info,
647 .state_after = mock_tree_info,
648 }));
649}
650
651} // namespace
652} // namespace bb::avm2::simulation
std::shared_ptr< Napi::ThreadSafeFunction > instance
std::shared_ptr< Napi::ThreadSafeFunction > revert_checkpoint
std::shared_ptr< Napi::ThreadSafeFunction > commit_checkpoint
std::shared_ptr< Napi::ThreadSafeFunction > create_checkpoint
#define NULLIFIER_TREE_HEIGHT
#define NOTE_HASH_TREE_HEIGHT
#define PUBLIC_DATA_TREE_HEIGHT
std::optional< ContractInstance > get_contract_instance(const AztecAddress &address) const override
std::optional< ContractClass > get_contract_class(const ContractClassId &class_id) const override
std::optional< std::string > get_debug_function_name(const AztecAddress &address, const FunctionSelector &selector) const override
std::optional< FF > get_bytecode_commitment(const ContractClassId &class_id) const override
SiblingPath get_sibling_path(MerkleTreeId tree_id, index_t leaf_index) const override
std::optional< std::string > get_debug_function_name(const AztecAddress &address, const FunctionSelector &selector) const override
std::optional< ContractClass > get_contract_class(const ContractClassId &class_id) const override
std::optional< FF > get_bytecode_commitment(const ContractClassId &class_id) const override
void dump_hints(ExecutionHints &hints)
std::optional< ContractInstance > get_contract_instance(const AztecAddress &address) const override
void dump_hints(ExecutionHints &hints)
SiblingPath get_sibling_path(MerkleTreeId tree_id, index_t leaf_index) const override
GetLowIndexedLeafResponse get_low_indexed_leaf(MerkleTreeId tree_id, const FF &value) const override
auto & get_tree_info_helper(world_state::MerkleTreeId tree_id, auto &tree_roots)
Definition db_types.hpp:71
AvmProvingInputs inputs
HintingContractsDB hinting_contract_db
TreeSnapshots mock_tree_info
HintedRawMerkleDB base_merkle_db
HintingRawDB hinting_merkle_db
HintedRawContractDB base_contract_db
::bb::crypto::merkle_tree::fr_sibling_path SiblingPath
Definition db.hpp:36
::bb::world_state::MerkleTreeId MerkleTreeId
Definition db.hpp:35
fr_sibling_path get_sibling_path(TypeOfTree &tree, index_t index, bool includeUncommitted=true, bool expected_success=true)
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:185
std::vector< uint8_t > read_file(const std::string &filename, size_t bytes=0)
Definition file_io.hpp:29
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
TreeSnapshots starting_tree_roots
Definition avm_io.hpp:372
std::vector< GetSiblingPathHint > get_sibling_path_hints
Definition avm_io.hpp:373
std::vector< DebugFunctionNameHint > debug_function_names
Definition avm_io.hpp:367
std::vector< ContractDBCreateCheckpointHint > contract_db_create_checkpoint_hints
Definition avm_io.hpp:368
std::vector< ContractDBCommitCheckpointHint > contract_db_commit_checkpoint_hints
Definition avm_io.hpp:369
std::vector< CommitCheckpointHint > commit_checkpoint_hints
Definition avm_io.hpp:385
std::vector< CreateCheckpointHint > create_checkpoint_hints
Definition avm_io.hpp:384
std::vector< ContractInstanceHint > contract_instances
Definition avm_io.hpp:364
std::vector< ContractClassHint > contract_classes
Definition avm_io.hpp:365
std::vector< BytecodeCommitmentHint > bytecode_commitments
Definition avm_io.hpp:366