Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
tx_execution.test.cpp
Go to the documentation of this file.
14
15#include <gmock/gmock.h>
16#include <gtest/gtest.h>
17
18namespace bb::avm2::simulation {
19namespace {
20
21using ::testing::_;
22using ::testing::NiceMock;
23using ::testing::Return;
24using ::testing::ReturnRef;
25
26class TxExecutionTest : public ::testing::Test {
27 protected:
28 TxExecutionTest() = default;
29
30 NiceMock<MockContextProvider> context_provider;
31 EventEmitter<TxEvent> tx_event_emitter;
32 NiceMock<MockContractDB> contract_db;
33 NiceMock<MockHighLevelMerkleDB> merkle_db;
34 NiceMock<MockExecution> execution;
35 NiceMock<MockFieldGreaterThan> field_gt;
36 NiceMock<MockPoseidon2> poseidon2;
37 NiceMock<MockWrittenPublicDataSlotsTreeCheck> written_public_data_slots_tree_check;
38 NiceMock<MockRetrievedBytecodesTreeCheck> retrieved_bytecodes_tree_check;
39 SideEffectTracker side_effect_tracker; // Using the real thing.
40 NoopCallStackMetadataCollector call_stack_metadata_collector;
41 TxExecution tx_execution = TxExecution(execution,
42 context_provider,
43 contract_db,
44 merkle_db,
45 written_public_data_slots_tree_check,
46 retrieved_bytecodes_tree_check,
47 side_effect_tracker,
48 field_gt,
50 call_stack_metadata_collector,
51 tx_event_emitter);
52};
53
54TEST_F(TxExecutionTest, simulateTx)
55{
56 // Create a mock transaction
57 Tx tx = {
58 .hash = "0x1234567890abcdef",
59 .non_revertible_accumulated_data =
60 AccumulatedData{
61 .note_hashes = testing::random_fields(5),
62 .nullifiers = testing::random_fields(6),
63 .l2_to_l1_messages = testing::random_l2_to_l1_messages(2),
64 },
65 .revertible_accumulated_data =
66 AccumulatedData{
67 .note_hashes = testing::random_fields(5),
68 .nullifiers = testing::random_fields(2),
69 .l2_to_l1_messages = testing::random_l2_to_l1_messages(2),
70 },
71 .setup_enqueued_calls = testing::random_enqueued_calls(1),
72 .app_logic_enqueued_calls = testing::random_enqueued_calls(1),
73 .teardown_enqueued_call = testing::random_enqueued_calls(1)[0],
74 .fee_payer = FF::random_element(),
75 };
76
77 AppendOnlyTreeSnapshot dummy_snapshot = {
78 .root = 0,
79 .next_available_leaf_index = 0,
80 };
81 TreeStates tree_state = {
82 .note_hash_tree = { .tree = dummy_snapshot, .counter = 0 },
83 .nullifier_tree = { .tree = dummy_snapshot, .counter = 0 },
84 .l1_to_l2_message_tree = { .tree = dummy_snapshot, .counter = 0 },
85 .public_data_tree = { .tree = dummy_snapshot, .counter = 0 },
86 };
87 ON_CALL(merkle_db, get_tree_state()).WillByDefault([&]() { return tree_state; });
88 ON_CALL(merkle_db, siloed_nullifier_write(_)).WillByDefault(Return());
89 // Number of Enqueued Calls in the transaction : 1 setup, 1 app logic, and 1 teardown
90
91 auto setup_context = std::make_unique<NiceMock<MockContext>>();
92 ON_CALL(*setup_context, halted()).WillByDefault(Return(true)); // dont do any actual
93
94 auto app_logic_context = std::make_unique<NiceMock<MockContext>>();
95 ON_CALL(*app_logic_context, halted()).WillByDefault(Return(true));
96
97 auto teardown_context = std::make_unique<NiceMock<MockContext>>();
98 ON_CALL(*teardown_context, halted()).WillByDefault(Return(true));
99
100 // Configure mock execution to return successful results
101 EnqueuedCallResult successful_result = {
102 .success = true, // This is the key - mark execution as successful
103 .gas_used = Gas{ 100, 100 },
104
105 };
106
107 ON_CALL(execution, execute(_)).WillByDefault(Return(successful_result));
108
109 EXPECT_CALL(context_provider, make_enqueued_context)
110 .WillOnce(Return(std::move(setup_context)))
111 .WillOnce(Return(std::move(app_logic_context)))
112 .WillOnce(Return(std::move(teardown_context)));
113 EXPECT_CALL(merkle_db, create_checkpoint()).Times(1);
114
115 EXPECT_CALL(merkle_db, pad_trees()).Times(1);
116
118
119 // Check the event counts
120 bool has_startup_event = false;
121 auto expected_private_append_tree_events =
122 tx.non_revertible_accumulated_data.note_hashes.size() + tx.non_revertible_accumulated_data.nullifiers.size() +
123 tx.revertible_accumulated_data.note_hashes.size() + tx.revertible_accumulated_data.nullifiers.size();
124 auto actual_private_append_tree_events = 0;
125
126 auto expected_l2_l1_msg_events = tx.non_revertible_accumulated_data.l2_to_l1_messages.size() +
127 tx.revertible_accumulated_data.l2_to_l1_messages.size();
128 auto actual_l2_l1_msg_events = 0;
129
130 auto expected_public_call_events = 3; // setup, app logic, teardown
131 auto actual_public_call_events = 0;
132
133 bool has_collect_fee_event = false;
134
135 // Get PrivateAppendTreeEvent from tx event dump events
136 auto events = tx_event_emitter.get_events();
137 for (const auto& tx_event : events) {
139 has_startup_event = true;
140 continue;
141 }
142 auto event = std::get<TxPhaseEvent>(tx_event).event;
144 actual_private_append_tree_events++;
145 }
147 actual_l2_l1_msg_events++;
148 }
150 actual_public_call_events++;
151 }
153 has_collect_fee_event = true;
154 }
155 }
156
157 EXPECT_TRUE(has_startup_event);
158 EXPECT_EQ(actual_private_append_tree_events, expected_private_append_tree_events);
159 EXPECT_EQ(expected_l2_l1_msg_events, actual_l2_l1_msg_events);
160 EXPECT_EQ(expected_public_call_events, actual_public_call_events);
161 EXPECT_TRUE(has_collect_fee_event);
162}
163
164TEST_F(TxExecutionTest, NoteHashLimitReached)
165{
166 // Create a mock transaction
167 Tx tx = {
168 .hash = "0x1234567890abcdef",
169 .non_revertible_accumulated_data =
170 AccumulatedData{
172 .nullifiers = testing::random_fields(1),
173 },
174 .revertible_accumulated_data =
175 AccumulatedData{
176 .note_hashes = testing::random_fields(1),
177 },
178 .app_logic_enqueued_calls = testing::random_enqueued_calls(1),
179 .fee_payer = FF::random_element(),
180 };
181
182 AppendOnlyTreeSnapshot dummy_snapshot = {
183 .root = 0,
184 .next_available_leaf_index = 0,
185 };
186 TreeStates tree_state = {
187 .note_hash_tree = { .tree = dummy_snapshot, .counter = 0 },
188 .nullifier_tree = { .tree = dummy_snapshot, .counter = 0 },
189 .l1_to_l2_message_tree = { .tree = dummy_snapshot, .counter = 0 },
190 .public_data_tree = { .tree = dummy_snapshot, .counter = 0 },
191 };
192 ON_CALL(merkle_db, get_tree_state()).WillByDefault([&]() { return tree_state; });
193 ON_CALL(merkle_db, siloed_nullifier_write(_)).WillByDefault([&](const auto& /*nullifier*/) {
194 tree_state.nullifier_tree.counter++;
195 });
196 ON_CALL(merkle_db, siloed_note_hash_write(_)).WillByDefault([&](const auto& /*note_hash*/) {
197 tree_state.note_hash_tree.counter++;
198 return true;
199 });
200 ON_CALL(merkle_db, unique_note_hash_write(_)).WillByDefault([&](const auto& /*note_hash*/) {
201 tree_state.note_hash_tree.counter++;
202 return true;
203 });
204
205 EXPECT_CALL(merkle_db, create_checkpoint()).Times(2); // once at start, once after app-logic revert
206
208
209 // Check the event counts
210 bool has_startup_event = false;
211 auto expected_private_append_tree_events =
212 tx.non_revertible_accumulated_data.note_hashes.size() + tx.non_revertible_accumulated_data.nullifiers.size() +
213 tx.revertible_accumulated_data.note_hashes.size() + tx.revertible_accumulated_data.nullifiers.size();
214 auto actual_private_append_tree_events = 0;
215
216 auto expected_l2_l1_msg_events = tx.non_revertible_accumulated_data.l2_to_l1_messages.size() +
217 tx.revertible_accumulated_data.l2_to_l1_messages.size();
218 auto actual_l2_l1_msg_events = 0;
219
220 auto expected_public_call_events = 0; // None, since we revert before the public call
221 auto actual_public_call_events = 0;
222 auto reverts = 0;
223
224 bool has_collect_fee_event = false;
225
226 // Get PrivateAppendTreeEvent from tx event dump events
227 auto events = tx_event_emitter.get_events();
228 for (const auto& tx_event : events) {
230 has_startup_event = true;
231 continue;
232 }
233 TxPhaseEvent phase_event = std::get<TxPhaseEvent>(tx_event);
234 if (phase_event.reverted) {
235 reverts++;
236 }
237 auto event = phase_event.event;
239 actual_private_append_tree_events++;
240 }
242 actual_l2_l1_msg_events++;
243 }
245 actual_public_call_events++;
246 }
248 has_collect_fee_event = true;
249 }
250 }
251
252 EXPECT_TRUE(has_startup_event);
253 EXPECT_EQ(actual_private_append_tree_events, expected_private_append_tree_events);
254 EXPECT_EQ(expected_l2_l1_msg_events, actual_l2_l1_msg_events);
255 EXPECT_EQ(expected_public_call_events, actual_public_call_events);
256 EXPECT_TRUE(has_collect_fee_event);
257 EXPECT_EQ(reverts, 1);
258}
259
260TEST_F(TxExecutionTest, NullifierLimitReached)
261{
262 // Create a mock transaction
263 Tx tx = {
264 .hash = "0x1234567890abcdef",
265 .non_revertible_accumulated_data =
266 AccumulatedData{
268 },
269 .revertible_accumulated_data =
270 AccumulatedData{
271 .nullifiers = testing::random_fields(1),
272 },
273 .app_logic_enqueued_calls = testing::random_enqueued_calls(1),
274 .fee_payer = FF::random_element(),
275 };
276
277 AppendOnlyTreeSnapshot dummy_snapshot = {
278 .root = 0,
279 .next_available_leaf_index = 0,
280 };
281 TreeStates tree_state = {
282 .note_hash_tree = { .tree = dummy_snapshot, .counter = 0 },
283 .nullifier_tree = { .tree = dummy_snapshot, .counter = 0 },
284 .l1_to_l2_message_tree = { .tree = dummy_snapshot, .counter = 0 },
285 .public_data_tree = { .tree = dummy_snapshot, .counter = 0 },
286 };
287 ON_CALL(merkle_db, get_tree_state()).WillByDefault([&]() { return tree_state; });
288 ON_CALL(merkle_db, siloed_nullifier_write(_)).WillByDefault([&](const auto& /*nullifier*/) {
289 tree_state.nullifier_tree.counter++;
290 return true;
291 });
292 ON_CALL(merkle_db, siloed_note_hash_write(_)).WillByDefault([&](const auto& /*note_hash*/) {
293 tree_state.note_hash_tree.counter++;
294 return true;
295 });
296 ON_CALL(merkle_db, unique_note_hash_write(_)).WillByDefault([&](const auto& /*note_hash*/) {
297 tree_state.note_hash_tree.counter++;
298 return true;
299 });
300
301 EXPECT_CALL(merkle_db, create_checkpoint()).Times(2); // once at start, once after app-logic revert
302
304
305 // Check the event counts
306 bool has_startup_event = false;
307 auto expected_private_append_tree_events =
308 tx.non_revertible_accumulated_data.note_hashes.size() + tx.non_revertible_accumulated_data.nullifiers.size() +
309 tx.revertible_accumulated_data.note_hashes.size() + tx.revertible_accumulated_data.nullifiers.size();
310 auto actual_private_append_tree_events = 0;
311
312 auto expected_l2_l1_msg_events = tx.non_revertible_accumulated_data.l2_to_l1_messages.size() +
313 tx.revertible_accumulated_data.l2_to_l1_messages.size();
314 auto actual_l2_l1_msg_events = 0;
315
316 auto expected_public_call_events = 0; // None, since we revert before the public call
317 auto actual_public_call_events = 0;
318 auto reverts = 0;
319
320 bool has_collect_fee_event = false;
321
322 // Get PrivateAppendTreeEvent from tx event dump events
323 auto events = tx_event_emitter.get_events();
324 for (const auto& tx_event : events) {
326 has_startup_event = true;
327 continue;
328 }
329 TxPhaseEvent phase_event = std::get<TxPhaseEvent>(tx_event);
330 if (phase_event.reverted) {
331 reverts++;
332 }
333 auto event = phase_event.event;
335 actual_private_append_tree_events++;
336 }
338 actual_l2_l1_msg_events++;
339 }
341 actual_public_call_events++;
342 }
344 has_collect_fee_event = true;
345 }
346 }
347
348 EXPECT_TRUE(has_startup_event);
349 EXPECT_EQ(actual_private_append_tree_events, expected_private_append_tree_events);
350 EXPECT_EQ(expected_l2_l1_msg_events, actual_l2_l1_msg_events);
351 EXPECT_EQ(expected_public_call_events, actual_public_call_events);
352 EXPECT_TRUE(has_collect_fee_event);
353 EXPECT_EQ(reverts, 1);
354}
355
356TEST_F(TxExecutionTest, L2ToL1MessageLimitReached)
357{
358 // Create a mock transaction
359 Tx tx = {
360 .hash = "0x1234567890abcdef",
361 .non_revertible_accumulated_data =
362 AccumulatedData{
363 .nullifiers = testing::random_fields(1),
365 },
366 .revertible_accumulated_data =
367 AccumulatedData{
368 .l2_to_l1_messages = testing::random_l2_to_l1_messages(1),
369 },
370 .app_logic_enqueued_calls = testing::random_enqueued_calls(1),
371 .fee_payer = FF::random_element(),
372 };
373
374 AppendOnlyTreeSnapshot dummy_snapshot = {
375 .root = 0,
376 .next_available_leaf_index = 0,
377 };
378 TreeStates tree_state = {
379 .note_hash_tree = { .tree = dummy_snapshot, .counter = 0 },
380 .nullifier_tree = { .tree = dummy_snapshot, .counter = 0 },
381 .l1_to_l2_message_tree = { .tree = dummy_snapshot, .counter = 0 },
382 .public_data_tree = { .tree = dummy_snapshot, .counter = 0 },
383 };
384 ON_CALL(merkle_db, get_tree_state()).WillByDefault([&]() { return tree_state; });
385 ON_CALL(merkle_db, siloed_nullifier_write(_)).WillByDefault([&](const auto& /*nullifier*/) {
386 tree_state.nullifier_tree.counter++;
387 return true;
388 });
389 ON_CALL(merkle_db, siloed_note_hash_write(_)).WillByDefault([&](const auto& /*note_hash*/) {
390 tree_state.note_hash_tree.counter++;
391 return true;
392 });
393 ON_CALL(merkle_db, unique_note_hash_write(_)).WillByDefault([&](const auto& /*note_hash*/) {
394 tree_state.note_hash_tree.counter++;
395 return true;
396 });
397
398 EXPECT_CALL(merkle_db, create_checkpoint()).Times(2); // once at start, once after app-logic revert
399
401
402 // Check the event counts
403 bool has_startup_event = false;
404 auto expected_private_append_tree_events =
405 tx.non_revertible_accumulated_data.note_hashes.size() + tx.non_revertible_accumulated_data.nullifiers.size() +
406 tx.revertible_accumulated_data.note_hashes.size() + tx.revertible_accumulated_data.nullifiers.size();
407 auto actual_private_append_tree_events = 0;
408
409 auto expected_l2_l1_msg_events = tx.non_revertible_accumulated_data.l2_to_l1_messages.size() +
410 tx.revertible_accumulated_data.l2_to_l1_messages.size();
411 auto actual_l2_l1_msg_events = 0;
412
413 auto expected_public_call_events = 0; // None, since we revert before the public call
414 auto actual_public_call_events = 0;
415 auto reverts = 0;
416
417 bool has_collect_fee_event = false;
418
419 // Get PrivateAppendTreeEvent from tx event dump events
420 auto events = tx_event_emitter.get_events();
421 for (const auto& tx_event : events) {
423 has_startup_event = true;
424 continue;
425 }
426 TxPhaseEvent phase_event = std::get<TxPhaseEvent>(tx_event);
427 if (phase_event.reverted) {
428 reverts++;
429 }
430 auto event = phase_event.event;
432 actual_private_append_tree_events++;
433 }
435 actual_l2_l1_msg_events++;
436 }
438 actual_public_call_events++;
439 }
441 has_collect_fee_event = true;
442 }
443 }
444
445 EXPECT_TRUE(has_startup_event);
446 EXPECT_EQ(actual_private_append_tree_events, expected_private_append_tree_events);
447 EXPECT_EQ(expected_l2_l1_msg_events, actual_l2_l1_msg_events);
448 EXPECT_EQ(expected_public_call_events, actual_public_call_events);
449 EXPECT_TRUE(has_collect_fee_event);
450 EXPECT_EQ(reverts, 1);
451}
452
453} // namespace
454} // namespace bb::avm2::simulation
FieldGreaterThan field_gt
std::shared_ptr< Napi::ThreadSafeFunction > create_checkpoint
#define MAX_L2_TO_L1_MSGS_PER_TX
#define MAX_NOTE_HASHES_PER_TX
StrictMock< MockHighLevelMerkleDB > merkle_db
StrictMock< MockRetrievedBytecodesTreeCheck > retrieved_bytecodes_tree_check
StrictMock< MockContractDB > contract_db
TxExecutionResult simulate(const Tx &tx)
Simulates the entire transaction execution phases.
std::vector< PublicCallRequestWithCalldata > random_enqueued_calls(size_t n)
Definition fixtures.cpp:60
std::vector< ScopedL2ToL1Message > random_l2_to_l1_messages(size_t n)
Definition fixtures.cpp:43
std::vector< FF > random_fields(size_t n)
Definition fixtures.cpp:23
CommandResponse execute(BBApiRequest &request, Command &&command)
Executes a command by visiting a variant of all possible commands.
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:185
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
simulation::PublicDataTreeReadWriteEvent event
static field random_element(numeric::RNG *engine=nullptr) noexcept
SideEffectTracker side_effect_tracker
TxExecution tx_execution
NiceMock< MockContextProvider > context_provider
NiceMock< MockExecution > execution
NoopCallStackMetadataCollector call_stack_metadata_collector
EventEmitter< TxEvent > tx_event_emitter
NiceMock< MockWrittenPublicDataSlotsTreeCheck > written_public_data_slots_tree_check