Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
execution.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <concepts>
5#include <cstdint>
6#include <functional>
7#include <stdexcept>
8#include <string>
9
12
27
28namespace bb::avm2::simulation {
29
30// For every opcode execution method (e.g. Execution::add(), Execution::sub(), etc), it is crucial to preserve the
31// following order of operations (temporality groups 3,4,5,6):
32// 1. Temporality group 3 (Register read): Set the inputs and validate them. (RegisterValidationException might be
33// thrown.)
34// 2. Temporality group 4 (Gas): Consume gas. (OutOfGasException might be thrown.)
35// 3. Temporality group 5 (Opcode execution): Execute the opcode. (OpcodeExecutionException might be thrown.)
36// 4. Temporality group 6 (Register write): Set the output.
37
38// This order is crucial for the completeness of the circuit. In tracegen, we rely on this order to correctly
39// populate the execution trace. In particular, we stop processing if any of the above exceptions are thrown.
40
42{
43 BB_BENCH_NAME("Execution::add");
44 constexpr auto opcode = ExecutionOpCode::ADD;
45 auto& memory = context.get_memory();
46 MemoryValue a = memory.get(a_addr);
47 MemoryValue b = memory.get(b_addr);
48 set_and_validate_inputs(opcode, { a, b });
49
51
52 try {
53 MemoryValue c = alu.add(a, b);
54 memory.set(dst_addr, c);
55 set_output(opcode, c);
56 } catch (AluException& e) {
57 throw OpcodeExecutionException("Alu add operation failed");
58 }
59}
60
62{
63 BB_BENCH_NAME("Execution::sub");
64 constexpr auto opcode = ExecutionOpCode::SUB;
65 auto& memory = context.get_memory();
66 MemoryValue a = memory.get(a_addr);
67 MemoryValue b = memory.get(b_addr);
68 set_and_validate_inputs(opcode, { a, b });
69
71
72 try {
73 MemoryValue c = alu.sub(a, b);
74 memory.set(dst_addr, c);
75 set_output(opcode, c);
76 } catch (AluException& e) {
77 throw OpcodeExecutionException("Alu sub operation failed");
78 }
79}
80
82{
83 BB_BENCH_NAME("Execution::mul");
84 constexpr auto opcode = ExecutionOpCode::MUL;
85 auto& memory = context.get_memory();
86 MemoryValue a = memory.get(a_addr);
87 MemoryValue b = memory.get(b_addr);
88 set_and_validate_inputs(opcode, { a, b });
89
91
92 try {
93 MemoryValue c = alu.mul(a, b);
94 memory.set(dst_addr, c);
95 set_output(opcode, c);
96 } catch (AluException& e) {
97 throw OpcodeExecutionException("Alu mul operation failed");
98 }
99}
100
102{
103 BB_BENCH_NAME("Execution::div");
104 constexpr auto opcode = ExecutionOpCode::DIV;
105 auto& memory = context.get_memory();
106 MemoryValue a = memory.get(a_addr);
107 MemoryValue b = memory.get(b_addr);
108 set_and_validate_inputs(opcode, { a, b });
109
111
112 try {
113 MemoryValue c = alu.div(a, b);
114 memory.set(dst_addr, c);
115 set_output(opcode, c);
116 } catch (AluException& e) {
117 throw OpcodeExecutionException("Alu div operation failed");
118 }
119}
120
122{
123 BB_BENCH_NAME("Execution::fdiv");
124 constexpr auto opcode = ExecutionOpCode::FDIV;
125 auto& memory = context.get_memory();
126 MemoryValue a = memory.get(a_addr);
127 MemoryValue b = memory.get(b_addr);
128 set_and_validate_inputs(opcode, { a, b });
129
131
132 try {
133 MemoryValue c = alu.fdiv(a, b);
134 memory.set(dst_addr, c);
135 set_output(opcode, c);
136 } catch (AluException& e) {
137 throw OpcodeExecutionException("Alu fdiv operation failed");
138 }
139}
140
142{
143 BB_BENCH_NAME("Execution::eq");
144 constexpr auto opcode = ExecutionOpCode::EQ;
145 auto& memory = context.get_memory();
146 MemoryValue a = memory.get(a_addr);
147 MemoryValue b = memory.get(b_addr);
148 set_and_validate_inputs(opcode, { a, b });
149
151
152 try {
153 MemoryValue c = alu.eq(a, b);
154 memory.set(dst_addr, c);
155 set_output(opcode, c);
156 } catch (AluException& e) {
157 throw OpcodeExecutionException("Alu eq operation failed");
158 }
159}
160
162{
163 BB_BENCH_NAME("Execution::lt");
164 constexpr auto opcode = ExecutionOpCode::LT;
165 auto& memory = context.get_memory();
166 MemoryValue a = memory.get(a_addr);
167 MemoryValue b = memory.get(b_addr);
168 set_and_validate_inputs(opcode, { a, b });
169
171
172 try {
173 MemoryValue c = alu.lt(a, b);
174 memory.set(dst_addr, c);
175 set_output(opcode, c);
176 } catch (AluException& e) {
177 throw OpcodeExecutionException("Alu lt operation failed");
178 }
179}
180
182{
183 BB_BENCH_NAME("Execution::lte");
184 constexpr auto opcode = ExecutionOpCode::LT;
185 auto& memory = context.get_memory();
186 MemoryValue a = memory.get(a_addr);
187 MemoryValue b = memory.get(b_addr);
188 set_and_validate_inputs(opcode, { a, b });
189
191
192 try {
193 MemoryValue c = alu.lte(a, b);
194 memory.set(dst_addr, c);
195 set_output(opcode, c);
196 } catch (AluException& e) {
197 throw OpcodeExecutionException("Alu lte operation failed");
198 }
199}
200
202{
203 BB_BENCH_NAME("Execution::op_not");
204 constexpr auto opcode = ExecutionOpCode::NOT;
205 auto& memory = context.get_memory();
206 MemoryValue a = memory.get(src_addr);
207 set_and_validate_inputs(opcode, { a });
208
210
211 try {
212 MemoryValue b = alu.op_not(a);
213 memory.set(dst_addr, b);
214 set_output(opcode, b);
215 } catch (AluException& e) {
216 throw OpcodeExecutionException("Alu not operation failed");
217 }
218}
219
221{
222 BB_BENCH_NAME("Execution::shl");
223 constexpr auto opcode = ExecutionOpCode::SHL;
224 auto& memory = context.get_memory();
225 MemoryValue a = memory.get(a_addr);
226 MemoryValue b = memory.get(b_addr);
227 set_and_validate_inputs(opcode, { a, b });
228
230
231 try {
232 MemoryValue c = alu.shl(a, b);
233 memory.set(c_addr, c);
234 set_output(opcode, c);
235 } catch (const AluException& e) {
236 throw OpcodeExecutionException("SHL Exception: " + std::string(e.what()));
237 }
238}
239
241{
242 BB_BENCH_NAME("Execution::shr");
243 constexpr auto opcode = ExecutionOpCode::SHR;
244 auto& memory = context.get_memory();
245 MemoryValue a = memory.get(a_addr);
246 MemoryValue b = memory.get(b_addr);
247 set_and_validate_inputs(opcode, { a, b });
248
250
251 try {
252 MemoryValue c = alu.shr(a, b);
253 memory.set(c_addr, c);
254 set_output(opcode, c);
255 } catch (const AluException& e) {
256 throw OpcodeExecutionException("SHR Exception: " + std::string(e.what()));
257 }
258}
259
261{
262 BB_BENCH_NAME("Execution::cast");
263 constexpr auto opcode = ExecutionOpCode::CAST;
264 auto& memory = context.get_memory();
265 auto val = memory.get(src_addr);
266 set_and_validate_inputs(opcode, { val });
267
269 MemoryValue truncated = alu.truncate(val.as_ff(), static_cast<MemoryTag>(dst_tag));
270 memory.set(dst_addr, truncated);
271 set_output(opcode, truncated);
272}
273
275{
276 BB_BENCH_NAME("Execution::get_env_var");
277 constexpr auto opcode = ExecutionOpCode::GETENVVAR;
278 auto& memory = context.get_memory();
279
281
282 TaggedValue result;
283
284 EnvironmentVariable env_var = static_cast<EnvironmentVariable>(var_enum);
285 switch (env_var) {
287 result = TaggedValue::from<FF>(context.get_address());
288 break;
290 result = TaggedValue::from<FF>(context.get_msg_sender());
291 break;
293 result = TaggedValue::from<FF>(context.get_transaction_fee());
294 break;
296 result = TaggedValue::from<FF>(context.get_globals().chainId);
297 break;
299 result = TaggedValue::from<FF>(context.get_globals().version);
300 break;
302 result = TaggedValue::from<uint32_t>(context.get_globals().blockNumber);
303 break;
305 result = TaggedValue::from<uint64_t>(context.get_globals().timestamp);
306 break;
308 result = TaggedValue::from<uint128_t>(context.get_globals().gasFees.feePerL2Gas);
309 break;
311 result = TaggedValue::from<uint128_t>(context.get_globals().gasFees.feePerDaGas);
312 break;
314 result = TaggedValue::from<uint1_t>(context.get_is_static() ? 1 : 0);
315 break;
317 result = TaggedValue::from<uint32_t>(context.gas_left().l2Gas);
318 break;
320 result = TaggedValue::from<uint32_t>(context.gas_left().daGas);
321 break;
322 default:
323 throw OpcodeExecutionException("Invalid environment variable enum value");
324 }
325
326 memory.set(dst_addr, result);
327 set_output(opcode, result);
328}
329
330// TODO: My dispatch system makes me have a uint8_t tag. Rethink.
332{
333 BB_BENCH_NAME("Execution::set");
335
336 constexpr auto opcode = ExecutionOpCode::SET;
337 MemoryValue truncated = alu.truncate(value, static_cast<MemoryTag>(tag));
338 context.get_memory().set(dst_addr, truncated);
339 set_output(opcode, truncated);
340}
341
343{
344 BB_BENCH_NAME("Execution::mov");
345 constexpr auto opcode = ExecutionOpCode::MOV;
346 auto& memory = context.get_memory();
347 auto v = memory.get(src_addr);
348 set_and_validate_inputs(opcode, { v });
349
351
352 memory.set(dst_addr, v);
353 set_output(opcode, v);
354}
355
357 MemoryAddress l2_gas_offset,
358 MemoryAddress da_gas_offset,
359 MemoryAddress addr,
360 MemoryAddress cd_size_offset,
362{
363 BB_BENCH_NAME("Execution::call");
364 constexpr auto opcode = ExecutionOpCode::CALL;
365 auto& memory = context.get_memory();
366
367 // NOTE: these reads cannot fail due to addressing guarantees.
368 const auto& allocated_l2_gas_read = memory.get(l2_gas_offset);
369 const auto& allocated_da_gas_read = memory.get(da_gas_offset);
370 const auto& contract_address = memory.get(addr);
371 // Cd offset loads are deferred to calldatacopy
372 const auto& cd_size = memory.get(cd_size_offset);
373
374 set_and_validate_inputs(opcode, { allocated_l2_gas_read, allocated_da_gas_read, contract_address, cd_size });
375
376 get_gas_tracker().consume_gas(); // Base gas.
378 Gas{ allocated_l2_gas_read.as<uint32_t>(), allocated_da_gas_read.as<uint32_t>() });
379
380 // Tag check contract address + cd_size
382 /*msg_sender=*/context.get_address(),
383 /*transaction_fee=*/context.get_transaction_fee(),
384 /*parent_context=*/context,
385 /*cd_offset_address=*/cd_offset,
386 /*cd_size=*/cd_size.as<uint32_t>(),
387 /*is_static=*/context.get_is_static(),
388 /*gas_limit=*/gas_limit,
389 /*side_effect_states=*/context.get_side_effect_states(),
390 /*phase=*/context.get_phase());
391
392 // We do not recurse. This context will be use on the next cycle of execution.
393 handle_enter_call(context, std::move(nested_context));
394}
395
397 MemoryAddress l2_gas_offset,
398 MemoryAddress da_gas_offset,
399 MemoryAddress addr,
400 MemoryAddress cd_size_offset,
402{
403 BB_BENCH_NAME("Execution::static_call");
404 constexpr auto opcode = ExecutionOpCode::CALL;
405 auto& memory = context.get_memory();
406
407 // NOTE: these reads cannot fail due to addressing guarantees.
408 const auto& allocated_l2_gas_read = memory.get(l2_gas_offset);
409 const auto& allocated_da_gas_read = memory.get(da_gas_offset);
410 const auto& contract_address = memory.get(addr);
411 // Cd offset loads are deferred to calldatacopy
412 const auto& cd_size = memory.get(cd_size_offset);
413
414 set_and_validate_inputs(opcode, { allocated_l2_gas_read, allocated_da_gas_read, contract_address, cd_size });
415
416 get_gas_tracker().consume_gas(); // Base gas.
418 Gas{ allocated_l2_gas_read.as<uint32_t>(), allocated_da_gas_read.as<uint32_t>() });
419
420 // Tag check contract address + cd_size
422 /*msg_sender=*/context.get_address(),
423 /*transaction_fee=*/context.get_transaction_fee(),
424 /*parent_context=*/context,
425 /*cd_offset_address=*/cd_offset,
426 /*cd_size=*/cd_size.as<uint32_t>(),
427 /*is_static=*/true,
428 /*gas_limit=*/gas_limit,
429 /*side_effect_states=*/context.get_side_effect_states(),
430 /*phase=*/context.get_phase());
431
432 // We do not recurse. This context will be use on the next cycle of execution.
433 handle_enter_call(context, std::move(nested_context));
434}
435
437 MemoryAddress cd_size_offset,
440{
441 BB_BENCH_NAME("Execution::cd_copy");
442 constexpr auto opcode = ExecutionOpCode::CALLDATACOPY;
443 auto& memory = context.get_memory();
444 auto cd_copy_size = memory.get(cd_size_offset); // Tag check u32
445 auto cd_offset_read = memory.get(cd_offset); // Tag check u32
446 set_and_validate_inputs(opcode, { cd_copy_size, cd_offset_read });
447
448 get_gas_tracker().consume_gas({ .l2Gas = cd_copy_size.as<uint32_t>(), .daGas = 0 });
449
450 try {
451 data_copy.cd_copy(context, cd_copy_size.as<uint32_t>(), cd_offset_read.as<uint32_t>(), dst_addr);
452 } catch (const std::exception& e) {
453 throw OpcodeExecutionException("cd copy failed: " + std::string(e.what()));
454 }
455}
456
458 MemoryAddress rd_size_offset,
459 MemoryAddress rd_offset,
461{
462 BB_BENCH_NAME("Execution::rd_copy");
463 constexpr auto opcode = ExecutionOpCode::RETURNDATACOPY;
464 auto& memory = context.get_memory();
465 auto rd_copy_size = memory.get(rd_size_offset); // Tag check u32
466 auto rd_offset_read = memory.get(rd_offset); // Tag check u32
467 set_and_validate_inputs(opcode, { rd_copy_size, rd_offset_read });
468
469 get_gas_tracker().consume_gas({ .l2Gas = rd_copy_size.as<uint32_t>(), .daGas = 0 });
470
471 try {
472 data_copy.rd_copy(context, rd_copy_size.as<uint32_t>(), rd_offset_read.as<uint32_t>(), dst_addr);
473 } catch (const std::exception& e) {
474 throw OpcodeExecutionException("rd copy failed: " + std::string(e.what()));
475 }
476}
477
479{
480 BB_BENCH_NAME("Execution::rd_size");
481 constexpr auto opcode = ExecutionOpCode::RETURNDATASIZE;
482 auto& memory = context.get_memory();
483
485
486 // This is safe because the last_rd_size is tag checked on ret/revert to be U32
487 MemoryValue rd_size = MemoryValue::from<uint32_t>(context.get_last_rd_size());
488 memory.set(dst_addr, rd_size);
489 set_output(opcode, rd_size);
490}
491
493{
494 BB_BENCH_NAME("Execution::ret");
495 constexpr auto opcode = ExecutionOpCode::RETURN;
496 auto& memory = context.get_memory();
497 auto rd_size = memory.get(ret_size_offset);
498 set_and_validate_inputs(opcode, { rd_size });
499
501
502 set_execution_result({ .rd_offset = ret_offset,
503 .rd_size = rd_size.as<uint32_t>(),
504 .gas_used = context.get_gas_used(),
505 .side_effect_states = context.get_side_effect_states(),
506 .success = true });
507
508 context.halt();
509}
510
512{
513 BB_BENCH_NAME("Execution::revert");
514 constexpr auto opcode = ExecutionOpCode::REVERT;
515 auto& memory = context.get_memory();
516 auto rev_size = memory.get(rev_size_offset);
517 set_and_validate_inputs(opcode, { rev_size });
518
520
521 set_execution_result({ .rd_offset = rev_offset,
522 .rd_size = rev_size.as<uint32_t>(),
523 .gas_used = context.get_gas_used(),
524 .side_effect_states = context.get_side_effect_states(),
525 .success = false });
526
527 context.halt();
528}
529
531{
532 BB_BENCH_NAME("Execution::jump");
534
535 context.set_next_pc(loc);
536}
537
539{
540 BB_BENCH_NAME("Execution::jumpi");
541 constexpr auto opcode = ExecutionOpCode::JUMPI;
542 auto& memory = context.get_memory();
543
544 auto resolved_cond = memory.get(cond_addr);
545 set_and_validate_inputs(opcode, { resolved_cond });
546
548
549 if (resolved_cond.as<uint1_t>().value() == 1) {
550 context.set_next_pc(loc);
551 }
552}
553
555{
556 BB_BENCH_NAME("Execution::internal_call");
558
559 auto& internal_call_stack_manager = context.get_internal_call_stack_manager();
560 // The next pc is pushed onto the internal call stack. This will become return_pc later.
561 internal_call_stack_manager.push(context.get_next_pc());
562 context.set_next_pc(loc);
563}
564
566{
567 BB_BENCH_NAME("Execution::internal_return");
569
570 auto& internal_call_stack_manager = context.get_internal_call_stack_manager();
571 try {
572 auto next_pc = internal_call_stack_manager.pop();
573 context.set_next_pc(next_pc);
574 } catch (const std::exception& e) {
575 // Re-throw
576 throw OpcodeExecutionException("Internal return failed: " + std::string(e.what()));
577 }
578}
579
581{
582 BB_BENCH_NAME("Execution::keccak_permutation");
584
585 try {
586 keccakf1600.permutation(context.get_memory(), dst_addr, src_addr);
587 } catch (const KeccakF1600Exception& e) {
588 throw OpcodeExecutionException("Keccak permutation failed: " + std::string(e.what()));
589 }
590}
591
593 MemoryAddress level_offset,
594 MemoryAddress message_offset,
595 MemoryAddress fields_offset,
596 MemoryAddress fields_size_offset,
597 uint16_t message_size)
598{
599 BB_BENCH_NAME("Execution::debug_log");
601
603 context.get_address(),
604 level_offset,
605 message_offset,
606 message_size,
607 fields_offset,
608 fields_size_offset);
609}
610
612{
613 BB_BENCH_NAME("Execution::success_copy");
614 constexpr auto opcode = ExecutionOpCode::SUCCESSCOPY;
615 auto& memory = context.get_memory();
616
618
619 MemoryValue success = MemoryValue::from<uint1_t>(context.get_last_success());
620 memory.set(dst_addr, success);
621 set_output(opcode, success);
622}
623
625{
626 BB_BENCH_NAME("Execution::and_op");
627 constexpr auto opcode = ExecutionOpCode::AND;
628 auto& memory = context.get_memory();
629 MemoryValue a = memory.get(a_addr);
630 MemoryValue b = memory.get(b_addr);
631 set_and_validate_inputs(opcode, { a, b });
632
633 // Dynamic gas consumption for bitwise is dependent on the tag, FF tags are valid here but
634 // will result in an exception in the bitwise subtrace.
635 get_gas_tracker().consume_gas({ .l2Gas = get_tag_bytes(a.get_tag()), .daGas = 0 });
636
637 try {
638 MemoryValue c = bitwise.and_op(a, b);
639 memory.set(dst_addr, c);
640 set_output(opcode, c);
641 } catch (const BitwiseException& e) {
642 throw OpcodeExecutionException("Bitwise AND Exeception");
643 }
644}
645
647{
648 BB_BENCH_NAME("Execution::or_op");
649 constexpr auto opcode = ExecutionOpCode::OR;
650 auto& memory = context.get_memory();
651 MemoryValue a = memory.get(a_addr);
652 MemoryValue b = memory.get(b_addr);
653 set_and_validate_inputs(opcode, { a, b });
654
655 // Dynamic gas consumption for bitwise is dependent on the tag, FF tags are valid here but
656 // will result in an exception in the bitwise subtrace.
657 get_gas_tracker().consume_gas({ .l2Gas = get_tag_bytes(a.get_tag()), .daGas = 0 });
658
659 try {
660 MemoryValue c = bitwise.or_op(a, b);
661 memory.set(dst_addr, c);
662 set_output(opcode, c);
663 } catch (const BitwiseException& e) {
664 throw OpcodeExecutionException("Bitwise OR Exception");
665 }
666}
667
669{
670 BB_BENCH_NAME("Execution::xor_op");
671 constexpr auto opcode = ExecutionOpCode::XOR;
672 auto& memory = context.get_memory();
673 MemoryValue a = memory.get(a_addr);
674 MemoryValue b = memory.get(b_addr);
675 set_and_validate_inputs(opcode, { a, b });
676
677 // Dynamic gas consumption for bitwise is dependent on the tag, FF tags are valid here but
678 // will result in an exception in the bitwise subtrace.
679 get_gas_tracker().consume_gas({ .l2Gas = get_tag_bytes(a.get_tag()), .daGas = 0 });
680
681 try {
682 MemoryValue c = bitwise.xor_op(a, b);
683 memory.set(dst_addr, c);
684 set_output(opcode, c);
685 } catch (const BitwiseException& e) {
686 throw OpcodeExecutionException("Bitwise XOR Exception");
687 }
688}
689
691{
692 BB_BENCH_NAME("Execution::sload");
693 constexpr auto opcode = ExecutionOpCode::SLOAD;
694
695 auto& memory = context.get_memory();
696
697 auto slot = memory.get(slot_addr);
698 set_and_validate_inputs(opcode, { slot });
699
701
702 auto value = MemoryValue::from<FF>(merkle_db.storage_read(context.get_address(), slot.as<FF>()));
703
704 memory.set(dst_addr, value);
705 set_output(opcode, value);
706}
707
709{
710 BB_BENCH_NAME("Execution::sstore");
711 constexpr auto opcode = ExecutionOpCode::SSTORE;
712
713 auto& memory = context.get_memory();
714
715 auto slot = memory.get(slot_addr);
716 auto value = memory.get(src_addr);
717 set_and_validate_inputs(opcode, { value, slot });
718
719 bool was_slot_written_before = merkle_db.was_storage_written(context.get_address(), slot.as_ff());
720 uint32_t da_gas_factor = static_cast<uint32_t>(!was_slot_written_before);
721 get_gas_tracker().consume_gas({ .l2Gas = 0, .daGas = da_gas_factor });
722
723 if (context.get_is_static()) {
724 throw OpcodeExecutionException("SSTORE: Cannot write to storage in static context");
725 }
726
727 if (!was_slot_written_before &&
729 throw OpcodeExecutionException("SSTORE: Maximum number of data writes reached");
730 }
731
732 merkle_db.storage_write(context.get_address(), slot.as_ff(), value.as_ff(), false);
733}
734
736 MemoryAddress unique_note_hash_addr,
737 MemoryAddress leaf_index_addr,
739{
740 BB_BENCH_NAME("Execution::note_hash_exists");
741 constexpr auto opcode = ExecutionOpCode::NOTEHASHEXISTS;
742
743 auto& memory = context.get_memory();
744 auto unique_note_hash = memory.get(unique_note_hash_addr);
745 auto leaf_index = memory.get(leaf_index_addr);
746 set_and_validate_inputs(opcode, { unique_note_hash, leaf_index });
747
749
750 uint64_t leaf_index_value = leaf_index.as<uint64_t>();
751
752 bool index_in_range = greater_than.gt(NOTE_HASH_TREE_LEAF_COUNT, leaf_index_value);
753
755
756 if (index_in_range) {
757 value = MemoryValue::from<uint1_t>(merkle_db.note_hash_exists(leaf_index_value, unique_note_hash.as<FF>()));
758 } else {
759 value = MemoryValue::from<uint1_t>(0);
760 }
761
762 memory.set(dst_addr, value);
763 set_output(opcode, value);
764}
765
767 MemoryAddress nullifier_offset,
768 MemoryAddress address_offset,
769 MemoryAddress exists_offset)
770{
771 BB_BENCH_NAME("Execution::nullifier_exists");
772 constexpr auto opcode = ExecutionOpCode::NULLIFIEREXISTS;
773 auto& memory = context.get_memory();
774
775 auto nullifier = memory.get(nullifier_offset);
776 auto address = memory.get(address_offset);
777 set_and_validate_inputs(opcode, { nullifier, address });
778
780
781 // Check nullifier existence via MerkleDB
782 // (this also tag checks address and nullifier as FFs)
783 auto exists = merkle_db.nullifier_exists(address.as_ff(), nullifier.as_ff());
784
785 // Write result to memory
786 // (assigns tag u1 to result)
787 TaggedValue result = TaggedValue::from<uint1_t>(exists ? 1 : 0);
788 memory.set(exists_offset, result);
789 set_output(opcode, result);
790}
791
793{
794 BB_BENCH_NAME("Execution::emit_nullifier");
795 constexpr auto opcode = ExecutionOpCode::EMITNULLIFIER;
796
797 auto& memory = context.get_memory();
798 MemoryValue nullifier = memory.get(nullifier_addr);
800
802
803 if (context.get_is_static()) {
804 throw OpcodeExecutionException("EMITNULLIFIER: Cannot emit nullifier in static context");
805 }
806
808 throw OpcodeExecutionException("EMITNULLIFIER: Maximum number of nullifiers reached");
809 }
810
811 // Emit nullifier via MerkleDB.
812 try {
813 merkle_db.nullifier_write(context.get_address(), nullifier.as<FF>());
814 } catch (const NullifierCollisionException& e) {
815 throw OpcodeExecutionException(format("EMITNULLIFIER: ", e.what()));
816 }
817}
818
820 MemoryAddress address_offset,
821 MemoryAddress dst_offset,
822 uint8_t member_enum)
823{
824 BB_BENCH_NAME("Execution::get_contract_instance");
825 constexpr auto opcode = ExecutionOpCode::GETCONTRACTINSTANCE;
826 auto& memory = context.get_memory();
827
828 // Execution can still handle address memory read and tag checking
829 auto address_value = memory.get(address_offset);
830 AztecAddress contract_address = address_value.as<AztecAddress>();
831 set_and_validate_inputs(opcode, { address_value });
832
834
835 // Call the dedicated opcode component to get the contract instance, validate the enum,
836 // handle other errors, and perform the memory writes.
837 try {
839 } catch (const GetContractInstanceException& e) {
840 throw OpcodeExecutionException("GetContractInstance Exception");
841 }
842
843 // No `set_output` here since the dedicated component handles memory writes.
844}
845
847{
848 BB_BENCH_NAME("Execution::emit_note_hash");
849 constexpr auto opcode = ExecutionOpCode::EMITNOTEHASH;
850
851 auto& memory = context.get_memory();
852 auto note_hash = memory.get(note_hash_addr);
853 set_and_validate_inputs(opcode, { note_hash });
854
856
857 if (context.get_is_static()) {
858 throw OpcodeExecutionException("EMITNOTEHASH: Cannot emit note hash in static context");
859 }
860
862 throw OpcodeExecutionException("EMITNOTEHASH: Maximum number of note hashes reached");
863 }
864
865 merkle_db.note_hash_write(context.get_address(), note_hash.as<FF>());
866}
867
869 MemoryAddress msg_hash_addr,
870 MemoryAddress leaf_index_addr,
872{
873 BB_BENCH_NAME("Execution::l1_to_l2_message_exists");
874 constexpr auto opcode = ExecutionOpCode::L1TOL2MSGEXISTS;
875
876 auto& memory = context.get_memory();
877 auto msg_hash = memory.get(msg_hash_addr);
878 auto leaf_index = memory.get(leaf_index_addr);
879 set_and_validate_inputs(opcode, { msg_hash, leaf_index });
880
882
883 uint64_t leaf_index_value = leaf_index.as<uint64_t>();
884
885 bool index_in_range = greater_than.gt(L1_TO_L2_MSG_TREE_LEAF_COUNT, leaf_index_value);
886
888
889 if (index_in_range) {
890 value = MemoryValue::from<uint1_t>(merkle_db.l1_to_l2_msg_exists(leaf_index_value, msg_hash.as<FF>()));
891 } else {
892 value = MemoryValue::from<uint1_t>(0);
893 }
894
895 memory.set(dst_addr, value);
896 set_output(opcode, value);
897}
898
900{
901 BB_BENCH_NAME("Execution::poseidon2_permutation");
903 try {
904 poseidon2.permutation(context.get_memory(), src_addr, dst_addr);
905 } catch (const Poseidon2Exception& e) {
906 throw OpcodeExecutionException("Poseidon2 permutation failed: " + std::string(e.what()));
907 }
908}
909
911 MemoryAddress p_x_addr,
912 MemoryAddress p_y_addr,
913 MemoryAddress p_inf_addr,
914 MemoryAddress q_x_addr,
915 MemoryAddress q_y_addr,
916 MemoryAddress q_inf_addr,
918{
919 BB_BENCH_NAME("Execution::ecc_add");
920 constexpr auto opcode = ExecutionOpCode::ECADD;
921 auto& memory = context.get_memory();
922
923 // Read the points from memory.
924 const MemoryValue& p_x = memory.get(p_x_addr);
925 const MemoryValue& p_y = memory.get(p_y_addr);
926 const MemoryValue& p_inf = memory.get(p_inf_addr);
927
928 const MemoryValue& q_x = memory.get(q_x_addr);
929 const MemoryValue& q_y = memory.get(q_y_addr);
930 const MemoryValue& q_inf = memory.get(q_inf_addr);
931
932 set_and_validate_inputs(opcode, { p_x, p_y, p_inf, q_x, q_y, q_inf });
934
935 // Once inputs are tag checked the conversion to EmbeddedCurvePoint is safe, on curve checks are done in the add
936 // method.
937 EmbeddedCurvePoint p = EmbeddedCurvePoint(p_x.as_ff(), p_y.as_ff(), p_inf == MemoryValue::from<uint1_t>(1));
938 EmbeddedCurvePoint q = EmbeddedCurvePoint(q_x.as_ff(), q_y.as_ff(), q_inf == MemoryValue::from<uint1_t>(1));
939
940 try {
942 } catch (const EccException& e) {
943 throw OpcodeExecutionException("Embedded curve add failed: " + std::string(e.what()));
944 }
945}
946
948 MemoryAddress value_addr,
949 MemoryAddress radix_addr,
950 MemoryAddress num_limbs_addr,
951 MemoryAddress is_output_bits_addr, // Decides if output is U1 or U8
953{
954 BB_BENCH_NAME("Execution::to_radix_be");
955 constexpr auto opcode = ExecutionOpCode::TORADIXBE;
956 auto& memory = context.get_memory();
957
958 const MemoryValue& value = memory.get(value_addr); // Field
959 const MemoryValue& radix = memory.get(radix_addr); // U32
960 const MemoryValue& num_limbs = memory.get(num_limbs_addr); // U32
961 const MemoryValue& is_output_bits = memory.get(is_output_bits_addr); // U1
962
963 // Tag check the inputs
964 {
965 BB_BENCH_NAME("Execution::to_radix_be::set_and_validate_inputs");
966 set_and_validate_inputs(opcode, { value, radix, num_limbs, is_output_bits });
967 }
968
969 // The range check for a valid radix (2 <= radix <= 256) is done in the gadget.
970 // However, in order to compute the dynamic gas value we need to constrain the radix
971 // to be <= 256 since the `get_p_limbs_per_radix` lookup table is only defined for the range [0, 256].
972 // This does mean that the <= 256 check is duplicated - this can be optimized later.
973
974 // The dynamic gas factor is the maximum of the num_limbs requested by the opcode and the number of limbs
975 // the gadget that the field modulus, p, decomposes into given a radix (num_p_limbs).
976 // See to_radix.pil for how these values impact the row count.
977
978 // The lookup table of radix decomposed limbs of the modulus p is defined for radix values [0, 256],
979 // so for any radix value greater than 256 we set num_p_limbs to 32 - with
980 // the understanding the opcode will fail in the gadget (since the radix is invalid).
981 uint32_t radix_value = radix.as<uint32_t>();
982 uint32_t num_p_limbs = greater_than.gt(radix.as<uint32_t>(), 256)
983 ? 32
984 : static_cast<uint32_t>(get_p_limbs_per_radix_size(radix_value));
985
986 // Compute the dynamic gas factor - done this way to trigger relevant circuit interactions
987 if (greater_than.gt(num_limbs.as<uint32_t>(), num_p_limbs)) {
988 get_gas_tracker().consume_gas({ .l2Gas = num_limbs.as<uint32_t>(), .daGas = 0 });
989 } else {
990 get_gas_tracker().consume_gas({ .l2Gas = num_p_limbs, .daGas = 0 });
991 }
992
993 try {
994 // Call the gadget to perform the conversion.
995 to_radix.to_be_radix(memory,
996 value.as_ff(),
997 radix.as<uint32_t>(),
998 num_limbs.as<uint32_t>(),
999 is_output_bits.as<uint1_t>().value() == 1,
1000 dst_addr);
1001 } catch (const ToRadixException& e) {
1002 throw OpcodeExecutionException("ToRadixBe gadget failed: " + std::string(e.what()));
1003 }
1004}
1005
1007{
1008 BB_BENCH_NAME("Execution::emit_unencrypted_log");
1009 constexpr auto opcode = ExecutionOpCode::EMITUNENCRYPTEDLOG;
1010 auto& memory = context.get_memory();
1011
1012 const MemoryValue& log_size = memory.get(log_size_offset);
1013 set_and_validate_inputs(opcode, { log_size });
1014 uint32_t log_size_int = log_size.as<uint32_t>();
1015
1016 get_gas_tracker().consume_gas({ .l2Gas = log_size_int, .daGas = log_size_int });
1017
1018 // Call the dedicated opcode component to emit the log
1019 try {
1021 memory, context, context.get_address(), log_offset, log_size_int);
1022 } catch (const EmitUnencryptedLogException& e) {
1023 throw OpcodeExecutionException("EmitUnencryptedLog Exception");
1024 }
1025}
1026
1028{
1029 BB_BENCH_NAME("Execution::send_l2_to_l1_msg");
1030 constexpr auto opcode = ExecutionOpCode::SENDL2TOL1MSG;
1031 auto& memory = context.get_memory();
1032
1033 const MemoryValue& recipient = memory.get(recipient_addr);
1034 const MemoryValue& content = memory.get(content_addr);
1035 set_and_validate_inputs(opcode, { recipient, content });
1036
1038
1039 auto side_effects_states_before = context.get_side_effect_states();
1040
1041 if (context.get_is_static()) {
1042 throw OpcodeExecutionException("SENDL2TOL1MSG: Cannot send L2 to L1 message in static context");
1043 }
1044
1045 if (side_effects_states_before.numL2ToL1Messages == MAX_L2_TO_L1_MSGS_PER_TX) {
1046 throw OpcodeExecutionException("SENDL2TOL1MSG: Maximum number of L2 to L1 messages reached");
1047 }
1048
1049 // TODO: We don't store the l2 to l1 message in the context since it's not needed until cpp has to generate
1050 // public inputs.
1051
1052 side_effects_states_before.numL2ToL1Messages++;
1053 context.set_side_effect_states(side_effects_states_before);
1054}
1055
1057 MemoryAddress output_addr,
1058 MemoryAddress state_addr,
1059 MemoryAddress input_addr)
1060{
1061 BB_BENCH_NAME("Execution::sha256_compression");
1063
1064 try {
1065 sha256.compression(context.get_memory(), state_addr, input_addr, output_addr);
1066 } catch (const Sha256CompressionException& e) {
1067 throw OpcodeExecutionException("Sha256 Compression failed: " + std::string(e.what()));
1068 }
1069}
1070
1071// This context interface is a top-level enqueued one.
1072// NOTE: For the moment this trace is not returning the context back.
1074{
1075 BB_BENCH_NAME("Execution::execute");
1076 external_call_stack.push(std::move(enqueued_call_context));
1077
1078 while (!external_call_stack.empty()) {
1079 // We fix the context at this point. Even if the opcode changes the stack
1080 // we'll always use this in the loop.
1081 auto& context = *external_call_stack.top();
1082
1083 // We'll be filling in the event as we go. And we always emit at the end.
1084 ExecutionEvent ex_event;
1085
1086 try {
1087 // State before doing anything.
1088 ex_event.before_context_event = context.serialize_context_event();
1090 auto pc = context.get_pc();
1091
1092 // Temporality group 1: Bytecode retrieval. //
1093
1094 // We try to get the bytecode id. This can throw if the contract is not deployed or if we have retrieved too
1095 // many unique class ids. Note: bytecode_id is tracked in context events, not in the top-level execution
1096 // event. It is already included in the before_context_event (defaulting to 0 on error/not-found).
1097 context.get_bytecode_manager().get_bytecode_id();
1098
1099 // Temporality group 2: Instruction fetching and addressing. //
1100
1101 // We try to fetch an instruction.
1102 Instruction instruction = context.get_bytecode_manager().read_instruction(pc);
1103
1104 ex_event.wire_instruction = instruction;
1105 debug("@", pc, " ", instruction.to_string());
1106 context.set_next_pc(pc + static_cast<uint32_t>(instruction.size_in_bytes()));
1107
1108 // Resolve the operands.
1110 std::vector<Operand> resolved_operands = addressing->resolve(instruction, context.get_memory());
1111
1113 // Temporality group 3: Registers read. (triggered in each opcode (dispatch_opcode()) with
1114 // set_and_validate_inputs(opcode, { ... });)
1115 // Temporality group 4: Gas. (triggered in each opcode (dispatch_opcode()) with
1116 // get_gas_tracker().consume_gas();)
1117 // Temporality group 5: Opcode execution. (in dispatch_opcode())
1118 // Temporality group 6: Register write. (in dispatch_opcode())
1119
1121 dispatch_opcode(instruction.get_exec_opcode(), context, resolved_operands);
1122 }
1123 // TODO(fcarreiro): handle this in a better way.
1124 catch (const BytecodeRetrievalError& e) {
1125 vinfo("Bytecode retrieval error:: ", e.what());
1128 } catch (const InstructionFetchingError& e) {
1129 vinfo("Instruction fetching error: ", e.what());
1132 } catch (const AddressingException& e) {
1133 vinfo("Addressing exception: ", e.what());
1136 } catch (const RegisterValidationException& e) {
1137 vinfo("Register validation exception: ", e.what());
1140 } catch (const OutOfGasException& e) {
1141 vinfo("Out of gas exception: ", e.what());
1142 ex_event.error = ExecutionError::GAS;
1144 } catch (const OpcodeExecutionException& e) {
1145 vinfo("Opcode execution exception: ", e.what());
1148 } catch (const std::exception& e) {
1149 // This is a coding error, we should not get here.
1150 // All exceptions should fall in the above catch blocks.
1151 info("An unhandled exception occurred: ", e.what());
1152 throw e;
1153 }
1154
1155 // We always do what follows. "Finally".
1156 // Move on to the next pc.
1157 context.set_pc(context.get_next_pc());
1159
1160 // TODO: we set the inputs and outputs here and into the execution event, but maybe there's a better way
1161 ex_event.inputs = get_inputs();
1162 ex_event.output = get_output();
1163
1164 // State after the opcode.
1165 ex_event.after_context_event = context.serialize_context_event();
1166 events.emit(std::move(ex_event));
1167
1168 // If the context has halted, we need to exit the external call.
1169 // The external call stack is expected to be popped.
1170 if (context.halted()) {
1172 }
1173 }
1174
1175 return get_execution_result();
1176}
1177
1179{
1180 ctx_stack_events.emit(
1181 { .id = parent_context.get_context_id(),
1182 .parent_id = parent_context.get_parent_id(),
1183 .entered_context_id = child_context->get_context_id(), // gets the context id of the child!
1184 .next_pc = parent_context.get_next_pc(),
1185 .msg_sender = parent_context.get_msg_sender(),
1186 .contract_addr = parent_context.get_address(),
1187 .bytecode_id = parent_context.get_bytecode_manager()
1189 .value(), // Bytecode should have been retrieved in the parent context if it issued a call.
1190 .is_static = parent_context.get_is_static(),
1191 .parent_cd_addr = parent_context.get_parent_cd_addr(),
1192 .parent_cd_size = parent_context.get_parent_cd_size(),
1193 .parent_gas_used = parent_context.get_parent_gas_used(),
1194 .parent_gas_limit = parent_context.get_parent_gas_limit(),
1195 .tree_states = merkle_db.get_tree_state(),
1196 .written_public_data_slots_tree_snapshot = parent_context.get_written_public_data_slots_tree_snapshot(),
1197 .side_effect_states = parent_context.get_side_effect_states() });
1198
1199 external_call_stack.push(std::move(child_context));
1200}
1201
1203{
1204 BB_BENCH_NAME("Execution::handle_exit_call");
1205
1206 // NOTE: the current (child) context should not be modified here, since it was already emitted.
1208 external_call_stack.pop();
1210
1211 // We only handle reverting/committing of nested calls. Enqueued calls are handled by TX execution.
1212 if (!external_call_stack.empty()) {
1213 // Note: committing or reverting the db here also commits or reverts the
1214 // tracked nullifiers, public writes dictionary, etc. These structures
1215 // "listen" to the db changes.
1216 if (result.success) {
1218 } else {
1220 }
1221
1222 auto& parent_context = *external_call_stack.top();
1223 // was not top level, communicate with parent
1224 parent_context.set_last_rd_addr(result.rd_offset);
1225 parent_context.set_last_rd_size(result.rd_size);
1226 parent_context.set_last_success(result.success);
1227 // Safe since the nested context gas limit should be clamped to the available gas.
1228 parent_context.set_gas_used(result.gas_used + parent_context.get_gas_used());
1229 if (result.success) {
1230 parent_context.set_side_effect_states(result.side_effect_states);
1231 }
1232 parent_context.set_child_context(std::move(child_context));
1233
1234 // TODO(fcarreiro): move somewhere else.
1235 if (parent_context.get_checkpoint_id_at_creation() != merkle_db.get_checkpoint_id()) {
1236 throw std::runtime_error(format("Checkpoint id mismatch: ",
1237 parent_context.get_checkpoint_id_at_creation(),
1238 " != ",
1240 " (gone back to the wrong db/context)"));
1241 }
1242 }
1243 // Else: was top level. ExecutionResult is already set and that will be returned.
1244}
1245
1247{
1248 context.set_gas_used(context.get_gas_limit()); // Consume all gas.
1249 context.halt();
1251 .rd_offset = 0,
1252 .rd_size = 0,
1253 .gas_used = context.get_gas_used(),
1254 .side_effect_states = context.get_side_effect_states(),
1255 .success = false,
1256 });
1257}
1258
1261 const std::vector<Operand>& resolved_operands)
1262{
1263 BB_BENCH_NAME("Execution::dispatch_opcode");
1264
1265 // TODO: consider doing this even before the dispatch.
1266 inputs = {};
1267 output = TaggedValue::from<FF>(0);
1268
1269 debug("Dispatching opcode: ", opcode, " (", static_cast<uint32_t>(opcode), ")");
1270 switch (opcode) {
1272 call_with_operands(&Execution::add, context, resolved_operands);
1273 break;
1275 call_with_operands(&Execution::sub, context, resolved_operands);
1276 break;
1278 call_with_operands(&Execution::mul, context, resolved_operands);
1279 break;
1281 call_with_operands(&Execution::div, context, resolved_operands);
1282 break;
1284 call_with_operands(&Execution::fdiv, context, resolved_operands);
1285 break;
1287 call_with_operands(&Execution::eq, context, resolved_operands);
1288 break;
1290 call_with_operands(&Execution::lt, context, resolved_operands);
1291 break;
1293 call_with_operands(&Execution::lte, context, resolved_operands);
1294 break;
1296 call_with_operands(&Execution::op_not, context, resolved_operands);
1297 break;
1299 call_with_operands(&Execution::shl, context, resolved_operands);
1300 break;
1302 call_with_operands(&Execution::shr, context, resolved_operands);
1303 break;
1305 call_with_operands(&Execution::cast, context, resolved_operands);
1306 break;
1309 break;
1311 call_with_operands(&Execution::set, context, resolved_operands);
1312 break;
1314 call_with_operands(&Execution::mov, context, resolved_operands);
1315 break;
1317 call_with_operands(&Execution::call, context, resolved_operands);
1318 break;
1321 break;
1323 call_with_operands(&Execution::ret, context, resolved_operands);
1324 break;
1326 call_with_operands(&Execution::revert, context, resolved_operands);
1327 break;
1329 call_with_operands(&Execution::jump, context, resolved_operands);
1330 break;
1332 call_with_operands(&Execution::jumpi, context, resolved_operands);
1333 break;
1335 call_with_operands(&Execution::cd_copy, context, resolved_operands);
1336 break;
1338 call_with_operands(&Execution::rd_copy, context, resolved_operands);
1339 break;
1342 break;
1345 break;
1348 break;
1351 break;
1353 call_with_operands(&Execution::rd_size, context, resolved_operands);
1354 break;
1356 call_with_operands(&Execution::debug_log, context, resolved_operands);
1357 break;
1359 call_with_operands(&Execution::and_op, context, resolved_operands);
1360 break;
1362 call_with_operands(&Execution::or_op, context, resolved_operands);
1363 break;
1365 call_with_operands(&Execution::xor_op, context, resolved_operands);
1366 break;
1368 call_with_operands(&Execution::sload, context, resolved_operands);
1369 break;
1371 call_with_operands(&Execution::sstore, context, resolved_operands);
1372 break;
1375 break;
1378 break;
1381 break;
1384 break;
1387 break;
1390 break;
1393 break;
1395 call_with_operands(&Execution::ecc_add, context, resolved_operands);
1396 break;
1399 break;
1402 break;
1405 break;
1408 break;
1409 default:
1410 // NOTE: Keep this a `std::runtime_error` so that the main loop panics.
1411 throw std::runtime_error("Tried to dispatch unknown execution opcode: " +
1412 std::to_string(static_cast<uint32_t>(opcode)));
1413 }
1414}
1415
1416// Some template magic to dispatch the opcode by deducing the number of arguments and types,
1417// and making the appropriate checks and casts.
1418template <typename... Ts>
1421 const std::vector<Operand>& resolved_operands)
1422{
1423 assert(resolved_operands.size() == sizeof...(Ts));
1424 auto operand_indices = std::make_index_sequence<sizeof...(Ts)>{};
1425 [f, this, &context, &resolved_operands]<std::size_t... Is>(std::index_sequence<Is...>) {
1426 (this->*f)(context, resolved_operands.at(Is).to<std::decay_t<Ts>>()...);
1427 }(operand_indices);
1428}
1429
1430// Sets the register inputs and validates the tags.
1431// The tag information is taken from the instruction info database (exec spec).
1433{
1434 const auto& register_info = instruction_info_db.get(opcode).register_info;
1435 assert(inputs.size() == register_info.num_inputs());
1436 this->inputs = std::move(inputs);
1437 for (size_t i = 0; i < register_info.num_inputs(); i++) {
1438 if (register_info.expected_tag(i) && register_info.expected_tag(i) != this->inputs.at(i).get_tag()) {
1439 throw RegisterValidationException(format("Input ",
1440 i,
1441 " tag ",
1442 std::to_string(this->inputs.at(i).get_tag()),
1443 " does not match expected tag ",
1444 std::to_string(*register_info.expected_tag(i))));
1445 }
1446 }
1447}
1448
1450{
1451 const auto& register_info = instruction_info_db.get(opcode).register_info;
1452 (void)register_info; // To please GCC.
1453 assert(register_info.num_outputs() == 1);
1454 this->output = std::move(output);
1455}
1456
1457} // namespace bb::avm2::simulation
MemoryTag dst_tag
#define NOTE_HASH_TREE_LEAF_COUNT
#define L1_TO_L2_MSG_TREE_LEAF_COUNT
#define MAX_L2_TO_L1_MSGS_PER_TX
#define MAX_NOTE_HASHES_PER_TX
#define MAX_NULLIFIERS_PER_TX
#define MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:218
virtual std::optional< BytecodeId > get_retrieved_bytecode_id()=0
virtual const AztecAddress & get_msg_sender() const =0
virtual Gas get_parent_gas_limit() const =0
virtual uint32_t get_next_pc() const =0
virtual uint32_t get_parent_cd_size() const =0
virtual MemoryAddress get_parent_cd_addr() const =0
virtual AppendOnlyTreeSnapshot get_written_public_data_slots_tree_snapshot()=0
virtual SideEffectStates & get_side_effect_states()=0
virtual uint32_t get_parent_id() const =0
virtual bool get_is_static() const =0
virtual BytecodeManagerInterface & get_bytecode_manager()=0
virtual const AztecAddress & get_address() const =0
virtual uint32_t get_context_id() const =0
virtual Gas get_parent_gas_used() const =0
virtual uint32_t get_next_context_id() const =0
virtual std::unique_ptr< ContextInterface > make_nested_context(AztecAddress address, AztecAddress msg_sender, FF transaction_fee, ContextInterface &parent_context, MemoryAddress cd_offset_address, uint32_t cd_size, bool is_static, Gas gas_limit, SideEffectStates side_effect_states, TransactionPhase phase)=0
virtual void debug_log(MemoryInterface &memory, AztecAddress contract_address, MemoryAddress level_offset, MemoryAddress message_offset, uint16_t message_size, MemoryAddress fields_offset, MemoryAddress fields_size_offset)=0
virtual EmbeddedCurvePoint add(const EmbeddedCurvePoint &p, const EmbeddedCurvePoint &q)=0
virtual void emit_unencrypted_log(MemoryInterface &memory, ContextInterface &context, AztecAddress contract_address, MemoryAddress log_offset, uint32_t log_size)=0
virtual std::unique_ptr< GasTrackerInterface > make_gas_tracker(GasEvent &gas_event, const Instruction &instruction, ContextInterface &context)=0
virtual std::unique_ptr< AddressingInterface > make_addressing(AddressingEvent &event)=0
void lt(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
void emit_note_hash(ContextInterface &context, MemoryAddress note_hash_addr)
void mov(ContextInterface &context, MemoryAddress src_addr, MemoryAddress dst_addr)
const TaggedValue & get_output() const
void static_call(ContextInterface &context, MemoryAddress l2_gas_offset, MemoryAddress da_gas_offset, MemoryAddress addr, MemoryAddress cd_size_offset, MemoryAddress cd_offset)
void debug_log(ContextInterface &context, MemoryAddress level_offset, MemoryAddress message_offset, MemoryAddress fields_offset, MemoryAddress fields_size_offset, uint16_t message_size)
EventEmitterInterface< ExecutionEvent > & events
void cd_copy(ContextInterface &context, MemoryAddress cd_size_offset, MemoryAddress cd_offset, MemoryAddress dst_addr)
std::unique_ptr< GasTrackerInterface > gas_tracker
void send_l2_to_l1_msg(ContextInterface &context, MemoryAddress recipient_addr, MemoryAddress content_addr)
void dispatch_opcode(ExecutionOpCode opcode, ContextInterface &context, const std::vector< Operand > &resolved_operands)
ExecutionComponentsProviderInterface & execution_components
void sstore(ContextInterface &context, MemoryAddress src_addr, MemoryAddress slot_addr)
void set(ContextInterface &context, MemoryAddress dst_addr, uint8_t tag, const FF &value)
void internal_return(ContextInterface &context)
void set_output(ExecutionOpCode opcode, TaggedValue output)
virtual GasTrackerInterface & get_gas_tracker()
void poseidon2_permutation(ContextInterface &context, MemoryAddress src_addr, MemoryAddress dst_addr)
void success_copy(ContextInterface &context, MemoryAddress dst_addr)
void fdiv(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
void jumpi(ContextInterface &context, MemoryAddress cond_addr, uint32_t loc)
void sub(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
Definition execution.cpp:61
void rd_copy(ContextInterface &context, MemoryAddress rd_size_offset, MemoryAddress rd_offset, MemoryAddress dst_addr)
void l1_to_l2_message_exists(ContextInterface &context, MemoryAddress msg_hash_addr, MemoryAddress leaf_index_addr, MemoryAddress dst_addr)
void emit_unencrypted_log(ContextInterface &context, MemoryAddress log_size_offset, MemoryAddress log_offset)
void div(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
void emit_nullifier(ContextInterface &context, MemoryAddress nullifier_addr)
EventEmitterInterface< ContextStackEvent > & ctx_stack_events
void set_execution_result(ExecutionResult exec_result)
void ecc_add(ContextInterface &context, MemoryAddress p_x_addr, MemoryAddress p_y_addr, MemoryAddress p_inf_addr, MemoryAddress q_x_addr, MemoryAddress q_y_addr, MemoryAddress q_inf_addr, MemoryAddress dst_addr)
void keccak_permutation(ContextInterface &context, MemoryAddress dst_addr, MemoryAddress src_addr)
void jump(ContextInterface &context, uint32_t loc)
void sha256_compression(ContextInterface &context, MemoryAddress output_addr, MemoryAddress state_addr, MemoryAddress input_addr)
void sload(ContextInterface &context, MemoryAddress slot_addr, MemoryAddress dst_addr)
void ret(ContextInterface &context, MemoryAddress ret_size_offset, MemoryAddress ret_offset)
std::vector< TaggedValue > inputs
void internal_call(ContextInterface &context, uint32_t loc)
void op_not(ContextInterface &context, MemoryAddress src_addr, MemoryAddress dst_addr)
void handle_exceptional_halt(ContextInterface &context)
void handle_enter_call(ContextInterface &parent_context, std::unique_ptr< ContextInterface > child_context)
void nullifier_exists(ContextInterface &context, MemoryAddress nullifier_offset, MemoryAddress address_offset, MemoryAddress exists_offset)
void note_hash_exists(ContextInterface &context, MemoryAddress unique_note_hash_addr, MemoryAddress leaf_index_addr, MemoryAddress dst_addr)
ContextProviderInterface & context_provider
ExecutionResult execute(std::unique_ptr< ContextInterface > enqueued_call_context) override
void shl(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress c_addr)
void cast(ContextInterface &context, MemoryAddress src_addr, MemoryAddress dst_addr, uint8_t dst_tag)
void to_radix_be(ContextInterface &context, MemoryAddress value_addr, MemoryAddress radix_addr, MemoryAddress num_limbs_addr, MemoryAddress is_output_bits_addr, MemoryAddress dst_addr)
EmitUnencryptedLogInterface & emit_unencrypted_log_component
void eq(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
void call(ContextInterface &context, MemoryAddress l2_gas_offset, MemoryAddress da_gas_offset, MemoryAddress addr, MemoryAddress cd_size_offset, MemoryAddress cd_offset)
std::stack< std::unique_ptr< ContextInterface > > external_call_stack
GreaterThanInterface & greater_than
void set_and_validate_inputs(ExecutionOpCode opcode, std::vector< TaggedValue > inputs)
void revert(ContextInterface &context, MemoryAddress rev_size_offset, MemoryAddress rev_offset)
void rd_size(ContextInterface &context, MemoryAddress dst_addr)
void mul(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
Definition execution.cpp:81
void or_op(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
const std::vector< TaggedValue > & get_inputs() const
DebugLoggerInterface & debug_log_component
void xor_op(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
void get_contract_instance(ContextInterface &context, MemoryAddress address_offset, MemoryAddress dst_offset, uint8_t member_enum)
ExecutionResult get_execution_result() const
GetContractInstanceInterface & get_contract_instance_component
void get_env_var(ContextInterface &context, MemoryAddress dst_addr, uint8_t var_enum)
void lte(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
const InstructionInfoDBInterface & instruction_info_db
HighLevelMerkleDBInterface & merkle_db
void and_op(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
void shr(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress c_addr)
ExecutionIdManagerInterface & execution_id_manager
void add(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
Definition execution.cpp:41
void call_with_operands(void(Execution::*f)(ContextInterface &, Ts...), ContextInterface &context, const std::vector< Operand > &resolved_operands)
virtual void consume_gas(const Gas &dynamic_gas_factor={ 0, 0 })=0
virtual Gas compute_gas_limit_for_call(const Gas &allocated_gas)=0
virtual void get_contract_instance(MemoryInterface &memory, const AztecAddress &contract_address, MemoryAddress dst_offset, uint8_t member_enum)=0
virtual bool gt(const FF &a, const FF &b)=0
virtual bool note_hash_exists(uint64_t leaf_index, const FF &unique_note_hash) const =0
virtual FF storage_read(const AztecAddress &contract_address, const FF &slot) const =0
virtual uint32_t get_checkpoint_id() const =0
virtual bool was_storage_written(const AztecAddress &contract_address, const FF &slot) const =0
virtual void note_hash_write(const AztecAddress &contract_address, const FF &note_hash)=0
virtual bool nullifier_exists(const AztecAddress &contract_address, const FF &nullifier) const =0
virtual void storage_write(const AztecAddress &contract_address, const FF &slot, const FF &value, bool is_protocol_write)=0
virtual bool l1_to_l2_msg_exists(uint64_t leaf_index, const FF &msg_hash) const =0
virtual void nullifier_write(const AztecAddress &contract_address, const FF &nullifier)=0
virtual TreeStates get_tree_state() const =0
virtual const ExecInstructionSpec & get(ExecutionOpCode opcode) const =0
A 1-bit unsigned integer type.
Definition uint1.hpp:15
constexpr uint8_t value() const noexcept
Definition uint1.hpp:62
std::string format(Args... args)
Definition log.hpp:21
#define vinfo(...)
Definition log.hpp:79
#define debug(...)
Definition log.hpp:61
void info(Args... args)
Definition log.hpp:74
uint32_t dst_addr
FF a
FF b
uint64_t daGas
Instruction instruction
size_t get_p_limbs_per_radix_size(size_t radix)
Definition to_radix.cpp:54
StandardAffinePoint< AvmFlavorSettings::EmbeddedCurve::AffineElement > EmbeddedCurvePoint
Definition field.hpp:12
uint32_t MemoryAddress
AvmFlavorSettings::FF FF
Definition field.hpp:10
uint8_t get_tag_bytes(ValueTag tag)
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
uint32_t cd_offset
std::vector< TaggedValue > inputs