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>;
37using tx_context = bb::avm2::tx_context<FF>;
38
39TEST(TxExecutionConstrainingTest, NegativeEmptyTrace)
40{
41 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(testing::empty_trace()), "START_WITH_SEL");
42}
43
44TEST(TxExecutionConstrainingTest, NegativeEarlyEnd)
45{
46 TestTraceContainer trace({
47 {
48 // Row 0
49 { C::precomputed_first_row, 1 },
50 },
51 {
52 // Row 1
53 { C::tx_sel, 1 },
54 },
55 });
56 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_NO_EARLY_END), "NO_EARLY_END");
57}
58
59TEST(TxExecutionConstrainingTest, NegativeNoExtraneousRows)
60{
61 TestTraceContainer trace({
62 {
63 // Row 0
64 { C::precomputed_first_row, 1 },
65 },
66 {
67 // Row 1
68 { C::tx_sel, 0 },
69 },
70 {
71 // Row 2
72 { C::tx_sel, 1 },
73 },
74 });
75 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_TRACE_CONTINUITY), "TRACE_CONTINUITY");
76}
77
78class TxExecutionConstrainingTestHelper : public ::testing::Test {
79 public:
80 tracegen::PrecomputedTraceBuilder precomputed_builder;
81 tracegen::PublicInputsTraceBuilder public_inputs_builder;
82 static void set_initial_columns(TestTraceContainer& trace,
83 const std::vector<PublicCallRequest>& setup_call_requests,
84 const std::vector<PublicCallRequest>& app_logic_call_requests)
85 {
86 uint32_t row = 0;
87 // Prepended first row:
88 trace.set(row++, { { { C::precomputed_clk, 0 }, { C::precomputed_first_row, 1 } } });
89 // Nullifer, note, and message insertion:
90 trace.set(
91 row++,
92 { {
93 { C::tx_sel, 1 },
94 { C::tx_start_tx, 1 },
95 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NULLIFIER_INSERTION) },
96 { C::tx_is_padded, 1 },
97 { C::tx_is_tree_insert_phase, 1 },
98 { C::tx_sel_non_revertible_append_nullifier, 1 },
99
100 { C::tx_read_pi_start_offset,
103 { C::tx_sel_read_phase_length, 1 },
104 { C::tx_read_pi_length_offset,
106
107 { C::tx_start_phase, 1 },
108 { C::tx_end_phase, 1 },
109 { C::tx_next_context_id, 1 },
110 } });
111 trace.set(row++,
112 { {
113 { C::tx_sel, 1 },
114 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NOTE_INSERTION) },
115 { C::tx_is_padded, 1 },
116 { C::tx_is_tree_insert_phase, 1 },
117 { C::tx_sel_non_revertible_append_note_hash, 1 },
118 { C::tx_read_pi_start_offset,
120 { C::tx_read_pi_offset,
122 { C::tx_sel_read_phase_length, 1 },
123 { C::tx_read_pi_length_offset,
125 { C::tx_start_phase, 1 },
126 { C::tx_end_phase, 1 },
127 { C::tx_next_context_id, 1 },
128 } });
129 trace.set(
130 row++,
131 { {
132 { C::tx_sel, 1 },
133 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_L2_TO_L1_MESSAGE) },
134 { C::tx_is_padded, 1 },
135 { C::tx_sel_non_revertible_append_l2_l1_msg, 1 },
136
137 { C::tx_read_pi_start_offset,
139 { C::tx_read_pi_offset,
141 { C::tx_sel_read_phase_length, 1 },
142 { C::tx_read_pi_length_offset,
145
146 { C::tx_start_phase, 1 },
147 { C::tx_end_phase, 1 },
148 { C::tx_next_context_id, 1 },
149 } });
150 // Setup calls:
151 auto calls_remaining = setup_call_requests.size();
153 for (auto setup_call : setup_call_requests) {
154 bool is_first_call = calls_remaining == setup_call_requests.size();
155 trace.set(row++,
156 { {
157 { C::tx_sel, 1 },
158 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::SETUP) },
159 { C::tx_start_phase, is_first_call ? 1 : 0 },
160 { C::tx_end_phase, calls_remaining == 1 ? 1 : 0 },
161 { C::tx_sel_read_phase_length, is_first_call ? 1 : 0 },
162 // Lookup Precomputed Table Values
163 { C::tx_is_public_call_request, 1 },
164 { C::tx_should_process_call_request, 1 },
165 { C::tx_read_pi_start_offset, AVM_PUBLIC_INPUTS_PUBLIC_SETUP_CALL_REQUESTS_ROW_IDX },
166 { C::tx_read_pi_offset, read_pi_offset },
167 { C::tx_read_pi_length_offset,
169 { C::tx_remaining_phase_counter, calls_remaining },
170 { C::tx_remaining_phase_inv, FF(calls_remaining).invert() },
171 { C::tx_remaining_phase_minus_one_inv,
172 calls_remaining == 1 ? 0 : FF(calls_remaining - 1).invert() },
173 // Public Input Loaded Values
174 { C::tx_msg_sender, setup_call.msg_sender },
175 { C::tx_contract_addr, setup_call.contract_address },
176 { C::tx_is_static, setup_call.is_static_call },
177 { C::tx_calldata_hash, setup_call.calldata_hash },
178 } });
179 calls_remaining--;
180 read_pi_offset++;
181 }
182 // Nullifer, note, and message insertion:
183 trace.set(
184 row++,
185 { {
186 { C::tx_sel, 1 },
187 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NULLIFIER_INSERTION) },
188 { C::tx_is_padded, 1 },
189 { C::tx_is_tree_insert_phase, 1 },
190 { C::tx_sel_revertible_append_nullifier, 1 },
191 { C::tx_is_revertible, 1 },
192 { C::tx_read_pi_start_offset,
195 { C::tx_sel_read_phase_length, 1 },
196 { C::tx_read_pi_length_offset,
198 { C::tx_next_phase_on_revert, static_cast<uint8_t>(TransactionPhase::TEARDOWN) },
199 { C::tx_start_phase, 1 },
200 { C::tx_end_phase, 1 },
201 } });
202 trace.set(
203 row++,
204 { {
205 { C::tx_sel, 1 },
206 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NOTE_INSERTION) },
207 { C::tx_is_padded, 1 },
208 { C::tx_is_tree_insert_phase, 1 },
209 { C::tx_sel_revertible_append_note_hash, 1 },
210 { C::tx_is_revertible, 1 },
211 { C::tx_read_pi_start_offset,
214 { C::tx_sel_read_phase_length, 1 },
215 { C::tx_read_pi_length_offset,
217 { C::tx_next_phase_on_revert, static_cast<uint8_t>(TransactionPhase::TEARDOWN) },
218 { C::tx_start_phase, 1 },
219 { C::tx_end_phase, 1 },
220 } });
221 trace.set(
222 row++,
223 { {
224 { C::tx_sel, 1 },
225 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_L2_TO_L1_MESSAGE) },
226 { C::tx_is_padded, 1 },
227 { C::tx_sel_revertible_append_l2_l1_msg, 1 },
228 { C::tx_is_revertible, 1 },
229 { C::tx_read_pi_start_offset,
232 { C::tx_sel_read_phase_length, 1 },
233 { C::tx_read_pi_length_offset,
236 { C::tx_next_phase_on_revert, static_cast<uint8_t>(TransactionPhase::TEARDOWN) },
237 { C::tx_start_phase, 1 },
238 { C::tx_end_phase, 1 },
239 } });
240 // App logic calls:
241 calls_remaining = app_logic_call_requests.size();
243 for (auto app_logic_call : app_logic_call_requests) {
244 bool is_first_call = calls_remaining == app_logic_call_requests.size();
245 trace.set(row++,
246 { {
247 { C::tx_sel, 1 },
248 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::APP_LOGIC) },
249 { C::tx_start_phase, is_first_call ? 1 : 0 },
250 { C::tx_end_phase, calls_remaining == 1 ? 1 : 0 },
251 { C::tx_sel_read_phase_length, is_first_call ? 1 : 0 },
252 // Lookup Precomputed Table Values
253 { C::tx_is_public_call_request, 1 },
254 { C::tx_should_process_call_request, 1 },
255 { C::tx_read_pi_start_offset, AVM_PUBLIC_INPUTS_PUBLIC_APP_LOGIC_CALL_REQUESTS_ROW_IDX },
256 { C::tx_read_pi_offset, read_pi_offset },
257 { C::tx_read_pi_length_offset,
259 { C::tx_remaining_phase_counter, calls_remaining },
260 { C::tx_remaining_phase_inv, FF(calls_remaining).invert() },
261 { C::tx_remaining_phase_minus_one_inv,
262 calls_remaining == 1 ? 0 : FF(calls_remaining - 1).invert() },
263 { C::tx_is_revertible, 1 },
264 { C::tx_next_phase_on_revert, static_cast<uint8_t>(TransactionPhase::TEARDOWN) },
265 // Public Input Loaded Values
266 { C::tx_msg_sender, app_logic_call.msg_sender },
267 { C::tx_contract_addr, app_logic_call.contract_address },
268 { C::tx_is_static, app_logic_call.is_static_call },
269 { C::tx_calldata_hash, app_logic_call.calldata_hash },
270 } });
271 calls_remaining--;
272 read_pi_offset++;
273 }
274 // Teardown:
275 trace.set(row++,
276 { {
277 { C::tx_sel, 1 },
278 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::TEARDOWN) },
279 { C::tx_is_teardown, 1 },
280 { C::tx_sel_read_phase_length, 1 },
281 { C::tx_read_pi_start_offset, AVM_PUBLIC_INPUTS_PUBLIC_TEARDOWN_CALL_REQUEST_ROW_IDX },
282 { C::tx_read_pi_length_offset,
285 { C::tx_is_public_call_request, 1 },
286 { C::tx_is_revertible, 1 },
287 { C::tx_next_phase_on_revert, static_cast<uint8_t>(TransactionPhase::COLLECT_GAS_FEES) },
288 { C::tx_start_phase, 1 },
289 { C::tx_end_phase, 1 },
290 } });
291 // Collect fees
292 trace.set(row++,
293 { {
294 { C::tx_sel, 1 },
295 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::COLLECT_GAS_FEES) },
296 { C::tx_remaining_phase_counter, 1 },
297 { C::tx_remaining_phase_inv, 1 },
298 { C::tx_is_collect_fee, 1 },
299 { C::tx_read_pi_start_offset, AVM_PUBLIC_INPUTS_EFFECTIVE_GAS_FEES_ROW_IDX },
300 { C::tx_read_pi_offset, AVM_PUBLIC_INPUTS_EFFECTIVE_GAS_FEES_ROW_IDX },
301 { C::tx_write_pi_offset, AVM_PUBLIC_INPUTS_TRANSACTION_FEE_ROW_IDX },
302 { C::tx_fee_juice_contract_address, FEE_JUICE_ADDRESS },
303 { C::tx_fee_juice_balances_slot_constant, FEE_JUICE_BALANCES_SLOT },
304 { C::tx_fee_payer_pi_offset, AVM_PUBLIC_INPUTS_FEE_PAYER_ROW_IDX },
305 { C::tx_start_phase, 1 },
306 { C::tx_end_phase, 1 },
307 { C::tx_uint32_max, 0xffffffff },
308 } });
309 // Tree Padding
310 trace.set(row++,
311 { {
312 { C::tx_sel, 1 },
313 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::TREE_PADDING) },
314 { C::tx_start_phase, 1 },
315 { C::tx_end_phase, 1 },
316 { C::tx_is_tree_padding, 1 },
317 { C::tx_remaining_phase_counter, 1 },
318 { C::tx_remaining_phase_inv, 1 },
319 { C::tx_next_note_hash_tree_size, MAX_NOTE_HASHES_PER_TX },
320 { C::tx_next_nullifier_tree_size, MAX_NULLIFIERS_PER_TX },
321 } });
322 // Cleanup
323 trace.set(row++,
324 { {
325 { C::tx_sel, 1 },
326 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::CLEANUP) },
327 { C::tx_start_phase, 1 },
328 { C::tx_end_phase, 1 },
329 { C::tx_is_cleanup, 1 },
330 { C::tx_remaining_phase_counter, 1 },
331 { C::tx_remaining_phase_inv, 1 },
332 } });
333 }
334};
335
336TEST_F(TxExecutionConstrainingTestHelper, SimpleControlFlowRead)
337{
338 auto test_public_inputs = testing::PublicInputsBuilder()
339 .rand_public_setup_call_requests(2)
340 .rand_public_app_logic_call_requests(1)
341 .build();
342
343 auto first_setup_call_request = test_public_inputs.public_setup_call_requests[0];
344 auto second_setup_call_request = test_public_inputs.public_setup_call_requests[1];
345 auto app_logic_call_request = test_public_inputs.public_app_logic_call_requests[0];
346
347 TestTraceContainer trace;
348 set_initial_columns(trace, { first_setup_call_request, second_setup_call_request }, { app_logic_call_request });
349
350 // Set is_padded at teardown:
351 trace.set(10,
352 { {
353 { C::tx_is_padded, 1 },
354 } });
355
356 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
357 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
358
361
362 check_relation<tx>(trace);
363 check_interaction<TxTraceBuilder,
367}
368
369TEST_F(TxExecutionConstrainingTestHelper, SimpleHandleRevert)
370{
371 auto test_public_inputs = testing::PublicInputsBuilder()
372 .rand_public_setup_call_requests(2)
373 .rand_public_app_logic_call_requests(2)
374 .set_reverted(true)
375 .build();
376
377 auto first_setup_call_request = test_public_inputs.public_setup_call_requests[0];
378 auto second_setup_call_request = test_public_inputs.public_setup_call_requests[1];
379 auto first_app_logic_call_request = test_public_inputs.public_app_logic_call_requests[0];
380 auto second_app_logic_call_request = test_public_inputs.public_app_logic_call_requests[1];
381
382 TestTraceContainer trace;
383 set_initial_columns(trace,
384 { first_setup_call_request, second_setup_call_request },
385 { first_app_logic_call_request, second_app_logic_call_request });
386
387 // Set teardown as padded:
388 trace.set(11, { { { C::tx_is_padded, 1 } } });
389
390 // Set the second app logic call to have reverted:
391 trace.set(10, { { { C::tx_reverted, 1 } } });
392
393 // Set some tx_context values:
394 trace.set(1, { { { C::tx_start_tx, 1 } } });
395 for (uint32_t i = 10; i < 15; i++) {
396 trace.set(i, { { { C::tx_tx_reverted, 1 } } });
397 }
398 trace.set(14, { { { C::tx_reverted_pi_offset, AVM_PUBLIC_INPUTS_REVERTED_ROW_IDX } } });
399
400 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
401 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
402
405
406 check_relation<tx>(trace);
407 check_relation<tx_context>(
409 check_interaction<TxTraceBuilder, lookup_tx_context_public_inputs_read_reverted_settings>(trace);
410}
411
412TEST_F(TxExecutionConstrainingTestHelper, JumpOnRevert)
413{
414 TestTraceContainer trace;
415 set_initial_columns(
416 trace,
417 { PublicCallRequest{ .msg_sender = 0, .contract_address = 0, .is_static_call = false, .calldata_hash = 0 } },
418 {});
419
420 // Set reverted at row 7, message phase:
421 trace.set(7,
422 { {
423 { C::tx_is_padded, 0 },
424 { C::tx_sel_revertible_append_l2_l1_msg, 0 }, // switch off for testing
425 { C::tx_remaining_phase_counter, 1 },
426 { C::tx_remaining_phase_inv, 1 },
427 { C::tx_is_revertible, 1 },
428 { C::tx_reverted, 1 },
429 } });
430 // Set teardown as padded:
431 trace.set(8, { { { C::tx_is_padded, 1 } } });
432
435
436 check_relation<tx>(trace);
437}
438
439} // namespace
440
441TEST(TxExecutionConstrainingTest, WriteTreeValue)
442{
444
445 auto pub_inputs_col = test_public_inputs.to_columns();
447 // Row 0
448 { { C::precomputed_clk, 0 }, { C::precomputed_first_row, 1 } },
449
450 // Row 1
451 { { C::tx_sel, 1 },
452 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NULLIFIER_INSERTION) },
453 { C::tx_start_phase, 1 },
454
455 { C::tx_read_pi_length_offset,
458
459 { C::tx_is_tree_insert_phase, 1 },
460 { C::tx_leaf_value, test_public_inputs.previous_non_revertible_accumulated_data.nullifiers[0] },
461 { C::tx_prev_num_nullifiers_emitted, 0 },
462 { C::tx_next_num_nullifiers_emitted, 1 },
463 { C::tx_end_phase, 1 } },
464
465 // Row 2
466 { { C::tx_sel, 1 },
467 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NOTE_INSERTION) },
468 { C::tx_start_phase, 1 },
469
470 { C::tx_read_pi_length_offset,
473
474 { C::tx_is_tree_insert_phase, 1 },
475 { C::tx_leaf_value, test_public_inputs.previous_non_revertible_accumulated_data.note_hashes[0] },
476 { C::tx_prev_num_note_hashes_emitted, 0 },
477 { C::tx_next_num_note_hashes_emitted, 1 },
478 { C::tx_end_phase, 1 } },
479
480 // Row 3
481 { { C::tx_sel, 1 },
482 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_L2_TO_L1_MESSAGE) },
483 { C::tx_start_phase, 1 },
484 { C::tx_read_pi_length_offset,
488
489 { C::tx_sel_non_revertible_append_l2_l1_msg, 1 },
490 { C::tx_l2_l1_msg_content,
491 test_public_inputs.previous_non_revertible_accumulated_data.l2_to_l1_msgs[0].message.content },
492 { C::tx_l2_l1_msg_recipient,
493 test_public_inputs.previous_non_revertible_accumulated_data.l2_to_l1_msgs[0].message.recipient },
494 { C::tx_l2_l1_msg_contract_address,
495 test_public_inputs.previous_non_revertible_accumulated_data.l2_to_l1_msgs[0].contract_address },
496 { C::tx_end_phase, 1 } },
497
498 // Row 4
499 // Setup
500 { { C::tx_sel, 1 },
501 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::SETUP) },
502 { C::tx_start_phase, 1 },
503 { C::tx_is_padded, 1 },
505 { C::tx_end_phase, 1 } },
506
507 // Row 5
508 { { C::tx_sel, 1 },
509 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NULLIFIER_INSERTION) },
510 { C::tx_start_phase, 1 },
511
512 { C::tx_read_pi_length_offset,
515
516 { C::tx_is_tree_insert_phase, 1 },
517 { C::tx_leaf_value, test_public_inputs.previous_revertible_accumulated_data.nullifiers[0] },
518 { C::tx_prev_num_nullifiers_emitted, 1 },
519 { C::tx_next_num_nullifiers_emitted, 2 },
520 { C::tx_end_phase, 1 } },
521
522 // Row 6
523 { { C::tx_sel, 1 },
524 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NOTE_INSERTION) },
525 { C::tx_start_phase, 1 },
526
527 { C::tx_read_pi_length_offset,
530
531 { C::tx_is_tree_insert_phase, 1 },
532 { C::tx_leaf_value, test_public_inputs.previous_revertible_accumulated_data.note_hashes[0] },
533 { C::tx_prev_num_note_hashes_emitted, 1 },
534 { C::tx_next_num_note_hashes_emitted, 2 },
535 { C::tx_end_phase, 1 } },
536
537 // Row 7
538 { { C::tx_sel, 1 },
539 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_L2_TO_L1_MESSAGE) },
540 { C::tx_start_phase, 1 },
541
542 { C::tx_read_pi_length_offset,
546
547 { C::tx_sel_revertible_append_l2_l1_msg, 1 },
548 { C::tx_l2_l1_msg_content,
549 test_public_inputs.previous_revertible_accumulated_data.l2_to_l1_msgs[0].message.content },
550 { C::tx_l2_l1_msg_recipient,
551 test_public_inputs.previous_revertible_accumulated_data.l2_to_l1_msgs[0].message.recipient },
552 { C::tx_l2_l1_msg_contract_address,
553 test_public_inputs.previous_revertible_accumulated_data.l2_to_l1_msgs[0].contract_address },
554 { C::tx_end_phase, 1 } },
555
556 // App Logic
557 // Row 8
558 { { C::tx_sel, 1 },
559 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::APP_LOGIC) },
560 { C::tx_start_phase, 1 },
561 { C::tx_is_padded, 1 },
563 { C::tx_end_phase, 1 } },
564
565 // Row 9
566 { { C::tx_sel, 1 },
567 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::TEARDOWN) },
569 { C::tx_is_padded, 1 },
570 { C::tx_start_phase, 1 },
571 { C::tx_end_phase, 1 } },
572 });
573
576 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
577
580
584}
585
586TEST_F(TxExecutionConstrainingTestHelper, CollectFees)
587{
588 auto test_public_inputs = testing::PublicInputsBuilder()
590 .rand_public_app_logic_call_requests(1)
591 .rand_public_teardown_call_request()
592 .build();
593
594 auto first_setup_call_request = test_public_inputs.public_setup_call_requests[0];
595 auto second_setup_call_request = test_public_inputs.public_setup_call_requests[1];
596 auto app_logic_call_request = test_public_inputs.public_app_logic_call_requests[0];
597 auto teardown_call_request = test_public_inputs.public_teardown_call_request;
598
600 set_initial_columns(trace, { first_setup_call_request, second_setup_call_request }, { app_logic_call_request });
601
602 // Set gas used values:
603 for (uint32_t i = 1; i < 4; i++) {
604 trace.set(i,
605 { {
606 { C::tx_prev_da_gas_used, 1 },
607 { C::tx_prev_l2_gas_used, 100 },
608 { C::tx_next_da_gas_used, 1 },
609 { C::tx_next_l2_gas_used, 100 },
610 } });
611 }
612
613 trace.set(4,
614 { {
615 { C::tx_prev_da_gas_used, 1 },
616 { C::tx_prev_l2_gas_used, 100 },
617 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 1 },
618 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 100 },
619 { C::tx_next_da_gas_used, 2 },
620 { C::tx_next_l2_gas_used, 200 },
621 { C::tx_next_da_gas_used_sent_to_enqueued_call, 2 },
622 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 200 },
623 } });
624
625 trace.set(5,
626 { {
627 { C::tx_prev_da_gas_used, 2 },
628 { C::tx_prev_l2_gas_used, 200 },
629 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 2 },
630 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 200 },
631 { C::tx_next_da_gas_used, 3 },
632 { C::tx_next_l2_gas_used, 300 },
633 { C::tx_next_da_gas_used_sent_to_enqueued_call, 3 },
634 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 300 },
635 } });
636
637 for (uint32_t i = 6; i < 9; i++) {
638 trace.set(i,
639 { {
640 { C::tx_prev_da_gas_used, 3 },
641 { C::tx_prev_l2_gas_used, 300 },
642 { C::tx_next_da_gas_used, 3 },
643 { C::tx_next_l2_gas_used, 300 },
644 } });
645 }
646
647 trace.set(9,
648 { {
649 { C::tx_prev_da_gas_used, 3 },
650 { C::tx_prev_l2_gas_used, 300 },
651 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 3 },
652 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 300 },
653 { C::tx_next_da_gas_used, 4 },
654 { C::tx_next_l2_gas_used, 400 },
655 { C::tx_next_da_gas_used_sent_to_enqueued_call, 4 },
656 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 400 },
657 } });
658
659 trace.set(10,
660 { {
661 { C::tx_should_process_call_request, 1 },
662 { C::tx_remaining_phase_counter, 1 },
663 { C::tx_remaining_phase_inv, 1 },
664 // Public Input Loaded Values
665 { C::tx_msg_sender, teardown_call_request.msg_sender },
666 { C::tx_contract_addr, teardown_call_request.contract_address },
667 { C::tx_is_static, teardown_call_request.is_static_call },
668 { C::tx_calldata_hash, teardown_call_request.calldata_hash },
669 { C::tx_prev_da_gas_used, 4 },
670 { C::tx_prev_l2_gas_used, 400 },
671 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 0 },
672 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 0 },
673 { C::tx_next_da_gas_used, 4 },
674 { C::tx_next_l2_gas_used, 400 },
675 { C::tx_next_da_gas_used_sent_to_enqueued_call, 13213 },
676 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 456789 },
677 } });
678
679 trace.set(11,
680 { {
681 { C::tx_prev_da_gas_used, 4 },
682 { C::tx_prev_l2_gas_used, 400 },
683 { C::tx_next_da_gas_used, 4 },
684 { C::tx_next_l2_gas_used, 400 },
685 } });
686
687 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
688 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
689
692
693 check_relation<tx>(trace);
699}
700
701TEST(TxExecutionConstrainingTest, NegativeTreePaddingChecks)
702{
704 {
705 // Row 0
706 { C::precomputed_first_row, 1 },
707 },
708 {
709 // Row 1
710 { C::tx_sel, 1 },
711 { C::tx_is_tree_padding, 1 },
712 { C::tx_prev_note_hash_tree_root, 42 },
713 { C::tx_next_note_hash_tree_root, 42 },
714 { C::tx_prev_note_hash_tree_size, 5 },
715 { C::tx_next_note_hash_tree_size, MAX_NOTE_HASHES_PER_TX },
716 { C::tx_prev_num_note_hashes_emitted, 5 },
717 { C::tx_next_num_note_hashes_emitted, 5 },
718 { C::tx_prev_nullifier_tree_root, 43 },
719 { C::tx_next_nullifier_tree_root, 43 },
720 { C::tx_prev_nullifier_tree_size, 7 },
721 { C::tx_next_nullifier_tree_size, MAX_NULLIFIERS_PER_TX },
722 { C::tx_prev_num_nullifiers_emitted, 7 },
723 { C::tx_next_num_nullifiers_emitted, 7 },
724 },
725 });
727
728 check_relation<tx_context>(trace,
735
736 // Negative test: change note hash root in tree padding
737 trace.set(C::tx_next_note_hash_tree_root, 1, 999);
739 "NOTE_HASH_ROOT_IMMUTABILITY");
740
741 // Negative test: change num emitted note hashes in tree padding
742 trace.set(C::tx_next_num_note_hashes_emitted, 1, 999);
744 "NOTE_HASH_COUNT_IMMUTABILITY");
745
746 // Negative test: change nullifier tree root in tree padding
747 trace.set(C::tx_next_nullifier_tree_root, 1, 999);
749 "NULLIFIER_ROOT_IMMUTABILITY");
750
751 // Negative test: change num emitted nullifiers in tree padding
752 trace.set(C::tx_next_num_nullifiers_emitted, 1, 999);
754 "NULLIFIER_COUNT_IMMUTABILITY");
755
756 // Negative test: wrong note hash padding check
757 trace.set(C::tx_next_note_hash_tree_size, 1, MAX_NOTE_HASHES_PER_TX - 1);
758 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_PAD_NOTE_HASH_TREE), "PAD_NOTE_HASH_TREE");
759
760 // Negative test: wrong nullifier padding check
761 trace.set(C::tx_next_nullifier_tree_size, 1, MAX_NULLIFIERS_PER_TX - 1);
762 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_PAD_NULLIFIER_TREE), "PAD_NULLIFIER_TREE");
763}
764
777
779{
782 auto test_public_inputs = testing::PublicInputsBuilder()
784 .rand_public_app_logic_call_requests(2)
785 .build();
786
787 std::vector<FF> dummy_setup_calldata = { 1, 2 };
788 std::vector<FF> dummy_setup_calldata_preimage = dummy_setup_calldata;
789 dummy_setup_calldata_preimage.insert(dummy_setup_calldata_preimage.begin(), GENERATOR_INDEX__PUBLIC_CALLDATA);
790 FF dummy_setup_calldata_hash = poseidon2.hash(dummy_setup_calldata_preimage);
791 test_public_inputs.public_setup_call_requests[0].calldata_hash = dummy_setup_calldata_hash;
792
793 std::vector<FF> non_empty_calldata = { 2, 3, 4 };
794 std::vector<FF> non_empty_calldata_preimage = non_empty_calldata;
795 non_empty_calldata_preimage.insert(non_empty_calldata_preimage.begin(), GENERATOR_INDEX__PUBLIC_CALLDATA);
796 FF non_empty_calldata_hash = poseidon2.hash(non_empty_calldata_preimage);
797 test_public_inputs.public_app_logic_call_requests[0].calldata_hash = non_empty_calldata_hash;
798
799 FF empty_calldata_hash = poseidon2.hash({ GENERATOR_INDEX__PUBLIC_CALLDATA });
800 test_public_inputs.public_app_logic_call_requests[1].calldata_hash = empty_calldata_hash;
801
803 { .context_id = 1, .calldata = dummy_setup_calldata },
804 { .context_id = 5, .calldata = non_empty_calldata },
805 { .context_id = 6, .calldata = {} },
806 };
807
808 auto setup_call_request = test_public_inputs.public_setup_call_requests[0];
809 auto non_empty_calldata_app_logic_call_request = test_public_inputs.public_app_logic_call_requests[0];
810 auto empty_calldata_app_logic_call_request = test_public_inputs.public_app_logic_call_requests[1];
811
813
814 set_initial_columns(trace,
815 { setup_call_request },
816 { non_empty_calldata_app_logic_call_request, empty_calldata_app_logic_call_request });
817
818 // Set calldata specific values for setup:
819 trace.set(4, { { { C::tx_calldata_size, 2 }, { C::tx_next_context_id, 1 } } });
820
821 // Set calldata specific values for non empty calldata call:
822 trace.set(8, { { { C::tx_calldata_size, 3 }, { C::tx_next_context_id, 5 } } });
823
824 // Set calldata specific values for empty calldata call:
825 trace.set(9, { { { C::tx_calldata_size, 0 }, { C::tx_next_context_id, 6 } } });
826
827 // Set teardown as padded:
828 trace.set(10, { { { C::tx_is_padded, 1 } } });
829
830 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
831 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
832
835
837
838 calldata_builder.process_retrieval(calldata_events, trace);
839 calldata_builder.process_hashing(calldata_events, trace);
840
841 check_relation<tx>(trace);
847 check_relation<bb::avm2::calldata_hashing<FF>>(trace);
848 check_all_interactions<tracegen::CalldataTraceBuilder>(trace);
849}
850} // 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_REVERTED_ROW_IDX
#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:767
::testing::StrictMock< simulation::MockGreaterThan > mock_gt
Definition tx.test.cpp:771
simulation::EventEmitter< simulation::Poseidon2PermutationEvent > perm_event_emitter
Definition tx.test.cpp:768
::testing::StrictMock< simulation::MockExecutionIdManager > mock_execution_id_manager
Definition tx.test.cpp:772
simulation::EventEmitter< simulation::Poseidon2PermutationMemoryEvent > perm_mem_event_emitter
Definition tx.test.cpp:769
PublicInputsBuilder & rand_public_setup_call_requests(size_t n)
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:15
static constexpr size_t SR_NULLIFIER_SIZE_IMMUTABILITY
static constexpr size_t SR_INIT_TX_REVERTED
static constexpr size_t SR_NULLIFIER_COUNT_IMMUTABILITY
static constexpr size_t SR_NOTE_HASH_ROOT_IMMUTABILITY
static constexpr size_t SR_NOTE_HASH_SIZE_IMMUTABILITY
static constexpr size_t SR_NULLIFIER_ROOT_IMMUTABILITY
static constexpr size_t SR_NOTE_HASH_COUNT_IMMUTABILITY
static constexpr size_t SR_TX_REVERTED_CONTINUITY
static constexpr size_t SR_SET_TX_REVERTED
static constexpr size_t SR_NO_EARLY_END
Definition tx.hpp:47
static constexpr size_t SR_PAD_NOTE_HASH_TREE
Definition tx.hpp:66
static constexpr size_t SR_TRACE_CONTINUITY
Definition tx.hpp:41
static constexpr size_t SR_PAD_NULLIFIER_TREE
Definition tx.hpp:67
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
PrecomputedTraceBuilder precomputed_builder
Definition alu.test.cpp:120
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:441
TestTraceContainer empty_trace()
Definition fixtures.cpp:153
lookup_settings< lookup_tx_read_phase_spec_settings_ > lookup_tx_read_phase_spec_settings
permutation_settings< perm_tx_read_calldata_hash_settings_ > perm_tx_read_calldata_hash_settings
Definition perms_tx.hpp:33
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
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.
Definition avm_io.cpp:142
std::array< PublicCallRequest, MAX_ENQUEUED_CALLS_PER_TX > public_setup_call_requests
Definition avm_io.hpp:38
tracegen::PublicInputsTraceBuilder public_inputs_builder
Definition tx.test.cpp:81