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 <stdexcept>
4#include <string>
5
40
41namespace bb::avm2::simulation {
42
70{
71 BB_BENCH_NAME("Execution::add");
72 constexpr auto opcode = ExecutionOpCode::ADD;
73 auto& memory = context.get_memory();
74 const MemoryValue& a = memory.get(a_addr);
75 const auto& b = memory.get(b_addr);
76 set_and_validate_inputs(opcode, { a, b });
77
79
80 try {
81 MemoryValue c = alu.add(a, b);
82 memory.set(dst_addr, c);
83 set_output(opcode, c);
84 } catch (AluException& e) {
85 throw OpcodeExecutionException("Alu add operation failed: " + std::string(e.what()));
86 }
87}
88
101{
102 BB_BENCH_NAME("Execution::sub");
103 constexpr auto opcode = ExecutionOpCode::SUB;
104 auto& memory = context.get_memory();
105 const auto& a = memory.get(a_addr);
106 const auto& b = memory.get(b_addr);
107 set_and_validate_inputs(opcode, { a, b });
108
110
111 try {
112 MemoryValue c = alu.sub(a, b);
113 memory.set(dst_addr, c);
114 set_output(opcode, c);
115 } catch (AluException& e) {
116 throw OpcodeExecutionException("Alu sub operation failed");
117 }
118}
119
132{
133 BB_BENCH_NAME("Execution::mul");
134 constexpr auto opcode = ExecutionOpCode::MUL;
135 auto& memory = context.get_memory();
136 const auto& a = memory.get(a_addr);
137 const auto& b = memory.get(b_addr);
138 set_and_validate_inputs(opcode, { a, b });
139
141
142 try {
143 MemoryValue c = alu.mul(a, b);
144 memory.set(dst_addr, c);
145 set_output(opcode, c);
146 } catch (AluException& e) {
147 throw OpcodeExecutionException("Alu mul operation failed: " + std::string(e.what()));
148 }
149}
150
166{
167 BB_BENCH_NAME("Execution::div");
168 constexpr auto opcode = ExecutionOpCode::DIV;
169 auto& memory = context.get_memory();
170 const auto& a = memory.get(a_addr);
171 MemoryValue b = memory.get(b_addr);
172 set_and_validate_inputs(opcode, { a, b });
173
175
176 try {
177 MemoryValue c = alu.div(a, b);
178 memory.set(dst_addr, c);
179 set_output(opcode, c);
180 } catch (AluException& e) {
181 throw OpcodeExecutionException("Alu div operation failed: " + std::string(e.what()));
182 }
183}
184
200{
201 BB_BENCH_NAME("Execution::fdiv");
202 constexpr auto opcode = ExecutionOpCode::FDIV;
203 auto& memory = context.get_memory();
204 const auto& a = memory.get(a_addr);
205 const auto& b = memory.get(b_addr);
206 set_and_validate_inputs(opcode, { a, b });
207
209
210 try {
211 MemoryValue c = alu.fdiv(a, b);
212 memory.set(dst_addr, c);
213 set_output(opcode, c);
214 } catch (AluException& e) {
215 throw OpcodeExecutionException("Alu fdiv operation failed: " + std::string(e.what()));
216 }
217}
218
231{
232 BB_BENCH_NAME("Execution::eq");
233 constexpr auto opcode = ExecutionOpCode::EQ;
234 auto& memory = context.get_memory();
235 const auto& a = memory.get(a_addr);
236 const auto& b = memory.get(b_addr);
237 set_and_validate_inputs(opcode, { a, b });
238
240
241 try {
242 MemoryValue c = alu.eq(a, b);
243 memory.set(dst_addr, c);
244 set_output(opcode, c);
245 } catch (AluException& e) {
246 throw OpcodeExecutionException("Alu eq operation failed: " + std::string(e.what()));
247 }
248}
249
262{
263 BB_BENCH_NAME("Execution::lt");
264 constexpr auto opcode = ExecutionOpCode::LT;
265 auto& memory = context.get_memory();
266 const auto& a = memory.get(a_addr);
267 const auto& b = memory.get(b_addr);
268 set_and_validate_inputs(opcode, { a, b });
269
271
272 try {
273 MemoryValue c = alu.lt(a, b);
274 memory.set(dst_addr, c);
275 set_output(opcode, c);
276 } catch (AluException& e) {
277 throw OpcodeExecutionException("Alu lt operation failed: " + std::string(e.what()));
278 }
279}
280
293{
294 BB_BENCH_NAME("Execution::lte");
295 constexpr auto opcode = ExecutionOpCode::LT;
296 auto& memory = context.get_memory();
297 const auto& a = memory.get(a_addr);
298 const auto& b = memory.get(b_addr);
299 set_and_validate_inputs(opcode, { a, b });
300
302
303 try {
304 MemoryValue c = alu.lte(a, b);
305 memory.set(dst_addr, c);
306 set_output(opcode, c);
307 } catch (AluException& e) {
308 throw OpcodeExecutionException("Alu lte operation failed: " + std::string(e.what()));
309 }
310}
311
323{
324 BB_BENCH_NAME("Execution::op_not");
325 constexpr auto opcode = ExecutionOpCode::NOT;
326 auto& memory = context.get_memory();
327 const auto& a = memory.get(src_addr);
328 set_and_validate_inputs(opcode, { a });
329
331
332 try {
333 MemoryValue b = alu.op_not(a);
334 memory.set(dst_addr, b);
335 set_output(opcode, b);
336 } catch (AluException& e) {
337 throw OpcodeExecutionException("Alu not operation failed: " + std::string(e.what()));
338 }
339}
340
355{
356 BB_BENCH_NAME("Execution::shl");
357 constexpr auto opcode = ExecutionOpCode::SHL;
358 auto& memory = context.get_memory();
359 const auto& a = memory.get(a_addr);
360 const auto& b = memory.get(b_addr);
361 set_and_validate_inputs(opcode, { a, b });
362
364
365 try {
366 MemoryValue c = alu.shl(a, b);
367 memory.set(dst_addr, c);
368 set_output(opcode, c);
369 } catch (const AluException& e) {
370 throw OpcodeExecutionException("SHL Exception: " + std::string(e.what()));
371 }
372}
373
388{
389 BB_BENCH_NAME("Execution::shr");
390 constexpr auto opcode = ExecutionOpCode::SHR;
391 auto& memory = context.get_memory();
392 const auto& a = memory.get(a_addr);
393 const auto& b = memory.get(b_addr);
394 set_and_validate_inputs(opcode, { a, b });
395
397
398 try {
399 MemoryValue c = alu.shr(a, b);
400 memory.set(dst_addr, c);
401 set_output(opcode, c);
402 } catch (const AluException& e) {
403 throw OpcodeExecutionException("SHR Exception: " + std::string(e.what()));
404 }
405}
406
418{
419 BB_BENCH_NAME("Execution::cast");
420 constexpr auto opcode = ExecutionOpCode::CAST;
421 auto& memory = context.get_memory();
422 const auto& val = memory.get(src_addr);
423 set_and_validate_inputs(opcode, { val });
424
426 MemoryValue truncated = alu.truncate(val.as_ff(), static_cast<MemoryTag>(dst_tag));
427 memory.set(dst_addr, truncated);
428 set_output(opcode, truncated);
429}
430
442{
443 BB_BENCH_NAME("Execution::get_env_var");
444 constexpr auto opcode = ExecutionOpCode::GETENVVAR;
445 auto& memory = context.get_memory();
446
448
449 MemoryValue result;
450
451 EnvironmentVariable env_var = static_cast<EnvironmentVariable>(var_enum);
452 switch (env_var) {
454 result = MemoryValue::from<FF>(context.get_address());
455 break;
457 result = MemoryValue::from<FF>(context.get_msg_sender());
458 break;
460 result = MemoryValue::from<FF>(context.get_transaction_fee());
461 break;
463 result = MemoryValue::from<FF>(context.get_globals().chain_id);
464 break;
466 result = MemoryValue::from<FF>(context.get_globals().version);
467 break;
469 result = MemoryValue::from<uint32_t>(context.get_globals().block_number);
470 break;
472 result = MemoryValue::from<uint64_t>(context.get_globals().timestamp);
473 break;
475 result = MemoryValue::from<uint128_t>(context.get_globals().gas_fees.fee_per_l2_gas);
476 break;
478 result = MemoryValue::from<uint128_t>(context.get_globals().gas_fees.fee_per_da_gas);
479 break;
481 result = MemoryValue::from<uint1_t>(context.get_is_static() ? 1 : 0);
482 break;
484 result = MemoryValue::from<uint32_t>(context.gas_left().l2_gas);
485 break;
487 result = MemoryValue::from<uint32_t>(context.gas_left().da_gas);
488 break;
489 default:
490 throw OpcodeExecutionException("Invalid environment variable enum value");
491 }
492
493 memory.set(dst_addr, result);
494 set_output(opcode, result);
495}
496
508// TODO: My dispatch system makes me have a uint8_t tag. Rethink.
510{
511 BB_BENCH_NAME("Execution::set");
513
514 constexpr auto opcode = ExecutionOpCode::SET;
515 MemoryValue truncated = alu.truncate(value, static_cast<MemoryTag>(dst_tag));
516 context.get_memory().set(dst_addr, truncated);
517 set_output(opcode, truncated);
518}
519
530{
531 BB_BENCH_NAME("Execution::mov");
532 constexpr auto opcode = ExecutionOpCode::MOV;
533 auto& memory = context.get_memory();
534 const auto& v = memory.get(src_addr);
535 set_and_validate_inputs(opcode, { v });
536
538
539 memory.set(dst_addr, v);
540 set_output(opcode, v);
541}
542
565 MemoryAddress l2_gas_offset,
566 MemoryAddress da_gas_offset,
567 MemoryAddress addr,
568 MemoryAddress cd_size_offset,
570{
571 BB_BENCH_NAME("Execution::call");
572 constexpr auto opcode = ExecutionOpCode::CALL;
573 auto& memory = context.get_memory();
574
575 // NOTE: these reads cannot fail due to addressing guarantees.
576 const auto& allocated_l2_gas_read = memory.get(l2_gas_offset);
577 const auto& allocated_da_gas_read = memory.get(da_gas_offset);
578 const auto& contract_address = memory.get(addr);
579 // Cd offset loads are deferred to calldatacopy
580 const auto& cd_size = memory.get(cd_size_offset);
581
582 set_and_validate_inputs(opcode, { allocated_l2_gas_read, allocated_da_gas_read, contract_address, cd_size });
583
584 get_gas_tracker().consume_gas(); // Base gas.
586 Gas{ allocated_l2_gas_read.as<uint32_t>(), allocated_da_gas_read.as<uint32_t>() });
587
588 // Tag check contract address + cd_size
590 /*msg_sender=*/context.get_address(),
591 /*transaction_fee=*/context.get_transaction_fee(),
592 /*parent_context=*/context,
593 /*cd_offset_address=*/cd_offset,
594 /*cd_size=*/cd_size.as<uint32_t>(),
595 /*is_static=*/context.get_is_static(),
596 /*gas_limit=*/gas_limit,
597 /*phase=*/context.get_phase());
598
599 // We do not recurse. This context will be use on the next cycle of execution.
600 handle_enter_call(context, std::move(nested_context));
601}
602
625 MemoryAddress l2_gas_offset,
626 MemoryAddress da_gas_offset,
627 MemoryAddress addr,
628 MemoryAddress cd_size_offset,
630{
631 BB_BENCH_NAME("Execution::static_call");
632 constexpr auto opcode = ExecutionOpCode::STATICCALL;
633 auto& memory = context.get_memory();
634
635 // NOTE: these reads cannot fail due to addressing guarantees.
636 const auto& allocated_l2_gas_read = memory.get(l2_gas_offset);
637 const auto& allocated_da_gas_read = memory.get(da_gas_offset);
638 const auto& contract_address = memory.get(addr);
639 // Cd offset loads are deferred to calldatacopy
640 const auto& cd_size = memory.get(cd_size_offset);
641
642 set_and_validate_inputs(opcode, { allocated_l2_gas_read, allocated_da_gas_read, contract_address, cd_size });
643
644 get_gas_tracker().consume_gas(); // Base gas.
646 Gas{ allocated_l2_gas_read.as<uint32_t>(), allocated_da_gas_read.as<uint32_t>() });
647
648 // Tag check contract address + cd_size
650 /*msg_sender=*/context.get_address(),
651 /*transaction_fee=*/context.get_transaction_fee(),
652 /*parent_context=*/context,
653 /*cd_offset_address=*/cd_offset,
654 /*cd_size=*/cd_size.as<uint32_t>(),
655 /*is_static=*/true,
656 /*gas_limit=*/gas_limit,
657 /*phase=*/context.get_phase());
658
659 // We do not recurse. This context will be use on the next cycle of execution.
660 handle_enter_call(context, std::move(nested_context));
661}
662
680 MemoryAddress cd_size_offset,
683{
684 BB_BENCH_NAME("Execution::cd_copy");
685 constexpr auto opcode = ExecutionOpCode::CALLDATACOPY;
686 auto& memory = context.get_memory();
687 const auto& cd_copy_size = memory.get(cd_size_offset); // Tag check u32
688 const auto& cd_offset_read = memory.get(cd_offset); // Tag check u32
689 set_and_validate_inputs(opcode, { cd_copy_size, cd_offset_read });
690
691 get_gas_tracker().consume_gas({ .l2_gas = cd_copy_size.as<uint32_t>(), .da_gas = 0 });
692
693 try {
694 data_copy.cd_copy(context, cd_copy_size.as<uint32_t>(), cd_offset_read.as<uint32_t>(), dst_addr);
695 } catch (const DataCopyException& e) {
696 throw OpcodeExecutionException("cd copy failed: " + std::string(e.what()));
697 }
698}
699
716 MemoryAddress rd_size_offset,
717 MemoryAddress rd_offset,
719{
720 BB_BENCH_NAME("Execution::rd_copy");
721 constexpr auto opcode = ExecutionOpCode::RETURNDATACOPY;
722 auto& memory = context.get_memory();
723 const auto& rd_copy_size = memory.get(rd_size_offset); // Tag check u32
724 const auto& rd_offset_read = memory.get(rd_offset); // Tag check u32
725 set_and_validate_inputs(opcode, { rd_copy_size, rd_offset_read });
726
727 get_gas_tracker().consume_gas({ .l2_gas = rd_copy_size.as<uint32_t>(), .da_gas = 0 });
728
729 try {
730 data_copy.rd_copy(context, rd_copy_size.as<uint32_t>(), rd_offset_read.as<uint32_t>(), dst_addr);
731 } catch (const DataCopyException& e) {
732 throw OpcodeExecutionException("rd copy failed: " + std::string(e.what()));
733 }
734}
735
745{
746 BB_BENCH_NAME("Execution::rd_size");
747 constexpr auto opcode = ExecutionOpCode::RETURNDATASIZE;
748 auto& memory = context.get_memory();
749
751
752 // This is safe because the last_rd_size is tag checked on ret/revert to be U32
753 MemoryValue rd_size = MemoryValue::from<uint32_t>(context.get_last_rd_size());
754 memory.set(dst_addr, rd_size);
755 set_output(opcode, rd_size);
756}
757
772{
773 BB_BENCH_NAME("Execution::ret");
774 constexpr auto opcode = ExecutionOpCode::RETURN;
775 auto& memory = context.get_memory();
776 const auto& rd_size = memory.get(ret_size_offset);
777 set_and_validate_inputs(opcode, { rd_size });
778
780
781 set_execution_result({ .rd_offset = ret_offset,
782 .rd_size = rd_size.as<uint32_t>(),
783 .gas_used = context.get_gas_used(),
784 .success = true,
785 .halting_pc = context.get_pc(),
786 .halting_message = std::nullopt });
787
788 context.halt();
789}
790
805{
806 BB_BENCH_NAME("Execution::revert");
807 constexpr auto opcode = ExecutionOpCode::REVERT;
808 auto& memory = context.get_memory();
809 const auto& rev_size = memory.get(rev_size_offset);
810 set_and_validate_inputs(opcode, { rev_size });
811
813
814 set_execution_result({ .rd_offset = rev_offset,
815 .rd_size = rev_size.as<uint32_t>(),
816 .gas_used = context.get_gas_used(),
817 .success = false,
818 .halting_pc = context.get_pc(),
819 .halting_message = "Assertion failed: " });
820
821 context.halt();
822}
823
834{
835 BB_BENCH_NAME("Execution::jump");
837
838 context.set_next_pc(loc);
839}
840
854{
855 BB_BENCH_NAME("Execution::jumpi");
856 constexpr auto opcode = ExecutionOpCode::JUMPI;
857 auto& memory = context.get_memory();
858
859 const auto& resolved_cond = memory.get(cond_addr);
860 set_and_validate_inputs(opcode, { resolved_cond });
861
863
864 if (resolved_cond.as<uint1_t>().value() == 1) {
865 context.set_next_pc(loc);
866 }
867}
868
880{
881 BB_BENCH_NAME("Execution::internal_call");
883
884 auto& internal_call_stack_manager = context.get_internal_call_stack_manager();
885 // The next pc is pushed onto the internal call stack. This will become return_pc later.
886 internal_call_stack_manager.push(context.get_pc(), context.get_next_pc());
887 context.set_next_pc(loc);
888}
889
900{
901 BB_BENCH_NAME("Execution::internal_return");
903
904 auto& internal_call_stack_manager = context.get_internal_call_stack_manager();
905 try {
906 auto next_pc = internal_call_stack_manager.pop();
907 context.set_next_pc(next_pc);
908 } catch (const InternalCallStackException& e) {
909 // Re-throw
910 throw OpcodeExecutionException("Internal return failed: " + std::string(e.what()));
911 }
912}
913
927{
928 BB_BENCH_NAME("Execution::keccak_permutation");
930
931 try {
932 keccakf1600.permutation(context.get_memory(), dst_addr, src_addr);
933 } catch (const KeccakF1600Exception& e) {
934 throw OpcodeExecutionException("Keccak permutation failed: " + std::string(e.what()));
935 }
936}
937
953 MemoryAddress level_offset,
954 MemoryAddress message_offset,
955 MemoryAddress fields_offset,
956 MemoryAddress fields_size_offset,
957 uint16_t message_size)
958{
959 BB_BENCH_NAME("Execution::debug_log");
961
963 context.get_address(),
964 level_offset,
965 message_offset,
966 message_size,
967 fields_offset,
968 fields_size_offset);
969}
970
980{
981 BB_BENCH_NAME("Execution::success_copy");
982 constexpr auto opcode = ExecutionOpCode::SUCCESSCOPY;
983 auto& memory = context.get_memory();
984
986
987 MemoryValue success = MemoryValue::from<uint1_t>(context.get_last_success());
988 memory.set(dst_addr, success);
989 set_output(opcode, success);
990}
991
1006{
1007 BB_BENCH_NAME("Execution::and_op");
1008 constexpr auto opcode = ExecutionOpCode::AND;
1009 auto& memory = context.get_memory();
1010 const auto& a = memory.get(a_addr);
1011 const auto& b = memory.get(b_addr);
1012 set_and_validate_inputs(opcode, { a, b });
1013
1014 // Dynamic gas consumption for bitwise is dependent on the tag, FF tags are valid here but
1015 // will result in an exception in the bitwise subtrace.
1016 get_gas_tracker().consume_gas({ .l2_gas = get_tag_bytes(a.get_tag()), .da_gas = 0 });
1017
1018 try {
1019 MemoryValue c = bitwise.and_op(a, b);
1020 memory.set(dst_addr, c);
1021 set_output(opcode, c);
1022 } catch (const BitwiseException& e) {
1023 throw OpcodeExecutionException("Bitwise AND Exeception: " + std::string(e.what()));
1024 }
1025}
1026
1043{
1044 BB_BENCH_NAME("Execution::or_op");
1045 constexpr auto opcode = ExecutionOpCode::OR;
1046 auto& memory = context.get_memory();
1047 const auto& a = memory.get(a_addr);
1048 const auto& b = memory.get(b_addr);
1049 set_and_validate_inputs(opcode, { a, b });
1050
1051 // Dynamic gas consumption for bitwise is dependent on the tag, FF tags are valid here but
1052 // will result in an exception in the bitwise subtrace.
1053 get_gas_tracker().consume_gas({ .l2_gas = get_tag_bytes(a.get_tag()), .da_gas = 0 });
1054
1055 try {
1056 MemoryValue c = bitwise.or_op(a, b);
1057 memory.set(dst_addr, c);
1058 set_output(opcode, c);
1059 } catch (const BitwiseException& e) {
1060 throw OpcodeExecutionException("Bitwise OR Exception: " + std::string(e.what()));
1061 }
1062}
1063
1078{
1079 BB_BENCH_NAME("Execution::xor_op");
1080 constexpr auto opcode = ExecutionOpCode::XOR;
1081 auto& memory = context.get_memory();
1082 const auto& a = memory.get(a_addr);
1083 const auto& b = memory.get(b_addr);
1084 set_and_validate_inputs(opcode, { a, b });
1085
1086 // Dynamic gas consumption for bitwise is dependent on the tag, FF tags are valid here but
1087 // will result in an exception in the bitwise subtrace.
1088 get_gas_tracker().consume_gas({ .l2_gas = get_tag_bytes(a.get_tag()), .da_gas = 0 });
1089
1090 try {
1091 MemoryValue c = bitwise.xor_op(a, b);
1092 memory.set(dst_addr, c);
1093 set_output(opcode, c);
1094 } catch (const BitwiseException& e) {
1095 throw OpcodeExecutionException("Bitwise XOR Exception: " + std::string(e.what()));
1096 }
1097}
1098
1113{
1114 BB_BENCH_NAME("Execution::sload");
1115 constexpr auto opcode = ExecutionOpCode::SLOAD;
1116
1117 auto& memory = context.get_memory();
1118
1119 const auto& slot = memory.get(slot_addr);
1120 set_and_validate_inputs(opcode, { slot });
1121
1123
1124 auto value = MemoryValue::from<FF>(merkle_db.storage_read(context.get_address(), slot.as<FF>()));
1125
1126 memory.set(dst_addr, value);
1127 set_output(opcode, value);
1128}
1129
1146{
1147 BB_BENCH_NAME("Execution::sstore");
1148 constexpr auto opcode = ExecutionOpCode::SSTORE;
1149
1150 auto& memory = context.get_memory();
1151
1152 const auto& slot = memory.get(slot_addr);
1153 const auto& value = memory.get(src_addr);
1154 set_and_validate_inputs(opcode, { value, slot });
1155
1156 bool was_slot_written_before = merkle_db.was_storage_written(context.get_address(), slot.as_ff());
1157 uint32_t da_gas_factor = static_cast<uint32_t>(!was_slot_written_before);
1158 get_gas_tracker().consume_gas({ .l2_gas = 0, .da_gas = da_gas_factor });
1159
1160 if (context.get_is_static()) {
1162 "SSTORE: Static call cannot update the state. Cannot write to storage in static context");
1163 }
1164
1165 if (!was_slot_written_before &&
1167 throw OpcodeExecutionException("SSTORE: Maximum number of data writes reached");
1168 }
1169
1170 merkle_db.storage_write(context.get_address(), slot.as_ff(), value.as_ff(), false);
1171}
1172
1189 MemoryAddress unique_note_hash_addr,
1190 MemoryAddress leaf_index_addr,
1192{
1193 BB_BENCH_NAME("Execution::note_hash_exists");
1194 constexpr auto opcode = ExecutionOpCode::NOTEHASHEXISTS;
1195
1196 auto& memory = context.get_memory();
1197 const auto& unique_note_hash = memory.get(unique_note_hash_addr);
1198 const auto& leaf_index = memory.get(leaf_index_addr);
1199 set_and_validate_inputs(opcode, { unique_note_hash, leaf_index });
1200
1202
1203 uint64_t leaf_index_value = leaf_index.as<uint64_t>();
1204
1205 bool index_in_range = greater_than.gt(NOTE_HASH_TREE_LEAF_COUNT, leaf_index_value);
1206
1208
1209 if (index_in_range) {
1210 value = MemoryValue::from<uint1_t>(merkle_db.note_hash_exists(leaf_index_value, unique_note_hash.as<FF>()));
1211 } else {
1212 value = MemoryValue::from<uint1_t>(0);
1213 }
1214
1215 memory.set(dst_addr, value);
1216 set_output(opcode, value);
1217}
1218
1233 MemoryAddress nullifier_offset,
1234 MemoryAddress address_offset,
1235 MemoryAddress exists_offset)
1236{
1237 BB_BENCH_NAME("Execution::nullifier_exists");
1238 constexpr auto opcode = ExecutionOpCode::NULLIFIEREXISTS;
1239 auto& memory = context.get_memory();
1240
1241 const auto& nullifier = memory.get(nullifier_offset);
1242 const auto& address = memory.get(address_offset);
1244
1246
1247 // Check nullifier existence via MerkleDB
1248 // (this also tag checks address and nullifier as FFs)
1249 auto exists = merkle_db.nullifier_exists(address.as_ff(), nullifier.as_ff());
1250
1251 // Write result to memory
1252 // (assigns tag u1 to result)
1253 MemoryValue result = MemoryValue::from<uint1_t>(exists ? 1 : 0);
1254 memory.set(exists_offset, result);
1255 set_output(opcode, result);
1256}
1257
1273{
1274 BB_BENCH_NAME("Execution::emit_nullifier");
1275 constexpr auto opcode = ExecutionOpCode::EMITNULLIFIER;
1276
1277 auto& memory = context.get_memory();
1278 const auto& nullifier = memory.get(nullifier_addr);
1280
1282
1283 if (context.get_is_static()) {
1285 "EMITNULLIFIER: Static call cannot update the state. Cannot emit nullifier in static context");
1286 }
1287
1289 throw OpcodeExecutionException("EMITNULLIFIER: Maximum number of nullifiers reached");
1290 }
1291
1292 // Emit nullifier via MerkleDB.
1293 try {
1294 merkle_db.nullifier_write(context.get_address(), nullifier.as<FF>());
1295 } catch (const NullifierCollisionException& e) {
1296 throw OpcodeExecutionException(format("EMITNULLIFIER: ", e.what()));
1297 }
1298}
1299
1318 MemoryAddress address_offset,
1319 MemoryAddress dst_offset,
1320 uint8_t member_enum)
1321{
1322 BB_BENCH_NAME("Execution::get_contract_instance");
1323 constexpr auto opcode = ExecutionOpCode::GETCONTRACTINSTANCE;
1324 auto& memory = context.get_memory();
1325
1326 // Execution can still handle address memory read and tag checking
1327 const auto& address_value = memory.get(address_offset);
1328 AztecAddress contract_address = address_value.as<AztecAddress>();
1329 set_and_validate_inputs(opcode, { address_value });
1330
1332
1333 // Call the dedicated opcode component to get the contract instance, validate the enum,
1334 // handle other errors, and perform the memory writes.
1335 try {
1337 } catch (const GetContractInstanceException& e) {
1338 throw OpcodeExecutionException("GetContractInstance Exception: " + std::string(e.what()));
1339 }
1340
1341 // No `set_output` here since the dedicated component handles memory writes.
1342}
1343
1358{
1359 BB_BENCH_NAME("Execution::emit_note_hash");
1360 constexpr auto opcode = ExecutionOpCode::EMITNOTEHASH;
1361
1362 auto& memory = context.get_memory();
1363 const auto& note_hash = memory.get(note_hash_addr);
1365
1367
1368 if (context.get_is_static()) {
1370 "EMITNOTEHASH: Static call cannot update the state. Cannot emit note hash in static context");
1371 }
1372
1374 throw OpcodeExecutionException("EMITNOTEHASH: Maximum number of note hashes reached");
1375 }
1376
1377 merkle_db.note_hash_write(context.get_address(), note_hash.as<FF>());
1378}
1379
1396 MemoryAddress msg_hash_addr,
1397 MemoryAddress leaf_index_addr,
1399{
1400 BB_BENCH_NAME("Execution::l1_to_l2_message_exists");
1401 constexpr auto opcode = ExecutionOpCode::L1TOL2MSGEXISTS;
1402
1403 auto& memory = context.get_memory();
1404 const auto& msg_hash = memory.get(msg_hash_addr);
1405 const auto& leaf_index = memory.get(leaf_index_addr);
1406 set_and_validate_inputs(opcode, { msg_hash, leaf_index });
1407
1409
1410 uint64_t leaf_index_value = leaf_index.as<uint64_t>();
1411
1412 bool index_in_range = greater_than.gt(L1_TO_L2_MSG_TREE_LEAF_COUNT, leaf_index_value);
1413
1415
1416 if (index_in_range) {
1417 value = MemoryValue::from<uint1_t>(merkle_db.l1_to_l2_msg_exists(leaf_index_value, msg_hash.as<FF>()));
1418 } else {
1419 value = MemoryValue::from<uint1_t>(0);
1420 }
1421
1422 memory.set(dst_addr, value);
1423 set_output(opcode, value);
1424}
1425
1440{
1441 BB_BENCH_NAME("Execution::poseidon2_permutation");
1443 try {
1444 poseidon2.permutation(context.get_memory(), src_addr, dst_addr);
1445 } catch (const Poseidon2Exception& e) {
1446 throw OpcodeExecutionException("Poseidon2 permutation failed: " + std::string(e.what()));
1447 }
1448}
1449
1476 MemoryAddress p_x_addr,
1477 MemoryAddress p_y_addr,
1478 MemoryAddress p_inf_addr,
1479 MemoryAddress q_x_addr,
1480 MemoryAddress q_y_addr,
1481 MemoryAddress q_inf_addr,
1483{
1484 BB_BENCH_NAME("Execution::ecc_add");
1485 constexpr auto opcode = ExecutionOpCode::ECADD;
1486 auto& memory = context.get_memory();
1487
1488 // Read the points from memory.
1489 const auto& p_x = memory.get(p_x_addr);
1490 const auto& p_y = memory.get(p_y_addr);
1491 const auto& p_inf = memory.get(p_inf_addr);
1492
1493 const auto& q_x = memory.get(q_x_addr);
1494 const auto& q_y = memory.get(q_y_addr);
1495 const auto& q_inf = memory.get(q_inf_addr);
1496
1497 set_and_validate_inputs(opcode, { p_x, p_y, p_inf, q_x, q_y, q_inf });
1499
1500 // Once inputs are tag checked the conversion to EmbeddedCurvePoint is safe, on curve checks are done in the add
1501 // method.
1502 EmbeddedCurvePoint p = EmbeddedCurvePoint(p_x.as_ff(), p_y.as_ff(), p_inf == MemoryValue::from<uint1_t>(1));
1503 EmbeddedCurvePoint q = EmbeddedCurvePoint(q_x.as_ff(), q_y.as_ff(), q_inf == MemoryValue::from<uint1_t>(1));
1504
1505 try {
1507 } catch (const EccException& e) {
1508 throw OpcodeExecutionException("Embedded curve add failed: " + std::string(e.what()));
1509 }
1510}
1511
1537 MemoryAddress value_addr,
1538 MemoryAddress radix_addr,
1539 MemoryAddress num_limbs_addr,
1540 MemoryAddress is_output_bits_addr, // Decides if output is U1 or U8
1542{
1543 BB_BENCH_NAME("Execution::to_radix_be");
1544 constexpr auto opcode = ExecutionOpCode::TORADIXBE;
1545 auto& memory = context.get_memory();
1546
1547 const auto& value = memory.get(value_addr); // Field
1548 const auto& radix = memory.get(radix_addr); // U32
1549 const auto& num_limbs = memory.get(num_limbs_addr); // U32
1550 const auto& is_output_bits = memory.get(is_output_bits_addr); // U1
1551
1552 // Tag check the inputs
1553 {
1554 BB_BENCH_NAME("Execution::to_radix_be::set_and_validate_inputs");
1555 set_and_validate_inputs(opcode, { value, radix, num_limbs, is_output_bits });
1556 }
1557
1558 // The range check for a valid radix (2 <= radix <= 256) is done in the gadget.
1559 // However, in order to compute the dynamic gas value we need to constrain the radix
1560 // to be <= 256 since the `get_p_limbs_per_radix` lookup table is only defined for the range [0, 256].
1561 // This does mean that the <= 256 check is duplicated - this can be optimized later.
1562
1563 // The dynamic gas factor is the maximum of the num_limbs requested by the opcode and the number of limbs
1564 // the gadget that the field modulus, p, decomposes into given a radix (num_p_limbs).
1565 // See to_radix.pil for how these values impact the row count.
1566
1567 // The lookup table of radix decomposed limbs of the modulus p is defined for radix values [0, 256],
1568 // so for any radix value greater than 256 we set num_p_limbs to 32 - with
1569 // the understanding the opcode will fail in the gadget (since the radix is invalid).
1570 uint32_t radix_value = radix.as<uint32_t>();
1571 uint32_t num_p_limbs = greater_than.gt(radix.as<uint32_t>(), 256)
1572 ? 32
1573 : static_cast<uint32_t>(get_p_limbs_per_radix_size(radix_value));
1574
1575 // Compute the dynamic gas factor - done this way to trigger relevant circuit interactions
1576 if (greater_than.gt(num_limbs.as<uint32_t>(), num_p_limbs)) {
1577 get_gas_tracker().consume_gas({ .l2_gas = num_limbs.as<uint32_t>(), .da_gas = 0 });
1578 } else {
1579 get_gas_tracker().consume_gas({ .l2_gas = num_p_limbs, .da_gas = 0 });
1580 }
1581
1582 try {
1583 // Call the gadget to perform the conversion.
1584 to_radix.to_be_radix(memory,
1585 value.as_ff(),
1586 radix.as<uint32_t>(),
1587 num_limbs.as<uint32_t>(),
1588 is_output_bits.as<uint1_t>().value() == 1,
1589 dst_addr);
1590 } catch (const ToRadixException& e) {
1591 throw OpcodeExecutionException("ToRadixBe gadget failed: " + std::string(e.what()));
1592 }
1593}
1594
1612{
1613 BB_BENCH_NAME("Execution::emit_unencrypted_log");
1614 constexpr auto opcode = ExecutionOpCode::EMITUNENCRYPTEDLOG;
1615 auto& memory = context.get_memory();
1616
1617 const auto& log_size = memory.get(log_size_offset);
1618 set_and_validate_inputs(opcode, { log_size });
1619 uint32_t log_size_int = log_size.as<uint32_t>();
1620
1621 get_gas_tracker().consume_gas({ .l2_gas = log_size_int, .da_gas = log_size_int });
1622
1623 // Call the dedicated opcode component to emit the log
1624 try {
1626 memory, context, context.get_address(), log_offset, log_size_int);
1627 } catch (const EmitUnencryptedLogException& e) {
1628 throw OpcodeExecutionException("EmitUnencryptedLog Exception: " + std::string(e.what()));
1629 }
1630}
1631
1648{
1649 BB_BENCH_NAME("Execution::send_l2_to_l1_msg");
1650 constexpr auto opcode = ExecutionOpCode::SENDL2TOL1MSG;
1651 auto& memory = context.get_memory();
1652
1653 const auto& recipient = memory.get(recipient_addr);
1654 const auto& content = memory.get(content_addr);
1655 set_and_validate_inputs(opcode, { recipient, content });
1656
1658
1659 if (context.get_is_static()) {
1661 "SENDL2TOL1MSG: Static call cannot update the state. Cannot send L2 to L1 message in static context");
1662 }
1663
1664 auto& side_effect_tracker = context.get_side_effect_tracker();
1665 const auto& side_effects = side_effect_tracker.get_side_effects();
1666
1667 if (side_effects.l2_to_l1_messages.size() == MAX_L2_TO_L1_MSGS_PER_TX) {
1668 throw OpcodeExecutionException("SENDL2TOL1MSG: Maximum number of L2 to L1 messages reached");
1669 }
1670
1671 side_effect_tracker.add_l2_to_l1_message(context.get_address(), EthAddress(recipient.as_ff()), content.as_ff());
1672}
1673
1689 MemoryAddress output_addr,
1690 MemoryAddress state_addr,
1691 MemoryAddress input_addr)
1692{
1693 BB_BENCH_NAME("Execution::sha256_compression");
1695
1696 try {
1697 sha256.compression(context.get_memory(), state_addr, input_addr, output_addr);
1698 } catch (const Sha256CompressionException& e) {
1699 throw OpcodeExecutionException("Sha256 Compression failed: " + std::string(e.what()));
1700 }
1701}
1702
1714{
1715 BB_BENCH_NAME("Execution::execute");
1716 call_stack_metadata_collector.notify_enter_call(enqueued_call_context->get_address(),
1717 0,
1718 make_calldata_provider(*enqueued_call_context),
1719 enqueued_call_context->get_is_static(),
1720 enqueued_call_context->get_gas_limit());
1721 external_call_stack.push(std::move(enqueued_call_context));
1722
1723 while (!external_call_stack.empty()) {
1724 // We fix the context at this point. Even if the opcode changes the stack
1725 // we'll always use this in the loop.
1726 auto& context = *external_call_stack.top();
1727
1728 // Default inputs and output initialization. This properly resets the values between two
1729 // opcode executions as well.
1730 inputs = {};
1731 output = MemoryValue::from<FF>(0);
1732
1733 // Members of the execution event which are set in the try block.
1735 AddressingEvent addressing_event;
1738
1739 // State before doing anything.
1740 const auto before_context_event = context.serialize_context_event();
1741 const auto next_context_id = context_provider.get_next_context_id();
1742 const auto pc = context.get_pc();
1743
1744 try {
1745 // Temporality group 1: Bytecode retrieval. //
1746
1747 // We try to get the bytecode id. This can throw if the contract is not deployed or if we have retrieved too
1748 // many unique class ids. Note: bytecode_id is tracked in context events, not in the top-level execution
1749 // event. It is already included in the before_context_event (defaulting to 0 on error/not-found).
1750 context.get_bytecode_manager().get_bytecode_id();
1751
1752 // Temporality group 2: Instruction fetching and addressing. //
1753
1754 // We try to fetch an instruction.
1755 instruction = context.get_bytecode_manager().read_instruction(pc);
1756
1757 debug("@", pc, " ", instruction.to_string());
1758 context.set_next_pc(pc + static_cast<uint32_t>(instruction.size_in_bytes()));
1759 // next_pc is overwritten in dispatch_opcode() for JUMP, JUMPI, INTERNALCALL, and INTERNALRETURN.
1760
1761 // Resolve the operands.
1762 auto addressing = execution_components.make_addressing(addressing_event);
1763 std::vector<Operand> resolved_operands = addressing->resolve(instruction, context.get_memory());
1764
1766 // Temporality group 3: Registers read. (triggered in each opcode (dispatch_opcode()) with
1767 // set_and_validate_inputs(opcode, { ... });)
1768 // Temporality group 4: Gas. (triggered in each opcode (dispatch_opcode()) with
1769 // get_gas_tracker().consume_gas();)
1770 // Temporality group 5: Opcode execution. (in dispatch_opcode())
1771 // Temporality group 6: Register write. (in dispatch_opcode())
1772
1774 dispatch_opcode(instruction.get_exec_opcode(), context, resolved_operands);
1775 }
1776 // TODO(fcarreiro): handle this in a better way.
1777 catch (const BytecodeRetrievalError& e) {
1778 vinfo("Bytecode retrieval error:: ", e.what());
1781 } catch (const InstructionFetchingError& e) {
1782 vinfo("Instruction fetching error: ", e.what());
1785 } catch (const AddressingException& e) {
1786 vinfo("Addressing exception: ", e.what());
1789 } catch (const RegisterValidationException& e) {
1790 vinfo("Register validation exception: ", e.what());
1793 } catch (const OutOfGasException& e) {
1794 vinfo("Out of gas exception: ", e.what());
1795 error = ExecutionError::GAS;
1797 } catch (const OpcodeExecutionException& e) {
1798 vinfo("Opcode execution exception: ", e.what());
1801 } catch (const std::exception& e) {
1802 // This is a coding error, we should not get here.
1803 // All exceptions should fall in the above catch blocks.
1804 info("An unhandled exception occurred: ", e.what());
1805 throw e;
1806 }
1807
1808 // We always do what follows. "Finally".
1809 // Move on to the next pc.
1810 context.set_pc(context.get_next_pc());
1812
1813 // TODO: We set the inputs and outputs here and into the execution event,
1814 // but maybe there's a better way to do this.
1815 events.emit({
1816 .error = error,
1817 .wire_instruction = instruction,
1818 .inputs = get_inputs(),
1819 .output = get_output(),
1820 .next_context_id = next_context_id,
1821 .addressing_event = addressing_event,
1822 .before_context_event = before_context_event,
1823 .after_context_event = context.serialize_context_event(),
1824 .gas_event = gas_event,
1825 });
1826
1827 // If the context has halted, we need to exit the external call.
1828 // The external call stack is expected to be popped.
1829 if (context.halted()) {
1831 }
1832 }
1833
1834 const ExecutionResult& result = get_execution_result();
1835 return {
1836 .success = result.success,
1837 .gas_used = result.gas_used,
1838 };
1839}
1840
1849{
1850 const auto& side_effects = parent_context.get_side_effect_tracker().get_side_effects();
1851
1852 // Optionally collect call stack metadata.
1853 call_stack_metadata_collector.notify_enter_call(child_context->get_address(),
1854 parent_context.get_pc(),
1855 make_calldata_provider(*child_context),
1856 child_context->get_is_static(),
1857 child_context->get_gas_limit());
1858
1859 ctx_stack_events.emit({
1860 .id = parent_context.get_context_id(),
1861 .parent_id = parent_context.get_parent_id(),
1862 .entered_context_id = child_context->get_context_id(), // gets the context id of the child!
1863 .next_pc = parent_context.get_next_pc(),
1864 .msg_sender = parent_context.get_msg_sender(),
1865 .contract_addr = parent_context.get_address(),
1866 .bytecode_id = parent_context.get_bytecode_manager()
1868 .value(), // Bytecode should have been retrieved in the parent context if it issued a call.
1869 .is_static = parent_context.get_is_static(),
1870 .parent_cd_addr = parent_context.get_parent_cd_addr(),
1871 .parent_cd_size = parent_context.get_parent_cd_size(),
1872 .parent_gas_used = parent_context.get_parent_gas_used(),
1873 .parent_gas_limit = parent_context.get_parent_gas_limit(),
1874 .tree_states = merkle_db.get_tree_state(),
1875 .written_public_data_slots_tree_snapshot = parent_context.get_written_public_data_slots_tree_snapshot(),
1876 // Non-tree-tracked side effects
1877 .numUnencryptedLogFields = side_effects.get_num_unencrypted_log_fields(),
1878 .numL2ToL1Messages = static_cast<uint32_t>(side_effects.l2_to_l1_messages.size()),
1879 });
1880
1881 external_call_stack.push(std::move(child_context));
1882}
1883
1888{
1889 BB_BENCH_NAME("Execution::handle_exit_call");
1890
1891 // NOTE: the current (child) context should not be modified here, since it was already emitted.
1893
1894 const ExecutionResult& result = get_execution_result();
1895
1896 // Optionally collect call stack metadata.
1898 result.success,
1899 result.halting_pc,
1900 result.halting_message,
1901 make_return_data_provider(*child_context, result.rd_offset, result.rd_size),
1902 make_internal_call_stack_provider(child_context->get_internal_call_stack_manager()));
1903
1904 external_call_stack.pop();
1905
1906 // We only handle reverting/committing of nested calls. Enqueued calls are handled by TX execution.
1907 if (!external_call_stack.empty()) {
1908 // Note: committing or reverting the db here also commits or reverts the
1909 // tracked nullifiers, public writes dictionary, etc. These structures
1910 // "listen" to the db changes.
1911 if (result.success) {
1913 } else {
1915 }
1916
1917 auto& parent_context = *external_call_stack.top();
1918 // was not top level, communicate with parent
1919 parent_context.set_last_rd_addr(result.rd_offset);
1920 parent_context.set_last_rd_size(result.rd_size);
1921 parent_context.set_last_success(result.success);
1922 // Safe since the nested context gas limit should be clamped to the available gas.
1923 parent_context.set_gas_used(result.gas_used + parent_context.get_gas_used());
1924 parent_context.set_child_context(std::move(child_context));
1925
1926 // TODO(fcarreiro): move somewhere else.
1927 if (parent_context.get_checkpoint_id_at_creation() != merkle_db.get_checkpoint_id()) {
1928 throw std::runtime_error(format("Checkpoint id mismatch: ",
1929 parent_context.get_checkpoint_id_at_creation(),
1930 " != ",
1932 " (gone back to the wrong db/context)"));
1933 }
1934 }
1935 // Else: was top level. ExecutionResult is already set and that will be returned.
1936}
1937
1945void Execution::handle_exceptional_halt(ContextInterface& context, const std::string& halting_message)
1946{
1947 context.set_gas_used(context.get_gas_limit()); // Consume all gas.
1948 context.halt();
1950 .rd_offset = 0,
1951 .rd_size = 0,
1952 .gas_used = context.get_gas_used(),
1953 .success = false,
1954 .halting_pc = context.get_pc(),
1955 .halting_message = halting_message,
1956 });
1957}
1958
1969 const std::vector<Operand>& resolved_operands)
1970{
1971 BB_BENCH_NAME("Execution::dispatch_opcode");
1972
1973 debug("Dispatching opcode: ", opcode, " (", static_cast<uint32_t>(opcode), ")");
1974 switch (opcode) {
1976 call_with_operands(&Execution::add, context, resolved_operands);
1977 break;
1979 call_with_operands(&Execution::sub, context, resolved_operands);
1980 break;
1982 call_with_operands(&Execution::mul, context, resolved_operands);
1983 break;
1985 call_with_operands(&Execution::div, context, resolved_operands);
1986 break;
1988 call_with_operands(&Execution::fdiv, context, resolved_operands);
1989 break;
1991 call_with_operands(&Execution::eq, context, resolved_operands);
1992 break;
1994 call_with_operands(&Execution::lt, context, resolved_operands);
1995 break;
1997 call_with_operands(&Execution::lte, context, resolved_operands);
1998 break;
2000 call_with_operands(&Execution::op_not, context, resolved_operands);
2001 break;
2003 call_with_operands(&Execution::shl, context, resolved_operands);
2004 break;
2006 call_with_operands(&Execution::shr, context, resolved_operands);
2007 break;
2009 call_with_operands(&Execution::cast, context, resolved_operands);
2010 break;
2013 break;
2015 call_with_operands(&Execution::set, context, resolved_operands);
2016 break;
2018 call_with_operands(&Execution::mov, context, resolved_operands);
2019 break;
2021 call_with_operands(&Execution::call, context, resolved_operands);
2022 break;
2025 break;
2027 call_with_operands(&Execution::ret, context, resolved_operands);
2028 break;
2030 call_with_operands(&Execution::revert, context, resolved_operands);
2031 break;
2033 call_with_operands(&Execution::jump, context, resolved_operands);
2034 break;
2036 call_with_operands(&Execution::jumpi, context, resolved_operands);
2037 break;
2039 call_with_operands(&Execution::cd_copy, context, resolved_operands);
2040 break;
2042 call_with_operands(&Execution::rd_copy, context, resolved_operands);
2043 break;
2046 break;
2049 break;
2052 break;
2055 break;
2057 call_with_operands(&Execution::rd_size, context, resolved_operands);
2058 break;
2060 call_with_operands(&Execution::debug_log, context, resolved_operands);
2061 break;
2063 call_with_operands(&Execution::and_op, context, resolved_operands);
2064 break;
2066 call_with_operands(&Execution::or_op, context, resolved_operands);
2067 break;
2069 call_with_operands(&Execution::xor_op, context, resolved_operands);
2070 break;
2072 call_with_operands(&Execution::sload, context, resolved_operands);
2073 break;
2075 call_with_operands(&Execution::sstore, context, resolved_operands);
2076 break;
2079 break;
2082 break;
2085 break;
2088 break;
2091 break;
2094 break;
2097 break;
2099 call_with_operands(&Execution::ecc_add, context, resolved_operands);
2100 break;
2103 break;
2106 break;
2109 break;
2112 break;
2113 default:
2114 // NOTE: Keep this a `std::runtime_error` so that the main loop panics.
2115 throw std::runtime_error("Tried to dispatch unknown execution opcode: " +
2116 std::to_string(static_cast<uint32_t>(opcode)));
2117 }
2118}
2119
2129template <typename... Ts>
2132 const std::vector<Operand>& resolved_operands)
2133{
2134 assert(resolved_operands.size() == sizeof...(Ts));
2135 auto operand_indices = std::make_index_sequence<sizeof...(Ts)>{};
2136 [f, this, &context, &resolved_operands]<std::size_t... Is>(std::index_sequence<Is...>) {
2137 (this->*f)(context, resolved_operands.at(Is).to<std::decay_t<Ts>>()...);
2138 }(operand_indices);
2139}
2140
2149{
2150 const auto& register_info = instruction_info_db.get(opcode).register_info;
2151 assert(inputs.size() == register_info.num_inputs());
2152 this->inputs = inputs;
2153 for (size_t i = 0; i < register_info.num_inputs(); i++) {
2154 if (register_info.expected_tag(i) && register_info.expected_tag(i) != this->inputs.at(i).get_tag()) {
2155 throw RegisterValidationException(format("Input ",
2156 i,
2157 " tag ",
2158 std::to_string(this->inputs.at(i).get_tag()),
2159 " does not match expected tag ",
2160 std::to_string(*register_info.expected_tag(i))));
2161 }
2162 }
2163}
2164
2172{
2173 const auto& register_info = instruction_info_db.get(opcode).register_info;
2174 (void)register_info; // To please GCC.
2175 assert(register_info.num_outputs() == 1);
2176 this->output = output;
2177}
2178
2179} // 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:219
virtual std::optional< BytecodeId > get_retrieved_bytecode_id()=0
virtual void notify_exit_call(bool success, uint32_t pc, const std::optional< std::string > &halting_message, const ReturnDataProvider &return_data_provider, const InternalCallStackProvider &internal_call_stack_provider)=0
virtual void notify_enter_call(const AztecAddress &contract_address, uint32_t caller_pc, const CalldataProvider &calldata_provider, bool is_static_call, const Gas &gas_limit)=0
virtual uint32_t get_pc() const =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 SideEffectTrackerInterface & get_side_effect_tracker()=0
virtual MemoryAddress get_parent_cd_addr() const =0
virtual AppendOnlyTreeSnapshot get_written_public_data_slots_tree_snapshot()=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, 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, const 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)
LT execution opcode handler: Check if the first value is less than the second.
void emit_note_hash(ContextInterface &context, MemoryAddress note_hash_addr)
EMITNOTEHASH execution opcode handler: Emit a note hash to the note hash tree.
std::vector< MemoryValue > inputs
void mov(ContextInterface &context, MemoryAddress src_addr, MemoryAddress dst_addr)
MOV execution opcode handler: Move a memory value from one memory location to another.
void static_call(ContextInterface &context, MemoryAddress l2_gas_offset, MemoryAddress da_gas_offset, MemoryAddress addr, MemoryAddress cd_size_offset, MemoryAddress cd_offset)
STATICCALL execution opcode handler: Call a contract in a static context. Creates a new (nested) exec...
void debug_log(ContextInterface &context, MemoryAddress level_offset, MemoryAddress message_offset, MemoryAddress fields_offset, MemoryAddress fields_size_offset, uint16_t message_size)
DEBUGLOG execution opcode handler: Log a debug message. Logs a debug message to the debug logger if t...
EventEmitterInterface< ExecutionEvent > & events
void cd_copy(ContextInterface &context, MemoryAddress cd_size_offset, MemoryAddress cd_offset, MemoryAddress dst_addr)
CALLDATACOPY execution opcode handler: Copy calldata from the parent context to the current context....
std::unique_ptr< GasTrackerInterface > gas_tracker
void send_l2_to_l1_msg(ContextInterface &context, MemoryAddress recipient_addr, MemoryAddress content_addr)
SENDL2TOL1MSG execution opcode handler: Send a L2 to L1 message.
void dispatch_opcode(ExecutionOpCode opcode, ContextInterface &context, const std::vector< Operand > &resolved_operands)
Dispatch an opcode. This is the main function that dispatches the opcode to the appropriate handler.
void set_execution_result(const ExecutionResult &exec_result)
ExecutionComponentsProviderInterface & execution_components
void sstore(ContextInterface &context, MemoryAddress src_addr, MemoryAddress slot_addr)
SSTORE execution opcode handler: Store a value in the public data tree.
const std::vector< MemoryValue > & get_inputs() const
void set(ContextInterface &context, MemoryAddress dst_addr, uint8_t tag, const FF &value)
SET execution opcode handler: Set the value at a memory location. If the value does not fit in the ta...
void internal_return(ContextInterface &context)
INTERNALRETURN execution opcode handler: Return from a function in the current context....
void set_output(ExecutionOpCode opcode, const MemoryValue &output)
Set the output register.
virtual GasTrackerInterface & get_gas_tracker()
void poseidon2_permutation(ContextInterface &context, MemoryAddress src_addr, MemoryAddress dst_addr)
POSEIDON2PERMUTATION execution opcode handler: Perform a Poseidon2 permutation on the input value....
void success_copy(ContextInterface &context, MemoryAddress dst_addr)
SUCCESSCOPY execution opcode handler: Copy the success flag to the destination memory location.
void fdiv(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
FDIV execution opcode handler: Divide two field values.
void jumpi(ContextInterface &context, MemoryAddress cond_addr, uint32_t loc)
JUMPI execution opcode handler: Jump to a new program counter conditionally. Next instruction will be...
void sub(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
SUB execution opcode handler: Subtract two values.
CallStackMetadataCollectorInterface & call_stack_metadata_collector
void rd_copy(ContextInterface &context, MemoryAddress rd_size_offset, MemoryAddress rd_offset, MemoryAddress dst_addr)
RETURNDATACOPY execution opcode handler: Copy return data from the current context to the parent cont...
void l1_to_l2_message_exists(ContextInterface &context, MemoryAddress msg_hash_addr, MemoryAddress leaf_index_addr, MemoryAddress dst_addr)
L1TOL2MSGEXISTS execution opcode handler: Check if a L2 to L1 message exists in the L1 to L2 message ...
void emit_unencrypted_log(ContextInterface &context, MemoryAddress log_size_offset, MemoryAddress log_offset)
EMITUNENCRYPTEDLOG execution opcode handler: Emit an unencrypted log.
void div(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
DIV execution opcode handler: Divide two values.
void emit_nullifier(ContextInterface &context, MemoryAddress nullifier_addr)
EMITNULLIFIER execution opcode handler: Emit a nullifier to the nullifier tree.
EventEmitterInterface< ContextStackEvent > & ctx_stack_events
const MemoryValue & get_output() const
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)
ECADD execution opcode handler: Perform an elliptic curve addition and write the result to the destin...
void keccak_permutation(ContextInterface &context, MemoryAddress dst_addr, MemoryAddress src_addr)
KECCAKF1600 execution opcode handler: Perform a Keccak permutation on the data.
void jump(ContextInterface &context, uint32_t loc)
JUMP execution opcode handler: Jump to a new program counter. Next instruction will be executed at th...
const ExecutionResult & get_execution_result() const
void sha256_compression(ContextInterface &context, MemoryAddress output_addr, MemoryAddress state_addr, MemoryAddress input_addr)
SHA256COMPRESSION execution opcode handler: Perform a SHA256 compression on the input and state value...
void sload(ContextInterface &context, MemoryAddress slot_addr, MemoryAddress dst_addr)
SLOAD execution opcode handler: Load a value from the public data tree. Loads a value from the public...
void ret(ContextInterface &context, MemoryAddress ret_size_offset, MemoryAddress ret_offset)
RETURN execution opcode handler: Return from a contract. Sets the execution result to the return data...
void internal_call(ContextInterface &context, uint32_t loc)
INTERNALCALL execution opcode handler: Call a function in the current context. Pushes the current pro...
void op_not(ContextInterface &context, MemoryAddress src_addr, MemoryAddress dst_addr)
NOT execution opcode handler: Perform bitwise NOT operation on a value.
void shl(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
SHL execution opcode handler: Perform left shift operation on a value.
void handle_enter_call(ContextInterface &parent_context, std::unique_ptr< ContextInterface > child_context)
Handle the entering of a call. This is called when a call is made from a context. This is a helper fu...
void nullifier_exists(ContextInterface &context, MemoryAddress nullifier_offset, MemoryAddress address_offset, MemoryAddress exists_offset)
NULLIFIEREXISTS execution opcode handler: Check if a nullifier exists in the nullifier tree.
void handle_exceptional_halt(ContextInterface &context, const std::string &halting_message)
Handle the exceptional halt of a context. This is called when an exception is thrown during the execu...
void note_hash_exists(ContextInterface &context, MemoryAddress unique_note_hash_addr, MemoryAddress leaf_index_addr, MemoryAddress dst_addr)
NOTEHASHEXISTS execution opcode handler: Check if a note hash exists in the note hash tree at the spe...
ContextProviderInterface & context_provider
void cast(ContextInterface &context, MemoryAddress src_addr, MemoryAddress dst_addr, uint8_t dst_tag)
CAST execution opcode handler: Cast a value to a different 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)
TORADIXBE execution opcode handler: Convert a value to a radix-based representation....
EmitUnencryptedLogInterface & emit_unencrypted_log_component
void eq(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
EQ execution opcode handler: Check if two values are equal.
void call(ContextInterface &context, MemoryAddress l2_gas_offset, MemoryAddress da_gas_offset, MemoryAddress addr, MemoryAddress cd_size_offset, MemoryAddress cd_offset)
CALL execution opcode handler: Call a contract. Creates a new (nested) execution context and triggers...
std::stack< std::unique_ptr< ContextInterface > > external_call_stack
void handle_exit_call()
Handle the exiting of a call. This is called when a call returns or reverts.
GreaterThanInterface & greater_than
void revert(ContextInterface &context, MemoryAddress rev_size_offset, MemoryAddress rev_offset)
REVERT execution opcode handler: Revert from a contract. Sets the execution result to the revert data...
void rd_size(ContextInterface &context, MemoryAddress dst_addr)
RETURNDATASIZE execution opcode handler: Get the size of the return data.
void mul(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
MUL execution opcode handler: Multiply two values.
void or_op(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
OR execution opcode handler: Perform a bitwise OR operation on the two input values.
void shr(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
SHR execution opcode handler: Perform right shift operation on a value.
DebugLoggerInterface & debug_log_component
void xor_op(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
XOR execution opcode handler: Perform a bitwise XOR operation on the two input values.
void get_contract_instance(ContextInterface &context, MemoryAddress address_offset, MemoryAddress dst_offset, uint8_t member_enum)
GETCONTRACTINSTANCE execution opcode handler: Get a contract instance. Gets a contract instance from ...
GetContractInstanceInterface & get_contract_instance_component
void set_and_validate_inputs(ExecutionOpCode opcode, const std::vector< MemoryValue > &inputs)
Set the register inputs and validate the tags. The tag information is taken from the instruction info...
void get_env_var(ContextInterface &context, MemoryAddress dst_addr, uint8_t var_enum)
GETENVVAR execution opcode handler: Get the value of an environment variable.
void lte(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
LTE execution opcode handler: Check if the first value is less than or equal to the second.
const InstructionInfoDBInterface & instruction_info_db
HighLevelMerkleDBInterface & merkle_db
void and_op(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
AND execution opcode handler: Perform a bitwise AND operation on the two input values.
EnqueuedCallResult execute(std::unique_ptr< ContextInterface > enqueued_call_context) override
Execute a top-level enqueued call.
ExecutionIdManagerInterface & execution_id_manager
void add(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
ADD execution opcode handler: Add two values.
Definition execution.cpp:69
void call_with_operands(void(Execution::*f)(ContextInterface &, Ts...), ContextInterface &context, const std::vector< Operand > &resolved_operands)
Call with operands. This is a template magic function to dispatch the opcode by deducing the number o...
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
virtual const TrackedSideEffects & get_side_effects() 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:22
#define vinfo(...)
Definition log.hpp:80
#define debug(...)
Definition log.hpp:62
void info(Args... args)
Definition log.hpp:75
uint32_t dst_addr
FF a
FF b
uint64_t da_gas
GasEvent gas_event
Instruction instruction
AvmProvingInputs inputs
InternalCallStackProvider make_internal_call_stack_provider(const InternalCallStackManagerInterface &internal_call_stack_manager)
ReturnDataProvider make_return_data_provider(const ContextInterface &context, uint32_t rd_offset, uint32_t rd_size)
CalldataProvider make_calldata_provider(const ContextInterface &context)
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::optional< std::string > halting_message
SideEffectTracker side_effect_tracker