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
11
25
26namespace bb::avm2::simulation {
27namespace {
28
29class RegisterValidationException : public std::runtime_error {
30 public:
31 RegisterValidationException(const std::string& message)
32 : std::runtime_error(message)
33 {}
34};
35
36class OpcodeExecutionException : public std::runtime_error {
37 public:
38 OpcodeExecutionException(const std::string& message)
39 : std::runtime_error(message)
40 {}
41};
42
43} // namespace
44
46{
47 constexpr auto opcode = ExecutionOpCode::ADD;
48 auto& memory = context.get_memory();
49 MemoryValue a = memory.get(a_addr);
50 MemoryValue b = memory.get(b_addr);
51 set_and_validate_inputs(opcode, { a, b });
52
54
55 try {
56 MemoryValue c = alu.add(a, b);
57 memory.set(dst_addr, c);
58 set_output(opcode, c);
59 } catch (AluException& e) {
60 throw OpcodeExecutionException("Alu add operation failed");
61 }
62}
63
65{
66 constexpr auto opcode = ExecutionOpCode::SUB;
67 auto& memory = context.get_memory();
68 MemoryValue a = memory.get(a_addr);
69 MemoryValue b = memory.get(b_addr);
70 set_and_validate_inputs(opcode, { a, b });
71
73
74 try {
75 MemoryValue c = alu.sub(a, b);
76 memory.set(dst_addr, c);
77 set_output(opcode, c);
78 } catch (AluException& e) {
79 throw OpcodeExecutionException("Alu sub operation failed");
80 }
81}
82
84{
85 constexpr auto opcode = ExecutionOpCode::MUL;
86 auto& memory = context.get_memory();
87 MemoryValue a = memory.get(a_addr);
88 MemoryValue b = memory.get(b_addr);
89 set_and_validate_inputs(opcode, { a, b });
90
92
93 try {
94 MemoryValue c = alu.mul(a, b);
95 memory.set(dst_addr, c);
96 set_output(opcode, c);
97 } catch (AluException& e) {
98 throw OpcodeExecutionException("Alu mul operation failed");
99 }
100}
101
103{
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 constexpr auto opcode = ExecutionOpCode::FDIV;
124 auto& memory = context.get_memory();
125 MemoryValue a = memory.get(a_addr);
126 MemoryValue b = memory.get(b_addr);
127 set_and_validate_inputs(opcode, { a, b });
128
130
131 try {
132 MemoryValue c = alu.fdiv(a, b);
133 memory.set(dst_addr, c);
134 set_output(opcode, c);
135 } catch (AluException& e) {
136 throw OpcodeExecutionException("Alu fdiv operation failed");
137 }
138}
139
141{
142 constexpr auto opcode = ExecutionOpCode::EQ;
143 auto& memory = context.get_memory();
144 MemoryValue a = memory.get(a_addr);
145 MemoryValue b = memory.get(b_addr);
146 set_and_validate_inputs(opcode, { a, b });
147
149
150 try {
151 MemoryValue c = alu.eq(a, b);
152 memory.set(dst_addr, c);
153 set_output(opcode, c);
154 } catch (AluException& e) {
155 throw OpcodeExecutionException("Alu eq operation failed");
156 }
157}
158
160{
161 constexpr auto opcode = ExecutionOpCode::LT;
162 auto& memory = context.get_memory();
163 MemoryValue a = memory.get(a_addr);
164 MemoryValue b = memory.get(b_addr);
165 set_and_validate_inputs(opcode, { a, b });
166
168
169 try {
170 MemoryValue c = alu.lt(a, b);
171 memory.set(dst_addr, c);
172 set_output(opcode, c);
173 } catch (AluException& e) {
174 throw OpcodeExecutionException("Alu lt operation failed");
175 }
176}
177
179{
180 constexpr auto opcode = ExecutionOpCode::LT;
181 auto& memory = context.get_memory();
182 MemoryValue a = memory.get(a_addr);
183 MemoryValue b = memory.get(b_addr);
184 set_and_validate_inputs(opcode, { a, b });
185
187
188 try {
189 MemoryValue c = alu.lte(a, b);
190 memory.set(dst_addr, c);
191 set_output(opcode, c);
192 } catch (AluException& e) {
193 throw OpcodeExecutionException("Alu lte operation failed");
194 }
195}
196
198{
199 constexpr auto opcode = ExecutionOpCode::NOT;
200 auto& memory = context.get_memory();
201 MemoryValue a = memory.get(src_addr);
202 set_and_validate_inputs(opcode, { a });
203
205
206 try {
207 MemoryValue b = alu.op_not(a);
208 memory.set(dst_addr, b);
209 set_output(opcode, b);
210 } catch (AluException& e) {
211 throw OpcodeExecutionException("Alu not operation failed");
212 }
213}
214
216{
217 constexpr auto opcode = ExecutionOpCode::SHL;
218 auto& memory = context.get_memory();
219 MemoryValue a = memory.get(a_addr);
220 MemoryValue b = memory.get(b_addr);
221 set_and_validate_inputs(opcode, { a, b });
222
224
225 try {
226 MemoryValue c = alu.shl(a, b);
227 memory.set(c_addr, c);
228 set_output(opcode, c);
229 } catch (const AluException& e) {
230 throw OpcodeExecutionException("SHL Exception: " + std::string(e.what()));
231 }
232}
233
235{
236 constexpr auto opcode = ExecutionOpCode::SHR;
237 auto& memory = context.get_memory();
238 MemoryValue a = memory.get(a_addr);
239 MemoryValue b = memory.get(b_addr);
240 set_and_validate_inputs(opcode, { a, b });
241
243
244 try {
245 MemoryValue c = alu.shr(a, b);
246 memory.set(c_addr, c);
247 set_output(opcode, c);
248 } catch (const AluException& e) {
249 throw OpcodeExecutionException("SHR Exception: " + std::string(e.what()));
250 }
251}
252
254{
255 constexpr auto opcode = ExecutionOpCode::CAST;
256 auto& memory = context.get_memory();
257 auto val = memory.get(src_addr);
258 set_and_validate_inputs(opcode, { val });
259
261 MemoryValue truncated = alu.truncate(val.as_ff(), static_cast<MemoryTag>(dst_tag));
262 memory.set(dst_addr, truncated);
263 set_output(opcode, truncated);
264}
265
267{
268 constexpr auto opcode = ExecutionOpCode::GETENVVAR;
269 auto& memory = context.get_memory();
270
272
273 TaggedValue result;
274
275 EnvironmentVariable env_var = static_cast<EnvironmentVariable>(var_enum);
276 switch (env_var) {
278 result = TaggedValue::from<FF>(context.get_address());
279 break;
281 result = TaggedValue::from<FF>(context.get_msg_sender());
282 break;
284 result = TaggedValue::from<FF>(context.get_transaction_fee());
285 break;
287 result = TaggedValue::from<FF>(context.get_globals().chainId);
288 break;
290 result = TaggedValue::from<FF>(context.get_globals().version);
291 break;
293 result = TaggedValue::from<uint32_t>(context.get_globals().blockNumber);
294 break;
296 result = TaggedValue::from<uint64_t>(context.get_globals().timestamp);
297 break;
299 result = TaggedValue::from<uint128_t>(context.get_globals().gasFees.feePerL2Gas);
300 break;
302 result = TaggedValue::from<uint128_t>(context.get_globals().gasFees.feePerDaGas);
303 break;
305 result = TaggedValue::from<uint1_t>(context.get_is_static() ? 1 : 0);
306 break;
308 result = TaggedValue::from<uint32_t>(context.gas_left().l2Gas);
309 break;
311 result = TaggedValue::from<uint32_t>(context.gas_left().daGas);
312 break;
313 default:
314 throw OpcodeExecutionException("Invalid environment variable enum value");
315 }
316
317 memory.set(dst_addr, result);
318 set_output(opcode, result);
319}
320
321// TODO: My dispatch system makes me have a uint8_t tag. Rethink.
323{
325
326 constexpr auto opcode = ExecutionOpCode::SET;
327 MemoryValue truncated = alu.truncate(value, static_cast<MemoryTag>(tag));
328 context.get_memory().set(dst_addr, truncated);
329 set_output(opcode, truncated);
330}
331
333{
334 constexpr auto opcode = ExecutionOpCode::MOV;
335 auto& memory = context.get_memory();
336 auto v = memory.get(src_addr);
337 set_and_validate_inputs(opcode, { v });
338
340
341 memory.set(dst_addr, v);
342 set_output(opcode, v);
343}
344
346 MemoryAddress l2_gas_offset,
347 MemoryAddress da_gas_offset,
348 MemoryAddress addr,
349 MemoryAddress cd_size_offset,
351{
352 constexpr auto opcode = ExecutionOpCode::CALL;
353 auto& memory = context.get_memory();
354
355 // NOTE: these reads cannot fail due to addressing guarantees.
356 const auto& allocated_l2_gas_read = memory.get(l2_gas_offset);
357 const auto& allocated_da_gas_read = memory.get(da_gas_offset);
358 const auto& contract_address = memory.get(addr);
359 // Cd offset loads are deferred to calldatacopy
360 const auto& cd_size = memory.get(cd_size_offset);
361
362 set_and_validate_inputs(opcode, { allocated_l2_gas_read, allocated_da_gas_read, contract_address, cd_size });
363
364 get_gas_tracker().consume_gas(); // Base gas.
366 Gas{ allocated_l2_gas_read.as<uint32_t>(), allocated_da_gas_read.as<uint32_t>() });
367
368 // Tag check contract address + cd_size
370 /*msg_sender=*/context.get_address(),
371 /*transaction_fee=*/context.get_transaction_fee(),
372 /*parent_context=*/context,
373 /*cd_offset_address=*/cd_offset,
374 /*cd_size=*/cd_size.as<uint32_t>(),
375 /*is_static=*/context.get_is_static(),
376 /*gas_limit=*/gas_limit,
377 /*side_effect_states=*/context.get_side_effect_states(),
378 /*phase=*/context.get_phase());
379
380 // We do not recurse. This context will be use on the next cycle of execution.
381 handle_enter_call(context, std::move(nested_context));
382}
383
385 MemoryAddress l2_gas_offset,
386 MemoryAddress da_gas_offset,
387 MemoryAddress addr,
388 MemoryAddress cd_size_offset,
390{
391 constexpr auto opcode = ExecutionOpCode::CALL;
392 auto& memory = context.get_memory();
393
394 // NOTE: these reads cannot fail due to addressing guarantees.
395 const auto& allocated_l2_gas_read = memory.get(l2_gas_offset);
396 const auto& allocated_da_gas_read = memory.get(da_gas_offset);
397 const auto& contract_address = memory.get(addr);
398 // Cd offset loads are deferred to calldatacopy
399 const auto& cd_size = memory.get(cd_size_offset);
400
401 set_and_validate_inputs(opcode, { allocated_l2_gas_read, allocated_da_gas_read, contract_address, cd_size });
402
403 get_gas_tracker().consume_gas(); // Base gas.
405 Gas{ allocated_l2_gas_read.as<uint32_t>(), allocated_da_gas_read.as<uint32_t>() });
406
407 // Tag check contract address + cd_size
409 /*msg_sender=*/context.get_address(),
410 /*transaction_fee=*/context.get_transaction_fee(),
411 /*parent_context=*/context,
412 /*cd_offset_address=*/cd_offset,
413 /*cd_size=*/cd_size.as<uint32_t>(),
414 /*is_static=*/true,
415 /*gas_limit=*/gas_limit,
416 /*side_effect_states=*/context.get_side_effect_states(),
417 /*phase=*/context.get_phase());
418
419 // We do not recurse. This context will be use on the next cycle of execution.
420 handle_enter_call(context, std::move(nested_context));
421}
422
424 MemoryAddress cd_size_offset,
427{
428 constexpr auto opcode = ExecutionOpCode::CALLDATACOPY;
429 auto& memory = context.get_memory();
430 auto cd_copy_size = memory.get(cd_size_offset); // Tag check u32
431 auto cd_offset_read = memory.get(cd_offset); // Tag check u32
432 set_and_validate_inputs(opcode, { cd_copy_size, cd_offset_read });
433
434 get_gas_tracker().consume_gas({ .l2Gas = cd_copy_size.as<uint32_t>(), .daGas = 0 });
435
436 try {
437 data_copy.cd_copy(context, cd_copy_size.as<uint32_t>(), cd_offset_read.as<uint32_t>(), dst_addr);
438 } catch (const std::exception& e) {
439 throw OpcodeExecutionException("cd copy failed: " + std::string(e.what()));
440 }
441}
442
444 MemoryAddress rd_size_offset,
445 MemoryAddress rd_offset,
447{
448 constexpr auto opcode = ExecutionOpCode::RETURNDATACOPY;
449 auto& memory = context.get_memory();
450 auto rd_copy_size = memory.get(rd_size_offset); // Tag check u32
451 auto rd_offset_read = memory.get(rd_offset); // Tag check u32
452 set_and_validate_inputs(opcode, { rd_copy_size, rd_offset_read });
453
454 get_gas_tracker().consume_gas({ .l2Gas = rd_copy_size.as<uint32_t>(), .daGas = 0 });
455
456 try {
457 data_copy.rd_copy(context, rd_copy_size.as<uint32_t>(), rd_offset_read.as<uint32_t>(), dst_addr);
458 } catch (const std::exception& e) {
459 throw OpcodeExecutionException("rd copy failed: " + std::string(e.what()));
460 }
461}
462
464{
465 constexpr auto opcode = ExecutionOpCode::RETURNDATASIZE;
466 auto& memory = context.get_memory();
467
469
470 // This is safe because the last_rd_size is tag checked on ret/revert to be U32
471 MemoryValue rd_size = MemoryValue::from<uint32_t>(context.get_last_rd_size());
472 memory.set(dst_addr, rd_size);
473 set_output(opcode, rd_size);
474}
475
477{
478 constexpr auto opcode = ExecutionOpCode::RETURN;
479 auto& memory = context.get_memory();
480 auto rd_size = memory.get(ret_size_offset);
481 set_and_validate_inputs(opcode, { rd_size });
482
484
485 set_execution_result({ .rd_offset = ret_offset,
486 .rd_size = rd_size.as<uint32_t>(),
487 .gas_used = context.get_gas_used(),
488 .side_effect_states = context.get_side_effect_states(),
489 .success = true });
490
491 context.halt();
492}
493
495{
496 constexpr auto opcode = ExecutionOpCode::REVERT;
497 auto& memory = context.get_memory();
498 auto rev_size = memory.get(rev_size_offset);
499 set_and_validate_inputs(opcode, { rev_size });
500
502
503 set_execution_result({ .rd_offset = rev_offset,
504 .rd_size = rev_size.as<uint32_t>(),
505 .gas_used = context.get_gas_used(),
506 .side_effect_states = context.get_side_effect_states(),
507 .success = false });
508
509 context.halt();
510}
511
513{
515
516 context.set_next_pc(loc);
517}
518
520{
521 constexpr auto opcode = ExecutionOpCode::JUMPI;
522 auto& memory = context.get_memory();
523
524 auto resolved_cond = memory.get(cond_addr);
525 set_and_validate_inputs(opcode, { resolved_cond });
526
528
529 if (resolved_cond.as<uint1_t>().value() == 1) {
530 context.set_next_pc(loc);
531 }
532}
533
535{
537
538 auto& internal_call_stack_manager = context.get_internal_call_stack_manager();
539 // The next pc is pushed onto the internal call stack. This will become return_pc later.
540 internal_call_stack_manager.push(context.get_next_pc());
541 context.set_next_pc(loc);
542}
543
545{
547
548 auto& internal_call_stack_manager = context.get_internal_call_stack_manager();
549 try {
550 auto next_pc = internal_call_stack_manager.pop();
551 context.set_next_pc(next_pc);
552 } catch (const std::exception& e) {
553 // Re-throw
554 throw OpcodeExecutionException("Internal return failed: " + std::string(e.what()));
555 }
556}
557
559{
561
562 try {
563 keccakf1600.permutation(context.get_memory(), dst_addr, src_addr);
564 } catch (const KeccakF1600Exception& e) {
565 throw OpcodeExecutionException("Keccak permutation failed: " + std::string(e.what()));
566 }
567}
568
570 MemoryAddress message_offset,
571 MemoryAddress fields_offset,
572 MemoryAddress fields_size_offset,
573 uint16_t message_size,
574 bool is_debug_logging_enabled)
575{
577
578 // DebugLog is a no-op on the prover side. If it was compiled with assertions and ran in debug mode,
579 // we will print part of the log. However, for this opcode, we give priority to never failing and
580 // never griefing the prover. Some safety checks are done, but if a failure happens, we will just
581 // silently continue.
582 if (is_debug_logging_enabled) {
583 try {
584 auto& memory = context.get_memory();
585
586 // Get the fields size and validate its tag
587 const auto fields_size_value = memory.get(fields_size_offset);
588 const uint32_t fields_size = fields_size_value.as<uint32_t>();
589
590 // Read message and fields from memory
591 std::string message_as_str;
592 uint16_t truncated_message_size = std::min<uint16_t>(message_size, 100);
593 for (uint32_t i = 0; i < truncated_message_size; ++i) {
594 const auto message_field = memory.get(message_offset + i);
595 message_as_str += static_cast<char>(static_cast<uint8_t>(message_field.as_ff()));
596 }
597 message_as_str += ": [";
598
599 // Read fields
600 for (uint32_t i = 0; i < fields_size; ++i) {
601 const auto field = memory.get(fields_offset + i);
602 message_as_str += field_to_string(field);
603 if (i < fields_size - 1) {
604 message_as_str += ", ";
605 }
606 }
607 message_as_str += "]";
608
609 debug("DEBUGLOG: ", message_as_str);
610 } catch (const std::exception& e) {
611 debug("DEBUGLOG: Error: ", e.what());
612 }
613 }
614}
615
617{
618 constexpr auto opcode = ExecutionOpCode::SUCCESSCOPY;
619 auto& memory = context.get_memory();
620
622
623 MemoryValue success = MemoryValue::from<uint1_t>(context.get_last_success());
624 memory.set(dst_addr, success);
625 set_output(opcode, success);
626}
627
629{
630 constexpr auto opcode = ExecutionOpCode::AND;
631 auto& memory = context.get_memory();
632 MemoryValue a = memory.get(a_addr);
633 MemoryValue b = memory.get(b_addr);
634 set_and_validate_inputs(opcode, { a, b });
635
636 // Dynamic gas consumption for bitwise is dependent on the tag, FF tags are valid here but
637 // will result in an exception in the bitwise subtrace.
638 get_gas_tracker().consume_gas({ .l2Gas = get_tag_bytes(a.get_tag()), .daGas = 0 });
639
640 try {
641 MemoryValue c = bitwise.and_op(a, b);
642 memory.set(dst_addr, c);
643 set_output(opcode, c);
644 } catch (const BitwiseException& e) {
645 throw OpcodeExecutionException("Bitwise AND Exeception");
646 }
647}
648
650{
651 constexpr auto opcode = ExecutionOpCode::OR;
652 auto& memory = context.get_memory();
653 MemoryValue a = memory.get(a_addr);
654 MemoryValue b = memory.get(b_addr);
655 set_and_validate_inputs(opcode, { a, b });
656
657 // Dynamic gas consumption for bitwise is dependent on the tag, FF tags are valid here but
658 // will result in an exception in the bitwise subtrace.
659 get_gas_tracker().consume_gas({ .l2Gas = get_tag_bytes(a.get_tag()), .daGas = 0 });
660
661 try {
662 MemoryValue c = bitwise.or_op(a, b);
663 memory.set(dst_addr, c);
664 set_output(opcode, c);
665 } catch (const BitwiseException& e) {
666 throw OpcodeExecutionException("Bitwise OR Exception");
667 }
668}
669
671{
672 constexpr auto opcode = ExecutionOpCode::XOR;
673 auto& memory = context.get_memory();
674 MemoryValue a = memory.get(a_addr);
675 MemoryValue b = memory.get(b_addr);
676 set_and_validate_inputs(opcode, { a, b });
677
678 // Dynamic gas consumption for bitwise is dependent on the tag, FF tags are valid here but
679 // will result in an exception in the bitwise subtrace.
680 get_gas_tracker().consume_gas({ .l2Gas = get_tag_bytes(a.get_tag()), .daGas = 0 });
681
682 try {
683 MemoryValue c = bitwise.xor_op(a, b);
684 memory.set(dst_addr, c);
685 set_output(opcode, c);
686 } catch (const BitwiseException& e) {
687 throw OpcodeExecutionException("Bitwise XOR Exception");
688 }
689}
690
692{
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 constexpr auto opcode = ExecutionOpCode::SSTORE;
711
712 auto& memory = context.get_memory();
713
714 auto slot = memory.get(slot_addr);
715 auto value = memory.get(src_addr);
716 set_and_validate_inputs(opcode, { value, slot });
717
718 bool was_slot_written_before = merkle_db.was_storage_written(context.get_address(), slot.as_ff());
719 uint32_t da_gas_factor = static_cast<uint32_t>(!was_slot_written_before);
720 get_gas_tracker().consume_gas({ .l2Gas = 0, .daGas = da_gas_factor });
721
722 if (context.get_is_static()) {
723 throw OpcodeExecutionException("SSTORE: Cannot write to storage in static context");
724 }
725
726 if (!was_slot_written_before &&
728 throw OpcodeExecutionException("SSTORE: Maximum number of data writes reached");
729 }
730
731 merkle_db.storage_write(context.get_address(), slot.as_ff(), value.as_ff(), false);
732}
733
735 MemoryAddress unique_note_hash_addr,
736 MemoryAddress leaf_index_addr,
738{
739 constexpr auto opcode = ExecutionOpCode::NOTEHASHEXISTS;
740
741 auto& memory = context.get_memory();
742 auto unique_note_hash = memory.get(unique_note_hash_addr);
743 auto leaf_index = memory.get(leaf_index_addr);
744 set_and_validate_inputs(opcode, { unique_note_hash, leaf_index });
745
747
748 uint64_t leaf_index_value = leaf_index.as<uint64_t>();
749
750 bool index_in_range = greater_than.gt(NOTE_HASH_TREE_LEAF_COUNT, leaf_index_value);
751
753
754 if (index_in_range) {
755 value = MemoryValue::from<uint1_t>(merkle_db.note_hash_exists(leaf_index_value, unique_note_hash.as<FF>()));
756 } else {
757 value = MemoryValue::from<uint1_t>(0);
758 }
759
760 memory.set(dst_addr, value);
761 set_output(opcode, value);
762}
763
765 MemoryAddress nullifier_offset,
766 MemoryAddress address_offset,
767 MemoryAddress exists_offset)
768{
769 constexpr auto opcode = ExecutionOpCode::NULLIFIEREXISTS;
770 auto& memory = context.get_memory();
771
772 auto nullifier = memory.get(nullifier_offset);
773 auto address = memory.get(address_offset);
774 set_and_validate_inputs(opcode, { nullifier, address });
775
777
778 // Check nullifier existence via MerkleDB
779 // (this also tag checks address and nullifier as FFs)
780 auto exists = merkle_db.nullifier_exists(address.as_ff(), nullifier.as_ff());
781
782 // Write result to memory
783 // (assigns tag u1 to result)
784 TaggedValue result = TaggedValue::from<uint1_t>(exists ? 1 : 0);
785 memory.set(exists_offset, result);
786 set_output(opcode, result);
787}
788
790{
791 constexpr auto opcode = ExecutionOpCode::EMITNULLIFIER;
792
793 auto& memory = context.get_memory();
794 MemoryValue nullifier = memory.get(nullifier_addr);
796
798
799 if (context.get_is_static()) {
800 throw OpcodeExecutionException("EMITNULLIFIER: Cannot emit nullifier in static context");
801 }
802
804 throw OpcodeExecutionException("EMITNULLIFIER: Maximum number of nullifiers reached");
805 }
806
807 // Emit nullifier via MerkleDB
808 // (and tag check nullifier as FF)
809 bool success = merkle_db.nullifier_write(context.get_address(), nullifier.as<FF>());
810 if (!success) {
811 throw OpcodeExecutionException("EMITNULLIFIER: Nullifier collision");
812 }
813}
814
816 MemoryAddress address_offset,
817 MemoryAddress dst_offset,
818 uint8_t member_enum)
819{
820 constexpr auto opcode = ExecutionOpCode::GETCONTRACTINSTANCE;
821 auto& memory = context.get_memory();
822
823 // Execution can still handle address memory read and tag checking
824 auto address_value = memory.get(address_offset);
825 AztecAddress contract_address = address_value.as<AztecAddress>();
826 set_and_validate_inputs(opcode, { address_value });
827
829
830 // Call the dedicated opcode component to get the contract instance, validate the enum,
831 // handle other errors, and perform the memory writes.
832 try {
834 } catch (const GetContractInstanceException& e) {
835 throw OpcodeExecutionException("GetContractInstance Exception");
836 }
837
838 // No `set_output` here since the dedicated component handles memory writes.
839}
840
842{
843 constexpr auto opcode = ExecutionOpCode::EMITNOTEHASH;
844
845 auto& memory = context.get_memory();
846 auto note_hash = memory.get(note_hash_addr);
847 set_and_validate_inputs(opcode, { note_hash });
848
850
851 if (context.get_is_static()) {
852 throw OpcodeExecutionException("EMITNOTEHASH: Cannot emit note hash in static context");
853 }
854
856 throw OpcodeExecutionException("EMITNOTEHASH: Maximum number of note hashes reached");
857 }
858
859 merkle_db.note_hash_write(context.get_address(), note_hash.as<FF>());
860}
861
863 MemoryAddress msg_hash_addr,
864 MemoryAddress leaf_index_addr,
866{
867 constexpr auto opcode = ExecutionOpCode::L1TOL2MSGEXISTS;
868
869 auto& memory = context.get_memory();
870 auto msg_hash = memory.get(msg_hash_addr);
871 auto leaf_index = memory.get(leaf_index_addr);
872 set_and_validate_inputs(opcode, { msg_hash, leaf_index });
873
875
876 uint64_t leaf_index_value = leaf_index.as<uint64_t>();
877
878 bool index_in_range = greater_than.gt(L1_TO_L2_MSG_TREE_LEAF_COUNT, leaf_index_value);
879
881
882 if (index_in_range) {
883 value = MemoryValue::from<uint1_t>(merkle_db.l1_to_l2_msg_exists(leaf_index_value, msg_hash.as<FF>()));
884 } else {
885 value = MemoryValue::from<uint1_t>(0);
886 }
887
888 memory.set(dst_addr, value);
889 set_output(opcode, value);
890}
891
893{
895 try {
896 poseidon2.permutation(context.get_memory(), src_addr, dst_addr);
897 } catch (const Poseidon2Exception& e) {
898 throw OpcodeExecutionException("Poseidon2 permutation failed: " + std::string(e.what()));
899 }
900}
901
903 MemoryAddress p_x_addr,
904 MemoryAddress p_y_addr,
905 MemoryAddress p_inf_addr,
906 MemoryAddress q_x_addr,
907 MemoryAddress q_y_addr,
908 MemoryAddress q_inf_addr,
910{
911 constexpr auto opcode = ExecutionOpCode::ECADD;
912 auto& memory = context.get_memory();
913
914 // Read the points from memory.
915 MemoryValue p_x = memory.get(p_x_addr);
916 MemoryValue p_y = memory.get(p_y_addr);
917 MemoryValue p_inf = memory.get(p_inf_addr);
918
919 MemoryValue q_x = memory.get(q_x_addr);
920 MemoryValue q_y = memory.get(q_y_addr);
921 MemoryValue q_inf = memory.get(q_inf_addr);
922
923 set_and_validate_inputs(opcode, { p_x, p_y, p_inf, q_x, q_y, q_inf });
925
926 // Once inputs are tag checked the conversion to EmbeddedCurvePoint is safe, on curve checks are done in the add
927 // method.
928 EmbeddedCurvePoint p = EmbeddedCurvePoint(p_x.as_ff(), p_y.as_ff(), p_inf == MemoryValue::from<uint1_t>(1));
929 EmbeddedCurvePoint q = EmbeddedCurvePoint(q_x.as_ff(), q_y.as_ff(), q_inf == MemoryValue::from<uint1_t>(1));
930
931 try {
933 } catch (const EccException& e) {
934 throw OpcodeExecutionException("Embedded curve add failed: " + std::string(e.what()));
935 }
936}
937
939 MemoryAddress value_addr,
940 MemoryAddress radix_addr,
941 MemoryAddress num_limbs_addr,
942 MemoryAddress is_output_bits_addr, // Decides if output is U1 or U8
944{
945 constexpr auto opcode = ExecutionOpCode::TORADIXBE;
946 auto& memory = context.get_memory();
947
948 MemoryValue value = memory.get(value_addr); // Field
949 MemoryValue radix = memory.get(radix_addr); // U32
950 MemoryValue num_limbs = memory.get(num_limbs_addr); // U32
951 MemoryValue is_output_bits = memory.get(is_output_bits_addr); // U1
952
953 // Tag check the inputs
954 set_and_validate_inputs(opcode, { value, radix, num_limbs, is_output_bits });
955
956 // The range check for a valid radix (2 <= radix <= 256) is done in the gadget.
957 // However, in order to compute the dynamic gas value we need to constrain the radix
958 // to be <= 256 since the `get_p_limbs_per_radix` lookup table is only defined for the range [0, 256].
959 // This does mean that the <= 256 check is duplicated - this can be optimized later.
960
961 // The dynamic gas factor is the maximum of the num_limbs requested by the opcode and the number of limbs
962 // the gadget that the field modulus, p, decomposes into given a radix (num_p_limbs).
963 // See to_radix.pil for how these values impact the row count.
964
965 // The lookup table of radix decomposed limbs of the modulus p is defined for radix values [0, 256],
966 // so for any radix value greater than 256 we set num_p_limbs to 32 - with
967 // the understanding the opcode will fail in the gadget (since the radix is invalid).
968 uint32_t radix_value = radix.as<uint32_t>();
969 uint32_t num_p_limbs = greater_than.gt(radix.as<uint32_t>(), 256)
970 ? 32
971 : static_cast<uint32_t>(get_p_limbs_per_radix()[radix_value].size());
972
973 // Compute the dynamic gas factor - done this way to trigger relevant circuit interactions
974 if (greater_than.gt(num_limbs.as<uint32_t>(), num_p_limbs)) {
975 get_gas_tracker().consume_gas({ .l2Gas = num_limbs.as<uint32_t>(), .daGas = 0 });
976
977 } else {
978 get_gas_tracker().consume_gas({ .l2Gas = num_p_limbs, .daGas = 0 });
979 }
980
981 try {
982 // Call the gadget to perform the conversion.
983 to_radix.to_be_radix(memory,
984 value.as_ff(),
985 radix_value,
986 num_limbs.as<uint32_t>(),
987 is_output_bits.as<uint1_t>().value() == 1,
988 dst_addr);
989 } catch (const ToRadixException& e) {
990 throw OpcodeExecutionException("ToRadixBe gadget failed: " + std::string(e.what()));
991 }
992}
993
995{
996 constexpr auto opcode = ExecutionOpCode::EMITUNENCRYPTEDLOG;
997 auto& memory = context.get_memory();
998
999 auto first_field = memory.get(log_offset);
1000 auto log_size = memory.get(log_size_offset);
1001 set_and_validate_inputs(opcode, { first_field, log_size });
1002 uint32_t log_size_int = log_size.as<uint32_t>();
1003
1004 get_gas_tracker().consume_gas({ .daGas = log_size_int });
1005
1006 // Call the dedicated opcode component to emit the log
1007 try {
1009 memory, context, context.get_address(), log_offset, log_size_int);
1010 } catch (const EmitUnencryptedLogException& e) {
1011 throw OpcodeExecutionException("EmitUnencryptedLog Exception");
1012 }
1013}
1014
1016{
1017 constexpr auto opcode = ExecutionOpCode::SENDL2TOL1MSG;
1018 auto& memory = context.get_memory();
1019
1020 auto recipient = memory.get(recipient_addr);
1021 auto content = memory.get(content_addr);
1022 set_and_validate_inputs(opcode, { recipient, content });
1023
1025
1026 auto side_effects_states_before = context.get_side_effect_states();
1027
1028 if (context.get_is_static()) {
1029 throw OpcodeExecutionException("SENDL2TOL1MSG: Cannot send L2 to L1 message in static context");
1030 }
1031
1032 if (side_effects_states_before.numL2ToL1Messages == MAX_L2_TO_L1_MSGS_PER_TX) {
1033 throw OpcodeExecutionException("SENDL2TOL1MSG: Maximum number of L2 to L1 messages reached");
1034 }
1035
1036 // TODO: We don't store the l2 to l1 message in the context since it's not needed until cpp has to generate
1037 // public inputs.
1038
1039 side_effects_states_before.numL2ToL1Messages++;
1040 context.set_side_effect_states(side_effects_states_before);
1041}
1042
1044 MemoryAddress output_addr,
1045 MemoryAddress state_addr,
1046 MemoryAddress input_addr)
1047{
1049
1050 try {
1051 sha256.compression(context.get_memory(), state_addr, input_addr, output_addr);
1052 } catch (const Sha256CompressionException& e) {
1053 throw OpcodeExecutionException("Sha256 Compression failed: " + std::string(e.what()));
1054 }
1055}
1056
1057// This context interface is a top-level enqueued one.
1058// NOTE: For the moment this trace is not returning the context back.
1060{
1061 external_call_stack.push(std::move(enqueued_call_context));
1062
1063 while (!external_call_stack.empty()) {
1064 // We fix the context at this point. Even if the opcode changes the stack
1065 // we'll always use this in the loop.
1066 auto& context = *external_call_stack.top();
1067
1068 // We'll be filling in the event as we go. And we always emit at the end.
1069 ExecutionEvent ex_event;
1070
1071 try {
1072 // State before doing anything.
1073 ex_event.before_context_event = context.serialize_context_event();
1075 auto pc = context.get_pc();
1076
1078
1079 // We try to get the bytecode id. This can throw if the contract is not deployed.
1080 // Note: bytecode_id is tracked in context events, not in the top-level execution event.
1081 // It is already included in the before_context_event (defaulting to 0 on error/not-found).
1082 context.get_bytecode_manager().get_bytecode_id();
1083
1085
1086 // We try to fetch an instruction.
1087 Instruction instruction = context.get_bytecode_manager().read_instruction(pc);
1088
1089 ex_event.wire_instruction = instruction;
1090 debug("@", pc, " ", instruction.to_string());
1091 context.set_next_pc(pc + static_cast<uint32_t>(instruction.size_in_bytes()));
1092
1094
1095 // Resolve the operands.
1097 std::vector<Operand> resolved_operands = addressing->resolve(instruction, context.get_memory());
1098
1100
1102 dispatch_opcode(instruction.get_exec_opcode(), context, resolved_operands);
1103 }
1104 // TODO(fcarreiro): handle this in a better way.
1105 catch (const BytecodeNotFoundError& e) {
1106 vinfo("Bytecode not found: ", e.what());
1109 } catch (const InstructionFetchingError& e) {
1110 vinfo("Instruction fetching error: ", e.what());
1113 } catch (const AddressingException& e) {
1114 vinfo("Addressing exception: ", e.what());
1117 } catch (const RegisterValidationException& e) {
1118 vinfo("Register validation exception: ", e.what());
1121 } catch (const OutOfGasException& e) {
1122 vinfo("Out of gas exception: ", e.what());
1123 ex_event.error = ExecutionError::GAS;
1125 } catch (const OpcodeExecutionException& e) {
1126 vinfo("Opcode execution exception: ", e.what());
1129 } catch (const std::exception& e) {
1130 // This is a coding error, we should not get here.
1131 // All exceptions should fall in the above catch blocks.
1132 info("An unhandled exception occurred: ", e.what());
1133 throw e;
1134 }
1135
1136 // We always do what follows. "Finally".
1137 // Move on to the next pc.
1138 context.set_pc(context.get_next_pc());
1140
1141 // TODO: we set the inputs and outputs here and into the execution event, but maybe there's a better way
1142 ex_event.inputs = get_inputs();
1143 ex_event.output = get_output();
1144
1145 // State after the opcode.
1146 ex_event.after_context_event = context.serialize_context_event();
1147 events.emit(std::move(ex_event));
1148
1149 // If the context has halted, we need to exit the external call.
1150 // The external call stack is expected to be popped.
1151 if (context.halted()) {
1153 }
1154 }
1155
1156 return get_execution_result();
1157}
1158
1160{
1161 ctx_stack_events.emit(
1162 { .id = parent_context.get_context_id(),
1163 .parent_id = parent_context.get_parent_id(),
1164 .entered_context_id = child_context->get_context_id(), // gets the context id of the child!
1165 .next_pc = parent_context.get_next_pc(),
1166 .msg_sender = parent_context.get_msg_sender(),
1167 .contract_addr = parent_context.get_address(),
1168 .bytecode_id = parent_context.get_bytecode_manager().try_get_bytecode_id().value_or(FF(0)),
1169 .is_static = parent_context.get_is_static(),
1170 .parent_cd_addr = parent_context.get_parent_cd_addr(),
1171 .parent_cd_size = parent_context.get_parent_cd_size(),
1172 .parent_gas_used = parent_context.get_parent_gas_used(),
1173 .parent_gas_limit = parent_context.get_parent_gas_limit(),
1174 .tree_states = merkle_db.get_tree_state(),
1175 .written_public_data_slots_tree_snapshot = parent_context.get_written_public_data_slots_tree_snapshot(),
1176 .side_effect_states = parent_context.get_side_effect_states() });
1177
1178 external_call_stack.push(std::move(child_context));
1179}
1180
1182{
1183 // NOTE: the current (child) context should not be modified here, since it was already emitted.
1185 external_call_stack.pop();
1187
1188 // We only handle reverting/committing of nested calls. Enqueued calls are handled by TX execution.
1189 if (!external_call_stack.empty()) {
1190 // Note: committing or reverting the db here also commits or reverts the
1191 // tracked nullifiers, public writes dictionary, etc. These structures
1192 // "listen" to the db changes.
1193 if (result.success) {
1195 } else {
1197 }
1198
1199 auto& parent_context = *external_call_stack.top();
1200 // was not top level, communicate with parent
1201 parent_context.set_last_rd_addr(result.rd_offset);
1202 parent_context.set_last_rd_size(result.rd_size);
1203 parent_context.set_last_success(result.success);
1204 // Safe since the nested context gas limit should be clamped to the available gas.
1205 parent_context.set_gas_used(result.gas_used + parent_context.get_gas_used());
1206 if (result.success) {
1207 parent_context.set_side_effect_states(result.side_effect_states);
1208 }
1209 parent_context.set_child_context(std::move(child_context));
1210
1211 // TODO(fcarreiro): move somewhere else.
1212 if (parent_context.get_checkpoint_id_at_creation() != merkle_db.get_checkpoint_id()) {
1213 throw std::runtime_error(format("Checkpoint id mismatch: ",
1214 parent_context.get_checkpoint_id_at_creation(),
1215 " != ",
1217 " (gone back to the wrong db/context)"));
1218 }
1219 }
1220 // Else: was top level. ExecutionResult is already set and that will be returned.
1221}
1222
1224{
1225 context.set_gas_used(context.get_gas_limit()); // Consume all gas.
1226 context.halt();
1228 .rd_offset = 0,
1229 .rd_size = 0,
1230 .gas_used = context.get_gas_used(),
1231 .side_effect_states = context.get_side_effect_states(),
1232 .success = false,
1233 });
1234}
1235
1238 const std::vector<Operand>& resolved_operands)
1239{
1240 // TODO: consider doing this even before the dispatch.
1241 inputs = {};
1242 output = TaggedValue::from<FF>(0);
1243
1244 debug("Dispatching opcode: ", opcode, " (", static_cast<uint32_t>(opcode), ")");
1245 switch (opcode) {
1247 call_with_operands(&Execution::add, context, resolved_operands);
1248 break;
1250 call_with_operands(&Execution::sub, context, resolved_operands);
1251 break;
1253 call_with_operands(&Execution::mul, context, resolved_operands);
1254 break;
1256 call_with_operands(&Execution::div, context, resolved_operands);
1257 break;
1259 call_with_operands(&Execution::fdiv, context, resolved_operands);
1260 break;
1262 call_with_operands(&Execution::eq, context, resolved_operands);
1263 break;
1265 call_with_operands(&Execution::lt, context, resolved_operands);
1266 break;
1268 call_with_operands(&Execution::lte, context, resolved_operands);
1269 break;
1271 call_with_operands(&Execution::op_not, context, resolved_operands);
1272 break;
1274 call_with_operands(&Execution::shl, context, resolved_operands);
1275 break;
1277 call_with_operands(&Execution::shr, context, resolved_operands);
1278 break;
1280 call_with_operands(&Execution::cast, context, resolved_operands);
1281 break;
1284 break;
1286 call_with_operands(&Execution::set, context, resolved_operands);
1287 break;
1289 call_with_operands(&Execution::mov, context, resolved_operands);
1290 break;
1292 call_with_operands(&Execution::call, context, resolved_operands);
1293 break;
1296 break;
1298 call_with_operands(&Execution::ret, context, resolved_operands);
1299 break;
1301 call_with_operands(&Execution::revert, context, resolved_operands);
1302 break;
1304 call_with_operands(&Execution::jump, context, resolved_operands);
1305 break;
1307 call_with_operands(&Execution::jumpi, context, resolved_operands);
1308 break;
1310 call_with_operands(&Execution::cd_copy, context, resolved_operands);
1311 break;
1313 call_with_operands(&Execution::rd_copy, context, resolved_operands);
1314 break;
1317 break;
1320 break;
1323 break;
1326 break;
1328 call_with_operands(&Execution::rd_size, context, resolved_operands);
1329 break;
1332 resolved_operands.at(0).as<MemoryAddress>(),
1333 resolved_operands.at(1).as<MemoryAddress>(),
1334 resolved_operands.at(2).as<MemoryAddress>(),
1335 resolved_operands.at(3).as<uint16_t>(),
1337 break;
1339 call_with_operands(&Execution::and_op, context, resolved_operands);
1340 break;
1342 call_with_operands(&Execution::or_op, context, resolved_operands);
1343 break;
1345 call_with_operands(&Execution::xor_op, context, resolved_operands);
1346 break;
1348 call_with_operands(&Execution::sload, context, resolved_operands);
1349 break;
1351 call_with_operands(&Execution::sstore, context, resolved_operands);
1352 break;
1355 break;
1358 break;
1361 break;
1364 break;
1367 break;
1370 break;
1373 break;
1375 call_with_operands(&Execution::ecc_add, context, resolved_operands);
1376 break;
1379 break;
1382 break;
1385 break;
1388 break;
1389 default:
1390 // NOTE: Keep this a `std::runtime_error` so that the main loop panics.
1391 throw std::runtime_error("Tried to dispatch unknown execution opcode: " +
1392 std::to_string(static_cast<uint32_t>(opcode)));
1393 }
1394}
1395
1396// Some template magic to dispatch the opcode by deducing the number of arguments and types,
1397// and making the appropriate checks and casts.
1398template <typename... Ts>
1401 const std::vector<Operand>& resolved_operands)
1402{
1403 assert(resolved_operands.size() == sizeof...(Ts));
1404 auto operand_indices = std::make_index_sequence<sizeof...(Ts)>{};
1405 [f, this, &context, &resolved_operands]<std::size_t... Is>(std::index_sequence<Is...>) {
1406 // FIXME(fcarreiro): we go through FF here.
1407 (this->*f)(context, static_cast<Ts>(resolved_operands.at(Is).as_ff())...);
1408 }(operand_indices);
1409}
1410
1411// Sets the register inputs and validates the tags.
1412// The tag information is taken from the instruction info database (exec spec).
1414{
1415 const auto& register_info = instruction_info_db.get(opcode).register_info;
1416 assert(inputs.size() == register_info.num_inputs());
1417 this->inputs = std::move(inputs);
1418 for (size_t i = 0; i < register_info.num_inputs(); i++) {
1419 if (register_info.expected_tag(i) && register_info.expected_tag(i) != this->inputs.at(i).get_tag()) {
1420 throw RegisterValidationException(format("Input ",
1421 i,
1422 " tag ",
1423 std::to_string(this->inputs.at(i).get_tag()),
1424 " does not match expected tag ",
1425 std::to_string(*register_info.expected_tag(i))));
1426 }
1427 }
1428}
1429
1431{
1432 const auto& register_info = instruction_info_db.get(opcode).register_info;
1433 (void)register_info; // To please GCC.
1434 assert(register_info.num_outputs() == 1);
1435 this->output = std::move(output);
1436}
1437
1438} // 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
virtual std::optional< BytecodeId > try_get_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 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)
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 emit_unencrypted_log(ContextInterface &context, MemoryAddress log_offset, MemoryAddress log_size_offset)
void sub(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
Definition execution.cpp:64
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 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)
void debug_log(ContextInterface &context, MemoryAddress message_offset, MemoryAddress fields_offset, MemoryAddress fields_size_offset, uint16_t message_size, bool is_debug_logging_enabled)
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:83
void or_op(ContextInterface &context, MemoryAddress a_addr, MemoryAddress b_addr, MemoryAddress dst_addr)
const std::vector< TaggedValue > & get_inputs() const
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:45
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, 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 bool 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:20
void debug(Args... args)
Definition log.hpp:59
void vinfo(Args... args)
Definition log.hpp:76
void info(Args... args)
Definition log.hpp:70
uint32_t dst_addr
FF a
FF b
uint64_t daGas
Instruction instruction
bool debug_logging
Definition log.cpp:12
const std::array< std::vector< uint8_t >, 257 > & get_p_limbs_per_radix()
Definition to_radix.cpp:33
StandardAffinePoint< AvmFlavorSettings::EmbeddedCurve::AffineElement > EmbeddedCurvePoint
Definition field.hpp:12
std::string field_to_string(const FF &ff)
Definition stringify.cpp:5
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
General class for prime fields see Prime field documentation["field documentation"] for general imple...