Skip to content

Commit 5ecbaf2

Browse files
authored
feat(avm)!: opcode error for infallible opcodes (#16629)
This PR constrains `execution.sel_opcode_error` to be `0` for opcodes that cannot fail during the opcode execution stage. I gathered the following list of "infallible" opcodes looking at `execution.cpp` and seeing which ones do not throw `OpcodeExecutionException`: * [x] CAST * [x] SET * [x] MOV * [x] CALL * [x] STATICCALL * [x] RETURNDATASIZE * [x] RETURN * [x] REVERT * [x] JUMP * [x] JUMPI * [x] INTERNALCALL * [x] DEBUGLOG * [x] SUCCESSCOPY * [x] SLOAD (was already done) * [x] NOTEHASHEXISTS (was already done) * [x] NULLIFIEREXISTS (was already done) * [x] L1TOL2MESSAGEEXISTS (was already done) NOTE: Calls/returns use `sel_error` in a way that might be wrong, and tracegen for them is certainly wrong. I made some small changes but mostly added a new entry in our structural list to fix this. Closes #15375.
2 parents 581b16f + f203d01 commit 5ecbaf2

File tree

15 files changed

+92
-69
lines changed

15 files changed

+92
-69
lines changed

barretenberg/cpp/pil/vm2/alu.pil

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,9 +513,9 @@ sel_op_truncate * (1 - sel_op_truncate) = 0;
513513
// Note that we enforce the output tag through this dispatching lookup by having ia_tag matching both mem_tag_reg[1] and rop[2].
514514
#[EXEC_DISPATCHING_CAST]
515515
execution.sel_execute_cast {
516-
execution.register[0], execution.rop[2], execution.subtrace_operation_id, execution.register[1], execution.mem_tag_reg[1]
516+
execution.register[0], execution.rop[2], execution.subtrace_operation_id, execution.register[1], execution.mem_tag_reg[1], execution.sel_opcode_error
517517
} in sel_op_truncate {
518-
ia, ia_tag, op_id, ic, ia_tag
518+
ia, ia_tag, op_id, ic, ia_tag, /*sel_opcode_error=*/ precomputed.zero
519519
};
520520

521521
// SET DISPATCHING
@@ -534,9 +534,9 @@ execution.sel_execute_cast {
534534
// Note that we enforce the output tag through this dispatching lookup by having ia_tag matching both mem_tag_reg[0] and rop[1].
535535
#[EXEC_DISPATCHING_SET]
536536
execution.sel_execute_set {
537-
execution.rop[2], execution.rop[1], execution.subtrace_operation_id, execution.register[0], execution.mem_tag_reg[0]
537+
execution.rop[2], execution.rop[1], execution.subtrace_operation_id, execution.register[0], execution.mem_tag_reg[0], execution.sel_opcode_error
538538
} in sel_op_truncate {
539-
ia, ia_tag, op_id, ic, ic_tag
539+
ia, ia_tag, op_id, ic, ic_tag, /*sel_opcode_error=*/ precomputed.zero
540540
};
541541

542542
// 3 cases for truncation:

barretenberg/cpp/pil/vm2/context.pil

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
include "context_stack.pil";
22

3+
// TODO: Usage of sel_error in this whole file has to be reconsidered.
4+
35
// This is a virtual gadget, which is part of the execution trace.
46
// This subtrace is focused on managing the changes to the context.
57
// By default (i.e. when not executing a context-changing opcode),
@@ -12,12 +14,12 @@ namespace execution;
1214

1315
// Guaranteed to be boolean because sel_execute_call & sel_execute_static_call are mutually exclusive
1416
pol commit sel_enter_call;
15-
// Handle error separately since it takes priority and may occur during an sel_x_call
16-
sel_enter_call = (sel_execute_call + sel_execute_static_call) * (1 - sel_error);
17+
// The following selectors will be 0 if there is an error during execution (before the opcode execution step).
18+
sel_enter_call = sel_execute_call + sel_execute_static_call;
1719
// CALL & precomputed.first_row are mutually exclusive
1820
sel_enter_call * precomputed.first_row = 0;
1921

20-
// sel_exit_call is used to flag if we are returning or reverting or we error
22+
// sel_exit_call is used to flag if we are returning or reverting or there has been ANY error during execution
2123
// sel_execute_revert & sel_execute_return are mutually exclusive
2224
pol commit sel_exit_call;
2325
sel_exit_call = 1 - (1 - sel_execute_revert - sel_execute_return) * (1 - sel_error);

barretenberg/cpp/pil/vm2/execution.pil

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,15 @@ sel * (1 - sel_execute_emit_unencrypted_log) * (prev_num_unencrypted_logs - num_
642642
#[NUM_L2_TO_L1_MESSAGES_NOT_CHANGED]
643643
sel * (1 - sel_execute_send_l2_to_l1_msg) * (prev_num_l2_to_l1_messages - num_l2_to_l1_messages) = 0;
644644

645+
// Some opcodes cannot fail during opcode execution. They should not be able to set sel_opcode_error.
646+
// Note that some of these opcodes can fail in the stages before execution.
647+
// This list only takes into account the opcodes that are handled by the execution subtrace.
648+
// Infallible opcodes which are handled by other traces should constrain sel_opcode_error there.
649+
(sel_execute_mov + sel_execute_returndata_size + sel_execute_jump +
650+
sel_execute_jumpi + sel_execute_debug_log + sel_execute_success_copy +
651+
sel_execute_call + sel_execute_static_call + sel_execute_internal_call +
652+
sel_execute_return + sel_execute_revert) * sel_opcode_error = 0;
653+
645654
/**************************************************************************************************
646655
* Temporality group 6: Register write.
647656
**************************************************************************************************/

barretenberg/cpp/pil/vm2/opcodes/internal_call.pil

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ namespace execution;
1313
// When we encounter this case, the internal call information is reset on the next row (i.e. internal_call_id = 1, return_id = 0 and next_internal_call_id = 2)
1414
pol RESET_NEXT_CALL_ID = sel_enter_call + enqueued_call_start';
1515

16-
// sel_exit_call includes the error case, so we gate sel_execute_internal_call and sel_execute_internal_return by (1 - sel_error). This makes them all mututally exclusive.
16+
// sel_exit_call includes external exit cases (revert, return, any execution error).
17+
// sel_execute_internal_return could be 1 and sel_opcode_error could be 1 at the same time (which would set sel_error=1 and sel_exit_call=1),
18+
// so we need to gate them by (1 - sel_error) here. We gate both of them out of excessive caution.
1719
// When we encounter this case, the internal call information in the next row is constrained to change (incremented, unwound, etc)
1820
pol NEW_NEXT_CALL_ID = (sel_execute_internal_call + sel_execute_internal_return) * (1 - sel_error) + sel_exit_call;
1921

barretenberg/cpp/pil/vm2/opcodes/nullifier_exists.pil

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
include "../constants_gen.pil";
2+
13
/**
24
* This virtual gadget implements the NullifierExists opcode, which checks if a nullifier exists
35
* in the nullifier tree for a given contract address.
@@ -34,7 +36,7 @@ namespace execution; // this is a virtual gadget that shares rows with the execu
3436
// Inputs
3537
/*nullifier=*/ register[0], // input: nullifier to check
3638
prev_nullifier_tree_root, // input: tree root from context
37-
/*should_silo=1*/ sel_execute_nullifier_exists, // input: should_silo = 1 (always silo for contract nullifiers)
39+
/*should_silo=1*/ sel_execute_nullifier_exists, // input: should_silo = 1 (always silo for contract nullifiers)
3840
/*contract_address=*/ register[1] // input: contract address for siloing
3941
} in nullifier_check.sel {
4042
// Outputs

barretenberg/cpp/pil/vm2/opcodes/sload.pil

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ namespace execution; // this is a virtual gadget that shares rows with the execu
2121
#[skippable_if]
2222
sel_execute_sload = 0; // from execution.pil.
2323

24-
#[SLOAD_SUCCESS]
25-
sel_execute_sload * sel_opcode_error = 0;
26-
2724
#[STORAGE_READ]
2825
sel_execute_sload {
2926
contract_address, // From context
@@ -40,3 +37,5 @@ namespace execution; // this is a virtual gadget that shares rows with the execu
4037
#[SLOAD_FF_OUTPUT_TAG]
4138
sel_execute_sload * (constants.MEM_TAG_FF - mem_tag_reg[1]) = 0;
4239

40+
#[SLOAD_SUCCESS]
41+
sel_execute_sload * sel_opcode_error = 0;

barretenberg/cpp/src/barretenberg/vm2/generated/relations/context.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ template <typename FF_> class contextImpl {
1515
using FF = FF_;
1616

1717
static constexpr std::array<size_t, 69> SUBRELATION_PARTIAL_LENGTHS = {
18-
3, 3, 3, 3, 4, 3, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 3, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 6, 5, 5, 5,
18+
2, 3, 3, 3, 4, 3, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 3, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 6, 5, 5, 5,
1919
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5
2020
};
2121

barretenberg/cpp/src/barretenberg/vm2/generated/relations/context_impl.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ void contextImpl<FF_>::accumulate(ContainerOverSubrelations& evals,
3939
{
4040
using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>;
4141
auto tmp = (in.get(C::execution_sel_enter_call) -
42-
(in.get(C::execution_sel_execute_call) + in.get(C::execution_sel_execute_static_call)) *
43-
(FF(1) - in.get(C::execution_sel_error)));
42+
(in.get(C::execution_sel_execute_call) + in.get(C::execution_sel_execute_static_call)));
4443
tmp *= scaling_factor;
4544
std::get<0>(evals) += typename Accumulator::View(tmp);
4645
}

barretenberg/cpp/src/barretenberg/vm2/generated/relations/execution.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ template <typename FF_> class executionImpl {
1414
public:
1515
using FF = FF_;
1616

17-
static constexpr std::array<size_t, 87> SUBRELATION_PARTIAL_LENGTHS = {
18-
3, 3, 3, 3, 2, 4, 3, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
19-
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3,
20-
3, 3, 3, 3, 3, 4, 3, 5, 6, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2
17+
static constexpr std::array<size_t, 88> SUBRELATION_PARTIAL_LENGTHS = {
18+
3, 3, 3, 3, 2, 4, 3, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
19+
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3,
20+
3, 3, 3, 4, 3, 5, 6, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2
2121
};
2222

2323
template <typename AllEntities> inline static bool skip(const AllEntities& in)

barretenberg/cpp/src/barretenberg/vm2/generated/relations/execution_impl.hpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -684,20 +684,32 @@ void executionImpl<FF_>::accumulate(ContainerOverSubrelations& evals,
684684
}
685685
{
686686
using Accumulator = typename std::tuple_element_t<85, ContainerOverSubrelations>;
687-
auto tmp = (in.get(C::execution_sel_should_write_registers) -
688-
in.get(C::execution_sel_should_execute_opcode) * (FF(1) - in.get(C::execution_sel_opcode_error)));
687+
auto tmp = (in.get(C::execution_sel_execute_mov) + in.get(C::execution_sel_execute_returndata_size) +
688+
in.get(C::execution_sel_execute_jump) + in.get(C::execution_sel_execute_jumpi) +
689+
in.get(C::execution_sel_execute_debug_log) + in.get(C::execution_sel_execute_success_copy) +
690+
in.get(C::execution_sel_execute_call) + in.get(C::execution_sel_execute_static_call) +
691+
in.get(C::execution_sel_execute_internal_call) + in.get(C::execution_sel_execute_return) +
692+
in.get(C::execution_sel_execute_revert)) *
693+
in.get(C::execution_sel_opcode_error);
689694
tmp *= scaling_factor;
690695
std::get<85>(evals) += typename Accumulator::View(tmp);
691696
}
692697
{
693698
using Accumulator = typename std::tuple_element_t<86, ContainerOverSubrelations>;
699+
auto tmp = (in.get(C::execution_sel_should_write_registers) -
700+
in.get(C::execution_sel_should_execute_opcode) * (FF(1) - in.get(C::execution_sel_opcode_error)));
701+
tmp *= scaling_factor;
702+
std::get<86>(evals) += typename Accumulator::View(tmp);
703+
}
704+
{
705+
using Accumulator = typename std::tuple_element_t<87, ContainerOverSubrelations>;
694706
auto tmp = (in.get(C::execution_sel_error) -
695707
(in.get(C::execution_sel_bytecode_retrieval_failure) +
696708
in.get(C::execution_sel_instruction_fetching_failure) + in.get(C::execution_sel_addressing_error) +
697709
in.get(C::execution_sel_register_read_error) + in.get(C::execution_sel_out_of_gas) +
698710
in.get(C::execution_sel_opcode_error)));
699711
tmp *= scaling_factor;
700-
std::get<86>(evals) += typename Accumulator::View(tmp);
712+
std::get<87>(evals) += typename Accumulator::View(tmp);
701713
}
702714
}
703715

0 commit comments

Comments
 (0)