Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
public_data_tree_check.test.cpp
Go to the documentation of this file.
2
3#include <gmock/gmock.h>
4#include <gtest/gtest.h>
5
16
17namespace bb::avm2::simulation {
18
19using ::testing::_;
20using ::testing::ElementsAre;
21using ::testing::Return;
22using ::testing::StrictMock;
23
25
26namespace {
27
28TEST(AvmSimulationPublicDataTree, ReadExists)
29{
30 StrictMock<MockExecutionIdManager> execution_id_manager;
31 StrictMock<MockPoseidon2> poseidon2;
32 StrictMock<MockMerkleCheck> merkle_check;
33 StrictMock<MockFieldGreaterThan> field_gt;
34
35 EventEmitter<PublicDataTreeCheckEvent> event_emitter;
36 PublicDataTreeCheck public_data_tree_check(poseidon2, merkle_check, field_gt, execution_id_manager, event_emitter);
37
38 AztecAddress contract_address = 27;
39 FF slot = 42;
40 FF leaf_slot = unconstrained_compute_leaf_slot(contract_address, slot);
41
44 uint64_t low_leaf_index = 30;
45 std::vector<FF> sibling_path = { 1, 2, 3, 4, 5 };
46 AppendOnlyTreeSnapshot snapshot = {
47 .root = 123456,
48 .next_available_leaf_index = 128,
49 };
50 FF value = 27;
51
52 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
53 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) {
54 return RawPoseidon2::hash(input);
55 });
56 EXPECT_CALL(merkle_check,
57 assert_membership(DOM_SEP__PUBLIC_DATA_MERKLE, low_leaf_hash, low_leaf_index, _, snapshot.root))
58 .WillRepeatedly(Return());
59
60 public_data_tree_check.assert_read(slot, contract_address, value, low_leaf, low_leaf_index, sibling_path, snapshot);
61
62 PublicDataTreeReadWriteEvent expect_event = {
63 .contract_address = contract_address,
64 .slot = slot,
65 .value = value,
66 .leaf_slot = leaf_slot,
67 .prev_snapshot = snapshot,
68 .low_leaf_preimage = low_leaf,
69 .low_leaf_hash = low_leaf_hash,
70 .low_leaf_index = low_leaf_index,
71 };
72 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(expect_event));
73
74 // Negative test: wrong value
75 value = 28;
76 EXPECT_THROW_WITH_MESSAGE(public_data_tree_check.assert_read(
77 slot, contract_address, value, low_leaf, low_leaf_index, sibling_path, snapshot),
78 "Leaf value does not match value");
79}
80
81TEST(AvmSimulationPublicDataTree, ReadNotExistsLowPointsToInfinity)
82{
83 StrictMock<MockExecutionIdManager> execution_id_manager;
84 StrictMock<MockPoseidon2> poseidon2;
85 StrictMock<MockMerkleCheck> merkle_check;
86 StrictMock<MockFieldGreaterThan> field_gt;
87
88 EventEmitter<PublicDataTreeCheckEvent> event_emitter;
89 PublicDataTreeCheck public_data_tree_check(poseidon2, merkle_check, field_gt, execution_id_manager, event_emitter);
90
91 AztecAddress contract_address = 27;
92 FF slot = 42;
93 FF leaf_slot = unconstrained_compute_leaf_slot(contract_address, slot);
94
95 // Not the same slot
98 uint64_t low_leaf_index = 30;
99 std::vector<FF> sibling_path = { 1, 2, 3, 4, 5 };
100 AppendOnlyTreeSnapshot snapshot = {
101 .root = 123456,
102 .next_available_leaf_index = 128,
103 };
104 FF value = 0;
105
106 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
107 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) {
108 return RawPoseidon2::hash(input);
109 });
110 EXPECT_CALL(merkle_check,
111 assert_membership(DOM_SEP__PUBLIC_DATA_MERKLE, low_leaf_hash, low_leaf_index, _, snapshot.root))
112 .WillRepeatedly(Return());
113 EXPECT_CALL(field_gt, ff_gt(leaf_slot, low_leaf.leaf.slot)).WillRepeatedly(Return(true));
114
115 public_data_tree_check.assert_read(slot, contract_address, value, low_leaf, low_leaf_index, sibling_path, snapshot);
116 PublicDataTreeReadWriteEvent expect_event = {
117 .contract_address = contract_address,
118 .slot = slot,
119 .value = value,
120 .leaf_slot = leaf_slot,
121 .prev_snapshot = snapshot,
122 .low_leaf_preimage = low_leaf,
123 .low_leaf_hash = low_leaf_hash,
124 .low_leaf_index = low_leaf_index,
125 };
126 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(expect_event));
127
128 // Negative test: wrong value
129 value = 1;
130 EXPECT_THROW_WITH_MESSAGE(public_data_tree_check.assert_read(
131 slot, contract_address, value, low_leaf, low_leaf_index, sibling_path, snapshot),
132 "Value is nonzero for a non existing slot");
133
134 // Negative test: failed leaf_slot > low_leaf_preimage.value.slot
135 EXPECT_CALL(field_gt, ff_gt(leaf_slot, low_leaf.leaf.slot)).WillOnce(Return(false));
136 EXPECT_THROW_WITH_MESSAGE(public_data_tree_check.assert_read(
137 slot, contract_address, value, low_leaf, low_leaf_index, sibling_path, snapshot),
138 "Low leaf slot is GTE leaf slot");
139}
140
141TEST(AvmSimulationPublicDataTree, ReadNotExistsLowPointsToAnotherLeaf)
142{
143 StrictMock<MockExecutionIdManager> execution_id_manager;
144 StrictMock<MockPoseidon2> poseidon2;
145 StrictMock<MockMerkleCheck> merkle_check;
146 StrictMock<MockFieldGreaterThan> field_gt;
147
148 EventEmitter<PublicDataTreeCheckEvent> event_emitter;
149 PublicDataTreeCheck public_data_tree_check(poseidon2, merkle_check, field_gt, execution_id_manager, event_emitter);
150
151 AztecAddress contract_address = 27;
152 FF slot = 42;
153 FF leaf_slot = unconstrained_compute_leaf_slot(contract_address, slot);
154
157 uint64_t low_leaf_index = 30;
158 std::vector<FF> sibling_path = { 1, 2, 3, 4, 5 };
159 AppendOnlyTreeSnapshot snapshot = {
160 .root = 123456,
161 .next_available_leaf_index = 128,
162 };
163 FF value = 0;
164
165 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
166 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) {
167 return RawPoseidon2::hash(input);
168 });
169 EXPECT_CALL(merkle_check,
170 assert_membership(DOM_SEP__PUBLIC_DATA_MERKLE, low_leaf_hash, low_leaf_index, _, snapshot.root))
171 .WillRepeatedly(Return());
172 EXPECT_CALL(field_gt, ff_gt(leaf_slot, low_leaf.leaf.slot)).WillRepeatedly(Return(true));
173 EXPECT_CALL(field_gt, ff_gt(low_leaf.nextKey, leaf_slot)).WillRepeatedly(Return(true));
174
175 public_data_tree_check.assert_read(slot, contract_address, value, low_leaf, low_leaf_index, sibling_path, snapshot);
176 PublicDataTreeReadWriteEvent expect_event = {
177 .contract_address = contract_address,
178 .slot = slot,
179 .value = value,
180 .leaf_slot = leaf_slot,
181 .prev_snapshot = snapshot,
182 .low_leaf_preimage = low_leaf,
183 .low_leaf_hash = low_leaf_hash,
184 .low_leaf_index = low_leaf_index,
185 };
186 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(expect_event));
187
188 // Negative test: wrong value
189 value = 1;
190 EXPECT_THROW_WITH_MESSAGE(public_data_tree_check.assert_read(
191 slot, contract_address, value, low_leaf, low_leaf_index, sibling_path, snapshot),
192 "Value is nonzero for a non existing slot");
193
194 // Negative test: failed low_leaf_preimage.nextValue > leaf_slot
195 EXPECT_CALL(field_gt, ff_gt(low_leaf.nextKey, leaf_slot)).WillOnce(Return(false));
196 EXPECT_THROW_WITH_MESSAGE(public_data_tree_check.assert_read(
197 slot, contract_address, value, low_leaf, low_leaf_index, sibling_path, snapshot),
198 "Leaf slot is GTE low leaf next slot");
199}
200
201TEST(AvmSimulationPublicDataTree, WriteExists)
202{
203 StrictMock<MockExecutionIdManager> execution_id_manager;
204 StrictMock<MockPoseidon2> poseidon2;
205 StrictMock<MockMerkleCheck> merkle_check;
206 StrictMock<MockFieldGreaterThan> field_gt;
207
208 EventEmitter<PublicDataTreeCheckEvent> event_emitter;
209 PublicDataTreeCheck public_data_tree_check(poseidon2, merkle_check, field_gt, execution_id_manager, event_emitter);
210
211 AztecAddress contract_address = 27;
212 FF slot = 42;
213 std::vector<FF> leaf_slot_inputs = { DOM_SEP__PUBLIC_LEAF_SLOT, contract_address, slot };
214 FF leaf_slot = RawPoseidon2::hash(leaf_slot_inputs);
215 FF new_value = 27;
216
218
221 uint64_t low_leaf_index = 30;
222 public_data_tree.update_element(low_leaf_index, low_leaf_hash);
223
224 AppendOnlyTreeSnapshot prev_snapshot =
225 AppendOnlyTreeSnapshot{ .root = public_data_tree.root(), .next_available_leaf_index = 128 };
226 std::vector<FF> low_leaf_sibling_path = public_data_tree.get_sibling_path(low_leaf_index);
227
228 PublicDataTreeLeafPreimage updated_low_leaf = low_leaf;
229 updated_low_leaf.leaf.value = new_value;
230 FF updated_low_leaf_hash = RawPoseidon2::hash(updated_low_leaf.get_hash_inputs());
231 public_data_tree.update_element(low_leaf_index, updated_low_leaf_hash);
232
233 FF intermediate_root = public_data_tree.root();
234 std::vector<FF> insertion_sibling_path = public_data_tree.get_sibling_path(prev_snapshot.next_available_leaf_index);
235
236 // No insertion happens
237 AppendOnlyTreeSnapshot next_snapshot =
238 AppendOnlyTreeSnapshot{ .root = intermediate_root,
239 .next_available_leaf_index = prev_snapshot.next_available_leaf_index };
240
241 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
242 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) {
243 return RawPoseidon2::hash(input);
244 });
245
246 EXPECT_CALL(
247 merkle_check,
248 write(DOM_SEP__PUBLIC_DATA_MERKLE, low_leaf_hash, updated_low_leaf_hash, low_leaf_index, _, prev_snapshot.root))
249 .WillRepeatedly(Return(intermediate_root));
250
251 AppendOnlyTreeSnapshot result_snapshot = public_data_tree_check.write(slot,
252 contract_address,
253 new_value,
254 low_leaf,
255 low_leaf_index,
256 low_leaf_sibling_path,
257 prev_snapshot,
258 insertion_sibling_path,
259 false);
260
261 EXPECT_EQ(next_snapshot, result_snapshot);
262
263 PublicDataTreeReadWriteEvent expect_event = {
264 .contract_address = contract_address,
265 .slot = slot,
266 .value = new_value,
267 .leaf_slot = leaf_slot,
268 .prev_snapshot = prev_snapshot,
269 .low_leaf_preimage = low_leaf,
270 .low_leaf_hash = low_leaf_hash,
271 .low_leaf_index = low_leaf_index,
272 .write_data = PublicDataWriteData{ .updated_low_leaf_preimage = updated_low_leaf,
273 .updated_low_leaf_hash = updated_low_leaf_hash,
274 .new_leaf_hash = 0,
275 .intermediate_root = intermediate_root,
276 .next_snapshot = next_snapshot },
277 .execution_id = 1,
278 };
279 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(expect_event));
280}
281
282TEST(AvmSimulationPublicDataTree, WriteAndUpdate)
283{
284 StrictMock<MockExecutionIdManager> execution_id_manager;
285 StrictMock<MockPoseidon2> poseidon2;
286 StrictMock<MockMerkleCheck> merkle_check;
287 StrictMock<MockFieldGreaterThan> field_gt;
288
289 EventEmitter<PublicDataTreeCheckEvent> event_emitter;
290 PublicDataTreeCheck public_data_tree_check(poseidon2, merkle_check, field_gt, execution_id_manager, event_emitter);
291
292 AztecAddress contract_address = 27;
293 FF slot = 42;
294 FF leaf_slot = unconstrained_compute_leaf_slot(contract_address, slot);
295 FF new_value = 27;
296 FF low_leaf_slot = 40;
297
299
302 uint64_t low_leaf_index = 30;
303 public_data_tree.update_element(low_leaf_index, low_leaf_hash);
304
305 AppendOnlyTreeSnapshot prev_snapshot =
306 AppendOnlyTreeSnapshot{ .root = public_data_tree.root(), .next_available_leaf_index = 128 };
307 std::vector<FF> low_leaf_sibling_path = public_data_tree.get_sibling_path(low_leaf_index);
308
309 PublicDataTreeLeafPreimage updated_low_leaf = low_leaf;
310 updated_low_leaf.nextIndex = prev_snapshot.next_available_leaf_index;
311 updated_low_leaf.nextKey = leaf_slot;
312 FF updated_low_leaf_hash = RawPoseidon2::hash(updated_low_leaf.get_hash_inputs());
313 public_data_tree.update_element(low_leaf_index, updated_low_leaf_hash);
314
315 FF intermediate_root = public_data_tree.root();
316 std::vector<FF> insertion_sibling_path = public_data_tree.get_sibling_path(prev_snapshot.next_available_leaf_index);
317
319 PublicDataTreeLeafPreimage(PublicDataLeafValue(leaf_slot, new_value), low_leaf.nextIndex, low_leaf.nextKey);
320 FF new_leaf_hash = RawPoseidon2::hash(new_leaf.get_hash_inputs());
321 public_data_tree.update_element(prev_snapshot.next_available_leaf_index, new_leaf_hash);
322
323 AppendOnlyTreeSnapshot next_snapshot =
324 AppendOnlyTreeSnapshot{ .root = public_data_tree.root(),
325 .next_available_leaf_index = prev_snapshot.next_available_leaf_index + 1 };
326
327 uint32_t execution_id = 1;
328 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly([&]() { return execution_id++; });
329
330 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) {
331 return RawPoseidon2::hash(input);
332 });
333 EXPECT_CALL(
334 merkle_check,
335 write(DOM_SEP__PUBLIC_DATA_MERKLE, low_leaf_hash, updated_low_leaf_hash, low_leaf_index, _, prev_snapshot.root))
336 .WillRepeatedly(Return(intermediate_root));
337 EXPECT_CALL(field_gt, ff_gt(leaf_slot, low_leaf.leaf.slot)).WillRepeatedly(Return(true));
338 EXPECT_CALL(merkle_check,
340 FF(0),
341 new_leaf_hash,
342 prev_snapshot.next_available_leaf_index,
343 _,
344 intermediate_root))
345 .WillRepeatedly(Return(next_snapshot.root));
346
347 AppendOnlyTreeSnapshot snapshot_after_write = public_data_tree_check.write(slot,
348 contract_address,
349 new_value,
350 low_leaf,
351 low_leaf_index,
352 low_leaf_sibling_path,
353 prev_snapshot,
354 insertion_sibling_path,
355 false);
356
357 EXPECT_EQ(next_snapshot, snapshot_after_write);
358
359 PublicDataTreeReadWriteEvent write_event = {
360 .contract_address = contract_address,
361 .slot = slot,
362 .value = new_value,
363 .leaf_slot = leaf_slot,
364 .prev_snapshot = prev_snapshot,
365 .low_leaf_preimage = low_leaf,
366 .low_leaf_hash = low_leaf_hash,
367 .low_leaf_index = low_leaf_index,
368 .write_data = PublicDataWriteData{ .updated_low_leaf_preimage = updated_low_leaf,
369 .updated_low_leaf_hash = updated_low_leaf_hash,
370 .new_leaf_hash = new_leaf_hash,
371 .intermediate_root = intermediate_root,
372 .next_snapshot = next_snapshot },
373 .execution_id = 1,
374 };
375
376 low_leaf_index = prev_snapshot.next_available_leaf_index;
377 prev_snapshot = snapshot_after_write;
378
379 low_leaf = PublicDataTreeLeafPreimage(PublicDataLeafValue(leaf_slot, new_value), 0, 0);
381 public_data_tree.update_element(low_leaf_index, low_leaf_hash);
382 low_leaf_sibling_path = public_data_tree.get_sibling_path(low_leaf_index);
383
384 new_value = 28;
385
386 updated_low_leaf = low_leaf;
387 updated_low_leaf.leaf.value = new_value;
388 updated_low_leaf_hash = RawPoseidon2::hash(updated_low_leaf.get_hash_inputs());
389 public_data_tree.update_element(low_leaf_index, updated_low_leaf_hash);
390
391 intermediate_root = public_data_tree.root();
392 insertion_sibling_path = public_data_tree.get_sibling_path(prev_snapshot.next_available_leaf_index);
393
394 // No insertion happens
395 next_snapshot = AppendOnlyTreeSnapshot{ .root = intermediate_root,
396 .next_available_leaf_index = prev_snapshot.next_available_leaf_index };
397
398 EXPECT_CALL(
399 merkle_check,
400 write(DOM_SEP__PUBLIC_DATA_MERKLE, low_leaf_hash, updated_low_leaf_hash, low_leaf_index, _, prev_snapshot.root))
401 .WillRepeatedly(Return(intermediate_root));
402 AppendOnlyTreeSnapshot snapshot_after_update = public_data_tree_check.write(slot,
403 contract_address,
404 new_value,
405 low_leaf,
406 low_leaf_index,
407 low_leaf_sibling_path,
408 prev_snapshot,
409 insertion_sibling_path,
410 true);
411
412 EXPECT_EQ(next_snapshot, snapshot_after_update);
413
414 PublicDataTreeReadWriteEvent update_event = {
415 .contract_address = contract_address,
416 .slot = slot,
417 .value = new_value,
418 .leaf_slot = leaf_slot,
419 .prev_snapshot = prev_snapshot,
420 .low_leaf_preimage = low_leaf,
421 .low_leaf_hash = low_leaf_hash,
422 .low_leaf_index = low_leaf_index,
423 .write_data = PublicDataWriteData{ .updated_low_leaf_preimage = updated_low_leaf,
424 .updated_low_leaf_hash = updated_low_leaf_hash,
425 .new_leaf_hash = 0,
426 .intermediate_root = intermediate_root,
427 .next_snapshot = next_snapshot },
428 .execution_id = std::numeric_limits<uint32_t>::max(),
429 };
430
431 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(write_event, update_event));
432}
433
434} // namespace
435
436} // namespace bb::avm2::simulation
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
#define DOM_SEP__PUBLIC_LEAF_SLOT
#define DOM_SEP__PUBLIC_DATA_MERKLE
FieldGreaterThan field_gt
MerkleCheck merkle_check
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
ExecutionIdManager execution_id_manager
EventEmitter< DataCopyEvent > event_emitter
IndexedTreeLeafData low_leaf
AVM range check gadget for witness generation.
crypto::Poseidon2< crypto::Poseidon2Bn254ScalarFieldParams > poseidon2
IndexedLeaf< PublicDataLeafValue > PublicDataTreeLeafPreimage
::bb::crypto::merkle_tree::PublicDataLeafValue PublicDataLeafValue
Definition db.hpp:38
FF unconstrained_compute_leaf_slot(const AztecAddress &contract_address, const FF &slot)
Definition merkle.cpp:30
AvmFlavorSettings::FF FF
Definition field.hpp:10
void write(B &buf, field2< base_field, Params > const &value)
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)