From acf16ebe507a7a27f50bc024474cd6ef52f72d01 Mon Sep 17 00:00:00 2001 From: jeanmon Date: Mon, 11 Nov 2024 16:19:15 +0000 Subject: [PATCH] 9745: stop execution when an error occurs --- .../vm/avm/tests/execution.test.cpp | 18 +- .../src/barretenberg/vm/avm/trace/common.hpp | 10 + .../barretenberg/vm/avm/trace/execution.cpp | 593 +++++++++--------- .../src/barretenberg/vm/avm/trace/helper.cpp | 38 +- .../src/barretenberg/vm/avm/trace/helper.hpp | 2 + .../src/barretenberg/vm/avm/trace/trace.cpp | 576 +++++++++-------- .../src/barretenberg/vm/avm/trace/trace.hpp | 223 +++---- 7 files changed, 795 insertions(+), 665 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp index 21fcaf8cd2b9..1c9fd0479845 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp @@ -2087,6 +2087,7 @@ TEST_F(AvmExecutionTests, opCallOpcodes) // Calldata for l2_gas, da_gas, contract_address, nested_call_args (4 elements), std::vector calldata = { 17, 10, 34802342, 1, 2, 3, 4 }; std::string bytecode_preamble; + // Set up Gas offsets bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET for gas offset indirect "00" // Indirect flag @@ -2106,11 +2107,16 @@ TEST_F(AvmExecutionTests, opCallOpcodes) "03" // val 3 (the start of the args array) "13"; // dst_offset 19 // Set up args size offset - bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET for ret offset indirect + bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET for args size indirect "00" // Indirect flag + to_hex(AvmMemoryTag::U32) + - "04" // val 4 (the length of the args array) - "14"; // dst_offset 20 + "04" // val 4 - resolved address + "14"; // dst_offset 20 + bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + + to_hex(AvmMemoryTag::U32) + // + "00" // val 0 (args size) + "04"; // dst_offset 4 // Set up the ret offset bytecode_preamble += to_hex(OpCode::SET_16) + // opcode SET for ret offset indirect "00" // Indirect flag @@ -2118,7 +2124,7 @@ TEST_F(AvmExecutionTests, opCallOpcodes) "0100" // val 256 (the start of where to write the return data) "0015"; // dst_offset 21 // Set up the success offset - bytecode_preamble += to_hex(OpCode::SET_16) + // opcode SET for ret offset indirect + bytecode_preamble += to_hex(OpCode::SET_16) + // opcode SET for success offset indirect "00" // Indirect flag + to_hex(AvmMemoryTag::U32) + "0102" // val 258 (write the success flag at ret_offset + ret_size) @@ -2206,8 +2212,8 @@ TEST_F(AvmExecutionTests, opGetContractInstanceOpcode) std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag - + to_hex(AvmMemoryTag::U8) + to_hex(address_byte) + // val - "01" // dst_offset 0 + + to_hex(AvmMemoryTag::FF) + to_hex(address_byte) + // val + "01" // dst_offset 1 + to_hex(OpCode::GETCONTRACTINSTANCE) + // opcode GETCONTRACTINSTANCE "00" // Indirect flag + to_hex(static_cast(ContractInstanceMember::DEPLOYER)) + // member enum diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/common.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/common.hpp index fd5de470550e..c8983191ec4b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/common.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/common.hpp @@ -49,6 +49,16 @@ enum class AvmMemoryTag : uint32_t { static const uint32_t MAX_MEM_TAG = MEM_TAG_U128; +enum class AvmError : uint32_t { + NO_ERROR, + TAG_ERROR, + ADDR_RES_ERROR, + DIV_ZERO, + PARSING_ERROR, + ENV_VAR_UNKNOWN, + CONTRACT_INST_MEM_UNKNOWN +}; + static const size_t NUM_MEM_SPACES = 256; static const uint8_t INTERNAL_CALL_SPACE_ID = 255; static const uint32_t MAX_SIZE_INTERNAL_STACK = 1 << 16; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp index f2513a5b9f0f..aee55b03cefe 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp @@ -287,7 +287,8 @@ std::vector Execution::gen_trace(std::vector const& calldata, // is determined by this value which require read access to the code below. uint32_t pc = 0; uint32_t counter = 0; - while ((pc = trace_builder.get_pc()) < bytecode.size()) { + AvmError error = AvmError::NO_ERROR; + while (error == AvmError::NO_ERROR && (pc = trace_builder.get_pc()) < bytecode.size()) { auto inst = Deserialization::parse(bytecode, pc); debug("[PC:" + std::to_string(pc) + "] [IC:" + std::to_string(counter++) + "] " + inst.to_string() + @@ -297,404 +298,405 @@ std::vector Execution::gen_trace(std::vector const& calldata, // Compute // Compute - Arithmetic case OpCode::ADD_8: - trace_builder.op_add(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::ADD_8); + error = trace_builder.op_add(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::ADD_8); break; case OpCode::ADD_16: - trace_builder.op_add(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::ADD_16); + error = trace_builder.op_add(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::ADD_16); break; case OpCode::SUB_8: - trace_builder.op_sub(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::SUB_8); + error = trace_builder.op_sub(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::SUB_8); break; case OpCode::SUB_16: - trace_builder.op_sub(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::SUB_16); + error = trace_builder.op_sub(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::SUB_16); break; case OpCode::MUL_8: - trace_builder.op_mul(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::MUL_8); + error = trace_builder.op_mul(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::MUL_8); break; case OpCode::MUL_16: - trace_builder.op_mul(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::MUL_16); + error = trace_builder.op_mul(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::MUL_16); break; case OpCode::DIV_8: - trace_builder.op_div(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::DIV_8); + error = trace_builder.op_div(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::DIV_8); break; case OpCode::DIV_16: - trace_builder.op_div(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::DIV_16); + error = trace_builder.op_div(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::DIV_16); break; case OpCode::FDIV_8: - trace_builder.op_fdiv(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::FDIV_8); + error = trace_builder.op_fdiv(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::FDIV_8); break; case OpCode::FDIV_16: - trace_builder.op_fdiv(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::FDIV_16); + error = trace_builder.op_fdiv(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::FDIV_16); break; case OpCode::EQ_8: - trace_builder.op_eq(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::EQ_8); + error = trace_builder.op_eq(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::EQ_8); break; case OpCode::EQ_16: - trace_builder.op_eq(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::EQ_16); + error = trace_builder.op_eq(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::EQ_16); break; case OpCode::LT_8: - trace_builder.op_lt(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::LT_8); + error = trace_builder.op_lt(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::LT_8); break; case OpCode::LT_16: - trace_builder.op_lt(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::LT_16); + error = trace_builder.op_lt(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::LT_16); break; case OpCode::LTE_8: - trace_builder.op_lte(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::LTE_8); + error = trace_builder.op_lte(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::LTE_8); break; case OpCode::LTE_16: - trace_builder.op_lte(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::LTE_16); + error = trace_builder.op_lte(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::LTE_16); break; case OpCode::AND_8: - trace_builder.op_and(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::AND_8); + error = trace_builder.op_and(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::AND_8); break; case OpCode::AND_16: - trace_builder.op_and(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::AND_16); + error = trace_builder.op_and(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::AND_16); break; case OpCode::OR_8: - trace_builder.op_or(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::OR_8); + error = trace_builder.op_or(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::OR_8); break; case OpCode::OR_16: - trace_builder.op_or(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::OR_16); + error = trace_builder.op_or(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::OR_16); break; case OpCode::XOR_8: - trace_builder.op_xor(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::XOR_8); + error = trace_builder.op_xor(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::XOR_8); break; case OpCode::XOR_16: - trace_builder.op_xor(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::XOR_16); + error = trace_builder.op_xor(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::XOR_16); break; case OpCode::NOT_8: - trace_builder.op_not(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - OpCode::NOT_8); + error = trace_builder.op_not(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + OpCode::NOT_8); break; case OpCode::NOT_16: - trace_builder.op_not(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - OpCode::NOT_16); + error = trace_builder.op_not(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + OpCode::NOT_16); break; case OpCode::SHL_8: - trace_builder.op_shl(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::SHL_8); + error = trace_builder.op_shl(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::SHL_8); break; case OpCode::SHL_16: - trace_builder.op_shl(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::SHL_16); + error = trace_builder.op_shl(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::SHL_16); break; case OpCode::SHR_8: - trace_builder.op_shr(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::SHR_8); + error = trace_builder.op_shr(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::SHR_8); break; case OpCode::SHR_16: - trace_builder.op_shr(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - OpCode::SHR_16); + error = trace_builder.op_shr(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + OpCode::SHR_16); break; // Compute - Type Conversions case OpCode::CAST_8: - trace_builder.op_cast(std::get(inst.operands.at(0)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(1)), - OpCode::CAST_8); + error = trace_builder.op_cast(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1)), + OpCode::CAST_8); break; case OpCode::CAST_16: - trace_builder.op_cast(std::get(inst.operands.at(0)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(1)), - OpCode::CAST_16); + error = trace_builder.op_cast(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1)), + OpCode::CAST_16); break; // Execution Environment // TODO(https://github.com/AztecProtocol/aztec-packages/issues/6284): support indirect for below case OpCode::GETENVVAR_16: - trace_builder.op_get_env_var(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2))); + error = trace_builder.op_get_env_var(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2))); break; // Execution Environment - Calldata case OpCode::CALLDATACOPY: - trace_builder.op_calldata_copy(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3))); + error = trace_builder.op_calldata_copy(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3))); break; case OpCode::RETURNDATASIZE: - trace_builder.op_returndata_size(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1))); + error = trace_builder.op_returndata_size(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1))); break; case OpCode::RETURNDATACOPY: - trace_builder.op_returndata_copy(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3))); + error = trace_builder.op_returndata_copy(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3))); break; // Machine State - Internal Control Flow case OpCode::JUMP_32: - trace_builder.op_jump(std::get(inst.operands.at(0))); + error = trace_builder.op_jump(std::get(inst.operands.at(0))); break; case OpCode::JUMPI_32: - trace_builder.op_jumpi(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2))); + error = trace_builder.op_jumpi(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2))); break; case OpCode::INTERNALCALL: - trace_builder.op_internal_call(std::get(inst.operands.at(0))); + error = trace_builder.op_internal_call(std::get(inst.operands.at(0))); break; case OpCode::INTERNALRETURN: - trace_builder.op_internal_return(); + error = trace_builder.op_internal_return(); break; // Machine State - Memory case OpCode::SET_8: { - trace_builder.op_set(std::get(inst.operands.at(0)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(1)), - OpCode::SET_8); + error = trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1)), + OpCode::SET_8); break; } case OpCode::SET_16: { - trace_builder.op_set(std::get(inst.operands.at(0)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(1)), - OpCode::SET_16); + error = trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1)), + OpCode::SET_16); break; } case OpCode::SET_32: { - trace_builder.op_set(std::get(inst.operands.at(0)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(1)), - OpCode::SET_32); + error = trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1)), + OpCode::SET_32); break; } case OpCode::SET_64: { - trace_builder.op_set(std::get(inst.operands.at(0)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(1)), - OpCode::SET_64); + error = trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1)), + OpCode::SET_64); break; } case OpCode::SET_128: { - trace_builder.op_set(std::get(inst.operands.at(0)), - uint256_t::from_uint128(std::get(inst.operands.at(2))), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(1)), - OpCode::SET_128); + error = trace_builder.op_set(std::get(inst.operands.at(0)), + uint256_t::from_uint128(std::get(inst.operands.at(2))), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1)), + OpCode::SET_128); break; } case OpCode::SET_FF: { - trace_builder.op_set(std::get(inst.operands.at(0)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(1)), - OpCode::SET_FF); + error = trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1)), + OpCode::SET_FF); break; } case OpCode::MOV_8: - trace_builder.op_mov(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - OpCode::MOV_8); + error = trace_builder.op_mov(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + OpCode::MOV_8); break; case OpCode::MOV_16: - trace_builder.op_mov(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - OpCode::MOV_16); + error = trace_builder.op_mov(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + OpCode::MOV_16); break; // World State case OpCode::SLOAD: - trace_builder.op_sload(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - 1, - std::get(inst.operands.at(2))); + error = trace_builder.op_sload(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + 1, + std::get(inst.operands.at(2))); break; case OpCode::SSTORE: - trace_builder.op_sstore(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - 1, - std::get(inst.operands.at(2))); + error = trace_builder.op_sstore(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + 1, + std::get(inst.operands.at(2))); break; case OpCode::NOTEHASHEXISTS: - trace_builder.op_note_hash_exists(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3))); + error = trace_builder.op_note_hash_exists(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3))); break; case OpCode::EMITNOTEHASH: - trace_builder.op_emit_note_hash(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1))); + error = trace_builder.op_emit_note_hash(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1))); break; case OpCode::NULLIFIEREXISTS: - trace_builder.op_nullifier_exists(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3))); + error = trace_builder.op_nullifier_exists(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3))); break; case OpCode::EMITNULLIFIER: - trace_builder.op_emit_nullifier(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1))); + error = trace_builder.op_emit_nullifier(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1))); break; case OpCode::L1TOL2MSGEXISTS: - trace_builder.op_l1_to_l2_msg_exists(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3))); + error = trace_builder.op_l1_to_l2_msg_exists(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3))); break; case OpCode::GETCONTRACTINSTANCE: - trace_builder.op_get_contract_instance(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(4))); + error = trace_builder.op_get_contract_instance(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(4))); break; // Accrued Substate case OpCode::EMITUNENCRYPTEDLOG: - trace_builder.op_emit_unencrypted_log(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2))); + error = trace_builder.op_emit_unencrypted_log(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2))); break; case OpCode::SENDL2TOL1MSG: - trace_builder.op_emit_l2_to_l1_msg(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2))); + error = trace_builder.op_emit_l2_to_l1_msg(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2))); break; // Control Flow - Contract Calls case OpCode::CALL: - trace_builder.op_call(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(4)), - std::get(inst.operands.at(5))); + error = trace_builder.op_call(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(4)), + std::get(inst.operands.at(5))); break; case OpCode::STATICCALL: - trace_builder.op_static_call(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(4)), - std::get(inst.operands.at(5))); + error = trace_builder.op_static_call(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(4)), + std::get(inst.operands.at(5))); break; case OpCode::RETURN: { auto ret = trace_builder.op_return(std::get(inst.operands.at(0)), std::get(inst.operands.at(1)), std::get(inst.operands.at(2))); - returndata.insert(returndata.end(), ret.begin(), ret.end()); + error = ret.error; + returndata.insert(returndata.end(), ret.return_data.begin(), ret.return_data.end()); break; } @@ -703,7 +705,8 @@ std::vector Execution::gen_trace(std::vector const& calldata, auto ret = trace_builder.op_revert(std::get(inst.operands.at(0)), std::get(inst.operands.at(1)), std::get(inst.operands.at(2))); - returndata.insert(returndata.end(), ret.begin(), ret.end()); + error = ret.error; + returndata.insert(returndata.end(), ret.return_data.begin(), ret.return_data.end()); break; } @@ -712,68 +715,69 @@ std::vector Execution::gen_trace(std::vector const& calldata, auto ret = trace_builder.op_revert(std::get(inst.operands.at(0)), std::get(inst.operands.at(1)), std::get(inst.operands.at(2))); - returndata.insert(returndata.end(), ret.begin(), ret.end()); + error = ret.error; + returndata.insert(returndata.end(), ret.return_data.begin(), ret.return_data.end()); break; } // Misc case OpCode::DEBUGLOG: - trace_builder.op_debug_log(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(4))); + error = trace_builder.op_debug_log(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(4))); break; // Gadgets case OpCode::POSEIDON2PERM: - trace_builder.op_poseidon2_permutation(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2))); + error = trace_builder.op_poseidon2_permutation(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2))); break; case OpCode::SHA256COMPRESSION: - trace_builder.op_sha256_compression(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3))); + error = trace_builder.op_sha256_compression(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3))); break; case OpCode::KECCAKF1600: - trace_builder.op_keccakf1600(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2))); + error = trace_builder.op_keccakf1600(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2))); break; case OpCode::ECADD: - trace_builder.op_ec_add(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(4)), - std::get(inst.operands.at(5)), - std::get(inst.operands.at(6)), - std::get(inst.operands.at(7))); + error = trace_builder.op_ec_add(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(4)), + std::get(inst.operands.at(5)), + std::get(inst.operands.at(6)), + std::get(inst.operands.at(7))); break; case OpCode::MSM: - trace_builder.op_variable_msm(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(4))); + error = trace_builder.op_variable_msm(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(4))); break; // Conversions case OpCode::TORADIXBE: - trace_builder.op_to_radix_be(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(4)), - std::get(inst.operands.at(5))); + error = trace_builder.op_to_radix_be(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(4)), + std::get(inst.operands.at(5))); break; default: @@ -783,6 +787,15 @@ std::vector Execution::gen_trace(std::vector const& calldata, } } + if (error != AvmError::NO_ERROR) { + info("AVM stopped due to exceptional halting condition. Error: ", + to_name(error), + " at PC: ", + pc, + " IC: ", + counter - 1); // Need adjustement as counter increment occurs in loop body + } + auto trace = trace_builder.finalize(); show_trace_info(trace); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/helper.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/helper.cpp index 6c44d1b01e12..f946ff6ed455 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/helper.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/helper.cpp @@ -71,27 +71,27 @@ bool is_operand_indirect(uint8_t ind_value, uint8_t operand_idx) return (ind_value & (1 << operand_idx)) != 0; } -std::string to_hex(bb::avm_trace::AvmMemoryTag tag) +std::string to_hex(AvmMemoryTag tag) { return to_hex(static_cast(tag)); } -std::string to_name(bb::avm_trace::AvmMemoryTag tag) +std::string to_name(AvmMemoryTag tag) { switch (tag) { - case bb::avm_trace::AvmMemoryTag::FF: + case AvmMemoryTag::FF: return "Field"; - case bb::avm_trace::AvmMemoryTag::U1: + case AvmMemoryTag::U1: return "Uint1"; - case bb::avm_trace::AvmMemoryTag::U8: + case AvmMemoryTag::U8: return "Uint8"; - case bb::avm_trace::AvmMemoryTag::U16: + case AvmMemoryTag::U16: return "Uint16"; - case bb::avm_trace::AvmMemoryTag::U32: + case AvmMemoryTag::U32: return "Uint32"; - case bb::avm_trace::AvmMemoryTag::U64: + case AvmMemoryTag::U64: return "Uint64"; - case bb::avm_trace::AvmMemoryTag::U128: + case AvmMemoryTag::U128: return "Uint128"; default: throw std::runtime_error("Invalid memory tag"); @@ -99,6 +99,26 @@ std::string to_name(bb::avm_trace::AvmMemoryTag tag) } } +std::string to_name(AvmError error) +{ + switch (error) { + case AvmError::NO_ERROR: + return "NO ERROR"; + case AvmError::TAG_ERROR: + return "TAG ERROR"; + case AvmError::ADDR_RES_ERROR: + return "ADDRESS RESOLUTION ERROR"; + case AvmError::DIV_ZERO: + return "DIVISION BY ZERO"; + case AvmError::PARSING_ERROR: + return "PARSING ERROR"; + case AvmError::ENV_VAR_UNKNOWN: + return "ENVIRONMENT VARIABLE UNKNOWN"; + case AvmError::CONTRACT_INST_MEM_UNKNOWN: + return "CONTRACT INSTANCE MEMBER UNKNOWN"; + } +} + /** * * ONLY FOR TESTS - Required by dsl module and therefore cannot be moved to test/helpers.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/helper.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/helper.hpp index bd45a1b472c6..cc1976dc5011 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/helper.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/helper.hpp @@ -232,6 +232,8 @@ std::string to_hex(bb::avm_trace::AvmMemoryTag tag); std::string to_name(bb::avm_trace::AvmMemoryTag tag); +std::string to_name(AvmError error); + // Mutate the inputs void inject_end_gas_values(VmPublicInputs& public_inputs, std::vector& trace); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp index 4f7d056d55c2..336d3fb48318 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp @@ -306,7 +306,7 @@ AvmTraceBuilder::AvmTraceBuilder(VmPublicInputs public_inputs, * @param dst_offset An index in memory pointing to the output of the addition. * @param in_tag The instruction memory tag of the operands. */ -void AvmTraceBuilder::op_add( +AvmError AvmTraceBuilder::op_add( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -368,6 +368,7 @@ void AvmTraceBuilder::op_add( ASSERT(op_code == OpCode::ADD_8 || op_code == OpCode::ADD_16); pc += Deserialization::get_pc_increment(op_code); + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /** @@ -379,7 +380,7 @@ void AvmTraceBuilder::op_add( * @param dst_offset An index in memory pointing to the output of the subtraction. * @param in_tag The instruction memory tag of the operands. */ -void AvmTraceBuilder::op_sub( +AvmError AvmTraceBuilder::op_sub( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -441,6 +442,7 @@ void AvmTraceBuilder::op_sub( ASSERT(op_code == OpCode::SUB_8 || op_code == OpCode::SUB_16); pc += Deserialization::get_pc_increment(op_code); + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /** @@ -452,7 +454,7 @@ void AvmTraceBuilder::op_sub( * @param dst_offset An index in memory pointing to the output of the multiplication. * @param in_tag The instruction memory tag of the operands. */ -void AvmTraceBuilder::op_mul( +AvmError AvmTraceBuilder::op_mul( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -514,6 +516,7 @@ void AvmTraceBuilder::op_mul( ASSERT(op_code == OpCode::MUL_8 || op_code == OpCode::MUL_16); pc += Deserialization::get_pc_increment(op_code); + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /** @@ -525,7 +528,7 @@ void AvmTraceBuilder::op_mul( * @param dst_offset An index in memory pointing to the output of the division. * @param in_tag The instruction memory tag of the operands. */ -void AvmTraceBuilder::op_div( +AvmError AvmTraceBuilder::op_div( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -549,17 +552,16 @@ void AvmTraceBuilder::op_div( // output (c) in memory. FF c; FF inv; - FF error; + bool div_error = false; if (!b.is_zero()) { // If b is not zero, we prove it is not by providing its inverse as well inv = b.invert(); c = tag_match ? alu_trace_builder.op_div(a, b, in_tag, clk) : FF(0); - error = 0; } else { inv = 1; c = 0; - error = 1; + div_error = true; } // Write into memory value c from intermediate register ic. @@ -583,7 +585,7 @@ void AvmTraceBuilder::op_div( .main_mem_addr_a = FF(read_a.direct_address), .main_mem_addr_b = FF(read_b.direct_address), .main_mem_addr_c = FF(write_dst.direct_address), - .main_op_err = tag_match ? error : FF(1), + .main_op_err = tag_match ? FF(static_cast(div_error)) : FF(1), .main_pc = FF(pc), .main_r_in_tag = FF(static_cast(in_tag)), .main_rwc = FF(1), @@ -600,6 +602,7 @@ void AvmTraceBuilder::op_div( ASSERT(op_code == OpCode::DIV_8 || op_code == OpCode::DIV_16); pc += Deserialization::get_pc_increment(op_code); + return !tag_match ? AvmError::TAG_ERROR : div_error ? AvmError::DIV_ZERO : AvmError::NO_ERROR; } /** @@ -611,7 +614,7 @@ void AvmTraceBuilder::op_div( * @param dst_offset An index in memory pointing to the output of the division. * @param in_tag The instruction memory tag of the operands. */ -void AvmTraceBuilder::op_fdiv( +AvmError AvmTraceBuilder::op_fdiv( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -633,16 +636,15 @@ void AvmTraceBuilder::op_fdiv( FF b = read_b.val; FF c; FF inv; - FF error; + bool div_error = false; if (!b.is_zero()) { inv = b.invert(); c = a * inv; - error = 0; } else { inv = 1; c = 0; - error = 1; + div_error = true; } // Write into memory value c from intermediate register ic. @@ -666,7 +668,7 @@ void AvmTraceBuilder::op_fdiv( .main_mem_addr_a = FF(read_a.direct_address), .main_mem_addr_b = FF(read_b.direct_address), .main_mem_addr_c = FF(write_c.direct_address), - .main_op_err = tag_match ? error : FF(1), + .main_op_err = tag_match ? FF(static_cast(div_error)) : FF(1), .main_pc = FF(pc), .main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), .main_rwc = FF(1), @@ -683,6 +685,7 @@ void AvmTraceBuilder::op_fdiv( ASSERT(op_code == OpCode::FDIV_8 || op_code == OpCode::FDIV_16); pc += Deserialization::get_pc_increment(op_code); + return !tag_match ? AvmError::TAG_ERROR : div_error ? AvmError::DIV_ZERO : AvmError::NO_ERROR; } /************************************************************************************************** @@ -698,7 +701,8 @@ void AvmTraceBuilder::op_fdiv( * @param dst_offset An index in memory pointing to the output of the equality. * @param in_tag The instruction memory tag of the operands. */ -void AvmTraceBuilder::op_eq(uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) +AvmError AvmTraceBuilder::op_eq( + uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -757,9 +761,11 @@ void AvmTraceBuilder::op_eq(uint8_t indirect, uint32_t a_offset, uint32_t b_offs ASSERT(op_code == OpCode::EQ_8 || op_code == OpCode::EQ_16); pc += Deserialization::get_pc_increment(op_code); + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_lt(uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) +AvmError AvmTraceBuilder::op_lt( + uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -814,9 +820,10 @@ void AvmTraceBuilder::op_lt(uint8_t indirect, uint32_t a_offset, uint32_t b_offs ASSERT(op_code == OpCode::LT_8 || op_code == OpCode::LT_16); pc += Deserialization::get_pc_increment(op_code); + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_lte( +AvmError AvmTraceBuilder::op_lte( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -873,13 +880,14 @@ void AvmTraceBuilder::op_lte( ASSERT(op_code == OpCode::LTE_8 || op_code == OpCode::LTE_16); pc += Deserialization::get_pc_increment(op_code); + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /************************************************************************************************** * COMPUTE - BITWISE **************************************************************************************************/ -void AvmTraceBuilder::op_and( +AvmError AvmTraceBuilder::op_and( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -940,9 +948,11 @@ void AvmTraceBuilder::op_and( ASSERT(op_code == OpCode::AND_8 || op_code == OpCode::AND_16); pc += Deserialization::get_pc_increment(op_code); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_or(uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) +AvmError AvmTraceBuilder::op_or( + uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; auto [resolved_a, resolved_b, resolved_c] = @@ -1001,9 +1011,10 @@ void AvmTraceBuilder::op_or(uint8_t indirect, uint32_t a_offset, uint32_t b_offs ASSERT(op_code == OpCode::OR_8 || op_code == OpCode::OR_16); pc += Deserialization::get_pc_increment(op_code); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_xor( +AvmError AvmTraceBuilder::op_xor( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -1064,6 +1075,7 @@ void AvmTraceBuilder::op_xor( ASSERT(op_code == OpCode::XOR_8 || op_code == OpCode::XOR_16); pc += Deserialization::get_pc_increment(op_code); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /** @@ -1073,7 +1085,7 @@ void AvmTraceBuilder::op_xor( * @param a_offset An index in memory pointing to the only operand of Not. * @param dst_offset An index in memory pointing to the output of Not. */ -void AvmTraceBuilder::op_not(uint8_t indirect, uint32_t a_offset, uint32_t dst_offset, OpCode op_code) +AvmError AvmTraceBuilder::op_not(uint8_t indirect, uint32_t a_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -1126,9 +1138,10 @@ void AvmTraceBuilder::op_not(uint8_t indirect, uint32_t a_offset, uint32_t dst_o ASSERT(op_code == OpCode::NOT_8 || op_code == OpCode::NOT_16); pc += Deserialization::get_pc_increment(op_code); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_shl( +AvmError AvmTraceBuilder::op_shl( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -1187,9 +1200,10 @@ void AvmTraceBuilder::op_shl( ASSERT(op_code == OpCode::SHL_8 || op_code == OpCode::SHL_16); pc += Deserialization::get_pc_increment(op_code); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_shr( +AvmError AvmTraceBuilder::op_shr( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code) { auto clk = static_cast(main_trace.size()) + 1; @@ -1252,6 +1266,7 @@ void AvmTraceBuilder::op_shr( ASSERT(op_code == OpCode::SHR_8 || op_code == OpCode::SHR_16); pc += Deserialization::get_pc_increment(op_code); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /************************************************************************************************** @@ -1267,7 +1282,7 @@ void AvmTraceBuilder::op_shr( * @param dst_offset Offset of destination memory cell. * @param dst_tag Destination tag specifying the type the source value must be casted to. */ -void AvmTraceBuilder::op_cast( +AvmError AvmTraceBuilder::op_cast( uint8_t indirect, uint32_t a_offset, uint32_t dst_offset, AvmMemoryTag dst_tag, OpCode op_code) { auto const clk = static_cast(main_trace.size()) + 1; @@ -1310,6 +1325,7 @@ void AvmTraceBuilder::op_cast( ASSERT(op_code == OpCode::CAST_8 || op_code == OpCode::CAST_16); pc += Deserialization::get_pc_increment(op_code); + return AvmError::NO_ERROR; } /************************************************************************************************** @@ -1354,7 +1370,7 @@ Row AvmTraceBuilder::create_kernel_lookup_opcode(uint8_t indirect, uint32_t dst_ }; } -void AvmTraceBuilder::op_get_env_var(uint8_t indirect, uint8_t env_var, uint32_t dst_offset) +AvmError AvmTraceBuilder::op_get_env_var(uint8_t indirect, uint8_t env_var, uint32_t dst_offset) { if (env_var >= static_cast(EnvironmentVariable::MAX_ENV_VAR)) { // Error, bad enum operand @@ -1372,7 +1388,7 @@ void AvmTraceBuilder::op_get_env_var(uint8_t indirect, uint8_t env_var, uint32_t // Constrain gas cost gas_trace_builder.constrain_gas(static_cast(row.main_clk), OpCode::GETENVVAR_16); main_trace.push_back(row); - pc = UINT32_MAX; // Stop execution + return AvmError::ENV_VAR_UNKNOWN; } else { EnvironmentVariable var = static_cast(env_var); @@ -1421,6 +1437,7 @@ void AvmTraceBuilder::op_get_env_var(uint8_t indirect, uint8_t env_var, uint32_t break; } pc += Deserialization::get_pc_increment(OpCode::GETENVVAR_16); + return AvmError::NO_ERROR; } } @@ -1593,10 +1610,10 @@ void AvmTraceBuilder::op_fee_per_da_gas(uint8_t indirect, uint32_t dst_offset) * @param copy_size_offset The number of finite field elements to be copied into memory. * @param dst_offset The starting index of memory where calldata will be copied to. */ -void AvmTraceBuilder::op_calldata_copy(uint8_t indirect, - uint32_t cd_offset_address, - uint32_t copy_size_address, - uint32_t dst_offset) +AvmError AvmTraceBuilder::op_calldata_copy(uint8_t indirect, + uint32_t cd_offset_address, + uint32_t copy_size_address, + uint32_t dst_offset) { auto clk = static_cast(main_trace.size()) + 1; @@ -1641,9 +1658,10 @@ void AvmTraceBuilder::op_calldata_copy(uint8_t indirect, }); pc += Deserialization::get_pc_increment(OpCode::CALLDATACOPY); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_returndata_size(uint8_t indirect, uint32_t dst_offset) +AvmError AvmTraceBuilder::op_returndata_size(uint8_t indirect, uint32_t dst_offset) { auto const clk = static_cast(main_trace.size()) + 1; // This boolean will not be a trivial constant anymore once we constrain address resolution. @@ -1669,12 +1687,13 @@ void AvmTraceBuilder::op_returndata_size(uint8_t indirect, uint32_t dst_offset) }); pc += Deserialization::get_pc_increment(OpCode::RETURNDATASIZE); + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_returndata_copy(uint8_t indirect, - uint32_t rd_offset_address, - uint32_t copy_size_offset, - uint32_t dst_offset) +AvmError AvmTraceBuilder::op_returndata_copy(uint8_t indirect, + uint32_t rd_offset_address, + uint32_t copy_size_offset, + uint32_t dst_offset) { auto clk = static_cast(main_trace.size()) + 1; @@ -1719,6 +1738,7 @@ void AvmTraceBuilder::op_returndata_copy(uint8_t indirect, // is implemented with opcodes (SET and JUMP). write_slice_to_memory(dst_offset_resolved, AvmMemoryTag::FF, returndata_slice); } + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /************************************************************************************************** @@ -1791,7 +1811,7 @@ void AvmTraceBuilder::op_dagasleft(uint8_t indirect, uint32_t dst_offset) * * @param jmp_dest - The destination to jump to */ -void AvmTraceBuilder::op_jump(uint32_t jmp_dest, bool skip_gas) +AvmError AvmTraceBuilder::op_jump(uint32_t jmp_dest, bool skip_gas) { auto clk = static_cast(main_trace.size()) + 1; @@ -1811,6 +1831,7 @@ void AvmTraceBuilder::op_jump(uint32_t jmp_dest, bool skip_gas) // Adjust parameters for the next row pc = jmp_dest; + return AvmError::NO_ERROR; } /** @@ -1823,7 +1844,7 @@ void AvmTraceBuilder::op_jump(uint32_t jmp_dest, bool skip_gas) * @param jmp_dest The destination to jump to * @param cond_offset Offset of the condition */ -void AvmTraceBuilder::op_jumpi(uint8_t indirect, uint32_t jmp_dest, uint32_t cond_offset) +AvmError AvmTraceBuilder::op_jumpi(uint8_t indirect, uint32_t jmp_dest, uint32_t cond_offset) { auto clk = static_cast(main_trace.size()) + 1; @@ -1862,6 +1883,7 @@ void AvmTraceBuilder::op_jumpi(uint8_t indirect, uint32_t jmp_dest, uint32_t con // Adjust parameters for the next row pc = next_pc; + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /** @@ -1877,7 +1899,7 @@ void AvmTraceBuilder::op_jumpi(uint8_t indirect, uint32_t jmp_dest, uint32_t con * * @param jmp_dest - The destination to jump to */ -void AvmTraceBuilder::op_internal_call(uint32_t jmp_dest) +AvmError AvmTraceBuilder::op_internal_call(uint32_t jmp_dest) { auto clk = static_cast(main_trace.size()) + 1; const auto next_pc = pc + Deserialization::get_pc_increment(OpCode::INTERNALCALL); @@ -1910,6 +1932,7 @@ void AvmTraceBuilder::op_internal_call(uint32_t jmp_dest) // Adjust parameters for the next row pc = jmp_dest; internal_return_ptr++; + return AvmError::NO_ERROR; } /** @@ -1923,7 +1946,7 @@ void AvmTraceBuilder::op_internal_call(uint32_t jmp_dest) * TODO(https://github.com/AztecProtocol/aztec-packages/issues/3740): This function MUST come after a call * instruction. */ -void AvmTraceBuilder::op_internal_return() +AvmError AvmTraceBuilder::op_internal_return() { auto clk = static_cast(main_trace.size()) + 1; @@ -1951,6 +1974,7 @@ void AvmTraceBuilder::op_internal_return() pc = uint32_t(read_a.val); internal_return_ptr--; + return AvmError::NO_ERROR; } /************************************************************************************************** @@ -1971,7 +1995,7 @@ void AvmTraceBuilder::op_internal_return() * @param dst_offset Memory destination offset where val is written to * @param in_tag The instruction memory tag */ -void AvmTraceBuilder::op_set( +AvmError AvmTraceBuilder::op_set( uint8_t indirect, FF val_ff, uint32_t dst_offset, AvmMemoryTag in_tag, OpCode op_code, bool skip_gas) { auto const clk = static_cast(main_trace.size()) + 1; @@ -2006,6 +2030,7 @@ void AvmTraceBuilder::op_set( OpCode::SET_64, OpCode::SET_128, OpCode::SET_FF }; ASSERT(set_family.contains(op_code)); pc += Deserialization::get_pc_increment(op_code); + return write_c.tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /** @@ -2016,7 +2041,7 @@ void AvmTraceBuilder::op_set( * @param src_offset Offset of source memory cell * @param dst_offset Offset of destination memory cell */ -void AvmTraceBuilder::op_mov(uint8_t indirect, uint32_t src_offset, uint32_t dst_offset, OpCode op_code) +AvmError AvmTraceBuilder::op_mov(uint8_t indirect, uint32_t src_offset, uint32_t dst_offset, OpCode op_code) { auto const clk = static_cast(main_trace.size()) + 1; @@ -2057,6 +2082,7 @@ void AvmTraceBuilder::op_mov(uint8_t indirect, uint32_t src_offset, uint32_t dst ASSERT(op_code == OpCode::MOV_8 || op_code == OpCode::MOV_16); pc += Deserialization::get_pc_increment(op_code); + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /************************************************************************************************** @@ -2288,7 +2314,7 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_set_value_from_hint(uint8_ * WORLD STATE **************************************************************************************************/ -void AvmTraceBuilder::op_sload(uint8_t indirect, uint32_t slot_offset, uint32_t size, uint32_t dest_offset) +AvmError AvmTraceBuilder::op_sload(uint8_t indirect, uint32_t slot_offset, uint32_t size, uint32_t dest_offset) { auto clk = static_cast(main_trace.size()) + 1; @@ -2319,6 +2345,7 @@ void AvmTraceBuilder::op_sload(uint8_t indirect, uint32_t slot_offset, uint32_t // gas_trace_builder.constrain_gas(clk, OpCode::SLOAD); // clk++; + bool accumulated_tag_match = true; AddressWithMode write_dst = resolved_dest; // Loop over the size and write the hints to memory for (uint32_t i = 0; i < size; i++) { @@ -2344,6 +2371,7 @@ void AvmTraceBuilder::op_sload(uint8_t indirect, uint32_t slot_offset, uint32_t .main_w_in_tag = static_cast(AvmMemoryTag::FF), }; + accumulated_tag_match = accumulated_tag_match && write_a.tag_match; // Output storage read to kernel outputs (performs lookup) // Tuples of (slot, value) in the kernel lookup kernel_trace_builder.op_sload(clk, side_effect_counter, row.main_ib, row.main_ia); @@ -2363,9 +2391,10 @@ void AvmTraceBuilder::op_sload(uint8_t indirect, uint32_t slot_offset, uint32_t write_dst = AddressWithMode{ AddressingMode::DIRECT, write_a.direct_address + 1 }; } pc += Deserialization::get_pc_increment(OpCode::SLOAD); + return accumulated_tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_sstore(uint8_t indirect, uint32_t src_offset, uint32_t size, uint32_t slot_offset) +AvmError AvmTraceBuilder::op_sstore(uint8_t indirect, uint32_t src_offset, uint32_t size, uint32_t slot_offset) { auto clk = static_cast(main_trace.size()) + 1; @@ -2397,6 +2426,7 @@ void AvmTraceBuilder::op_sstore(uint8_t indirect, uint32_t src_offset, uint32_t // clk++; AddressWithMode read_src = resolved_src; + bool accumulated_tag_match = true; // This loop reads a _size_ number of elements from memory and places them into a tuple of (ele, slot) // in the kernel lookup. @@ -2421,6 +2451,7 @@ void AvmTraceBuilder::op_sstore(uint8_t indirect, uint32_t src_offset, uint32_t }; row.main_sel_op_sstore = FF(1); kernel_trace_builder.op_sstore(clk, side_effect_counter, row.main_ib, row.main_ia); + accumulated_tag_match = accumulated_tag_match && read_a.tag_match; // Constrain gas cost // TODO: when/if we move this to its own gadget, and we have 1 row only, we should pass the size as @@ -2437,12 +2468,13 @@ void AvmTraceBuilder::op_sstore(uint8_t indirect, uint32_t src_offset, uint32_t } pc += Deserialization::get_pc_increment(OpCode::SSTORE); + return accumulated_tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_note_hash_exists(uint8_t indirect, - uint32_t note_hash_offset, - uint32_t leaf_index_offset, - uint32_t dest_offset) +AvmError AvmTraceBuilder::op_note_hash_exists(uint8_t indirect, + uint32_t note_hash_offset, + uint32_t leaf_index_offset, + uint32_t dest_offset) { auto const clk = static_cast(main_trace.size()) + 1; @@ -2451,7 +2483,7 @@ void AvmTraceBuilder::op_note_hash_exists(uint8_t indirect, .resolve({ note_hash_offset, leaf_index_offset, dest_offset }, mem_trace_builder); const auto leaf_index = unconstrained_read_from_memory(resolved_leaf_index); - const bool op_valid = unconstrained_get_memory_tag(resolved_leaf_index) == AvmMemoryTag::FF; + bool op_valid = unconstrained_get_memory_tag(resolved_leaf_index) == AvmMemoryTag::FF; Row row; if (op_valid) { @@ -2463,6 +2495,7 @@ void AvmTraceBuilder::op_note_hash_exists(uint8_t indirect, row.main_ia, /*safe*/ static_cast(row.main_ib)); row.main_sel_op_note_hash_exists = FF(1); + op_valid = op_valid && row.main_tag_err == FF(0); } else { row = Row{ .main_clk = clk, @@ -2480,9 +2513,10 @@ void AvmTraceBuilder::op_note_hash_exists(uint8_t indirect, debug("note_hash_exists side-effect cnt: ", side_effect_counter); pc += Deserialization::get_pc_increment(OpCode::NOTEHASHEXISTS); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_emit_note_hash(uint8_t indirect, uint32_t note_hash_offset) +AvmError AvmTraceBuilder::op_emit_note_hash(uint8_t indirect, uint32_t note_hash_offset) { auto const clk = static_cast(main_trace.size()) + 1; @@ -2499,12 +2533,13 @@ void AvmTraceBuilder::op_emit_note_hash(uint8_t indirect, uint32_t note_hash_off side_effect_counter++; pc += Deserialization::get_pc_increment(OpCode::EMITNOTEHASH); + return row.main_tag_err == FF(0) ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_nullifier_exists(uint8_t indirect, - uint32_t nullifier_offset, - uint32_t address_offset, - uint32_t dest_offset) +AvmError AvmTraceBuilder::op_nullifier_exists(uint8_t indirect, + uint32_t nullifier_offset, + uint32_t address_offset, + uint32_t dest_offset) { auto const clk = static_cast(main_trace.size()) + 1; @@ -2513,7 +2548,7 @@ void AvmTraceBuilder::op_nullifier_exists(uint8_t indirect, Addressing<3>::fromWire(indirect, call_ptr) .resolve({ nullifier_offset, address_offset, dest_offset }, mem_trace_builder); - const bool op_valid = unconstrained_get_memory_tag(resolved_address) == AvmMemoryTag::FF; + bool op_valid = unconstrained_get_memory_tag(resolved_address) == AvmMemoryTag::FF; Row row; @@ -2523,6 +2558,7 @@ void AvmTraceBuilder::op_nullifier_exists(uint8_t indirect, kernel_trace_builder.op_nullifier_exists( clk, side_effect_counter, row.main_ia, /*safe*/ static_cast(row.main_ib)); row.main_sel_op_nullifier_exists = FF(1); + op_valid = op_valid && row.main_tag_err == FF(0); } else { row = Row{ .main_clk = clk, @@ -2542,9 +2578,10 @@ void AvmTraceBuilder::op_nullifier_exists(uint8_t indirect, side_effect_counter++; pc += Deserialization::get_pc_increment(OpCode::NULLIFIEREXISTS); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_emit_nullifier(uint8_t indirect, uint32_t nullifier_offset) +AvmError AvmTraceBuilder::op_emit_nullifier(uint8_t indirect, uint32_t nullifier_offset) { auto const clk = static_cast(main_trace.size()) + 1; @@ -2561,12 +2598,13 @@ void AvmTraceBuilder::op_emit_nullifier(uint8_t indirect, uint32_t nullifier_off side_effect_counter++; pc += Deserialization::get_pc_increment(OpCode::EMITNULLIFIER); + return row.main_tag_err == FF(0) ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_l1_to_l2_msg_exists(uint8_t indirect, - uint32_t log_offset, - uint32_t leaf_index_offset, - uint32_t dest_offset) +AvmError AvmTraceBuilder::op_l1_to_l2_msg_exists(uint8_t indirect, + uint32_t log_offset, + uint32_t leaf_index_offset, + uint32_t dest_offset) { auto const clk = static_cast(main_trace.size()) + 1; @@ -2575,7 +2613,7 @@ void AvmTraceBuilder::op_l1_to_l2_msg_exists(uint8_t indirect, .resolve({ log_offset, leaf_index_offset, dest_offset }, mem_trace_builder); const auto leaf_index = unconstrained_read_from_memory(resolved_leaf_index); - const bool op_valid = unconstrained_get_memory_tag(resolved_leaf_index) == AvmMemoryTag::FF; + bool op_valid = unconstrained_get_memory_tag(resolved_leaf_index) == AvmMemoryTag::FF; Row row; if (op_valid) { @@ -2586,6 +2624,7 @@ void AvmTraceBuilder::op_l1_to_l2_msg_exists(uint8_t indirect, row.main_ia, /*safe*/ static_cast(row.main_ib)); row.main_sel_op_l1_to_l2_msg_exists = FF(1); + op_valid = op_valid && row.main_tag_err == FF(0); } else { row = Row{ .main_clk = clk, @@ -2604,9 +2643,10 @@ void AvmTraceBuilder::op_l1_to_l2_msg_exists(uint8_t indirect, debug("l1_to_l2_msg_exists side-effect cnt: ", side_effect_counter); pc += Deserialization::get_pc_increment(OpCode::L1TOL2MSGEXISTS); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_get_contract_instance( +AvmError AvmTraceBuilder::op_get_contract_instance( uint8_t indirect, uint8_t member_enum, uint16_t address_offset, uint16_t dst_offset, uint16_t exists_offset) { auto clk = static_cast(main_trace.size()) + 1; @@ -2626,91 +2666,92 @@ void AvmTraceBuilder::op_get_contract_instance( }; main_trace.push_back(row); pc += Deserialization::get_pc_increment(OpCode::GETCONTRACTINSTANCE); - } else { - - ContractInstanceMember chosen_member = static_cast(member_enum); - - auto [resolved_address_offset, resolved_dst_offset, resolved_exists_offset] = - Addressing<3>::fromWire(indirect, call_ptr) - .resolve({ address_offset, dst_offset, exists_offset }, mem_trace_builder); - - auto read_address = constrained_read_from_memory( - call_ptr, clk, resolved_address_offset, AvmMemoryTag::FF, AvmMemoryTag::FF, IntermRegister::IA); - bool tag_match = read_address.tag_match; + return AvmError::CONTRACT_INST_MEM_UNKNOWN; + }; - // Read the contract instance - ContractInstanceHint instance = execution_hints.contract_instance_hints.at(read_address.val); + ContractInstanceMember chosen_member = static_cast(member_enum); - FF member_value; - switch (chosen_member) { - case ContractInstanceMember::DEPLOYER: - member_value = instance.deployer_addr; - break; - case ContractInstanceMember::CLASS_ID: - member_value = instance.contract_class_id; - break; - case ContractInstanceMember::INIT_HASH: - member_value = instance.initialisation_hash; - break; - default: - member_value = 0; - break; - } + auto [resolved_address_offset, resolved_dst_offset, resolved_exists_offset] = + Addressing<3>::fromWire(indirect, call_ptr) + .resolve({ address_offset, dst_offset, exists_offset }, mem_trace_builder); + + auto read_address = constrained_read_from_memory( + call_ptr, clk, resolved_address_offset, AvmMemoryTag::FF, AvmMemoryTag::FF, IntermRegister::IA); + bool tag_match = read_address.tag_match; + + // Read the contract instance + ContractInstanceHint instance = execution_hints.contract_instance_hints.at(read_address.val); + + FF member_value; + switch (chosen_member) { + case ContractInstanceMember::DEPLOYER: + member_value = instance.deployer_addr; + break; + case ContractInstanceMember::CLASS_ID: + member_value = instance.contract_class_id; + break; + case ContractInstanceMember::INIT_HASH: + member_value = instance.initialisation_hash; + break; + default: + member_value = 0; + break; + } - // TODO(8603): once instructions can have multiple different tags for writes, write dst as FF and exists as U1 - // auto write_dst = constrained_write_to_memory(call_ptr, clk, resolved_dst_offset, member_value, - // AvmMemoryTag::FF, AvmMemoryTag::FF, IntermRegister::IC); auto write_exists = - // constrained_write_to_memory(call_ptr, clk, resolved_exists_offset, instance.instance_found_in_address, - // AvmMemoryTag::FF, AvmMemoryTag::FF, IntermRegister::ID); + // TODO(8603): once instructions can have multiple different tags for writes, write dst as FF and exists as + // U1 auto write_dst = constrained_write_to_memory(call_ptr, clk, resolved_dst_offset, member_value, + // AvmMemoryTag::FF, AvmMemoryTag::FF, IntermRegister::IC); auto write_exists = + // constrained_write_to_memory(call_ptr, clk, resolved_exists_offset, instance.instance_found_in_address, + // AvmMemoryTag::FF, AvmMemoryTag::FF, IntermRegister::ID); - main_trace.push_back(Row{ - .main_clk = clk, - .main_call_ptr = call_ptr, - .main_ia = read_address.val, - // TODO(8603): uncomment this and below blocks once instructions can have multiple different tags for - // writes - //.main_ic = write_dst.val, - //.main_id = write_exists.val, - .main_ind_addr_a = FF(read_address.indirect_address), - //.main_ind_addr_c = FF(write_dst.indirect_address), - //.main_ind_addr_d = FF(write_exists.indirect_address), - .main_internal_return_ptr = FF(internal_return_ptr), - .main_mem_addr_a = FF(read_address.direct_address), - //.main_mem_addr_c = FF(write_dst.direct_address), - //.main_mem_addr_d = FF(write_exists.direct_address), - .main_pc = FF(pc), - .main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), - .main_sel_mem_op_a = FF(1), - //.main_sel_mem_op_c = FF(1), - //.main_sel_mem_op_d = FF(1), - .main_sel_op_get_contract_instance = FF(1), - .main_sel_resolve_ind_addr_a = FF(static_cast(read_address.is_indirect)), - //.main_sel_resolve_ind_addr_c = FF(static_cast(write_dst.is_indirect)), - //.main_sel_resolve_ind_addr_d = FF(static_cast(write_exists.is_indirect)), - .main_tag_err = FF(static_cast(!tag_match)), - }); + main_trace.push_back(Row{ + .main_clk = clk, + .main_call_ptr = call_ptr, + .main_ia = read_address.val, + // TODO(8603): uncomment this and below blocks once instructions can have multiple different tags for + // writes + //.main_ic = write_dst.val, + //.main_id = write_exists.val, + .main_ind_addr_a = FF(read_address.indirect_address), + //.main_ind_addr_c = FF(write_dst.indirect_address), + //.main_ind_addr_d = FF(write_exists.indirect_address), + .main_internal_return_ptr = FF(internal_return_ptr), + .main_mem_addr_a = FF(read_address.direct_address), + //.main_mem_addr_c = FF(write_dst.direct_address), + //.main_mem_addr_d = FF(write_exists.direct_address), + .main_pc = FF(pc), + .main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), + .main_sel_mem_op_a = FF(1), + //.main_sel_mem_op_c = FF(1), + //.main_sel_mem_op_d = FF(1), + .main_sel_op_get_contract_instance = FF(1), + .main_sel_resolve_ind_addr_a = FF(static_cast(read_address.is_indirect)), + //.main_sel_resolve_ind_addr_c = FF(static_cast(write_dst.is_indirect)), + //.main_sel_resolve_ind_addr_d = FF(static_cast(write_exists.is_indirect)), + .main_tag_err = FF(static_cast(!tag_match)), + }); - pc += Deserialization::get_pc_increment(OpCode::GETCONTRACTINSTANCE); + pc += Deserialization::get_pc_increment(OpCode::GETCONTRACTINSTANCE); - // Crucial to perform this operation after having incremented pc because write_slice_to_memory - // is implemented with opcodes (SET and JUMP). - // TODO(8603): once instructions can have multiple different tags for writes, remove this and do a constrained - // writes - write_to_memory(resolved_dst_offset, member_value, AvmMemoryTag::FF); - write_to_memory(resolved_exists_offset, FF(static_cast(instance.exists)), AvmMemoryTag::U1); + // Crucial to perform this operation after having incremented pc because write_slice_to_memory + // is implemented with opcodes (SET and JUMP). + // TODO(8603): once instructions can have multiple different tags for writes, remove this and do a + // constrained writes + write_to_memory(resolved_dst_offset, member_value, AvmMemoryTag::FF); + write_to_memory(resolved_exists_offset, FF(static_cast(instance.exists)), AvmMemoryTag::U1); - // TODO(dbanks12): compute contract address nullifier from instance preimage and perform membership check + // TODO(dbanks12): compute contract address nullifier from instance preimage and perform membership check - debug("contract_instance cnt: ", side_effect_counter); - side_effect_counter++; - } + debug("contract_instance cnt: ", side_effect_counter); + side_effect_counter++; + return tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /************************************************************************************************** * ACCRUED SUBSTATE **************************************************************************************************/ -void AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log_offset, uint32_t log_size_offset) +AvmError AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log_offset, uint32_t log_size_offset) { std::vector bytes_to_hash; @@ -2770,12 +2811,12 @@ void AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log_off // Truncate the hash to 31 bytes so it will be a valid field element FF trunc_hash = FF(from_buffer(output.data()) >> 8); - // The + 32 here is for the contract_address in bytes, the +4 is for the extra 4 bytes that contain log_size and - // is prefixed to message see toBuffer in unencrypted_l2_log.ts + // The + 32 here is for the contract_address in bytes, the +4 is for the extra 4 bytes that contain log_size + // and is prefixed to message see toBuffer in unencrypted_l2_log.ts FF length_of_preimage = num_bytes + 32 + 4; // The + 4 is because the kernels store the length of the - // processed log as 4 bytes; thus for this length value to match the log length stored in the kernels, we need - // to add four to the length here. [Copied from unencrypted_l2_log.ts] + // processed log as 4 bytes; thus for this length value to match the log length stored in the kernels, we + // need to add four to the length here. [Copied from unencrypted_l2_log.ts] FF metadata_log_length = length_of_preimage + 4; row = Row{ .main_clk = clk, @@ -2804,9 +2845,10 @@ void AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log_off debug("emit_unencrypted_log side-effect cnt: ", side_effect_counter); side_effect_counter++; pc += Deserialization::get_pc_increment(OpCode::EMITUNENCRYPTEDLOG); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } -void AvmTraceBuilder::op_emit_l2_to_l1_msg(uint8_t indirect, uint32_t recipient_offset, uint32_t content_offset) +AvmError AvmTraceBuilder::op_emit_l2_to_l1_msg(uint8_t indirect, uint32_t recipient_offset, uint32_t content_offset) { auto const clk = static_cast(main_trace.size()) + 1; @@ -2825,6 +2867,7 @@ void AvmTraceBuilder::op_emit_l2_to_l1_msg(uint8_t indirect, uint32_t recipient_ side_effect_counter++; pc += Deserialization::get_pc_increment(OpCode::SENDL2TOL1MSG); + return row.main_tag_err == FF(0) ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /************************************************************************************************** @@ -2832,13 +2875,13 @@ void AvmTraceBuilder::op_emit_l2_to_l1_msg(uint8_t indirect, uint32_t recipient_ **************************************************************************************************/ // Helper/implementation for CALL and STATICCALL -void AvmTraceBuilder::constrain_external_call(OpCode opcode, - uint16_t indirect, - uint32_t gas_offset, - uint32_t addr_offset, - uint32_t args_offset, - uint32_t args_size_offset, - uint32_t success_offset) +AvmError AvmTraceBuilder::constrain_external_call(OpCode opcode, + uint16_t indirect, + uint32_t gas_offset, + uint32_t addr_offset, + uint32_t args_offset, + uint32_t args_size_offset, + uint32_t success_offset) { ASSERT(opcode == OpCode::CALL || opcode == OpCode::STATICCALL); auto clk = static_cast(main_trace.size()) + 1; @@ -2919,6 +2962,7 @@ void AvmTraceBuilder::constrain_external_call(OpCode opcode, if (opcode == OpCode::CALL) { side_effect_counter = static_cast(hint.end_side_effect_counter); } + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /** @@ -2934,12 +2978,12 @@ void AvmTraceBuilder::constrain_external_call(OpCode opcode, * @param success_offset An index in memory pointing to where the success flag (U1) of the external call should be * stored */ -void AvmTraceBuilder::op_call(uint16_t indirect, - uint32_t gas_offset, - uint32_t addr_offset, - uint32_t args_offset, - uint32_t args_size_offset, - uint32_t success_offset) +AvmError AvmTraceBuilder::op_call(uint16_t indirect, + uint32_t gas_offset, + uint32_t addr_offset, + uint32_t args_offset, + uint32_t args_size_offset, + uint32_t success_offset) { return constrain_external_call( OpCode::CALL, indirect, gas_offset, addr_offset, args_offset, args_size_offset, success_offset); @@ -2958,12 +3002,12 @@ void AvmTraceBuilder::op_call(uint16_t indirect, * @param success_offset An index in memory pointing to where the success flag (U8) of the static call should be * stored */ -void AvmTraceBuilder::op_static_call(uint16_t indirect, - uint32_t gas_offset, - uint32_t addr_offset, - uint32_t args_offset, - uint32_t args_size_offset, - uint32_t success_offset) +AvmError AvmTraceBuilder::op_static_call(uint16_t indirect, + uint32_t gas_offset, + uint32_t addr_offset, + uint32_t args_offset, + uint32_t args_size_offset, + uint32_t success_offset) { return constrain_external_call( OpCode::STATICCALL, indirect, gas_offset, addr_offset, args_offset, args_size_offset, success_offset); @@ -2984,7 +3028,7 @@ void AvmTraceBuilder::op_static_call(uint16_t indirect, * @param ret_size The number of elements to be returned. * @return The returned memory region as a std::vector. */ -std::vector AvmTraceBuilder::op_return(uint8_t indirect, uint32_t ret_offset, uint32_t ret_size) +ReturnDataError AvmTraceBuilder::op_return(uint8_t indirect, uint32_t ret_offset, uint32_t ret_size) { auto clk = static_cast(main_trace.size()) + 1; gas_trace_builder.constrain_gas(clk, OpCode::RETURN, ret_size); @@ -3032,10 +3076,14 @@ std::vector AvmTraceBuilder::op_return(uint8_t indirect, uint32_t ret_offset }); pc = UINT32_MAX; // This ensures that no subsequent opcode will be executed. - return returndata; + + return ReturnDataError{ + .return_data = returndata, + .error = tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR, + }; } -std::vector AvmTraceBuilder::op_revert(uint8_t indirect, uint32_t ret_offset, uint32_t ret_size_offset) +ReturnDataError AvmTraceBuilder::op_revert(uint8_t indirect, uint32_t ret_offset, uint32_t ret_size_offset) { // TODO: This opcode is still masquerading as RETURN. auto clk = static_cast(main_trace.size()) + 1; @@ -3066,7 +3114,9 @@ std::vector AvmTraceBuilder::op_revert(uint8_t indirect, uint32_t ret_offset }); pc = UINT32_MAX; // This ensures that no subsequent opcode will be executed. - return {}; + return ReturnDataError{ + .error = op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR, + }; } // The only memory operation performed from the main trace is a possible indirect load for resolving the @@ -3093,18 +3143,23 @@ std::vector AvmTraceBuilder::op_revert(uint8_t indirect, uint32_t ret_offset }); pc = UINT32_MAX; // This ensures that no subsequent opcode will be executed. - return returndata; + + // op_valid == true otherwise, ret_size == 0 and we would have returned above. + return ReturnDataError{ + .return_data = returndata, + .error = tag_match ? AvmError::NO_ERROR : AvmError::TAG_ERROR, + }; } /************************************************************************************************** * MISC **************************************************************************************************/ -void AvmTraceBuilder::op_debug_log(uint8_t indirect, - uint32_t message_offset, - uint32_t message_size, - uint32_t fields_offset, - uint32_t fields_size_offset) +AvmError AvmTraceBuilder::op_debug_log(uint8_t indirect, + uint32_t message_offset, + uint32_t message_size, + uint32_t fields_offset, + uint32_t fields_size_offset) { auto clk = static_cast(main_trace.size()) + 1; @@ -3141,6 +3196,7 @@ void AvmTraceBuilder::op_debug_log(uint8_t indirect, }); pc += Deserialization::get_pc_increment(OpCode::DEBUGLOG); + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /************************************************************************************************** @@ -3156,7 +3212,7 @@ void AvmTraceBuilder::op_debug_log(uint8_t indirect, * @param output_offset An index in memory pointing to where the first Field value of the output array should be * stored. */ -void AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_offset, uint32_t output_offset) +AvmError AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_offset, uint32_t output_offset) { auto clk = static_cast(main_trace.size()) + 1; @@ -3171,16 +3227,6 @@ void AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_ // Constrain gas cost gas_trace_builder.constrain_gas(clk, OpCode::POSEIDON2PERM); - // Main trace contains on operand values from the bytecode and resolved indirects - main_trace.push_back(Row{ - .main_clk = clk, - .main_internal_return_ptr = FF(internal_return_ptr), - .main_mem_addr_a = resolved_input_offset, - .main_mem_addr_b = resolved_output_offset, - .main_pc = FF(pc), - .main_sel_op_poseidon2 = FF(1), - }); - // These read patterns will be refactored - we perform them here instead of in the poseidon gadget trace // even though they are "performed" by the gadget. AddressWithMode direct_src_offset = { AddressingMode::DIRECT, resolved_input_offset }; @@ -3214,51 +3260,70 @@ void AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_ IntermRegister::ID, AvmMemTraceBuilder::POSEIDON2); - std::array input = { read_a.val, read_b.val, read_c.val, read_d.val }; - std::array result = poseidon2_trace_builder.poseidon2_permutation( - input, call_ptr, clk, resolved_input_offset, resolved_output_offset); + bool op_valid = read_a.tag_match && read_b.tag_match && read_c.tag_match && read_d.tag_match; - std::vector ff_result; - for (uint32_t i = 0; i < 4; i++) { - ff_result.emplace_back(result[i]); + if (op_valid) { + std::array input = { read_a.val, read_b.val, read_c.val, read_d.val }; + std::array result = poseidon2_trace_builder.poseidon2_permutation( + input, call_ptr, clk, resolved_input_offset, resolved_output_offset); + + std::vector ff_result; + for (uint32_t i = 0; i < 4; i++) { + ff_result.emplace_back(result[i]); + } + // Write the result to memory after, see the comments at read to understand why this happens here. + AddressWithMode direct_dst_offset = { AddressingMode::DIRECT, resolved_output_offset }; + auto write_a = constrained_write_to_memory(call_ptr, + clk, + direct_dst_offset, + ff_result[0], + AvmMemoryTag::FF, + AvmMemoryTag::FF, + IntermRegister::IA, + AvmMemTraceBuilder::POSEIDON2); + auto write_b = constrained_write_to_memory(call_ptr, + clk, + direct_dst_offset + 1, + ff_result[1], + AvmMemoryTag::FF, + AvmMemoryTag::FF, + IntermRegister::IB, + AvmMemTraceBuilder::POSEIDON2); + auto write_c = constrained_write_to_memory(call_ptr, + clk, + direct_dst_offset + 2, + ff_result[2], + AvmMemoryTag::FF, + AvmMemoryTag::FF, + IntermRegister::IC, + AvmMemTraceBuilder::POSEIDON2); + + auto write_d = constrained_write_to_memory(call_ptr, + clk, + direct_dst_offset + 3, + ff_result[3], + AvmMemoryTag::FF, + AvmMemoryTag::FF, + IntermRegister::ID, + AvmMemTraceBuilder::POSEIDON2); + + op_valid = write_a.tag_match && write_b.tag_match && write_c.tag_match && write_d.tag_match; } - // Write the result to memory after, see the comments at read to understand why this happens here. - AddressWithMode direct_dst_offset = { AddressingMode::DIRECT, resolved_output_offset }; - constrained_write_to_memory(call_ptr, - clk, - direct_dst_offset, - ff_result[0], - AvmMemoryTag::FF, - AvmMemoryTag::FF, - IntermRegister::IA, - AvmMemTraceBuilder::POSEIDON2); - constrained_write_to_memory(call_ptr, - clk, - direct_dst_offset + 1, - ff_result[1], - AvmMemoryTag::FF, - AvmMemoryTag::FF, - IntermRegister::IB, - AvmMemTraceBuilder::POSEIDON2); - constrained_write_to_memory(call_ptr, - clk, - direct_dst_offset + 2, - ff_result[2], - AvmMemoryTag::FF, - AvmMemoryTag::FF, - IntermRegister::IC, - AvmMemTraceBuilder::POSEIDON2); - - constrained_write_to_memory(call_ptr, - clk, - direct_dst_offset + 3, - ff_result[3], - AvmMemoryTag::FF, - AvmMemoryTag::FF, - IntermRegister::ID, - AvmMemTraceBuilder::POSEIDON2); + + // Main trace contains on operand values from the bytecode and resolved indirects + main_trace.push_back(Row{ + .main_clk = clk, + .main_internal_return_ptr = FF(internal_return_ptr), + .main_mem_addr_a = resolved_input_offset, + .main_mem_addr_b = resolved_output_offset, + .main_op_err = FF(static_cast(!op_valid)), + .main_pc = FF(pc), + .main_sel_op_poseidon2 = FF(1), + }); pc += Deserialization::get_pc_increment(OpCode::POSEIDON2PERM); + + return op_valid ? AvmError::NO_ERROR : AvmError::TAG_ERROR; } /** @@ -3272,10 +3337,10 @@ void AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_ * @param output_offset An index in memory pointing to where the first U32 value of the output array should be * stored. */ -void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, - uint32_t output_offset, - uint32_t state_offset, - uint32_t inputs_offset) +AvmError AvmTraceBuilder::op_sha256_compression(uint8_t indirect, + uint32_t output_offset, + uint32_t state_offset, + uint32_t inputs_offset) { const uint32_t STATE_SIZE = 8; const uint32_t INPUTS_SIZE = 16; @@ -3295,7 +3360,7 @@ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, call_ptr, clk, resolved_inputs_offset, AvmMemoryTag::U32, AvmMemoryTag::FF, IntermRegister::IB); bool tag_match = read_a.tag_match && read_b.tag_match; - bool op_valid = true; + bool op_valid = tag_match; for (uint32_t i = 0; i < STATE_SIZE; i++) { op_valid = op_valid && unconstrained_get_memory_tag(resolved_state_offset + i) == AvmMemoryTag::U32; } @@ -3336,8 +3401,7 @@ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, }); if (!op_valid) { - pc = UINT32_MAX; - return; + return AvmError::TAG_ERROR; } // We store the current clk this main trace row occurred so that we can line up the sha256 gadget operation at @@ -3372,6 +3436,8 @@ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, // Crucial to perform this operation after having incremented pc because write_slice_to_memory // is implemented with opcodes (SET and JUMP). write_slice_to_memory(resolved_output_offset, AvmMemoryTag::U32, ff_result); + + return AvmError::NO_ERROR; } /** @@ -3382,7 +3448,7 @@ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, * @param input_offset An index in memory pointing to the first u64 value of the input array to be used in the next * instance of keccakf1600. */ -void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, uint32_t output_offset, uint32_t input_offset) +AvmError AvmTraceBuilder::op_keccakf1600(uint8_t indirect, uint32_t output_offset, uint32_t input_offset) { auto clk = static_cast(main_trace.size()) + 1; auto [resolved_output_offset, resolved_input_offset] = @@ -3391,7 +3457,7 @@ void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, uint32_t output_offset, u call_ptr, clk, resolved_input_offset, AvmMemoryTag::U64, AvmMemoryTag::FF, IntermRegister::IA); bool tag_match = input_read.tag_match; - bool op_valid = true; + bool op_valid = tag_match; for (uint32_t i = 0; i < KECCAKF1600_INPUT_SIZE; i++) { op_valid = op_valid && unconstrained_get_memory_tag(resolved_input_offset + i) == AvmMemoryTag::U64; } @@ -3415,8 +3481,7 @@ void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, uint32_t output_offset, u }); if (!op_valid) { - pc = UINT32_MAX; - return; + return AvmError::TAG_ERROR; } // Array input is fixed to 1600 bits @@ -3435,16 +3500,18 @@ void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, uint32_t output_offset, u // Crucial to perform this operation after having incremented pc because write_slice_to_memory // is implemented with opcodes (SET and JUMP). write_slice_to_memory(resolved_output_offset, AvmMemoryTag::U64, result); + + return AvmError::NO_ERROR; } -void AvmTraceBuilder::op_ec_add(uint16_t indirect, - uint32_t lhs_x_offset, - uint32_t lhs_y_offset, - uint32_t lhs_is_inf_offset, - uint32_t rhs_x_offset, - uint32_t rhs_y_offset, - uint32_t rhs_is_inf_offset, - uint32_t output_offset) +AvmError AvmTraceBuilder::op_ec_add(uint16_t indirect, + uint32_t lhs_x_offset, + uint32_t lhs_y_offset, + uint32_t lhs_is_inf_offset, + uint32_t rhs_x_offset, + uint32_t rhs_y_offset, + uint32_t rhs_is_inf_offset, + uint32_t output_offset) { auto clk = static_cast(main_trace.size()) + 1; auto [resolved_lhs_x_offset, @@ -3485,8 +3552,7 @@ void AvmTraceBuilder::op_ec_add(uint16_t indirect, .main_pc = FF(pc), .main_sel_op_ecadd = 1, }); - pc = UINT32_MAX; - return; + return AvmError::TAG_ERROR; } // Load lhs point @@ -3523,13 +3589,15 @@ void AvmTraceBuilder::op_ec_add(uint16_t indirect, write_to_memory(resolved_output_offset, result.x, AvmMemoryTag::FF); write_to_memory(resolved_output_offset + 1, result.y, AvmMemoryTag::FF); write_to_memory(resolved_output_offset + 2, result.is_point_at_infinity(), AvmMemoryTag::U1); + + return AvmError::NO_ERROR; } -void AvmTraceBuilder::op_variable_msm(uint8_t indirect, - uint32_t points_offset, - uint32_t scalars_offset, - uint32_t output_offset, - uint32_t point_length_offset) +AvmError AvmTraceBuilder::op_variable_msm(uint8_t indirect, + uint32_t points_offset, + uint32_t scalars_offset, + uint32_t output_offset, + uint32_t point_length_offset) { auto clk = static_cast(main_trace.size()) + 1; auto [resolved_points_offset, resolved_scalars_offset, resolved_output_offset, resolved_point_length_offset] = @@ -3572,8 +3640,7 @@ void AvmTraceBuilder::op_variable_msm(uint8_t indirect, .main_sel_op_msm = 1, }); - pc = UINT32_MAX; - return; + return AvmError::TAG_ERROR; } // Loading the points is a bit more complex since we need to read the coordinates and the infinity flags @@ -3636,6 +3703,8 @@ void AvmTraceBuilder::op_variable_msm(uint8_t indirect, write_to_memory(resolved_output_offset, result.x, AvmMemoryTag::FF); write_to_memory(resolved_output_offset + 1, result.y, AvmMemoryTag::FF); write_to_memory(resolved_output_offset + 2, result.is_point_at_infinity(), AvmMemoryTag::U1); + + return AvmError::NO_ERROR; } /************************************************************************************************** @@ -3648,17 +3717,17 @@ void AvmTraceBuilder::op_variable_msm(uint8_t indirect, * @param indirect A byte encoding information about indirect/direct memory access. * @param src_offset An index in memory pointing to the input of the To_Radix_BE conversion. * @param dst_offset An index in memory pointing to the output of the To_Radix_BE conversion. - * @param radix_offset An index in memory pointing to the strict upper bound of each converted limb, i.e., 0 <= limb < - * radix. + * @param radix_offset An index in memory pointing to the strict upper bound of each converted limb, i.e., 0 <= limb + * < radix. * @param num_limbs The number of limbs to the value into. * @param output_bits Should the output be U1s instead of U8s? */ -void AvmTraceBuilder::op_to_radix_be(uint8_t indirect, - uint32_t src_offset, - uint32_t dst_offset, - uint32_t radix_offset, - uint32_t num_limbs, - uint8_t output_bits) +AvmError AvmTraceBuilder::op_to_radix_be(uint8_t indirect, + uint32_t src_offset, + uint32_t dst_offset, + uint32_t radix_offset, + uint32_t num_limbs, + uint8_t output_bits) { auto clk = static_cast(main_trace.size()) + 1; @@ -3732,6 +3801,7 @@ void AvmTraceBuilder::op_to_radix_be(uint8_t indirect, // Crucial to perform this operation after having incremented pc because write_slice_to_memory // is implemented with opcodes (SET and JUMP). write_slice_to_memory(resolved_dst_offset, w_in_tag, res); + return error ? AvmError::TAG_ERROR : AvmError::NO_ERROR; } /************************************************************************************************** diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp index 79c15af424bd..c393c8057d90 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp @@ -23,6 +23,11 @@ namespace bb::avm_trace { using Row = bb::AvmFullRow; +struct ReturnDataError { + std::vector return_data; + AvmError error; +}; + // This is the internal context that we keep along the lifecycle of bytecode execution // to iteratively build the whole trace. This is effectively performing witness generation. // At the end of circuit building, mainTrace can be moved to AvmCircuitBuilder by calling @@ -40,47 +45,47 @@ class AvmTraceBuilder { uint32_t get_da_gas_left() const { return gas_trace_builder.get_da_gas_left(); } // Compute - Arithmetic - void op_add( + AvmError op_add( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::ADD_16); - void op_sub( + AvmError op_sub( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::SUB_16); - void op_mul( + AvmError op_mul( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::MUL_16); - void op_div( + AvmError op_div( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::DIV_16); - void op_fdiv( + AvmError op_fdiv( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::FDIV_16); // Compute - Comparators - void op_eq( + AvmError op_eq( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::EQ_16); - void op_lt( + AvmError op_lt( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::LT_16); - void op_lte( + AvmError op_lte( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::LTE_16); // Compute - Bitwise - void op_and( + AvmError op_and( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::AND_16); - void op_or( + AvmError op_or( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::OR_16); - void op_xor( + AvmError op_xor( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::XOR_16); - void op_not(uint8_t indirect, uint32_t a_offset, uint32_t dst_offset, OpCode op_code = OpCode::NOT_16); - void op_shl( + AvmError op_not(uint8_t indirect, uint32_t a_offset, uint32_t dst_offset, OpCode op_code = OpCode::NOT_16); + AvmError op_shl( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::SHL_16); - void op_shr( + AvmError op_shr( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, OpCode op_code = OpCode::SHR_16); // Compute - Type Conversions - void op_cast(uint8_t indirect, - uint32_t a_offset, - uint32_t dst_offset, - AvmMemoryTag dst_tag, - OpCode op_code = OpCode::CAST_16); + AvmError op_cast(uint8_t indirect, + uint32_t a_offset, + uint32_t dst_offset, + AvmMemoryTag dst_tag, + OpCode op_code = OpCode::CAST_16); // Execution Environment - void op_get_env_var(uint8_t indirect, uint8_t env_var, uint32_t dst_offset); + AvmError op_get_env_var(uint8_t indirect, uint8_t env_var, uint32_t dst_offset); void op_address(uint8_t indirect, uint32_t dst_offset); void op_sender(uint8_t indirect, uint32_t dst_offset); void op_function_selector(uint8_t indirect, uint32_t dst_offset); @@ -96,12 +101,15 @@ class AvmTraceBuilder { void op_fee_per_da_gas(uint8_t indirect, uint32_t dst_offset); // Execution Environment - Calldata - void op_calldata_copy(uint8_t indirect, uint32_t cd_offset_address, uint32_t copy_size_offset, uint32_t dst_offset); - void op_returndata_size(uint8_t indirect, uint32_t dst_offset); - void op_returndata_copy(uint8_t indirect, - uint32_t rd_offset_address, - uint32_t copy_size_offset, - uint32_t dst_offset); + AvmError op_calldata_copy(uint8_t indirect, + uint32_t cd_offset_address, + uint32_t copy_size_offset, + uint32_t dst_offset); + AvmError op_returndata_size(uint8_t indirect, uint32_t dst_offset); + AvmError op_returndata_copy(uint8_t indirect, + uint32_t rd_offset_address, + uint32_t copy_size_offset, + uint32_t dst_offset); // Machine State - Gas void op_l2gasleft(uint8_t indirect, uint32_t dst_offset); @@ -109,96 +117,97 @@ class AvmTraceBuilder { // Machine State - Internal Control Flow // TODO(8945): skip_gas boolean is temporary and should be removed once all fake rows are removed - void op_jump(uint32_t jmp_dest, bool skip_gas = false); - void op_jumpi(uint8_t indirect, uint32_t jmp_dest, uint32_t cond_offset); - // TODO(md): this program counter MUST be an operand to the OPCODE. - void op_internal_call(uint32_t jmp_dest); - void op_internal_return(); + AvmError op_jump(uint32_t jmp_dest, bool skip_gas = false); + AvmError op_jumpi(uint8_t indirect, uint32_t jmp_dest, uint32_t cond_offset); + AvmError op_internal_call(uint32_t jmp_dest); + AvmError op_internal_return(); // Machine State - Memory // TODO(8945): skip_gas boolean is temporary and should be removed once all fake rows are removed - void op_set(uint8_t indirect, - FF val, - uint32_t dst_offset, - AvmMemoryTag in_tag, - OpCode op_code = OpCode::SET_FF, - bool skip_gas = false); - void op_mov(uint8_t indirect, uint32_t src_offset, uint32_t dst_offset, OpCode op_code = OpCode::MOV_16); + AvmError op_set(uint8_t indirect, + FF val, + uint32_t dst_offset, + AvmMemoryTag in_tag, + OpCode op_code = OpCode::SET_FF, + bool skip_gas = false); + AvmError op_mov(uint8_t indirect, uint32_t src_offset, uint32_t dst_offset, OpCode op_code = OpCode::MOV_16); // World State - void op_sload(uint8_t indirect, uint32_t slot_offset, uint32_t size, uint32_t dest_offset); - void op_sstore(uint8_t indirect, uint32_t src_offset, uint32_t size, uint32_t slot_offset); - void op_note_hash_exists(uint8_t indirect, - uint32_t note_hash_offset, - uint32_t leaf_index_offset, - uint32_t dest_offset); - void op_emit_note_hash(uint8_t indirect, uint32_t note_hash_offset); - void op_nullifier_exists(uint8_t indirect, - uint32_t nullifier_offset, - uint32_t address_offset, - uint32_t dest_offset); - void op_emit_nullifier(uint8_t indirect, uint32_t nullifier_offset); - void op_l1_to_l2_msg_exists(uint8_t indirect, - uint32_t log_offset, - uint32_t leaf_index_offset, - uint32_t dest_offset); - void op_get_contract_instance( + AvmError op_sload(uint8_t indirect, uint32_t slot_offset, uint32_t size, uint32_t dest_offset); + AvmError op_sstore(uint8_t indirect, uint32_t src_offset, uint32_t size, uint32_t slot_offset); + AvmError op_note_hash_exists(uint8_t indirect, + uint32_t note_hash_offset, + uint32_t leaf_index_offset, + uint32_t dest_offset); + AvmError op_emit_note_hash(uint8_t indirect, uint32_t note_hash_offset); + AvmError op_nullifier_exists(uint8_t indirect, + uint32_t nullifier_offset, + uint32_t address_offset, + uint32_t dest_offset); + AvmError op_emit_nullifier(uint8_t indirect, uint32_t nullifier_offset); + AvmError op_l1_to_l2_msg_exists(uint8_t indirect, + uint32_t log_offset, + uint32_t leaf_index_offset, + uint32_t dest_offset); + AvmError op_get_contract_instance( uint8_t indirect, uint8_t member_enum, uint16_t address_offset, uint16_t dst_offset, uint16_t exists_offset); // Accrued Substate - void op_emit_unencrypted_log(uint8_t indirect, uint32_t log_offset, uint32_t log_size_offset); - void op_emit_l2_to_l1_msg(uint8_t indirect, uint32_t recipient_offset, uint32_t content_offset); + AvmError op_emit_unencrypted_log(uint8_t indirect, uint32_t log_offset, uint32_t log_size_offset); + AvmError op_emit_l2_to_l1_msg(uint8_t indirect, uint32_t recipient_offset, uint32_t content_offset); // Control Flow - Contract Calls - void op_call(uint16_t indirect, - uint32_t gas_offset, - uint32_t addr_offset, - uint32_t args_offset, - uint32_t args_size, - uint32_t success_offset); - void op_static_call(uint16_t indirect, - uint32_t gas_offset, - uint32_t addr_offset, - uint32_t args_offset, - uint32_t args_size, - uint32_t success_offset); - std::vector op_return(uint8_t indirect, uint32_t ret_offset, uint32_t ret_size); + AvmError op_call(uint16_t indirect, + uint32_t gas_offset, + uint32_t addr_offset, + uint32_t args_offset, + uint32_t args_size, + uint32_t success_offset); + AvmError op_static_call(uint16_t indirect, + uint32_t gas_offset, + uint32_t addr_offset, + uint32_t args_offset, + uint32_t args_size, + uint32_t success_offset); + ReturnDataError op_return(uint8_t indirect, uint32_t ret_offset, uint32_t ret_size); // REVERT Opcode (that just call return under the hood for now) - std::vector op_revert(uint8_t indirect, uint32_t ret_offset, uint32_t ret_size_offset); + ReturnDataError op_revert(uint8_t indirect, uint32_t ret_offset, uint32_t ret_size_offset); // Misc - void op_debug_log(uint8_t indirect, - uint32_t message_offset, - uint32_t message_size, - uint32_t fields_offset, - uint32_t fields_size_offset); + AvmError op_debug_log(uint8_t indirect, + uint32_t message_offset, + uint32_t message_size, + uint32_t fields_offset, + uint32_t fields_size_offset); // Gadgets - void op_poseidon2_permutation(uint8_t indirect, uint32_t input_offset, uint32_t output_offset); - void op_ec_add(uint16_t indirect, - uint32_t lhs_x_offset, - uint32_t lhs_y_offset, - uint32_t lhs_is_inf_offset, - uint32_t rhs_x_offset, - uint32_t rhs_y_offset, - uint32_t rhs_is_inf_offset, - uint32_t output_offset); - void op_variable_msm(uint8_t indirect, - uint32_t points_offset, - uint32_t scalars_offset, - uint32_t output_offset, - uint32_t point_length_offset); + AvmError op_poseidon2_permutation(uint8_t indirect, uint32_t input_offset, uint32_t output_offset); + AvmError op_sha256_compression(uint8_t indirect, + uint32_t output_offset, + uint32_t state_offset, + uint32_t inputs_offset); + AvmError op_keccakf1600(uint8_t indirect, uint32_t output_offset, uint32_t input_offset); + + AvmError op_ec_add(uint16_t indirect, + uint32_t lhs_x_offset, + uint32_t lhs_y_offset, + uint32_t lhs_is_inf_offset, + uint32_t rhs_x_offset, + uint32_t rhs_y_offset, + uint32_t rhs_is_inf_offset, + uint32_t output_offset); + AvmError op_variable_msm(uint8_t indirect, + uint32_t points_offset, + uint32_t scalars_offset, + uint32_t output_offset, + uint32_t point_length_offset); // Conversions - void op_to_radix_be(uint8_t indirect, - uint32_t src_offset, - uint32_t dst_offset, - uint32_t radix_offset, - uint32_t num_limbs, - uint8_t output_bits); - - // Future Gadgets -- pending changes in noir - void op_sha256_compression(uint8_t indirect, uint32_t output_offset, uint32_t state_offset, uint32_t inputs_offset); - void op_keccakf1600(uint8_t indirect, uint32_t output_offset, uint32_t input_offset); + AvmError op_to_radix_be(uint8_t indirect, + uint32_t src_offset, + uint32_t dst_offset, + uint32_t radix_offset, + uint32_t num_limbs, + uint8_t output_bits); std::vector finalize(); void reset(); @@ -282,13 +291,13 @@ class AvmTraceBuilder { uint32_t data_offset, uint32_t metadata_offset); - void constrain_external_call(OpCode opcode, - uint16_t indirect, - uint32_t gas_offset, - uint32_t addr_offset, - uint32_t args_offset, - uint32_t args_size_offset, - uint32_t success_offset); + AvmError constrain_external_call(OpCode opcode, + uint16_t indirect, + uint32_t gas_offset, + uint32_t addr_offset, + uint32_t args_offset, + uint32_t args_size_offset, + uint32_t success_offset); void execute_gasleft(EnvironmentVariable var, uint8_t indirect, uint32_t dst_offset);