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::execution_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_dom_sep_public_storage_map_slot, DOM_SEP__PUBLIC_STORAGE_MAP_SLOT },
305 { C::tx_fee_payer_pi_offset, AVM_PUBLIC_INPUTS_FEE_PAYER_ROW_IDX },
306 { C::tx_const_three, 3 },
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_next_note_hash_tree_size, MAX_NOTE_HASHES_PER_TX },
322 { C::tx_next_nullifier_tree_size, MAX_NULLIFIERS_PER_TX },
323 } });
324 // Cleanup
325 trace.set(row++,
326 { {
327 { C::tx_sel, 1 },
328 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::CLEANUP) },
329 { C::tx_start_phase, 1 },
330 { C::tx_end_phase, 1 },
331 { C::tx_is_cleanup, 1 },
332 { C::tx_remaining_phase_counter, 1 },
333 { C::tx_remaining_phase_inv, 1 },
334 } });
335 }
336};
337
338TEST_F(TxExecutionConstrainingTestHelper, SimpleControlFlowRead)
339{
340 auto test_public_inputs = testing::PublicInputsBuilder()
341 .rand_public_setup_call_requests(2)
342 .rand_public_app_logic_call_requests(1)
343 .build();
344
345 auto first_setup_call_request = test_public_inputs.public_setup_call_requests[0];
346 auto second_setup_call_request = test_public_inputs.public_setup_call_requests[1];
347 auto app_logic_call_request = test_public_inputs.public_app_logic_call_requests[0];
348
349 TestTraceContainer trace;
350 set_initial_columns(trace, { first_setup_call_request, second_setup_call_request }, { app_logic_call_request });
351
352 // Set is_padded at teardown:
353 trace.set(10,
354 { {
355 { C::tx_is_padded, 1 },
356 } });
357
358 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
359 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
360
363
364 check_relation<tx>(trace);
365 check_interaction<TxTraceBuilder,
369}
370
371TEST_F(TxExecutionConstrainingTestHelper, SimpleHandleRevert)
372{
373 auto test_public_inputs = testing::PublicInputsBuilder()
374 .rand_public_setup_call_requests(2)
375 .rand_public_app_logic_call_requests(2)
376 .set_reverted(true)
377 .build();
378
379 auto first_setup_call_request = test_public_inputs.public_setup_call_requests[0];
380 auto second_setup_call_request = test_public_inputs.public_setup_call_requests[1];
381 auto first_app_logic_call_request = test_public_inputs.public_app_logic_call_requests[0];
382 auto second_app_logic_call_request = test_public_inputs.public_app_logic_call_requests[1];
383
384 TestTraceContainer trace;
385 set_initial_columns(trace,
386 { first_setup_call_request, second_setup_call_request },
387 { first_app_logic_call_request, second_app_logic_call_request });
388
389 // Set teardown as padded:
390 trace.set(11, { { { C::tx_is_padded, 1 } } });
391
392 // Set the second app logic call to have reverted:
393 trace.set(10, { { { C::tx_reverted, 1 } } });
394
395 // Set some tx_context values:
396 trace.set(1, { { { C::tx_start_tx, 1 } } });
397 for (uint32_t i = 10; i < 15; i++) {
398 trace.set(i, { { { C::tx_tx_reverted, 1 } } });
399 }
400 trace.set(14, { { { C::tx_reverted_pi_offset, AVM_PUBLIC_INPUTS_REVERTED_ROW_IDX } } });
401
402 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
403 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
404
407
408 check_relation<tx>(trace);
409 check_relation<tx_context>(
411 check_interaction<TxTraceBuilder, lookup_tx_context_public_inputs_read_reverted_settings>(trace);
412}
413
414TEST_F(TxExecutionConstrainingTestHelper, JumpOnRevert)
415{
416 TestTraceContainer trace;
417 set_initial_columns(
418 trace,
419 { PublicCallRequest{ .msg_sender = 0, .contract_address = 0, .is_static_call = false, .calldata_hash = 0 } },
420 {});
421
422 // Set reverted at row 7, message phase:
423 trace.set(7,
424 { {
425 { C::tx_is_padded, 0 },
426 { C::tx_sel_revertible_append_l2_l1_msg, 0 }, // switch off for testing
427 { C::tx_remaining_phase_counter, 1 },
428 { C::tx_remaining_phase_inv, 1 },
429 { C::tx_is_revertible, 1 },
430 { C::tx_reverted, 1 },
431 } });
432 // Set teardown as padded:
433 trace.set(8, { { { C::tx_is_padded, 1 } } });
434
437
438 check_relation<tx>(trace);
439}
440
441} // namespace
442
443TEST(TxExecutionConstrainingTest, WriteTreeValue)
444{
446
447 auto pub_inputs_col = test_public_inputs.to_columns();
449 // Row 0
450 { { C::execution_clk, 0 }, { C::precomputed_first_row, 1 } },
451
452 // Row 1
453 { { C::tx_sel, 1 },
454 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NULLIFIER_INSERTION) },
455 { C::tx_start_phase, 1 },
456
457 { C::tx_read_pi_length_offset,
460
461 { C::tx_is_tree_insert_phase, 1 },
462 { C::tx_leaf_value, test_public_inputs.previous_non_revertible_accumulated_data.nullifiers[0] },
463 { C::tx_prev_num_nullifiers_emitted, 0 },
464 { C::tx_next_num_nullifiers_emitted, 1 },
465 { C::tx_end_phase, 1 } },
466
467 // Row 2
468 { { C::tx_sel, 1 },
469 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_NOTE_INSERTION) },
470 { C::tx_start_phase, 1 },
471
472 { C::tx_read_pi_length_offset,
475
476 { C::tx_is_tree_insert_phase, 1 },
477 { C::tx_leaf_value, test_public_inputs.previous_non_revertible_accumulated_data.note_hashes[0] },
478 { C::tx_prev_num_note_hashes_emitted, 0 },
479 { C::tx_next_num_note_hashes_emitted, 1 },
480 { C::tx_end_phase, 1 } },
481
482 // Row 3
483 { { C::tx_sel, 1 },
484 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::NR_L2_TO_L1_MESSAGE) },
485 { C::tx_start_phase, 1 },
486 { C::tx_read_pi_length_offset,
490
491 { C::tx_sel_non_revertible_append_l2_l1_msg, 1 },
492 { C::tx_l2_l1_msg_content,
493 test_public_inputs.previous_non_revertible_accumulated_data.l2_to_l1_msgs[0].message.content },
494 { C::tx_l2_l1_msg_recipient,
495 test_public_inputs.previous_non_revertible_accumulated_data.l2_to_l1_msgs[0].message.recipient },
496 { C::tx_l2_l1_msg_contract_address,
497 test_public_inputs.previous_non_revertible_accumulated_data.l2_to_l1_msgs[0].contract_address },
498 { C::tx_end_phase, 1 } },
499
500 // Row 4
501 // Setup
502 { { C::tx_sel, 1 },
503 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::SETUP) },
504 { C::tx_start_phase, 1 },
505 { C::tx_is_padded, 1 },
507 { C::tx_end_phase, 1 } },
508
509 // Row 5
510 { { C::tx_sel, 1 },
511 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NULLIFIER_INSERTION) },
512 { C::tx_start_phase, 1 },
513
514 { C::tx_read_pi_length_offset,
517
518 { C::tx_is_tree_insert_phase, 1 },
519 { C::tx_leaf_value, test_public_inputs.previous_revertible_accumulated_data.nullifiers[0] },
520 { C::tx_prev_num_nullifiers_emitted, 1 },
521 { C::tx_next_num_nullifiers_emitted, 2 },
522 { C::tx_end_phase, 1 } },
523
524 // Row 6
525 { { C::tx_sel, 1 },
526 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_NOTE_INSERTION) },
527 { C::tx_start_phase, 1 },
528
529 { C::tx_read_pi_length_offset,
532
533 { C::tx_is_tree_insert_phase, 1 },
534 { C::tx_leaf_value, test_public_inputs.previous_revertible_accumulated_data.note_hashes[0] },
535 { C::tx_prev_num_note_hashes_emitted, 1 },
536 { C::tx_next_num_note_hashes_emitted, 2 },
537 { C::tx_end_phase, 1 } },
538
539 // Row 7
540 { { C::tx_sel, 1 },
541 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::R_L2_TO_L1_MESSAGE) },
542 { C::tx_start_phase, 1 },
543
544 { C::tx_read_pi_length_offset,
548
549 { C::tx_sel_revertible_append_l2_l1_msg, 1 },
550 { C::tx_l2_l1_msg_content,
551 test_public_inputs.previous_revertible_accumulated_data.l2_to_l1_msgs[0].message.content },
552 { C::tx_l2_l1_msg_recipient,
553 test_public_inputs.previous_revertible_accumulated_data.l2_to_l1_msgs[0].message.recipient },
554 { C::tx_l2_l1_msg_contract_address,
555 test_public_inputs.previous_revertible_accumulated_data.l2_to_l1_msgs[0].contract_address },
556 { C::tx_end_phase, 1 } },
557
558 // App Logic
559 // Row 8
560 { { C::tx_sel, 1 },
561 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::APP_LOGIC) },
562 { C::tx_start_phase, 1 },
563 { C::tx_is_padded, 1 },
565 { C::tx_end_phase, 1 } },
566
567 // Row 9
568 { { C::tx_sel, 1 },
569 { C::tx_phase_value, static_cast<uint8_t>(TransactionPhase::TEARDOWN) },
571 { C::tx_is_padded, 1 },
572 { C::tx_start_phase, 1 },
573 { C::tx_end_phase, 1 } },
574 });
575
578 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
579
582
583 check_interaction<TxTraceBuilder, lookup_tx_read_tree_insert_value_settings>(trace);
584 check_interaction<TxTraceBuilder, lookup_tx_read_l2_l1_msg_settings>(trace);
585 check_interaction<TxTraceBuilder, lookup_tx_write_l2_l1_msg_settings>(trace);
586}
587
588TEST_F(TxExecutionConstrainingTestHelper, CollectFees)
589{
590 auto test_public_inputs = testing::PublicInputsBuilder()
592 .rand_public_app_logic_call_requests(1)
593 .rand_public_teardown_call_request()
594 .build();
595
596 auto first_setup_call_request = test_public_inputs.public_setup_call_requests[0];
597 auto second_setup_call_request = test_public_inputs.public_setup_call_requests[1];
598 auto app_logic_call_request = test_public_inputs.public_app_logic_call_requests[0];
599 auto teardown_call_request = test_public_inputs.public_teardown_call_request;
600
602 set_initial_columns(trace, { first_setup_call_request, second_setup_call_request }, { app_logic_call_request });
603
604 // Set gas used values:
605 for (uint32_t i = 1; i < 4; i++) {
606 trace.set(i,
607 { {
608 { C::tx_prev_da_gas_used, 1 },
609 { C::tx_prev_l2_gas_used, 100 },
610 { C::tx_next_da_gas_used, 1 },
611 { C::tx_next_l2_gas_used, 100 },
612 } });
613 }
614
615 trace.set(4,
616 { {
617 { C::tx_prev_da_gas_used, 1 },
618 { C::tx_prev_l2_gas_used, 100 },
619 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 1 },
620 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 100 },
621 { C::tx_next_da_gas_used, 2 },
622 { C::tx_next_l2_gas_used, 200 },
623 { C::tx_next_da_gas_used_sent_to_enqueued_call, 2 },
624 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 200 },
625 } });
626
627 trace.set(5,
628 { {
629 { C::tx_prev_da_gas_used, 2 },
630 { C::tx_prev_l2_gas_used, 200 },
631 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 2 },
632 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 200 },
633 { C::tx_next_da_gas_used, 3 },
634 { C::tx_next_l2_gas_used, 300 },
635 { C::tx_next_da_gas_used_sent_to_enqueued_call, 3 },
636 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 300 },
637 } });
638
639 for (uint32_t i = 6; i < 9; i++) {
640 trace.set(i,
641 { {
642 { C::tx_prev_da_gas_used, 3 },
643 { C::tx_prev_l2_gas_used, 300 },
644 { C::tx_next_da_gas_used, 3 },
645 { C::tx_next_l2_gas_used, 300 },
646 } });
647 }
648
649 trace.set(9,
650 { {
651 { C::tx_prev_da_gas_used, 3 },
652 { C::tx_prev_l2_gas_used, 300 },
653 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 3 },
654 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 300 },
655 { C::tx_next_da_gas_used, 4 },
656 { C::tx_next_l2_gas_used, 400 },
657 { C::tx_next_da_gas_used_sent_to_enqueued_call, 4 },
658 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 400 },
659 } });
660
661 trace.set(10,
662 { {
663 { C::tx_should_process_call_request, 1 },
664 { C::tx_remaining_phase_counter, 1 },
665 { C::tx_remaining_phase_inv, 1 },
666 // Public Input Loaded Values
667 { C::tx_msg_sender, teardown_call_request.msg_sender },
668 { C::tx_contract_addr, teardown_call_request.contract_address },
669 { C::tx_is_static, teardown_call_request.is_static_call },
670 { C::tx_calldata_hash, teardown_call_request.calldata_hash },
671 { C::tx_prev_da_gas_used, 4 },
672 { C::tx_prev_l2_gas_used, 400 },
673 { C::tx_prev_da_gas_used_sent_to_enqueued_call, 0 },
674 { C::tx_prev_l2_gas_used_sent_to_enqueued_call, 0 },
675 { C::tx_next_da_gas_used, 4 },
676 { C::tx_next_l2_gas_used, 400 },
677 { C::tx_next_da_gas_used_sent_to_enqueued_call, 13213 },
678 { C::tx_next_l2_gas_used_sent_to_enqueued_call, 456789 },
679 } });
680
681 trace.set(11,
682 { {
683 { C::tx_prev_da_gas_used, 4 },
684 { C::tx_prev_l2_gas_used, 400 },
685 { C::tx_next_da_gas_used, 4 },
686 { C::tx_next_l2_gas_used, 400 },
687 } });
688
689 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
690 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
691
694
695 check_relation<tx>(trace);
696 check_interaction<TxTraceBuilder, lookup_tx_read_phase_spec_settings>(trace);
697 check_interaction<TxTraceBuilder, lookup_tx_read_phase_length_settings>(trace);
698 check_interaction<TxTraceBuilder, lookup_tx_read_public_call_request_phase_settings>(trace);
699 check_interaction<TxTraceBuilder, lookup_tx_read_effective_fee_public_inputs_settings>(trace);
700 check_interaction<TxTraceBuilder, lookup_tx_read_fee_payer_public_inputs_settings>(trace);
701}
702
703TEST(TxExecutionConstrainingTest, NegativeTreePaddingChecks)
704{
706 {
707 // Row 0
708 { C::precomputed_first_row, 1 },
709 },
710 {
711 // Row 1
712 { C::tx_sel, 1 },
713 { C::tx_is_tree_padding, 1 },
714 { C::tx_prev_note_hash_tree_root, 42 },
715 { C::tx_next_note_hash_tree_root, 42 },
716 { C::tx_prev_note_hash_tree_size, 5 },
717 { C::tx_next_note_hash_tree_size, MAX_NOTE_HASHES_PER_TX },
718 { C::tx_prev_num_note_hashes_emitted, 5 },
719 { C::tx_next_num_note_hashes_emitted, 5 },
720 { C::tx_prev_nullifier_tree_root, 43 },
721 { C::tx_next_nullifier_tree_root, 43 },
722 { C::tx_prev_nullifier_tree_size, 7 },
723 { C::tx_next_nullifier_tree_size, MAX_NULLIFIERS_PER_TX },
724 { C::tx_prev_num_nullifiers_emitted, 7 },
725 { C::tx_next_num_nullifiers_emitted, 7 },
726 },
727 });
729
730 check_relation<tx_context>(trace,
737
738 // Negative test: change note hash root in tree padding
739 trace.set(C::tx_next_note_hash_tree_root, 1, 999);
741 "NOTE_HASH_ROOT_IMMUTABILITY");
742
743 // Negative test: change num emitted note hashes in tree padding
744 trace.set(C::tx_next_num_note_hashes_emitted, 1, 999);
746 "NOTE_HASH_COUNT_IMMUTABILITY");
747
748 // Negative test: change nullifier tree root in tree padding
749 trace.set(C::tx_next_nullifier_tree_root, 1, 999);
751 "NULLIFIER_ROOT_IMMUTABILITY");
752
753 // Negative test: change num emitted nullifiers in tree padding
754 trace.set(C::tx_next_num_nullifiers_emitted, 1, 999);
756 "NULLIFIER_COUNT_IMMUTABILITY");
757
758 // Negative test: wrong note hash padding check
759 trace.set(C::tx_next_note_hash_tree_size, 1, MAX_NOTE_HASHES_PER_TX - 1);
760 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_PAD_NOTE_HASH_TREE), "PAD_NOTE_HASH_TREE");
761
762 // Negative test: wrong nullifier padding check
763 trace.set(C::tx_next_nullifier_tree_size, 1, MAX_NULLIFIERS_PER_TX - 1);
764 EXPECT_THROW_WITH_MESSAGE(check_relation<tx>(trace, tx::SR_PAD_NULLIFIER_TREE), "PAD_NULLIFIER_TREE");
765}
766
779
781{
784 auto test_public_inputs = testing::PublicInputsBuilder()
786 .rand_public_app_logic_call_requests(2)
787 .build();
788
789 std::vector<FF> dummy_setup_calldata = { 1, 2 };
790 std::vector<FF> dummy_setup_calldata_preimage = dummy_setup_calldata;
791 dummy_setup_calldata_preimage.insert(dummy_setup_calldata_preimage.begin(), DOM_SEP__PUBLIC_CALLDATA);
792 FF dummy_setup_calldata_hash = poseidon2.hash(dummy_setup_calldata_preimage);
793 test_public_inputs.public_setup_call_requests[0].calldata_hash = dummy_setup_calldata_hash;
794
795 std::vector<FF> non_empty_calldata = { 2, 3, 4 };
796 std::vector<FF> non_empty_calldata_preimage = non_empty_calldata;
797 non_empty_calldata_preimage.insert(non_empty_calldata_preimage.begin(), DOM_SEP__PUBLIC_CALLDATA);
798 FF non_empty_calldata_hash = poseidon2.hash(non_empty_calldata_preimage);
799 test_public_inputs.public_app_logic_call_requests[0].calldata_hash = non_empty_calldata_hash;
800
801 FF empty_calldata_hash = poseidon2.hash({ DOM_SEP__PUBLIC_CALLDATA });
802 test_public_inputs.public_app_logic_call_requests[1].calldata_hash = empty_calldata_hash;
803
805 { .context_id = 1, .calldata = dummy_setup_calldata },
806 { .context_id = 5, .calldata = non_empty_calldata },
807 { .context_id = 6, .calldata = {} },
808 };
809
810 auto setup_call_request = test_public_inputs.public_setup_call_requests[0];
811 auto non_empty_calldata_app_logic_call_request = test_public_inputs.public_app_logic_call_requests[0];
812 auto empty_calldata_app_logic_call_request = test_public_inputs.public_app_logic_call_requests[1];
813
815
816 set_initial_columns(trace,
817 { setup_call_request },
818 { non_empty_calldata_app_logic_call_request, empty_calldata_app_logic_call_request });
819
820 // Set calldata specific values for setup:
821 trace.set(4, { { { C::tx_calldata_size, 2 }, { C::tx_next_context_id, 1 } } });
822
823 // Set calldata specific values for non empty calldata call:
824 trace.set(8, { { { C::tx_calldata_size, 3 }, { C::tx_next_context_id, 5 } } });
825
826 // Set calldata specific values for empty calldata call:
827 trace.set(9, { { { C::tx_calldata_size, 0 }, { C::tx_next_context_id, 6 } } });
828
829 // Set teardown as padded:
830 trace.set(10, { { { C::tx_is_padded, 1 } } });
831
832 public_inputs_builder.process_public_inputs(trace, test_public_inputs);
833 public_inputs_builder.process_public_inputs_aux_precomputed(trace);
834
837
839
840 calldata_builder.process_retrieval(calldata_events, trace);
841 calldata_builder.process_hashing(calldata_events, trace);
842
843 check_relation<tx>(trace);
849 check_relation<bb::avm2::calldata_hashing<FF>>(trace);
850 check_all_interactions<tracegen::CalldataTraceBuilder>(trace);
851}
852} // namespace bb::avm2::constraining
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
#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 DOM_SEP__PUBLIC_STORAGE_MAP_SLOT
#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 DOM_SEP__PUBLIC_CALLDATA
#define FEE_JUICE_ADDRESS
#define AVM_PUBLIC_INPUTS_PREVIOUS_NON_REVERTIBLE_ACCUMULATED_DATA_ARRAY_LENGTHS_NULLIFIERS_ROW_IDX
#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:769
::testing::StrictMock< simulation::MockGreaterThan > mock_gt
Definition tx.test.cpp:773
simulation::EventEmitter< simulation::Poseidon2PermutationEvent > perm_event_emitter
Definition tx.test.cpp:770
::testing::StrictMock< simulation::MockExecutionIdManager > mock_execution_id_manager
Definition tx.test.cpp:774
simulation::EventEmitter< simulation::Poseidon2PermutationMemoryEvent > perm_mem_event_emitter
Definition tx.test.cpp:771
PublicInputsBuilder & rand_public_setup_call_requests(size_t n)
PublicInputsBuilder & rand_previous_non_revertible_accumulated_data(size_t n)
void process_hash(const simulation::EventEmitterInterface< simulation::Poseidon2HashEvent >::Container &hash_events, TraceContainer &trace)
Processes the hash events for the Poseidon2 hash function. It populates the columns for the poseidon2...
void process_misc(TraceContainer &trace, const uint32_t num_rows=PRECOMPUTED_TRACE_SIZE)
void process_public_inputs(TraceContainer &trace, const PublicInputs &public_inputs)
Populate the public inputs trace columns from the given public inputs.
void set(Column col, uint32_t row, const FF &value)
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
void check_interaction(tracegen::TestTraceContainer &trace)
TEST(AvmFixedVKTests, FixedVKCommitments)
Test that the fixed VK commitments agree with the ones computed from precomputed columns.
TEST_F(AvmRecursiveTests, TwoLayerAvmRecursionFailsWithWrongPIs)
TestTraceContainer empty_trace()
Definition fixtures.cpp:153
lookup_settings< lookup_tx_read_phase_spec_settings_ > lookup_tx_read_phase_spec_settings
AvmFlavorSettings::FF FF
Definition field.hpp:10
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
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:37
Settings to be passed ot GenericLookupRelationImpl.
tracegen::PublicInputsTraceBuilder public_inputs_builder
Definition tx.test.cpp:81