Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
tx.test.cpp
Go to the documentation of this file.
1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
3
4#include <cstdint>
5#include <vector>
6
28
29namespace bb::avm2::constraining {
30namespace {
31
32using tracegen::TestTraceContainer;
33using tracegen::TxTraceBuilder;
35using C = Column;
36using tx = bb::avm2::tx<FF>;
37
38TEST(TxExecutionConstrainingTest, NegativeEmptyTrace)
39{
40 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(testing::empty_trace()), "SEL_ON_FIRST_ROW");
41}
42
43TEST(TxExecutionConstrainingTest, NegativeEarlyEnd)
44{
45 TestTraceContainer trace({
46 {
47 // Row 0
48 { C::precomputed_first_row, 1 },
49 },
50 {
51 // Row 1
52 { C::tx_sel, 1 },
53 },
54 });
55 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_NO_EARLY_END), "NO_EARLY_END");
56}
57
58TEST(TxExecutionConstrainingTest, NegativeNoExtraneousRows)
59{
60 TestTraceContainer trace({
61 {
62 // Row 0
63 { C::precomputed_first_row, 1 },
64 },
65 {
66 // Row 1
67 { C::tx_sel, 0 },
68 },
69 {
70 // Row 2
71 { C::tx_sel, 1 },
72 },
73 });
74 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_NO_EXTRANEOUS_ROWS), "NO_EXTRANEOUS_ROWS");
75}
76
77class TxExecutionConstrainingTestHelper : public ::testing::Test {
78 public:
79 tracegen::PrecomputedTraceBuilder precomputed_builder;
80 tracegen::PublicInputsTraceBuilder public_inputs_builder;
81 static void set_initial_columns(TestTraceContainer& trace,
82 const std::vector<PublicCallRequest>& setup_call_requests,
83 const std::vector<PublicCallRequest>& app_logic_call_requests)
84 {
85 uint32_t row = 0;
86 // Prepended first row:
87 trace.set(row++, { { { C::precomputed_clk, 0 }, { C::precomputed_first_row, 1 } } });
88 // Nullifer, note, and message insertion:
89 trace.set(
90 row++,
91 { {
92 { C::tx_sel, 1 },
93 { C::tx_start_tx, 1 },
94 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NULLIFIER_INSERTION) },
95 { C::tx_is_padded, 1 },
96 { C::tx_is_tree_insert_phase, 1 },
97 { C::tx_sel_non_revertible_append_nullifier, 1 },
98 { C::tx_sel_can_emit_nullifier, 1 },
99
101 { C::tx_sel_read_phase_length, 1 },
102 { C::tx_read_pi_length_offset,
104
105 { C::tx_start_phase, 1 },
106 { C::tx_end_phase, 1 },
107 { C::tx_next_context_id, 1 },
108 } });
109 trace.set(row++,
110 { {
111 { C::tx_sel, 1 },
112 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NOTE_INSERTION) },
113 { C::tx_is_padded, 1 },
114 { C::tx_is_tree_insert_phase, 1 },
115 { C::tx_sel_non_revertible_append_note_hash, 1 },
116 { C::tx_sel_can_emit_note_hash, 1 },
117 { C::tx_read_pi_offset,
119 { C::tx_sel_read_phase_length, 1 },
120 { C::tx_read_pi_length_offset,
122 { C::tx_start_phase, 1 },
123 { C::tx_end_phase, 1 },
124 { C::tx_next_context_id, 1 },
125 } });
126 trace.set(
127 row++,
128 { {
129 { C::tx_sel, 1 },
130 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_L2_TO_L1_MESSAGE) },
131 { C::tx_is_padded, 1 },
132 { C::tx_sel_non_revertible_append_l2_l1_msg, 1 },
133 { C::tx_sel_can_emit_l2_l1_msg, 1 },
134
135 { C::tx_read_pi_offset,
137 { C::tx_sel_read_phase_length, 1 },
138 { C::tx_read_pi_length_offset,
141
142 { C::tx_start_phase, 1 },
143 { C::tx_end_phase, 1 },
144 { C::tx_next_context_id, 1 },
145 } });
146 // Setup calls:
147 auto calls_remaining = setup_call_requests.size();
149 for (auto setup_call : setup_call_requests) {
150 bool is_first_call = calls_remaining == setup_call_requests.size();
151 trace.set(
152 row++,
153 { {
154 { C::tx_sel, 1 },
155 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::SETUP) },
156 { C::tx_start_phase, is_first_call ? 1 : 0 },
157 { C::tx_end_phase, calls_remaining == 1 ? 1 : 0 },
158 { C::tx_sel_read_phase_length, is_first_call ? 1 : 0 },
159 // Lookup Precomputed Table Values
160 { C::tx_is_public_call_request, 1 },
161 { C::tx_should_process_call_request, 1 },
162 { C::tx_read_pi_offset, read_pi_offset },
163 { C::tx_read_pi_length_offset,
165 { C::tx_remaining_phase_counter, calls_remaining },
166 { C::tx_remaining_phase_inv, FF(calls_remaining).invert() },
167 { C::tx_remaining_phase_minus_one_inv,
168 calls_remaining == 1 ? 0 : FF(calls_remaining - 1).invert() },
169 { C::tx_sel_can_emit_note_hash, 1 },
170 { C::tx_sel_can_emit_nullifier, 1 },
171 { C::tx_sel_can_write_public_data, 1 },
172 { C::tx_sel_can_emit_unencrypted_log, 1 },
173 { C::tx_sel_can_emit_l2_l1_msg, 1 },
174 // Public Input Loaded Values
175 { C::tx_msg_sender, setup_call.msgSender },
176 { C::tx_contract_addr, setup_call.contractAddress },
177 { C::tx_is_static, setup_call.isStaticCall },
178 { C::tx_calldata_hash, setup_call.calldataHash },
179 } });
180 calls_remaining--;
181 read_pi_offset++;
182 }
183 // Nullifer, note, and message insertion:
184 trace.set(
185 row++,
186 { {
187 { C::tx_sel, 1 },
188 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NULLIFIER_INSERTION) },
189 { C::tx_is_padded, 1 },
190 { C::tx_is_tree_insert_phase, 1 },
191 { C::tx_sel_revertible_append_nullifier, 1 },
192 { C::tx_sel_can_emit_nullifier, 1 },
193 { C::tx_is_revertible, 1 },
195 { C::tx_sel_read_phase_length, 1 },
196 { C::tx_read_pi_length_offset,
198 { C::tx_start_phase, 1 },
199 { C::tx_end_phase, 1 },
200 } });
201 trace.set(
202 row++,
203 { {
204 { C::tx_sel, 1 },
205 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NOTE_INSERTION) },
206 { C::tx_is_padded, 1 },
207 { C::tx_is_tree_insert_phase, 1 },
208 { C::tx_sel_revertible_append_note_hash, 1 },
209 { C::tx_sel_can_emit_note_hash, 1 },
210 { C::tx_is_revertible, 1 },
212 { C::tx_sel_read_phase_length, 1 },
213 { C::tx_read_pi_length_offset,
215 { C::tx_start_phase, 1 },
216 { C::tx_end_phase, 1 },
217 } });
218 trace.set(
219 row++,
220 { {
221 { C::tx_sel, 1 },
222 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_L2_TO_L1_MESSAGE) },
223 { C::tx_is_padded, 1 },
224 { C::tx_sel_revertible_append_l2_l1_msg, 1 },
225 { C::tx_sel_can_emit_l2_l1_msg, 1 },
226 { C::tx_is_revertible, 1 },
228 { C::tx_sel_read_phase_length, 1 },
229 { C::tx_read_pi_length_offset,
232 { C::tx_start_phase, 1 },
233 { C::tx_end_phase, 1 },
234 } });
235 // App logic calls:
236 calls_remaining = app_logic_call_requests.size();
238 for (auto app_logic_call : app_logic_call_requests) {
239 bool is_first_call = calls_remaining == app_logic_call_requests.size();
240 trace.set(
241 row++,
242 { {
243 { C::tx_sel, 1 },
244 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::APP_LOGIC) },
245 { C::tx_start_phase, is_first_call ? 1 : 0 },
246 { C::tx_end_phase, calls_remaining == 1 ? 1 : 0 },
247 { C::tx_sel_read_phase_length, is_first_call ? 1 : 0 },
248 // Lookup Precomputed Table Values
249 { C::tx_is_public_call_request, 1 },
250 { C::tx_should_process_call_request, 1 },
251 { C::tx_read_pi_offset, read_pi_offset },
252 { C::tx_read_pi_length_offset,
254 { C::tx_remaining_phase_counter, calls_remaining },
255 { C::tx_remaining_phase_inv, FF(calls_remaining).invert() },
256 { C::tx_remaining_phase_minus_one_inv,
257 calls_remaining == 1 ? 0 : FF(calls_remaining - 1).invert() },
258 { C::tx_is_revertible, 1 },
259 { C::tx_sel_can_emit_note_hash, 1 },
260 { C::tx_sel_can_emit_nullifier, 1 },
261 { C::tx_sel_can_write_public_data, 1 },
262 { C::tx_sel_can_emit_unencrypted_log, 1 },
263 { C::tx_sel_can_emit_l2_l1_msg, 1 },
264 // Public Input Loaded Values
265 { C::tx_msg_sender, app_logic_call.msgSender },
266 { C::tx_contract_addr, app_logic_call.contractAddress },
267 { C::tx_is_static, app_logic_call.isStaticCall },
268 { C::tx_calldata_hash, app_logic_call.calldataHash },
269 } });
270 calls_remaining--;
271 read_pi_offset++;
272 }
273 // Teardown:
274 trace.set(row++,
275 { {
276 { C::tx_sel, 1 },
277 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::TEARDOWN) },
278 { C::tx_is_teardown_phase, 1 },
279 { C::tx_sel_read_phase_length, 1 },
280 { C::tx_read_pi_length_offset,
283 { C::tx_is_public_call_request, 1 },
284 { C::tx_is_revertible, 1 },
285 { C::tx_sel_can_emit_note_hash, 1 },
286 { C::tx_sel_can_emit_nullifier, 1 },
287 { C::tx_sel_can_write_public_data, 1 },
288 { C::tx_sel_can_emit_unencrypted_log, 1 },
289 { C::tx_sel_can_emit_l2_l1_msg, 1 },
290 { C::tx_start_phase, 1 },
291 { C::tx_end_phase, 1 },
292 } });
293 // Collect fees
294 trace.set(row++,
295 { {
296 { C::tx_sel, 1 },
297 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::COLLECT_GAS_FEES) },
298 { C::tx_remaining_phase_counter, 1 },
299 { C::tx_remaining_phase_inv, 1 },
300 { C::tx_is_collect_fee, 1 },
301 { C::tx_sel_can_write_public_data, 1 },
302 { C::tx_read_pi_offset, AVM_PUBLIC_INPUTS_EFFECTIVE_GAS_FEES_ROW_IDX },
303 { C::tx_write_pi_offset, AVM_PUBLIC_INPUTS_TRANSACTION_FEE_ROW_IDX },
304 { C::tx_fee_juice_contract_address, FEE_JUICE_ADDRESS },
305 { C::tx_fee_juice_balances_slot, FEE_JUICE_BALANCES_SLOT },
306 { C::tx_fee_payer_pi_offset, AVM_PUBLIC_INPUTS_FEE_PAYER_ROW_IDX },
307 { C::tx_start_phase, 1 },
308 { C::tx_end_phase, 1 },
309 { C::tx_uint32_max, 0xffffffff },
310 } });
311 // Tree Padding
312 trace.set(row++,
313 { {
314 { C::tx_sel, 1 },
315 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::TREE_PADDING) },
316 { C::tx_start_phase, 1 },
317 { C::tx_end_phase, 1 },
318 { C::tx_is_tree_padding, 1 },
319 { C::tx_remaining_phase_counter, 1 },
320 { C::tx_remaining_phase_inv, 1 },
321 { C::tx_sel_can_emit_note_hash, 1 },
322 { C::tx_sel_can_emit_nullifier, 1 },
323 { C::tx_next_note_hash_tree_size, MAX_NOTE_HASHES_PER_TX },
324 { C::tx_next_nullifier_tree_size, MAX_NULLIFIERS_PER_TX },
325 } });
326 // Cleanup
327 trace.set(row++,
328 { {
329 { C::tx_sel, 1 },
330 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::CLEANUP) },
331 { C::tx_start_phase, 1 },
332 { C::tx_end_phase, 1 },
333 { C::tx_is_cleanup, 1 },
334 { C::tx_remaining_phase_counter, 1 },
335 { C::tx_remaining_phase_inv, 1 },
336 } });
337 }
338};
339
340TEST_F(TxExecutionConstrainingTestHelper, SimpleControlFlowRead)
341{
342 auto test_public_inputs = testing::PublicInputsBuilder()
343 .rand_public_setup_call_requests(2)
344 .rand_public_app_logic_call_requests(1)
345 .build();
346
347 auto first_setup_call_request = test_public_inputs.publicSetupCallRequests[0];
348 auto second_setup_call_request = test_public_inputs.publicSetupCallRequests[1];
349 auto app_logic_call_request = test_public_inputs.publicAppLogicCallRequests[0];
350
351 TestTraceContainer trace;
352 set_initial_columns(trace, { first_setup_call_request, second_setup_call_request }, { app_logic_call_request });
353
354 // Set is_padded at teardown:
355 trace.set(10,
356 { {
357 { C::tx_is_padded, 1 },
358 } });
359
360 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
361 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
362
365
366 check_relation<tx>(trace);
367 check_interaction<TxTraceBuilder,
371}
372
373TEST_F(TxExecutionConstrainingTestHelper, JumpOnRevert)
374{
375 TestTraceContainer trace;
376 set_initial_columns(
377 trace,
378 { PublicCallRequest{ .msgSender = 0, .contractAddress = 0, .isStaticCall = false, .calldataHash = 0 } },
379 {});
380
381 // Set reverted at row 7, message phase:
382 trace.set(7,
383 { {
384 { C::tx_is_padded, 0 },
385 { C::tx_sel_revertible_append_l2_l1_msg, 0 }, // switch off for testing
386 { C::tx_remaining_phase_counter, 1 },
387 { C::tx_remaining_phase_inv, 1 },
388 { C::tx_is_revertible, 1 },
389 { C::tx_reverted, 1 },
390 } });
391 // Set teardown as padded:
392 trace.set(8, { { { C::tx_is_padded, 1 } } });
393
395
396 check_relation<tx>(trace);
397 check_interaction<TxTraceBuilder, lookup_tx_phase_jump_on_revert_settings>(trace);
398}
399
400} // namespace
401
402TEST(TxExecutionConstrainingTest, WriteTreeValue)
403{
405
406 auto pub_inputs_col = test_public_inputs.to_columns();
408 // Row 0
409 { { C::precomputed_clk, 0 }, { C::precomputed_first_row, 1 } },
410
411 // Row 1
412 { { C::tx_sel, 1 },
413 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NULLIFIER_INSERTION) },
414 { C::tx_start_phase, 1 },
415
416 { C::tx_read_pi_length_offset,
419
420 { C::tx_is_tree_insert_phase, 1 },
421 { C::tx_leaf_value, test_public_inputs.previousNonRevertibleAccumulatedData.nullifiers[0] },
422 { C::tx_prev_num_nullifiers_emitted, 0 },
423 { C::tx_next_num_nullifiers_emitted, 1 },
424 { C::tx_end_phase, 1 } },
425
426 // Row 2
427 { { C::tx_sel, 1 },
428 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NOTE_INSERTION) },
429 { C::tx_start_phase, 1 },
430
431 { C::tx_read_pi_length_offset,
434
435 { C::tx_is_tree_insert_phase, 1 },
436 { C::tx_leaf_value, test_public_inputs.previousNonRevertibleAccumulatedData.noteHashes[0] },
437 { C::tx_prev_num_note_hashes_emitted, 0 },
438 { C::tx_next_num_note_hashes_emitted, 1 },
439 { C::tx_end_phase, 1 } },
440
441 // Row 3
442 { { C::tx_sel, 1 },
443 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_L2_TO_L1_MESSAGE) },
444 { C::tx_start_phase, 1 },
445 { C::tx_read_pi_length_offset,
449
450 { C::tx_sel_non_revertible_append_l2_l1_msg, 1 },
451 { C::tx_l2_l1_msg_content,
452 test_public_inputs.previousNonRevertibleAccumulatedData.l2ToL1Msgs[0].message.content },
453 { C::tx_l2_l1_msg_recipient,
454 test_public_inputs.previousNonRevertibleAccumulatedData.l2ToL1Msgs[0].message.recipient },
455 { C::tx_l2_l1_msg_contract_address,
456 test_public_inputs.previousNonRevertibleAccumulatedData.l2ToL1Msgs[0].contractAddress },
457 { C::tx_end_phase, 1 } },
458
459 // Row 4
460 // Setup
461 { { C::tx_sel, 1 },
462 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::SETUP) },
463 { C::tx_start_phase, 1 },
464 { C::tx_is_padded, 1 },
466 { C::tx_end_phase, 1 } },
467
468 // Row 5
469 { { C::tx_sel, 1 },
470 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NULLIFIER_INSERTION) },
471 { C::tx_start_phase, 1 },
472
473 { C::tx_read_pi_length_offset,
476
477 { C::tx_is_tree_insert_phase, 1 },
478 { C::tx_leaf_value, test_public_inputs.previousRevertibleAccumulatedData.nullifiers[0] },
479 { C::tx_prev_num_nullifiers_emitted, 1 },
480 { C::tx_next_num_nullifiers_emitted, 2 },
481 { C::tx_end_phase, 1 } },
482
483 // Row 6
484 { { C::tx_sel, 1 },
485 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NOTE_INSERTION) },
486 { C::tx_start_phase, 1 },
487
488 { C::tx_read_pi_length_offset,
491
492 { C::tx_is_tree_insert_phase, 1 },
493 { C::tx_leaf_value, test_public_inputs.previousRevertibleAccumulatedData.noteHashes[0] },
494 { C::tx_prev_num_note_hashes_emitted, 1 },
495 { C::tx_next_num_note_hashes_emitted, 2 },
496 { C::tx_end_phase, 1 } },
497
498 // Row 7
499 { { C::tx_sel, 1 },
500 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_L2_TO_L1_MESSAGE) },
501 { C::tx_start_phase, 1 },
502
503 { C::tx_read_pi_length_offset,
507
508 { C::tx_sel_revertible_append_l2_l1_msg, 1 },
509 { C::tx_l2_l1_msg_content,
510 test_public_inputs.previousRevertibleAccumulatedData.l2ToL1Msgs[0].message.content },
511 { C::tx_l2_l1_msg_recipient,
512 test_public_inputs.previousRevertibleAccumulatedData.l2ToL1Msgs[0].message.recipient },
513 { C::tx_l2_l1_msg_contract_address,
514 test_public_inputs.previousRevertibleAccumulatedData.l2ToL1Msgs[0].contractAddress },
515 { C::tx_end_phase, 1 } },
516
517 // App Logic
518 // Row 8
519 { { C::tx_sel, 1 },
520 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::APP_LOGIC) },
521 { C::tx_start_phase, 1 },
522 { C::tx_is_padded, 1 },
524 { C::tx_end_phase, 1 } },
525
526 // Row 9
527 { { C::tx_sel, 1 },
528 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::TEARDOWN) },
530 { C::tx_is_padded, 1 },
531 { C::tx_start_phase, 1 },
532 { C::tx_end_phase, 1 } },
533 });
534
537 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
538
541
545}
546
547TEST_F(TxExecutionConstrainingTestHelper, CollectFees)
548{
549 auto test_public_inputs = testing::PublicInputsBuilder()
553 .build();
554
555 auto first_setup_call_request = test_public_inputs.publicSetupCallRequests[0];
556 auto second_setup_call_request = test_public_inputs.publicSetupCallRequests[1];
557 auto app_logic_call_request = test_public_inputs.publicAppLogicCallRequests[0];
558 auto teardown_call_request = test_public_inputs.publicTeardownCallRequest;
559
561 set_initial_columns(trace, { first_setup_call_request, second_setup_call_request }, { app_logic_call_request });
562
563 // Set gas used values:
564 for (uint32_t i = 1; i < 4; i++) {
565 trace.set(i,
566 { {
567 { C::tx_prev_da_gas_used, 1 },
568 { C::tx_prev_l2_gas_used, 100 },
569 { C::tx_next_da_gas_used, 1 },
570 { C::tx_next_l2_gas_used, 100 },
571 } });
572 }
573
574 trace.set(4,
575 { {
576 { C::tx_prev_da_gas_used, 1 },
577 { C::tx_prev_l2_gas_used, 100 },
578 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 1 },
579 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 100 },
580 { C::tx_next_da_gas_used, 2 },
581 { C::tx_next_l2_gas_used, 200 },
582 { C::tx_next_da_gas_used_sent_to_enqueued_call, 2 },
583 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 200 },
584 } });
585
586 trace.set(5,
587 { {
588 { C::tx_prev_da_gas_used, 2 },
589 { C::tx_prev_l2_gas_used, 200 },
590 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 2 },
591 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 200 },
592 { C::tx_next_da_gas_used, 3 },
593 { C::tx_next_l2_gas_used, 300 },
594 { C::tx_next_da_gas_used_sent_to_enqueued_call, 3 },
595 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 300 },
596 } });
597
598 for (uint32_t i = 6; i < 9; i++) {
599 trace.set(i,
600 { {
601 { C::tx_prev_da_gas_used, 3 },
602 { C::tx_prev_l2_gas_used, 300 },
603 { C::tx_next_da_gas_used, 3 },
604 { C::tx_next_l2_gas_used, 300 },
605 } });
606 }
607
608 trace.set(9,
609 { {
610 { C::tx_prev_da_gas_used, 3 },
611 { C::tx_prev_l2_gas_used, 300 },
612 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 3 },
613 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 300 },
614 { C::tx_next_da_gas_used, 4 },
615 { C::tx_next_l2_gas_used, 400 },
616 { C::tx_next_da_gas_used_sent_to_enqueued_call, 4 },
617 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 400 },
618 } });
619
620 trace.set(10,
621 { {
622 { C::tx_should_process_call_request, 1 },
623 { C::tx_remaining_phase_counter, 1 },
624 { C::tx_remaining_phase_inv, 1 },
625 // Public Input Loaded Values
626 { C::tx_msg_sender, teardown_call_request.msgSender },
627 { C::tx_contract_addr, teardown_call_request.contractAddress },
628 { C::tx_is_static, teardown_call_request.isStaticCall },
629 { C::tx_calldata_hash, teardown_call_request.calldataHash },
630 { C::tx_prev_da_gas_used, 4 },
631 { C::tx_prev_l2_gas_used, 400 },
632 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 0 },
633 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 0 },
634 { C::tx_next_da_gas_used, 4 },
635 { C::tx_next_l2_gas_used, 400 },
636 { C::tx_next_da_gas_used_sent_to_enqueued_call, 13213 },
637 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 456789 },
638 } });
639
640 trace.set(11,
641 { {
642 { C::tx_prev_da_gas_used, 4 },
643 { C::tx_prev_l2_gas_used, 400 },
644 { C::tx_next_da_gas_used, 4 },
645 { C::tx_next_l2_gas_used, 400 },
646 } });
647
648 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
649 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
650
653
654 check_relation<tx>(trace);
660}
661
662TEST(TxExecutionConstrainingTest, NegativePaddingChecks)
663{
665 {
666 // Row 0
667 { C::precomputed_first_row, 1 },
668 },
669 {
670 // Row 1
671 { C::tx_sel, 1 },
672 { C::tx_is_tree_padding, 1 },
673 { C::tx_prev_note_hash_tree_root, 42 },
674 { C::tx_next_note_hash_tree_root, 42 },
675 { C::tx_prev_note_hash_tree_size, 5 },
676 { C::tx_next_note_hash_tree_size, MAX_NOTE_HASHES_PER_TX },
677 { C::tx_prev_num_note_hashes_emitted, 5 },
678 { C::tx_next_num_note_hashes_emitted, 5 },
679 { C::tx_prev_nullifier_tree_root, 43 },
680 { C::tx_next_nullifier_tree_root, 43 },
681 { C::tx_prev_nullifier_tree_size, 7 },
682 { C::tx_next_nullifier_tree_size, MAX_NULLIFIERS_PER_TX },
683 { C::tx_prev_num_nullifiers_emitted, 7 },
684 { C::tx_next_num_nullifiers_emitted, 7 },
685 },
686 });
687 check_relation<tx>(trace,
694
695 // Negative test: change note hash root in padding
696 trace.set(C::tx_next_note_hash_tree_root, 1, 999);
698 "NOTE_HASH_TREE_ROOT_IMMUTABLE_IN_PADDING");
699
700 // Negative test: change num emitted note hashes in padding
701 trace.set(C::tx_next_num_note_hashes_emitted, 1, 999);
703 "NOTE_HASHES_EMITTED_IMMUTABLE_IN_PADDING");
704
705 // Negative test: change nullifier tree root in padding
706 trace.set(C::tx_next_nullifier_tree_root, 1, 999);
708 "NULLIFIER_TREE_ROOT_IMMUTABLE_IN_PADDING");
709
710 // Negative test: change num emitted nullifiers in padding
711 trace.set(C::tx_next_num_nullifiers_emitted, 1, 999);
713 "NULLIFIERS_EMITTED_IMMUTABLE_IN_PADDING");
714
715 // Negative test: wrong note hash padding check
716 trace.set(C::tx_next_note_hash_tree_size, 1, MAX_NOTE_HASHES_PER_TX - 1);
717 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_PAD_NOTE_HASH_TREE), "PAD_NOTE_HASH_TREE");
718
719 // Negative test: wrong nullifier padding check
720 trace.set(C::tx_next_nullifier_tree_size, 1, MAX_NULLIFIERS_PER_TX - 1);
721 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_PAD_NULLIFIER_TREE), "PAD_NULLIFIER_TREE");
722}
723
736
738{
741 auto test_public_inputs = testing::PublicInputsBuilder()
744 .build();
745
746 std::vector<FF> dummy_setup_calldata = { 1, 2 };
747 std::vector<FF> dummy_setup_calldata_preimage = dummy_setup_calldata;
748 dummy_setup_calldata_preimage.insert(dummy_setup_calldata_preimage.begin(), GENERATOR_INDEX__PUBLIC_CALLDATA);
749 FF dummy_setup_calldata_hash = poseidon2.hash(dummy_setup_calldata_preimage);
750 test_public_inputs.publicSetupCallRequests[0].calldataHash = dummy_setup_calldata_hash;
751
752 std::vector<FF> non_empty_calldata = { 2, 3, 4 };
753 std::vector<FF> non_empty_calldata_preimage = non_empty_calldata;
754 non_empty_calldata_preimage.insert(non_empty_calldata_preimage.begin(), GENERATOR_INDEX__PUBLIC_CALLDATA);
755 FF non_empty_calldata_hash = poseidon2.hash(non_empty_calldata_preimage);
756 test_public_inputs.publicAppLogicCallRequests[0].calldataHash = non_empty_calldata_hash;
757
758 FF empty_calldata_hash = poseidon2.hash({ GENERATOR_INDEX__PUBLIC_CALLDATA });
759 test_public_inputs.publicAppLogicCallRequests[1].calldataHash = empty_calldata_hash;
760
762 { .context_id = 1,
763 .calldata_size = static_cast<uint32_t>(dummy_setup_calldata.size()),
764 .calldata = dummy_setup_calldata },
765 { .context_id = 5,
766 .calldata_size = static_cast<uint32_t>(non_empty_calldata.size()),
767 .calldata = non_empty_calldata },
768 { .context_id = 6, .calldata_size = 0, .calldata = {} }
769 };
770
771 auto setup_call_request = test_public_inputs.publicSetupCallRequests[0];
772 auto non_empty_calldata_app_logic_call_request = test_public_inputs.publicAppLogicCallRequests[0];
773 auto empty_calldata_app_logic_call_request = test_public_inputs.publicAppLogicCallRequests[1];
774
776
777 set_initial_columns(trace,
778 { setup_call_request },
779 { non_empty_calldata_app_logic_call_request, empty_calldata_app_logic_call_request });
780
781 // Set calldata specific values for setup:
782 trace.set(4, { { { C::tx_calldata_size, 2 }, { C::tx_next_context_id, 1 } } });
783
784 // Set calldata specific values for non empty calldata call:
785 trace.set(8, { { { C::tx_calldata_size, 3 }, { C::tx_next_context_id, 5 } } });
786
787 // Set calldata specific values for empty calldata call:
788 trace.set(9, { { { C::tx_calldata_size, 0 }, { C::tx_next_context_id, 6 } } });
789
790 // Set teardown as padded:
791 trace.set(10, { { { C::tx_is_padded, 1 } } });
792
793 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
794 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
795
798
800
801 calldata_builder.process_retrieval(calldata_events, trace);
802 calldata_builder.process_hashing(calldata_events, trace);
803
804 check_relation<tx>(trace);
810 check_relation<bb::avm2::calldata_hashing<FF>>(trace);
811 check_all_interactions<tracegen::CalldataTraceBuilder>(trace);
812}
813} // namespace bb::avm2::constraining
#define AVM_PUBLIC_INPUTS_PUBLIC_APP_LOGIC_CALL_REQUESTS_ROW_IDX
#define AVM_PUBLIC_INPUTS_EFFECTIVE_GAS_FEES_ROW_IDX
#define AVM_PUBLIC_INPUTS_PUBLIC_TEARDOWN_CALL_REQUEST_ROW_IDX
#define AVM_PUBLIC_INPUTS_FEE_PAYER_ROW_IDX
#define AVM_PUBLIC_INPUTS_AVM_ACCUMULATED_DATA_L2_TO_L1_MSGS_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_NON_REVERTIBLE_ACCUMULATED_DATA_NULLIFIERS_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_REVERTIBLE_ACCUMULATED_DATA_ARRAY_LENGTHS_NULLIFIERS_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_REVERTIBLE_ACCUMULATED_DATA_ARRAY_LENGTHS_NOTE_HASHES_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_REVERTIBLE_ACCUMULATED_DATA_ARRAY_LENGTHS_L2_TO_L1_MSGS_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_NON_REVERTIBLE_ACCUMULATED_DATA_L2_TO_L1_MSGS_ROW_IDX
#define AVM_PUBLIC_INPUTS_TRANSACTION_FEE_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_REVERTIBLE_ACCUMULATED_DATA_L2_TO_L1_MSGS_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_NON_REVERTIBLE_ACCUMULATED_DATA_NOTE_HASHES_ROW_IDX
#define AVM_PUBLIC_INPUTS_PUBLIC_SETUP_CALL_REQUESTS_ROW_IDX
#define AVM_PUBLIC_INPUTS_PUBLIC_CALL_REQUEST_ARRAY_LENGTHS_APP_LOGIC_CALLS_ROW_IDX
#define FEE_JUICE_ADDRESS
#define AVM_PUBLIC_INPUTS_PREVIOUS_NON_REVERTIBLE_ACCUMULATED_DATA_ARRAY_LENGTHS_NULLIFIERS_ROW_IDX
#define GENERATOR_INDEX__PUBLIC_CALLDATA
#define MAX_NOTE_HASHES_PER_TX
#define AVM_PUBLIC_INPUTS_PUBLIC_CALL_REQUEST_ARRAY_LENGTHS_TEARDOWN_CALL_ROW_IDX
#define FEE_JUICE_BALANCES_SLOT
#define AVM_PUBLIC_INPUTS_PREVIOUS_NON_REVERTIBLE_ACCUMULATED_DATA_ARRAY_LENGTHS_L2_TO_L1_MSGS_ROW_IDX
#define MAX_NULLIFIERS_PER_TX
#define AVM_PUBLIC_INPUTS_PUBLIC_CALL_REQUEST_ARRAY_LENGTHS_SETUP_CALLS_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_NON_REVERTIBLE_ACCUMULATED_DATA_ARRAY_LENGTHS_NOTE_HASHES_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_REVERTIBLE_ACCUMULATED_DATA_NOTE_HASHES_ROW_IDX
#define AVM_PUBLIC_INPUTS_PREVIOUS_REVERTIBLE_ACCUMULATED_DATA_NULLIFIERS_ROW_IDX
#define AVM_PUBLIC_INPUTS_COLUMNS_MAX_LENGTH
StrictMock< MockGreaterThan > mock_gt
EventEmitter< Poseidon2PermutationMemoryEvent > perm_mem_event_emitter
EventEmitter< Poseidon2PermutationEvent > perm_event_emitter
EventEmitter< Poseidon2HashEvent > hash_event_emitter
Poseidon2TraceBuilder poseidon2_builder
StrictMock< MockExecutionIdManager > mock_execution_id_manager
simulation::EventEmitter< simulation::Poseidon2HashEvent > hash_event_emitter
Definition tx.test.cpp:726
::testing::StrictMock< simulation::MockGreaterThan > mock_gt
Definition tx.test.cpp:730
simulation::EventEmitter< simulation::Poseidon2PermutationEvent > perm_event_emitter
Definition tx.test.cpp:727
::testing::StrictMock< simulation::MockExecutionIdManager > mock_execution_id_manager
Definition tx.test.cpp:731
simulation::EventEmitter< simulation::Poseidon2PermutationMemoryEvent > perm_mem_event_emitter
Definition tx.test.cpp:728
PublicInputsBuilder & rand_public_app_logic_call_requests(size_t n)
PublicInputsBuilder & rand_public_setup_call_requests(size_t n)
PublicInputsBuilder & rand_public_teardown_call_request()
PublicInputsBuilder & rand_previous_non_revertible_accumulated_data(size_t n)
std::unique_ptr< InteractionBuilderInterface > get_test_job(const std::string &interaction_name) const
void process_hash(const simulation::EventEmitterInterface< simulation::Poseidon2HashEvent >::Container &hash_events, TraceContainer &trace)
void process_misc(TraceContainer &trace, const uint32_t num_rows=MAX_AVM_TRACE_SIZE)
void process_public_inputs(TraceContainer &trace, const PublicInputs &public_inputs)
void set(Column col, uint32_t row, const FF &value)
static const InteractionDefinition interactions
Definition tx_trace.hpp:19
static constexpr size_t SR_NO_EARLY_END
Definition tx.hpp:43
static constexpr size_t SR_NO_EXTRANEOUS_ROWS
Definition tx.hpp:41
static constexpr size_t SR_NOTE_HASHES_EMITTED_IMMUTABLE_IN_PADDING
Definition tx.hpp:64
static constexpr size_t SR_NOTE_HASH_TREE_ROOT_IMMUTABLE_IN_PADDING
Definition tx.hpp:62
static constexpr size_t SR_NULLIFIER_TREE_ROOT_IMMUTABLE_IN_PADDING
Definition tx.hpp:65
static constexpr size_t SR_PAD_NOTE_HASH_TREE
Definition tx.hpp:63
static constexpr size_t SR_NULLIFIERS_EMITTED_IMMUTABLE_IN_PADDING
Definition tx.hpp:67
static constexpr size_t SR_PAD_NULLIFIER_TREE
Definition tx.hpp:66
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
PrecomputedTraceBuilder precomputed_builder
Definition alu.test.cpp:119
TestTraceContainer trace
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
Definition macros.hpp:7
TEST_F(AvmRecursiveTests, GoblinRecursion)
A test of the Goblinized AVM recursive verifier.
void check_interaction(tracegen::TestTraceContainer &trace)
TEST(TxExecutionConstrainingTest, WriteTreeValue)
Definition tx.test.cpp:402
TestTraceContainer empty_trace()
Definition fixtures.cpp:153
lookup_settings< lookup_tx_read_phase_table_settings_ > lookup_tx_read_phase_table_settings
@ NR_NOTE_INSERTION
@ R_NULLIFIER_INSERTION
@ NR_L2_TO_L1_MESSAGE
@ R_L2_TO_L1_MESSAGE
@ NR_NULLIFIER_INSERTION
lookup_settings< lookup_tx_read_calldata_hash_settings_ > lookup_tx_read_calldata_hash_settings
lookup_settings< lookup_tx_read_public_call_request_phase_settings_ > lookup_tx_read_public_call_request_phase_settings
AvmFlavorSettings::FF FF
Definition field.hpp:10
lookup_settings< lookup_tx_read_phase_length_settings_ > lookup_tx_read_phase_length_settings
typename Flavor::FF FF
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
EventEmitter< CalldataEvent > calldata_events
std::vector< std::vector< FF > > to_columns() const
Serialization to columns.
std::array< PublicCallRequest, MAX_ENQUEUED_CALLS_PER_TX > publicSetupCallRequests
tracegen::PublicInputsTraceBuilder public_inputs_builder
Definition tx.test.cpp:80