Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
tx_trace.cpp
Go to the documentation of this file.
2
7// #include "barretenberg/vm2/generated/relations/lookups_tx_context.hpp"
13
14#include <cstdint>
15#include <unordered_map>
16#include <variant>
17
18namespace bb::avm2::tracegen {
19
20namespace {
21
22using simulation::TxContextEvent;
23
24// helper type for the visitor #4
25template <class... Ts> struct overloaded : Ts... {
26 using Ts::operator()...;
27};
28// explicit deduction guide (not needed as of C++20)
29template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
30
31constexpr size_t NUM_PHASES = 12; // See TransactionPhase enum
32
33bool is_revertible(TransactionPhase phase)
34{
38}
39
40bool is_note_hash_insert_phase(TransactionPhase phase)
41{
43}
44
45bool is_nullifier_insert_phase(TransactionPhase phase)
46{
48}
49
50bool is_tree_insert_phase(TransactionPhase phase)
51{
52 return is_note_hash_insert_phase(phase) || is_nullifier_insert_phase(phase);
53}
54
55bool is_l2_l1_msg_phase(TransactionPhase phase)
56{
58}
59
60bool is_public_call_request_phase(TransactionPhase phase)
61{
62 return phase == TransactionPhase::SETUP || phase == TransactionPhase::APP_LOGIC ||
64}
65
66bool is_collect_fee_phase(TransactionPhase phase)
67{
69}
70
71bool is_tree_padding_phase(TransactionPhase phase)
72{
73 return phase == TransactionPhase::TREE_PADDING;
74}
75
76bool is_cleanup_phase(TransactionPhase phase)
77{
78 return phase == TransactionPhase::CLEANUP;
79}
80
81bool is_one_shot_phase(TransactionPhase phase)
82{
83 return is_collect_fee_phase(phase) || is_tree_padding_phase(phase) || is_cleanup_phase(phase);
84}
85
86bool is_teardown_phase(TransactionPhase phase)
87{
88 return phase == TransactionPhase::TEARDOWN;
89}
90
91// This is a helper to insert the previous and next tree state
92std::vector<std::pair<Column, FF>> insert_state(const TxContextEvent& prev_state, const TxContextEvent& next_state)
93{
94 return {
95 // Previous Tree State
96 // Note Hash
97 { Column::tx_prev_note_hash_tree_root, prev_state.tree_states.noteHashTree.tree.root },
98 { Column::tx_prev_note_hash_tree_size, prev_state.tree_states.noteHashTree.tree.nextAvailableLeafIndex },
99 { Column::tx_prev_num_note_hashes_emitted, prev_state.tree_states.noteHashTree.counter },
100 // Nullifier Tree Roots
101 { Column::tx_prev_nullifier_tree_root, prev_state.tree_states.nullifierTree.tree.root },
102 { Column::tx_prev_nullifier_tree_size, prev_state.tree_states.nullifierTree.tree.nextAvailableLeafIndex },
103 { Column::tx_prev_num_nullifiers_emitted, prev_state.tree_states.nullifierTree.counter },
104 // Public Data Tree Roots
105 { Column::tx_prev_public_data_tree_root, prev_state.tree_states.publicDataTree.tree.root },
106 { Column::tx_prev_public_data_tree_size, prev_state.tree_states.publicDataTree.tree.nextAvailableLeafIndex },
107 // Written Public Data Slots Tree Roots
108 { Column::tx_prev_written_public_data_slots_tree_root,
109 prev_state.written_public_data_slots_tree_snapshot.root },
110 { Column::tx_prev_written_public_data_slots_tree_size,
111 prev_state.written_public_data_slots_tree_snapshot.nextAvailableLeafIndex },
112 // L1 to L2 Message Tree Roots
113 { Column::tx_l1_l2_tree_root, prev_state.tree_states.l1ToL2MessageTree.tree.root },
114
115 // Next Tree State
116 { Column::tx_next_note_hash_tree_root, next_state.tree_states.noteHashTree.tree.root },
117 { Column::tx_next_note_hash_tree_size, next_state.tree_states.noteHashTree.tree.nextAvailableLeafIndex },
118 { Column::tx_next_num_note_hashes_emitted, next_state.tree_states.noteHashTree.counter },
119 // Nullifier Tree Roots
120 { Column::tx_next_nullifier_tree_root, next_state.tree_states.nullifierTree.tree.root },
121 { Column::tx_next_nullifier_tree_size, next_state.tree_states.nullifierTree.tree.nextAvailableLeafIndex },
122 { Column::tx_next_num_nullifiers_emitted, next_state.tree_states.nullifierTree.counter },
123 // Public Data Tree Roots
124 { Column::tx_next_public_data_tree_root, next_state.tree_states.publicDataTree.tree.root },
125 { Column::tx_next_public_data_tree_size, next_state.tree_states.publicDataTree.tree.nextAvailableLeafIndex },
126 // Written Public Data Slots Tree Roots
127 { Column::tx_next_written_public_data_slots_tree_root,
128 next_state.written_public_data_slots_tree_snapshot.root },
129 { Column::tx_next_written_public_data_slots_tree_size,
130 next_state.written_public_data_slots_tree_snapshot.nextAvailableLeafIndex },
131
132 // Prev sideffect state
133 { Column::tx_prev_num_unencrypted_logs, prev_state.side_effect_states.numUnencryptedLogs },
134 { Column::tx_prev_num_l2_to_l1_messages, prev_state.side_effect_states.numL2ToL1Messages },
135
136 // Next sideffect state
137 { Column::tx_next_num_unencrypted_logs, next_state.side_effect_states.numUnencryptedLogs },
138 { Column::tx_next_num_l2_to_l1_messages, next_state.side_effect_states.numL2ToL1Messages },
139
140 // Execution context
141 { Column::tx_next_context_id, prev_state.next_context_id },
142 };
143}
144
145std::vector<std::pair<Column, FF>> insert_side_effect_states(const SideEffectStates& prev_side_effect_states,
146 const SideEffectStates& next_side_effect_states)
147{
148 return {
149 {
150 Column::tx_prev_num_unencrypted_logs,
151 prev_side_effect_states.numUnencryptedLogs,
152 },
153 {
154 Column::tx_prev_num_l2_to_l1_messages,
155 prev_side_effect_states.numL2ToL1Messages,
156 },
157 {
158 Column::tx_next_num_unencrypted_logs,
159 next_side_effect_states.numUnencryptedLogs,
160 },
161 {
162 Column::tx_next_num_l2_to_l1_messages,
163 next_side_effect_states.numL2ToL1Messages,
164 },
165 };
166}
167
168// Helper to retrieve the read and write offsets and populate the read and write counters
169std::vector<std::pair<Column, FF>> handle_pi_read(TransactionPhase phase, uint32_t phase_length, uint32_t read_counter)
170
171{
172 auto [read_offset, write_offset, length_offset] = TxPhaseOffsetsTable::get_offsets(phase);
173
174 auto remaining_length = phase_length - read_counter;
175 auto remaining_length_inv = remaining_length == 0 ? 0 : FF(remaining_length).invert();
176 auto remaining_length_minus_one_inv = remaining_length - 1 == 0 ? 0 : FF(remaining_length - 1).invert();
177
178 return {
179 { Column::tx_read_pi_offset, read_offset + read_counter },
180 { Column::tx_read_pi_length_offset, length_offset - read_counter },
181
182 { Column::tx_remaining_phase_counter, remaining_length },
183 { Column::tx_remaining_phase_inv, remaining_length_inv },
184 { Column::tx_remaining_phase_minus_one_inv, remaining_length_minus_one_inv },
185 };
186}
187
188std::vector<std::pair<Column, FF>> handle_prev_gas_used(Gas prev_gas_used)
189{
190 return {
191 { Column::tx_prev_da_gas_used, prev_gas_used.daGas },
192 { Column::tx_prev_l2_gas_used, prev_gas_used.l2Gas },
193 };
194}
195
196std::vector<std::pair<Column, FF>> handle_next_gas_used(Gas next_gas_used)
197{
198 return {
199 { Column::tx_next_da_gas_used, next_gas_used.daGas },
200 { Column::tx_next_l2_gas_used, next_gas_used.l2Gas },
201 };
202}
203
204std::vector<std::pair<Column, FF>> handle_gas_limit(Gas gas_limit)
205{
206 return {
207 { Column::tx_da_gas_limit, gas_limit.daGas },
208 { Column::tx_l2_gas_limit, gas_limit.l2Gas },
209 };
210}
211
212std::vector<std::pair<Column, FF>> handle_enqueued_call_event(TransactionPhase phase,
213 const simulation::EnqueuedCallEvent& event)
214{
215 return { { Column::tx_is_public_call_request, 1 },
216 { Column::tx_should_process_call_request, 1 },
217 { Column::tx_is_teardown_phase, is_teardown_phase(phase) },
218 { Column::tx_msg_sender, event.msg_sender },
219 { Column::tx_contract_addr, event.contract_address },
220 { Column::tx_fee, event.transaction_fee },
221 { Column::tx_is_static, event.is_static },
222 { Column::tx_calldata_hash, event.calldata_hash },
223 { Column::tx_reverted, !event.success },
224 { Column::tx_prev_da_gas_used_sent_to_enqueued_call, event.start_gas.daGas },
225 { Column::tx_prev_l2_gas_used_sent_to_enqueued_call, event.start_gas.l2Gas },
226 { Column::tx_next_da_gas_used_sent_to_enqueued_call, event.end_gas.daGas },
227 { Column::tx_next_l2_gas_used_sent_to_enqueued_call, event.end_gas.l2Gas },
228 { Column::tx_gas_limit_pi_offset,
229 is_teardown_phase(phase) ? AVM_PUBLIC_INPUTS_GAS_SETTINGS_TEARDOWN_GAS_LIMITS_ROW_IDX : 0 },
230 { Column::tx_should_read_gas_limit, is_teardown_phase(phase) } };
231};
232
233std::vector<std::pair<Column, FF>> handle_note_hash_append(const simulation::PrivateAppendTreeEvent& event,
234 TransactionPhase phase,
235 const TxContextEvent& state_before,
236 bool reverted)
237{
238 uint32_t remaining_note_hashes = MAX_NOTE_HASHES_PER_TX - state_before.tree_states.noteHashTree.counter;
239
240 return {
241 { Column::tx_is_tree_insert_phase, 1 },
242 { Column::tx_leaf_value, event.leaf_value },
243 { Column::tx_remaining_side_effects_inv, remaining_note_hashes == 0 ? 0 : FF(remaining_note_hashes).invert() },
244 { Column::tx_sel_non_revertible_append_note_hash, phase == TransactionPhase::NR_NOTE_INSERTION },
245 { Column::tx_sel_revertible_append_note_hash, phase == TransactionPhase::R_NOTE_INSERTION },
246 { Column::tx_should_try_note_hash_append, 1 },
247 { Column::tx_should_note_hash_append, remaining_note_hashes > 0 },
248 { Column::tx_reverted, reverted ? 1 : 0 },
249 };
250}
251
252std::vector<std::pair<Column, FF>> handle_nullifier_append(const simulation::PrivateAppendTreeEvent& event,
253 TransactionPhase phase,
254 const TxContextEvent& state_before,
255 bool reverted)
256{
257 uint32_t remaining_nullifiers = MAX_NULLIFIERS_PER_TX - state_before.tree_states.nullifierTree.counter;
258
259 return {
260 { Column::tx_is_tree_insert_phase, 1 },
261 { Column::tx_leaf_value, event.leaf_value },
262 { Column::tx_remaining_side_effects_inv, remaining_nullifiers == 0 ? 0 : FF(remaining_nullifiers).invert() },
263 { Column::tx_sel_non_revertible_append_nullifier, phase == TransactionPhase::NR_NULLIFIER_INSERTION },
264 { Column::tx_sel_revertible_append_nullifier, phase == TransactionPhase::R_NULLIFIER_INSERTION },
265 { Column::tx_should_try_nullifier_append, 1 },
266 { Column::tx_should_nullifier_append, remaining_nullifiers > 0 },
267 { Column::tx_reverted, reverted ? 1 : 0 },
268 };
269}
270
271std::vector<std::pair<Column, FF>> handle_append_tree_event(const simulation::PrivateAppendTreeEvent& event,
272 TransactionPhase phase,
273 const TxContextEvent& state_before,
274 bool reverted)
275{
276 if (is_note_hash_insert_phase(phase)) {
277 return handle_note_hash_append(event, phase, state_before, reverted);
278 }
279 if (is_nullifier_insert_phase(phase)) {
280 return handle_nullifier_append(event, phase, state_before, reverted);
281 }
282 throw std::runtime_error("Invalid phase for append tree event");
283}
284
285std::vector<std::pair<Column, FF>> handle_l2_l1_msg_event(const simulation::PrivateEmitL2L1MessageEvent& event,
286 TransactionPhase phase,
287 const TxContextEvent& state_before,
288 bool reverted)
289{
290 uint32_t remaining_l2_to_l1_msgs = MAX_L2_TO_L1_MSGS_PER_TX - state_before.side_effect_states.numL2ToL1Messages;
291 return {
292 { Column::tx_sel_revertible_append_l2_l1_msg, phase == TransactionPhase::R_L2_TO_L1_MESSAGE },
293 { Column::tx_sel_non_revertible_append_l2_l1_msg, phase == TransactionPhase::NR_L2_TO_L1_MESSAGE },
294 { Column::tx_should_try_l2_l1_msg_append, 1 },
295 { Column::tx_remaining_side_effects_inv,
296 remaining_l2_to_l1_msgs == 0 ? 0 : FF(remaining_l2_to_l1_msgs).invert() },
297 { Column::tx_should_l2_l1_msg_append, remaining_l2_to_l1_msgs > 0 },
298 { Column::tx_l2_l1_msg_contract_address, event.scoped_msg.contractAddress },
299 { Column::tx_l2_l1_msg_recipient, event.scoped_msg.message.recipient },
300 { Column::tx_l2_l1_msg_content, event.scoped_msg.message.content },
301 { Column::tx_write_pi_offset,
303 state_before.side_effect_states.numL2ToL1Messages },
304 // Selectors
305 { Column::tx_reverted, reverted ? 1 : 0 },
306 };
307}
308
309std::vector<std::pair<Column, FF>> handle_collect_gas_fee_event(const simulation::CollectGasFeeEvent& event)
310{
311 return {
312 { Column::tx_is_collect_fee, 1 },
313 { Column::tx_effective_fee_per_da_gas, FF(event.effective_fee_per_da_gas) },
314 { Column::tx_effective_fee_per_l2_gas, FF(event.effective_fee_per_l2_gas) },
315 { Column::tx_fee_payer, event.fee_payer },
316 { Column::tx_fee_payer_pi_offset, AVM_PUBLIC_INPUTS_FEE_PAYER_ROW_IDX },
317 {
318 Column::tx_fee,
319 event.fee,
320 },
321 {
322 Column::tx_fee_juice_contract_address,
324 },
325 {
326 Column::tx_fee_juice_balances_slot,
328 },
329 {
330 Column::tx_fee_juice_balance_slot,
331 event.fee_juice_balance_slot,
332 },
333 {
334 Column::tx_fee_payer_balance,
335 event.fee_payer_balance,
336 },
337 {
338 Column::tx_fee_payer_new_balance,
339 event.fee_payer_balance - event.fee,
340 },
341 { Column::tx_uint32_max, 0xffffffff },
342 { Column::tx_write_pi_offset, AVM_PUBLIC_INPUTS_TRANSACTION_FEE_ROW_IDX },
343 };
344}
345
346std::vector<std::pair<Column, FF>> handle_tree_padding()
347{
348 return {
349 { Column::tx_is_tree_padding, 1 },
350 };
351}
352
353std::vector<std::pair<Column, FF>> handle_cleanup()
354{
355 return {
356 { Column::tx_is_cleanup, 1 },
357 // End state
358 { Column::tx_note_hash_pi_offset, AVM_PUBLIC_INPUTS_END_TREE_SNAPSHOTS_NOTE_HASH_TREE_ROW_IDX },
359 { Column::tx_should_read_note_hash_tree, 1 },
360 { Column::tx_nullifier_pi_offset, AVM_PUBLIC_INPUTS_END_TREE_SNAPSHOTS_NULLIFIER_TREE_ROW_IDX },
361 { Column::tx_should_read_nullifier_tree, 1 },
362 { Column::tx_public_data_pi_offset, AVM_PUBLIC_INPUTS_END_TREE_SNAPSHOTS_PUBLIC_DATA_TREE_ROW_IDX },
363 { Column::tx_should_read_public_data_tree, 1 },
365 { Column::tx_should_read_l1_l2_tree, 1 },
366 { Column::tx_gas_used_pi_offset, AVM_PUBLIC_INPUTS_END_GAS_USED_ROW_IDX },
367 { Column::tx_should_read_gas_used, 1 },
368 { Column::tx_array_length_note_hashes_pi_offset,
370 { Column::tx_array_length_nullifiers_pi_offset,
372 // Public data write counter is handled by the public data check trace due to squashing.
373 { Column::tx_array_length_l2_to_l1_messages_pi_offset,
375 { Column::tx_array_length_unencrypted_logs_pi_offset,
377 };
378}
379
380std::vector<std::pair<Column, FF>> handle_first_row()
381{
383 { Column::tx_start_tx, 1 },
384 { Column::tx_note_hash_pi_offset, AVM_PUBLIC_INPUTS_START_TREE_SNAPSHOTS_NOTE_HASH_TREE_ROW_IDX },
385 { Column::tx_should_read_note_hash_tree, 1 },
386 { Column::tx_nullifier_pi_offset, AVM_PUBLIC_INPUTS_START_TREE_SNAPSHOTS_NULLIFIER_TREE_ROW_IDX },
387 { Column::tx_should_read_nullifier_tree, 1 },
388 { Column::tx_public_data_pi_offset, AVM_PUBLIC_INPUTS_START_TREE_SNAPSHOTS_PUBLIC_DATA_TREE_ROW_IDX },
389 { Column::tx_should_read_public_data_tree, 1 },
391 { Column::tx_should_read_l1_l2_tree, 1 },
392 { Column::tx_gas_used_pi_offset, AVM_PUBLIC_INPUTS_START_GAS_USED_ROW_IDX },
393 { Column::tx_should_read_gas_used, 1 },
394 { Column::tx_gas_limit_pi_offset, AVM_PUBLIC_INPUTS_GAS_SETTINGS_GAS_LIMITS_ROW_IDX },
395 { Column::tx_should_read_gas_limit, 1 },
396 };
397
398 return columns;
399}
400
401std::vector<std::pair<Column, FF>> handle_padded_row(TransactionPhase phase, Gas gas_used, bool discard)
402{
403 // We should throw here - but tests are currently unsuitable
404 // assert(phase != TransactionPhase::COLLECT_GAS_FEES);
405
406 // TODO: We should probably split this into multiple functions, that are called if the padded phase is a specific
407 // phase.
409 { Column::tx_sel, 1 },
410 { Column::tx_discard, discard ? 1 : 0 },
411 { Column::tx_phase_value, static_cast<uint8_t>(phase) },
412 { Column::tx_setup_phase_value, static_cast<uint8_t>(TransactionPhase::SETUP) },
413 { Column::tx_is_padded, 1 },
414 { Column::tx_start_phase, 1 },
415 { Column::tx_sel_read_phase_length, !is_one_shot_phase(phase) },
416 // This is temporary because AvmVerifierTests.GoodPublicInputs doesnt collect gas fees, every transaction
417 // needs a collect gas fee
418 { Column::tx_is_collect_fee, is_collect_fee_phase(phase) ? 1 : 0 },
419 { Column::tx_end_phase, 1 },
420 // Selector specific
421 { Column::tx_is_tree_insert_phase, is_tree_insert_phase(phase) ? 1 : 0 },
422 { Column::tx_is_public_call_request, is_public_call_request_phase(phase) ? 1 : 0 },
423 { Column::tx_is_collect_fee, is_collect_fee_phase(phase) ? 1 : 0 },
424
425 { Column::tx_sel_revertible_append_note_hash, phase == TransactionPhase::R_NOTE_INSERTION ? 1 : 0 },
426 { Column::tx_sel_revertible_append_nullifier, phase == TransactionPhase::R_NULLIFIER_INSERTION ? 1 : 0 },
427 { Column::tx_sel_revertible_append_l2_l1_msg, phase == TransactionPhase::R_L2_TO_L1_MESSAGE ? 1 : 0 },
428 { Column::tx_sel_non_revertible_append_note_hash, phase == TransactionPhase::NR_NOTE_INSERTION ? 1 : 0 },
429 { Column::tx_sel_non_revertible_append_nullifier, phase == TransactionPhase::NR_NULLIFIER_INSERTION ? 1 : 0 },
430 { Column::tx_sel_non_revertible_append_l2_l1_msg, phase == TransactionPhase::NR_L2_TO_L1_MESSAGE ? 1 : 0 },
431
432 { Column::tx_is_collect_fee, is_collect_fee_phase(phase) ? 1 : 0 },
433
434 { Column::tx_is_revertible, is_revertible(phase) ? 1 : 0 },
435 // Public call request specific
436 { Column::tx_is_teardown_phase, is_teardown_phase(phase) },
437 { Column::tx_gas_limit_pi_offset,
438 is_teardown_phase(phase) ? AVM_PUBLIC_INPUTS_GAS_SETTINGS_TEARDOWN_GAS_LIMITS_ROW_IDX : 0 },
439 { Column::tx_should_read_gas_limit, is_teardown_phase(phase) },
440 // Gas used does not change in padding rows
441 { Column::tx_prev_da_gas_used_sent_to_enqueued_call,
442 is_public_call_request_phase(phase) && phase != TransactionPhase::TEARDOWN ? gas_used.daGas : 0 },
443 { Column::tx_prev_l2_gas_used_sent_to_enqueued_call,
444 is_public_call_request_phase(phase) && phase != TransactionPhase::TEARDOWN ? gas_used.l2Gas : 0 },
445 { Column::tx_next_da_gas_used_sent_to_enqueued_call,
446 is_public_call_request_phase(phase) && phase != TransactionPhase::TEARDOWN ? gas_used.daGas : 0 },
447 { Column::tx_next_l2_gas_used_sent_to_enqueued_call,
448 is_public_call_request_phase(phase) && phase != TransactionPhase::TEARDOWN ? gas_used.l2Gas : 0 },
449 };
450
451 return columns;
452}
453
454std::vector<std::pair<Column, FF>> handle_state_change_selectors(TransactionPhase phase)
455{
456 return {
457 { Column::tx_sel_can_emit_note_hash,
458 is_note_hash_insert_phase(phase) || is_public_call_request_phase(phase) || is_tree_padding_phase(phase) },
459 { Column::tx_sel_can_emit_nullifier,
460 is_nullifier_insert_phase(phase) || is_public_call_request_phase(phase) || is_tree_padding_phase(phase) },
461 { Column::tx_sel_can_write_public_data, is_collect_fee_phase(phase) || is_public_call_request_phase(phase) },
462 { Column::tx_sel_can_emit_unencrypted_log, is_public_call_request_phase(phase) },
463 { Column::tx_sel_can_emit_l2_l1_msg, is_l2_l1_msg_phase(phase) || is_public_call_request_phase(phase) },
464 };
465}
466
467} // namespace
468
471{
472 using C = Column;
473 uint32_t row = 1; // Shifts
474
475 // A nuance of the tracegen for the tx trace is that if there are no events in a phase, we still need to emit a
476 // row for this "skipped" row. This row is needed to simplify the circuit constraints and ensure that we have
477 // continuity in the tree state propagation
478
479 // We bucket the events by phase to make it easier to detect phases with no events
480 std::array<std::vector<const simulation::TxPhaseEvent*>, NUM_PHASES> phase_buckets = {};
481 // We have the phases in iterable form so that in the main loop when we and empty phase
482 // we can map back to this enum
495
497
498 bool r_insertion_or_app_logic_failure = false;
499 bool teardown_failure = false;
500 for (const auto& tx_event : events) {
502 startup_event = std::get<simulation::TxStartupEvent>(tx_event);
503 } else {
504 const simulation::TxPhaseEvent& tx_phase_event = std::get<simulation::TxPhaseEvent>(tx_event);
505 // Minus 1 since the enum is 1-indexed
506 phase_buckets[static_cast<uint8_t>(tx_phase_event.phase) - 1].push_back(&tx_phase_event);
507
508 // Set some flags for use when populating the discard column.
509 if (tx_phase_event.reverted) {
510 if (!is_revertible(tx_phase_event.phase)) {
511 throw std::runtime_error("Reverted in non-revertible phase: " +
512 std::to_string(static_cast<uint8_t>(tx_phase_event.phase)));
513 }
514
515 if (tx_phase_event.phase == TransactionPhase::TEARDOWN) {
516 teardown_failure = true;
517 } else {
518 r_insertion_or_app_logic_failure = true;
519 }
520 }
521 }
522 }
523
524 if (!startup_event.has_value()) {
525 throw std::runtime_error("Transaction startup event is missing");
526 }
527
528 const auto& startup_event_data = startup_event.value();
529
530 // This is the tree state we will use during the "skipped" phases
531 TxContextEvent propagated_state = startup_event_data.state;
532 TxContextEvent end_setup_snapshot = startup_event_data.state;
533 // Used to track the gas limit for the "padded" phases.
534 Gas current_gas_limit = startup_event_data.gas_limit;
535 Gas teardown_gas_limit = startup_event_data.teardown_gas_limit;
536 Gas gas_used = startup_event_data.state.gas_used;
537
538 // Go through each phase except startup and process the events in the phase
539 for (uint32_t i = 0; i < NUM_PHASES; i++) {
540 const auto& phase_events = phase_buckets[i];
541
542 TransactionPhase phase = phase_array[i];
543
544 bool discard = false;
545 if (is_revertible(phase)) {
546 if (phase == TransactionPhase::TEARDOWN) {
548 } else {
549 // Even if we don't fail until later in teardown, all revertible phases discard.
550 discard = teardown_failure || r_insertion_or_app_logic_failure;
551 }
552 }
553
554 if (is_teardown_phase(phase)) {
555 current_gas_limit = teardown_gas_limit;
556 }
557
558 if (phase_events.empty()) {
559 trace.set(row, insert_state(propagated_state, propagated_state));
560 trace.set(row, handle_padded_row(phase, gas_used, discard));
561 trace.set(row, handle_pi_read(phase, /*phase_length=*/0, /*read_counter*/ 0));
562 trace.set(row, handle_prev_gas_used(gas_used));
563 trace.set(row, handle_next_gas_used(gas_used));
564 trace.set(row, handle_gas_limit(current_gas_limit));
565 trace.set(row, handle_state_change_selectors(phase));
566 if (row == 1) {
567 trace.set(row, handle_first_row());
568 }
569 if (phase == TransactionPhase::SETUP) {
570 // If setup is empty, the end-setup-snapshot should just be current/propagated state
571 end_setup_snapshot = propagated_state;
572 }
573 row++;
574 continue;
575 }
576 // Count the number of steps in this phase
577 uint32_t phase_counter = 0;
578 uint32_t phase_length = static_cast<uint32_t>(phase_events.size());
579
580 // We have events to process in this phase
581 for (const auto& tx_phase_event : phase_events) {
582 // We always set the tree state
583 trace.set(row, insert_state(tx_phase_event->state_before, tx_phase_event->state_after));
584 trace.set(row,
585 insert_side_effect_states(tx_phase_event->state_before.side_effect_states,
586 tx_phase_event->state_after.side_effect_states));
587 trace.set(
588 row,
589 { {
590 { C::tx_sel, 1 },
591 { C::tx_discard, discard ? 1 : 0 },
592 { C::tx_phase_value, static_cast<uint8_t>(tx_phase_event->phase) },
593 { Column::tx_setup_phase_value, static_cast<uint8_t>(TransactionPhase::SETUP) },
594 { C::tx_is_padded, 0 },
595 { C::tx_start_phase, phase_counter == 0 ? 1 : 0 },
596 { C::tx_sel_read_phase_length, phase_counter == 0 && !is_one_shot_phase(tx_phase_event->phase) },
597 { C::tx_is_revertible, is_revertible(tx_phase_event->phase) ? 1 : 0 },
598 { C::tx_end_phase, phase_counter == phase_events.size() - 1 ? 1 : 0 },
599 } });
600 trace.set(row, handle_prev_gas_used(gas_used));
601 trace.set(row, handle_state_change_selectors(tx_phase_event->phase));
602 if (row == 1) {
603 trace.set(row, handle_first_row());
604 }
605
606 // Pattern match on the variant event type and call the appropriate handler
607 std::visit(
608 overloaded{ [&](const simulation::EnqueuedCallEvent& event) {
609 trace.set(row, handle_enqueued_call_event(tx_phase_event->phase, event));
610 // No explicit write counter for this phase
611 trace.set(row, handle_pi_read(tx_phase_event->phase, phase_length, phase_counter));
612
613 gas_used = tx_phase_event->state_after.gas_used;
614 },
616 trace.set(row,
617 handle_append_tree_event(event,
618 tx_phase_event->phase,
619 tx_phase_event->state_before,
620 tx_phase_event->reverted));
621
622 trace.set(row, handle_pi_read(tx_phase_event->phase, phase_length, phase_counter));
623 },
625 trace.set(row,
626 handle_l2_l1_msg_event(event,
627 tx_phase_event->phase,
628 tx_phase_event->state_before,
629 tx_phase_event->reverted));
630 trace.set(row, handle_pi_read(tx_phase_event->phase, phase_length, phase_counter));
631 },
633 trace.set(row, handle_pi_read(tx_phase_event->phase, 1, 0));
634 trace.set(row, handle_collect_gas_fee_event(event));
635 },
636 [&](const simulation::PadTreesEvent&) {
637 trace.set(row, handle_pi_read(tx_phase_event->phase, 1, 0));
638 trace.set(row, handle_tree_padding());
639 },
640 [&](const simulation::CleanupEvent&) {
641 trace.set(row, handle_pi_read(tx_phase_event->phase, 1, 0));
642 trace.set(row, handle_cleanup());
643 } },
644 tx_phase_event->event);
645 trace.set(row, handle_next_gas_used(gas_used));
646 trace.set(row, handle_gas_limit(current_gas_limit));
647
648 // Handle a potential phase jump due to a revert, we dont need to check if we are in a revertible phase
649 // since our witgen will have exited for any reverts in a non-revertible phase.
650 // If we revert in a phase that isnt TEARDOWN, we jump to TEARDOWN
651 if (tx_phase_event->reverted && tx_phase_event->phase != TransactionPhase::TEARDOWN) {
652 // Jump to the TEARDOWN phase
653 // we need to -2 because of the loop increment and because the enum is 1-indexed
654 i = static_cast<uint8_t>(TransactionPhase::TEARDOWN) - 2;
655 }
656 phase_counter++;
657 row++;
658 }
659 // In case we encounter another skip row
660 propagated_state = phase_events.back()->state_after;
661
662 if (phase == TransactionPhase::SETUP) {
663 // Store off the state at the end of setup to rollback to later on revert
664 end_setup_snapshot = phase_events.back()->state_after;
665 }
666 if (phase_events.back()->reverted) {
667 // On revert, roll back to end-setup-snapshot
668 // Even though tx-execution events should already do this,
669 // we need to update propagated state here so that any padded rows
670 // get the correct rolled-back state rather then the pre-rollback state.
671 propagated_state.tree_states = end_setup_snapshot.tree_states;
674 propagated_state.side_effect_states = end_setup_snapshot.side_effect_states;
675 // Note: we only rollback tree/side-effect states, not gas used or next_context_id.
676 }
677 }
678}
679
682 // These are all generic, think which, if any, can be made sequential.
684 .add<lookup_tx_phase_jump_on_revert_settings, InteractionType::LookupGeneric>()
686 .add<lookup_tx_read_public_call_request_phase_settings, InteractionType::LookupGeneric>()
688 .add<lookup_tx_dispatch_exec_end_settings, InteractionType::LookupGeneric>()
690 .add<lookup_tx_read_l2_l1_msg_settings, InteractionType::LookupGeneric>()
692 .add<lookup_tx_read_effective_fee_public_inputs_settings, InteractionType::LookupGeneric>()
694 .add<lookup_tx_balance_validation_settings, InteractionType::LookupGeneric>()
696 .add<lookup_tx_nullifier_append_settings, InteractionType::LookupGeneric>()
698 .add<lookup_tx_balance_update_settings, InteractionType::LookupGeneric>()
700 .add<lookup_tx_balance_slot_poseidon2_settings, InteractionType::LookupGeneric>()
702 .add<lookup_tx_context_public_inputs_nullifier_tree_settings, InteractionType::LookupIntoIndexedByClk>()
704 .add<lookup_tx_context_public_inputs_l1_l2_tree_settings, InteractionType::LookupIntoIndexedByClk>()
706 .add<lookup_tx_context_public_inputs_read_gas_limit_settings, InteractionType::LookupIntoIndexedByClk>()
708 .add<lookup_tx_context_public_inputs_write_nullifier_count_settings, InteractionType::LookupIntoIndexedByClk>()
714
715} // namespace bb::avm2::tracegen
#define AVM_PUBLIC_INPUTS_AVM_ACCUMULATED_DATA_ARRAY_LENGTHS_NOTE_HASHES_ROW_IDX
#define AVM_PUBLIC_INPUTS_END_TREE_SNAPSHOTS_L1_TO_L2_MESSAGE_TREE_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_GAS_SETTINGS_TEARDOWN_GAS_LIMITS_ROW_IDX
#define AVM_PUBLIC_INPUTS_TRANSACTION_FEE_ROW_IDX
#define AVM_PUBLIC_INPUTS_AVM_ACCUMULATED_DATA_ARRAY_LENGTHS_L2_TO_L1_MSGS_ROW_IDX
#define AVM_PUBLIC_INPUTS_END_GAS_USED_ROW_IDX
#define AVM_PUBLIC_INPUTS_AVM_ACCUMULATED_DATA_ARRAY_LENGTHS_NULLIFIERS_ROW_IDX
#define AVM_PUBLIC_INPUTS_START_TREE_SNAPSHOTS_L1_TO_L2_MESSAGE_TREE_ROW_IDX
#define AVM_PUBLIC_INPUTS_END_TREE_SNAPSHOTS_NULLIFIER_TREE_ROW_IDX
#define FEE_JUICE_ADDRESS
#define AVM_PUBLIC_INPUTS_START_TREE_SNAPSHOTS_NOTE_HASH_TREE_ROW_IDX
#define AVM_PUBLIC_INPUTS_AVM_ACCUMULATED_DATA_ARRAY_LENGTHS_PUBLIC_LOGS_ROW_IDX
#define AVM_PUBLIC_INPUTS_END_TREE_SNAPSHOTS_NOTE_HASH_TREE_ROW_IDX
#define MAX_L2_TO_L1_MSGS_PER_TX
#define MAX_NOTE_HASHES_PER_TX
#define AVM_PUBLIC_INPUTS_START_TREE_SNAPSHOTS_PUBLIC_DATA_TREE_ROW_IDX
#define FEE_JUICE_BALANCES_SLOT
#define AVM_PUBLIC_INPUTS_START_TREE_SNAPSHOTS_NULLIFIER_TREE_ROW_IDX
#define MAX_NULLIFIERS_PER_TX
#define AVM_PUBLIC_INPUTS_START_GAS_USED_ROW_IDX
#define AVM_PUBLIC_INPUTS_GAS_SETTINGS_GAS_LIMITS_ROW_IDX
#define AVM_PUBLIC_INPUTS_END_TREE_SNAPSHOTS_PUBLIC_DATA_TREE_ROW_IDX
InteractionDefinition & add(auto &&... args)
static const Offsets & get_offsets(TransactionPhase phase)
void process(const simulation::EventEmitterInterface< simulation::TxEvent >::Container &events, TraceContainer &trace)
Definition tx_trace.cpp:469
static const InteractionDefinition interactions
Definition tx_trace.hpp:19
TestTraceContainer trace
bool teardown_failure
lookup_settings< lookup_tx_context_public_inputs_write_l2_to_l1_message_count_settings_ > lookup_tx_context_public_inputs_write_l2_to_l1_message_count_settings
lookup_settings< lookup_tx_read_fee_payer_public_inputs_settings_ > lookup_tx_read_fee_payer_public_inputs_settings
lookup_settings< lookup_tx_balance_read_settings_ > lookup_tx_balance_read_settings
lookup_settings< lookup_tx_context_public_inputs_note_hash_tree_settings_ > lookup_tx_context_public_inputs_note_hash_tree_settings
lookup_settings< lookup_tx_read_tree_insert_value_settings_ > lookup_tx_read_tree_insert_value_settings
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_write_fee_public_inputs_settings_ > lookup_tx_write_fee_public_inputs_settings
lookup_settings< lookup_tx_context_public_inputs_public_data_tree_settings_ > lookup_tx_context_public_inputs_public_data_tree_settings
lookup_settings< lookup_tx_context_public_inputs_gas_used_settings_ > lookup_tx_context_public_inputs_gas_used_settings
lookup_settings< lookup_tx_write_l2_l1_msg_settings_ > lookup_tx_write_l2_l1_msg_settings
lookup_settings< lookup_tx_context_restore_state_on_revert_settings_ > lookup_tx_context_restore_state_on_revert_settings
lookup_settings< lookup_tx_context_public_inputs_write_note_hash_count_settings_ > lookup_tx_context_public_inputs_write_note_hash_count_settings
lookup_settings< lookup_tx_note_hash_append_settings_ > lookup_tx_note_hash_append_settings
lookup_settings< lookup_tx_dispatch_exec_start_settings_ > lookup_tx_dispatch_exec_start_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
std::string to_string(bb::avm2::ValueTag tag)
simulation::PublicDataTreeReadWriteEvent event
AppendOnlyTreeSnapshot written_public_data_slots_tree_snapshot