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