diff --git a/barretenberg/cpp/src/barretenberg/common/utils.cpp b/barretenberg/cpp/src/barretenberg/common/utils.cpp new file mode 100644 index 00000000000..00431bd4a74 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/common/utils.cpp @@ -0,0 +1,17 @@ +#include "./utils.hpp" + +namespace bb::utils { + +std::vector hex_to_bytes(const std::string& hex) +{ + std::vector bytes; + + for (unsigned int i = 0; i < hex.length(); i += 2) { + std::string byteString = hex.substr(i, 2); + bytes.push_back(static_cast(strtol(byteString.c_str(), nullptr, 16))); + } + + return bytes; +} + +} // namespace bb::utils \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/common/utils.hpp b/barretenberg/cpp/src/barretenberg/common/utils.hpp new file mode 100644 index 00000000000..f1ff5acffcb --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/common/utils.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +namespace bb::utils { + +/** + * @brief Routine to transform hexstring to vector of bytes. + * + * @param Hexadecimal string representation. + * @return Vector of uint8_t values. + */ +std::vector hex_to_bytes(const std::string& hex); + +} // namespace bb::utils \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.test.cpp b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.test.cpp index 76b324682a0..507c3378f26 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.test.cpp @@ -1,5 +1,6 @@ #include "ecdsa.hpp" #include "barretenberg/common/serialize.hpp" +#include "barretenberg/common/utils.hpp" #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" #include "barretenberg/ecc/curves/secp256r1/secp256r1.hpp" #include "barretenberg/serialize/test_helper.hpp" @@ -89,23 +90,10 @@ TEST(ecdsa, recover_public_key_secp256r1_sha256) EXPECT_EQ(recovered_public_key, account.public_key); } -std::vector HexToBytes(const std::string& hex) -{ - std::vector bytes; - - for (unsigned int i = 0; i < hex.length(); i += 2) { - std::string byteString = hex.substr(i, 2); - uint8_t byte = (uint8_t)strtol(byteString.c_str(), NULL, 16); - bytes.push_back(byte); - } - - return bytes; -} - TEST(ecdsa, check_overflowing_r_and_s_are_rejected) { - std::vector message_vec = HexToBytes("41414141"); + std::vector message_vec = utils::hex_to_bytes("41414141"); std::string message(message_vec.begin(), message_vec.end()); crypto::ecdsa_signature signature; @@ -181,10 +169,10 @@ TEST(ecdsa, verify_signature_secp256r1_sha256_NIST_1) }; crypto::ecdsa_signature sig{ r, s, 27 }; - std::vector message_vec = - HexToBytes("5905238877c77421f73e43ee3da6f2d9e2ccad5fc942dcec0cbd25482935faaf416983fe165b1a045ee2bcd2e6dca3bdf46" - "c4310a7461f9a37960ca672d3feb5473e253605fb1ddfd28065b53cb5858a8ad28175bf9bd386a5e471ea7a65c17cc934a9" - "d791e91491eb3754d03799790fe2d308d16146d5c9b0d0debd97d79ce8"); + std::vector message_vec = utils::hex_to_bytes( + "5905238877c77421f73e43ee3da6f2d9e2ccad5fc942dcec0cbd25482935faaf416983fe165b1a045ee2bcd2e6dca3bdf46" + "c4310a7461f9a37960ca672d3feb5473e253605fb1ddfd28065b53cb5858a8ad28175bf9bd386a5e471ea7a65c17cc934a9" + "d791e91491eb3754d03799790fe2d308d16146d5c9b0d0debd97d79ce8"); std::string message(message_vec.begin(), message_vec.end()); bool result = crypto::ecdsa_verify_signature( diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp index 10b3c347330..f8e06ef180b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp @@ -2,6 +2,7 @@ #include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" #include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" +#include using Flavor = bb::honk::flavor::AvmMiniFlavor; using FF = Flavor::FF; @@ -12,6 +13,9 @@ namespace avm_trace { // Number of rows static const size_t AVM_TRACE_SIZE = 256; enum class IntermRegister : uint32_t { IA = 0, IB = 1, IC = 2 }; + +// Keep following enum in sync with MAX_NEM_TAG below enum class AvmMemoryTag : uint32_t { U0 = 0, U8 = 1, U16 = 2, U32 = 3, U64 = 4, U128 = 5, FF = 6 }; +static const uint32_t MAX_MEM_TAG = 6; } // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.cpp new file mode 100644 index 00000000000..1a9da79a4d9 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.cpp @@ -0,0 +1,230 @@ +#include "AvmMini_execution.hpp" +#include "barretenberg/common/serialize.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_common.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_instructions.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_opcode.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_trace.hpp" +#include "barretenberg/vm/generated/AvmMini_composer.hpp" +#include +#include +#include +#include + +namespace avm_trace { + +/** + * @brief Run the bytecode, generate the corresponding execution trace and prove the correctness + * of the execution of the supplied bytecode. + * + * @param bytecode A vector of bytes representing the bytecode to execute. + * @param calldata expressed as a vector of finite field elements. + * @throws runtime_error exception when the bytecode is invalid. + * @return A zk proof of the execution. + */ +plonk::proof Execution::run_and_prove(std::vector const& bytecode, std::vector const& calldata) +{ + auto instructions = parse(bytecode); + auto trace = gen_trace(instructions, calldata); + auto circuit_builder = bb::AvmMiniCircuitBuilder(); + circuit_builder.set_trace(std::move(trace)); + + auto composer = bb::honk::AvmMiniComposer(); + auto prover = composer.create_prover(circuit_builder); + return prover.construct_proof(); +} + +/** + * @brief Parsing of the supplied bytecode into a vector of instructions. It essentially + * checks that each opcode value is in the defined range and extracts the operands + * for each opcode. + * + * @param bytecode The bytecode to be parsed as a vector of bytes/uint8_t + * @throws runtime_error exception when the bytecode is invalid. + * @return Vector of instructions + */ +std::vector Execution::parse(std::vector const& bytecode) +{ + std::vector instructions; + size_t pos = 0; + const auto length = bytecode.size(); + + while (pos < length) { + const uint8_t opcode_byte = bytecode.at(pos); + pos += AVM_OPCODE_BYTE_LENGTH; + + if (!Bytecode::is_valid(opcode_byte)) { + throw std::runtime_error("Invalid opcode byte: " + std::to_string(opcode_byte)); + } + + const auto opcode = static_cast(opcode_byte); + auto in_tag_u8 = static_cast(AvmMemoryTag::U0); + + if (Bytecode::has_in_tag(opcode)) { + if (pos + AVM_IN_TAG_BYTE_LENGTH > length) { + throw std::runtime_error("Instruction tag missing at position " + std::to_string(pos)); + } + in_tag_u8 = bytecode.at(pos); + if (in_tag_u8 == static_cast(AvmMemoryTag::U0) || in_tag_u8 > MAX_MEM_TAG) { + throw std::runtime_error("Instruction tag is invalid at position " + std::to_string(pos) + + " value: " + std::to_string(in_tag_u8)); + } + pos += AVM_IN_TAG_BYTE_LENGTH; + } + + auto const in_tag = static_cast(in_tag_u8); + std::vector operands{}; + size_t num_of_operands{}; + size_t operands_size{}; + + // SET opcode particularity about the number of operands depending on the + // instruction tag. Namely, a constant of type instruction tag and not a + // memory address is passed in the operands. + // The bytecode of the operands is of the form CONSTANT || dst_offset + // CONSTANT is of size k bits for type Uk, k=8,16,32,64,128 + // dst_offset is of size 32 bits + // CONSTANT has to be decomposed into 32-bit chunks + if (opcode == OpCode::SET) { + switch (in_tag) { + case AvmMemoryTag::U8: + num_of_operands = 2; + operands_size = 5; + break; + case AvmMemoryTag::U16: + num_of_operands = 2; + operands_size = 6; + break; + case AvmMemoryTag::U32: + num_of_operands = 2; + operands_size = 8; + break; + case AvmMemoryTag::U64: + num_of_operands = 3; + operands_size = 12; + break; + case AvmMemoryTag::U128: + num_of_operands = 5; + operands_size = 20; + break; + default: + throw std::runtime_error("Instruction tag for SET opcode is invalid at position " + + std::to_string(pos) + " value: " + std::to_string(in_tag_u8)); + break; + } + } else { + num_of_operands = Bytecode::OPERANDS_NUM.at(opcode); + operands_size = AVM_OPERAND_BYTE_LENGTH * num_of_operands; + } + + if (pos + operands_size > length) { + throw std::runtime_error("Operand is missing at position " + std::to_string(pos)); + } + + // We handle operands which are encoded with less than 4 bytes. + // This occurs for opcode SET and tag U8 and U16. + if (opcode == OpCode::SET && in_tag == AvmMemoryTag::U8) { + operands.push_back(static_cast(bytecode.at(pos))); + pos++; + num_of_operands--; + } else if (opcode == OpCode::SET && in_tag == AvmMemoryTag::U16) { + uint8_t const* ptr = &bytecode.at(pos); + uint16_t operand{}; + serialize::read(ptr, operand); + operands.push_back(static_cast(operand)); + pos += 2; + num_of_operands--; + } + + // Operands of size of 32 bits. + for (size_t i = 0; i < num_of_operands; i++) { + uint8_t const* ptr = &bytecode.at(pos); + uint32_t operand{}; + serialize::read(ptr, operand); + operands.push_back(operand); + pos += AVM_OPERAND_BYTE_LENGTH; + } + + instructions.emplace_back(opcode, operands, static_cast(in_tag)); + } + + return instructions; +} + +/** + * @brief Generate the execution trace pertaining to the supplied instructions. + * + * @param instructions A vector of the instructions to be executed. + * @param calldata expressed as a vector of finite field elements. + * @return The trace as a vector of Row. + */ +std::vector Execution::gen_trace(std::vector const& instructions, std::vector const& calldata) +{ + AvmMiniTraceBuilder trace_builder{}; + + for (auto const& inst : instructions) { + switch (inst.op_code) { + case OpCode::ADD: + trace_builder.add(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag); + break; + case OpCode::SUB: + trace_builder.sub(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag); + break; + case OpCode::MUL: + trace_builder.mul(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag); + break; + case OpCode::DIV: + trace_builder.div(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag); + break; + case OpCode::CALLDATACOPY: + trace_builder.calldata_copy(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), calldata); + break; + case OpCode::JUMP: + trace_builder.jump(inst.operands.at(0)); + break; + case OpCode::INTERNALCALL: + trace_builder.internal_call(inst.operands.at(0)); + break; + case OpCode::INTERNALRETURN: + trace_builder.internal_return(); + break; + case OpCode::SET: { + uint32_t dst_offset{}; + uint128_t val{}; + switch (inst.in_tag) { + case AvmMemoryTag::U8: + case AvmMemoryTag::U16: + case AvmMemoryTag::U32: + // U8, U16, U32 value represented in a single uint32_t operand + val = inst.operands.at(0); + dst_offset = inst.operands.at(1); + break; + case AvmMemoryTag::U64: // value represented as 2 uint32_t operands + val = inst.operands.at(0); + val <<= 32; + val += inst.operands.at(1); + dst_offset = inst.operands.at(2); + break; + case AvmMemoryTag::U128: // value represented as 4 uint32_t operands + for (size_t i = 0; i < 4; i++) { + val += inst.operands.at(i); + val <<= 32; + } + dst_offset = inst.operands.at(4); + break; + default: + break; + } + trace_builder.set(val, dst_offset, inst.in_tag); + break; + } + case OpCode::RETURN: + trace_builder.return_op(inst.operands.at(0), inst.operands.at(1)); + break; + default: + break; + } + } + return trace_builder.finalize(); +} + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.hpp new file mode 100644 index 00000000000..42baa71f5c0 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_common.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_instructions.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_trace.hpp" +#include +#include +#include + +namespace avm_trace { + +class Execution { + public: + Execution() = default; + + static size_t const AVM_OPERAND_BYTE_LENGTH = 4; // Keep in sync with TS code + static_assert(sizeof(uint32_t) / sizeof(uint8_t) == AVM_OPERAND_BYTE_LENGTH); + + static size_t const AVM_OPCODE_BYTE_LENGTH = 1; // Keep in sync with TS code + static size_t const AVM_IN_TAG_BYTE_LENGTH = 1; // Keep in sync with TS code + + static std::vector parse(std::vector const& bytecode); + static std::vector gen_trace(std::vector const& instructions, std::vector const& calldata); + static plonk::proof run_and_prove(std::vector const& bytecode, std::vector const& calldata); +}; + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_instructions.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_instructions.hpp new file mode 100644 index 00000000000..0cc18e56087 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_instructions.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "barretenberg/vm/avm_trace/AvmMini_common.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_opcode.hpp" +#include +#include + +namespace avm_trace { + +class Instruction { + public: + OpCode op_code; + std::vector operands; + AvmMemoryTag in_tag; + + Instruction() = delete; + explicit Instruction(OpCode op_code, std::vector operands, AvmMemoryTag in_tag) + : op_code(op_code) + , operands(std::move(operands)) + , in_tag(in_tag){}; +}; + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.cpp new file mode 100644 index 00000000000..ec3d7568f09 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.cpp @@ -0,0 +1,153 @@ +#include "AvmMini_opcode.hpp" +#include + +namespace avm_trace { + +const std::unordered_map Bytecode::OPERANDS_NUM = { + // Compute + // Compute - Arithmetic + { OpCode::ADD, 3 }, + { OpCode::SUB, 3 }, + { OpCode::MUL, 3 }, + { OpCode::DIV, 3 }, + //// Compute - Comparators + //{OpCode::EQ, }, + //{OpCode::LT, }, + //{OpCode::LTE, }, + //// Compute - Bitwise + //{OpCode::AND, }, + //{OpCode::OR, }, + //{OpCode::XOR, }, + //{OpCode::NOT, }, + //{OpCode::SHL, }, + //{OpCode::SHR, }, + //// Compute - Type Conversions + //{OpCode::CAST, }, + + //// Execution Environment + //{OpCode::ADDRESS, }, + //{OpCode::STORAGEADDRESS, }, + //{OpCode::ORIGIN, }, + //{OpCode::SENDER, }, + //{OpCode::PORTAL, }, + //{OpCode::FEEPERL1GAS, }, + //{OpCode::FEEPERL2GAS, }, + //{OpCode::FEEPERDAGAS, }, + //{OpCode::CONTRACTCALLDEPTH, }, + //// Execution Environment - Globals + //{OpCode::CHAINID, }, + //{OpCode::VERSION, }, + //{OpCode::BLOCKNUMBER, }, + //{OpCode::TIMESTAMP, }, + //{OpCode::COINBASE, }, + //{OpCode::BLOCKL1GASLIMIT, }, + //{OpCode::BLOCKL2GASLIMIT, }, + //{OpCode::BLOCKDAGASLIMIT, }, + // Execution Environment - Calldata + { OpCode::CALLDATACOPY, 3 }, + + //// Machine State + // Machine State - Gas + //{ OpCode::L1GASLEFT, }, + //{ OpCode::L2GASLEFT, }, + //{ OpCode::DAGASLEFT, }, + //// Machine State - Internal Control Flow + { OpCode::JUMP, 1 }, + { OpCode::JUMPI, 1 }, + { OpCode::INTERNALCALL, 1 }, + { OpCode::INTERNALRETURN, 0 }, + + //// Machine State - Memory + { OpCode::SET, 5 }, + //{ OpCode::MOV, }, + //{ OpCode::CMOV, }, + + //// World State + //{ OpCode::BLOCKHEADERBYNUMBER, }, + //{ OpCode::SLOAD, }, // Public Storage + //{ OpCode::SSTORE, }, // Public Storage + //{ OpCode::READL1TOL2MSG, }, // Messages + //{ OpCode::SENDL2TOL1MSG, }, // Messages + //{ OpCode::EMITNOTEHASH, }, // Notes & Nullifiers + //{ OpCode::EMITNULLIFIER, }, // Notes & Nullifiers + + //// Accrued Substate + //{ OpCode::EMITUNENCRYPTEDLOG, }, + + //// Control Flow - Contract Calls + //{ OpCode::CALL, }, + //{ OpCode::STATICCALL, }, + { OpCode::RETURN, 2 }, + // { OpCode::REVERT, }, + + //// Gadgets + //{ OpCode::KECCAK, }, + //{ OpCode::POSEIDON, }, +}; + +/** + * @brief Test whether a given byte reprents a valid opcode. + * + * @param byte The input byte. + * @return A boolean telling whether a corresponding opcode does match the input byte. + */ +bool Bytecode::is_valid(const uint8_t byte) +{ + return byte <= static_cast(OpCode::POSEIDON); +} + +/** + * @brief A function returning whether a supplied opcode has an instruction tag as argument. + * + * @param op_code The opcode + * @return A boolean set to true if the corresponding instruction needs a tag as argument. + */ +bool Bytecode::has_in_tag(OpCode const op_code) +{ + switch (op_code) { + case OpCode::ADDRESS: + case OpCode::STORAGEADDRESS: + case OpCode::ORIGIN: + case OpCode::SENDER: + case OpCode::PORTAL: + case OpCode::FEEPERL1GAS: + case OpCode::FEEPERL2GAS: + case OpCode::FEEPERDAGAS: + case OpCode::CONTRACTCALLDEPTH: + case OpCode::CHAINID: + case OpCode::VERSION: + case OpCode::BLOCKNUMBER: + case OpCode::TIMESTAMP: + case OpCode::COINBASE: + case OpCode::BLOCKL1GASLIMIT: + case OpCode::BLOCKL2GASLIMIT: + case OpCode::BLOCKDAGASLIMIT: + case OpCode::CALLDATACOPY: + case OpCode::L1GASLEFT: + case OpCode::L2GASLEFT: + case OpCode::DAGASLEFT: + case OpCode::JUMP: + case OpCode::JUMPI: + case OpCode::INTERNALCALL: + case OpCode::INTERNALRETURN: + case OpCode::MOV: + case OpCode::CMOV: + case OpCode::BLOCKHEADERBYNUMBER: + case OpCode::SLOAD: + case OpCode::SSTORE: + case OpCode::READL1TOL2MSG: + case OpCode::SENDL2TOL1MSG: + case OpCode::EMITNOTEHASH: + case OpCode::EMITNULLIFIER: + case OpCode::EMITUNENCRYPTEDLOG: + case OpCode::CALL: + case OpCode::STATICCALL: + case OpCode::RETURN: + case OpCode::REVERT: + return false; + default: + return true; + } +} + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.hpp new file mode 100644 index 00000000000..c945f6158c6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include +#include + +namespace avm_trace { +using std::size_t; + +/** + * All AVM opcodes (Keep in sync with TS counterpart code opcodes.ts) + * TODO: Once opcode values are definitive, we should assign them explicitly in the enum below + * and typescript code. This would increase robustness against unintended modifications. + * i.e.: ADD = 0, SUB = 1, etc, .... + * CAUTION: Any change in the list below needs to be carefully followed by + * a potential adaptation of Bytecode::is_valid method. + */ +enum class OpCode : uint8_t { + // Compute + // Compute - Arithmetic + ADD, + SUB, + MUL, + DIV, + // Compute - Comparators + EQ, + LT, + LTE, + // Compute - Bitwise + AND, + OR, + XOR, + NOT, + SHL, + SHR, + // Compute - Type Conversions + CAST, + + // Execution Environment + ADDRESS, + STORAGEADDRESS, + ORIGIN, + SENDER, + PORTAL, + FEEPERL1GAS, + FEEPERL2GAS, + FEEPERDAGAS, + CONTRACTCALLDEPTH, + // Execution Environment - Globals + CHAINID, + VERSION, + BLOCKNUMBER, + TIMESTAMP, + COINBASE, + BLOCKL1GASLIMIT, + BLOCKL2GASLIMIT, + BLOCKDAGASLIMIT, + // Execution Environment - Calldata + CALLDATACOPY, + + // Machine State + // Machine State - Gas + L1GASLEFT, + L2GASLEFT, + DAGASLEFT, + // Machine State - Internal Control Flow + JUMP, + JUMPI, + INTERNALCALL, + INTERNALRETURN, + // Machine State - Memory + SET, + MOV, + CMOV, + + // World State + BLOCKHEADERBYNUMBER, + SLOAD, // Public Storage + SSTORE, // Public Storage + READL1TOL2MSG, // Messages + SENDL2TOL1MSG, // Messages + EMITNOTEHASH, // Notes & Nullifiers + EMITNULLIFIER, // Notes & Nullifiers + + // Accrued Substate + EMITUNENCRYPTEDLOG, + + // Control Flow - Contract Calls + CALL, + STATICCALL, + RETURN, + REVERT, + + // Gadgets + KECCAK, + POSEIDON, +}; + +class Bytecode { + public: + static bool is_valid(uint8_t byte); + static bool has_in_tag(OpCode); + static const std::unordered_map OPERANDS_NUM; +}; + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp index 771a1590ea1..08952de4e1b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp @@ -279,10 +279,10 @@ void AvmMiniTraceBuilder::set(uint128_t val, uint32_t dst_offset, AvmMemoryTag i * @param dst_offset The starting index of memory where calldata will be copied to. * @param call_data_mem The vector containing calldata. */ -void AvmMiniTraceBuilder::call_data_copy(uint32_t cd_offset, - uint32_t copy_size, - uint32_t dst_offset, - std::vector const& call_data_mem) +void AvmMiniTraceBuilder::calldata_copy(uint32_t cd_offset, + uint32_t copy_size, + uint32_t dst_offset, + std::vector const& call_data_mem) { // We parallelize storing memory operations in chunk of 3, i.e., 1 per intermediate register. // The variable pos is an index pointing to the first storing operation (pertaining to intermediate @@ -375,6 +375,11 @@ void AvmMiniTraceBuilder::call_data_copy(uint32_t cd_offset, */ std::vector AvmMiniTraceBuilder::return_op(uint32_t ret_offset, uint32_t ret_size) { + if (ret_size == 0) { + halt(); + return std::vector{}; + } + // We parallelize loading memory operations in chunk of 3, i.e., 1 per intermediate register. // The variable pos is an index pointing to the first storing operation (pertaining to intermediate // register Ia) relative to ret_offset: diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp index af09f2e4d14..0d58753111e 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp @@ -4,6 +4,7 @@ #include "AvmMini_alu_trace.hpp" #include "AvmMini_common.hpp" +#include "AvmMini_instructions.hpp" #include "AvmMini_mem_trace.hpp" #include "barretenberg/common/throw_or_abort.hpp" @@ -55,10 +56,10 @@ class AvmMiniTraceBuilder { // CALLDATACOPY opcode with direct memory access, i.e., // M[dst_offset:dst_offset+copy_size] = calldata[cd_offset:cd_offset+copy_size] - void call_data_copy(uint32_t cd_offset, - uint32_t copy_size, - uint32_t dst_offset, - std::vector const& call_data_mem); + void calldata_copy(uint32_t cd_offset, + uint32_t copy_size, + uint32_t dst_offset, + std::vector const& call_data_mem); // RETURN opcode with direct memory access, i.e., // return(M[ret_offset:ret_offset+ret_size]) diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp index 15f4e0a37ee..6cde3d10186 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp @@ -3,8 +3,8 @@ #include "barretenberg/numeric/uint128/uint128.hpp" using namespace numeric; -using namespace tests_avm; namespace { +using namespace tests_avm; void common_validate_arithmetic_op(Row const& main_row, Row const& alu_row, @@ -35,7 +35,7 @@ void common_validate_arithmetic_op(Row const& main_row, // Check the instruction tag EXPECT_EQ(main_row.avmMini_in_tag, FF(static_cast(tag))); - // Check that intermediate rgiesters are correctly copied in Alu trace + // Check that intermediate registers are correctly copied in Alu trace EXPECT_EQ(alu_row.aluChip_alu_ia, a); EXPECT_EQ(alu_row.aluChip_alu_ib, b); EXPECT_EQ(alu_row.aluChip_alu_ic, c); @@ -184,7 +184,9 @@ std::vector gen_mutated_trace_mul(FF const& a, FF const& b, FF const& c_mut } // anonymous namespace +namespace tests_avm { using namespace avm_trace; + class AvmMiniArithmeticTests : public ::testing::Test { public: AvmMiniTraceBuilder trace_builder; @@ -244,7 +246,7 @@ class AvmMiniArithmeticNegativeTestsU128 : public AvmMiniArithmeticTests {}; TEST_F(AvmMiniArithmeticTestsFF, addition) { // trace_builder - trace_builder.call_data_copy(0, 3, 0, std::vector{ 37, 4, 11 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 37, 4, 11 }); // Memory layout: [37,4,11,0,0,0,....] trace_builder.add(0, 1, 4, AvmMemoryTag::FF); // [37,4,11,0,41,0,....] @@ -263,7 +265,7 @@ TEST_F(AvmMiniArithmeticTestsFF, addition) // Test on basic subtraction over finite field type. TEST_F(AvmMiniArithmeticTestsFF, subtraction) { - trace_builder.call_data_copy(0, 3, 0, std::vector{ 8, 4, 17 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 8, 4, 17 }); // Memory layout: [8,4,17,0,0,0,....] trace_builder.sub(2, 0, 1, AvmMemoryTag::FF); // [8,9,17,0,0,0....] @@ -282,7 +284,7 @@ TEST_F(AvmMiniArithmeticTestsFF, subtraction) // Test on basic multiplication over finite field type. TEST_F(AvmMiniArithmeticTestsFF, multiplication) { - trace_builder.call_data_copy(0, 3, 0, std::vector{ 5, 0, 20 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 5, 0, 20 }); // Memory layout: [5,0,20,0,0,0,....] trace_builder.mul(2, 0, 1, AvmMemoryTag::FF); // [5,100,20,0,0,0....] @@ -302,7 +304,7 @@ TEST_F(AvmMiniArithmeticTestsFF, multiplication) // Test on multiplication by zero over finite field type. TEST_F(AvmMiniArithmeticTestsFF, multiplicationByZero) { - trace_builder.call_data_copy(0, 1, 0, std::vector{ 127 }); + trace_builder.calldata_copy(0, 1, 0, std::vector{ 127 }); // Memory layout: [127,0,0,0,0,0,....] trace_builder.mul(0, 1, 2, AvmMemoryTag::FF); // [127,0,0,0,0,0....] @@ -322,7 +324,7 @@ TEST_F(AvmMiniArithmeticTestsFF, multiplicationByZero) // Test on basic division over finite field type. TEST_F(AvmMiniArithmeticTestsFF, division) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 15, 315 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 15, 315 }); // Memory layout: [15,315,0,0,0,0,....] trace_builder.div(1, 0, 2, AvmMemoryTag::FF); // [15,315,21,0,0,0....] @@ -345,7 +347,7 @@ TEST_F(AvmMiniArithmeticTestsFF, division) // Test on division with zero numerator over finite field type. TEST_F(AvmMiniArithmeticTestsFF, divisionNumeratorZero) { - trace_builder.call_data_copy(0, 1, 0, std::vector{ 15 }); + trace_builder.calldata_copy(0, 1, 0, std::vector{ 15 }); // Memory layout: [15,0,0,0,0,0,....] trace_builder.div(1, 0, 0, AvmMemoryTag::FF); // [0,0,0,0,0,0....] @@ -369,7 +371,7 @@ TEST_F(AvmMiniArithmeticTestsFF, divisionNumeratorZero) // We check that the operator error flag is raised. TEST_F(AvmMiniArithmeticTestsFF, divisionByZeroError) { - trace_builder.call_data_copy(0, 1, 0, std::vector{ 15 }); + trace_builder.calldata_copy(0, 1, 0, std::vector{ 15 }); // Memory layout: [15,0,0,0,0,0,....] trace_builder.div(0, 1, 2, AvmMemoryTag::FF); // [15,0,0,0,0,0....] @@ -419,7 +421,7 @@ TEST_F(AvmMiniArithmeticTestsFF, divisionZeroByZeroError) // No check on the evaluation is performed here. TEST_F(AvmMiniArithmeticTestsFF, mixedOperationsWithError) { - trace_builder.call_data_copy(0, 3, 2, std::vector{ 45, 23, 12 }); + trace_builder.calldata_copy(0, 3, 2, std::vector{ 45, 23, 12 }); // Memory layout: [0,0,45,23,12,0,0,0,....] trace_builder.add(2, 3, 4, AvmMemoryTag::FF); // [0,0,45,23,68,0,0,0,....] @@ -1397,7 +1399,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, multiplication) // Test on basic incorrect division over finite field type. TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionFF) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 15, 315 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 15, 315 }); // Memory layout: [15,315,0,0,0,0,....] trace_builder.div(1, 0, 2, AvmMemoryTag::FF); // [15,315,21,0,0,0....] @@ -1414,7 +1416,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionFF) // in the trace. TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionNoZeroButError) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 15, 315 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 15, 315 }); // Memory layout: [15,315,0,0,0,0,....] trace_builder.div(1, 0, 2, AvmMemoryTag::FF); // [15,315,21,0,0,0....] @@ -1440,7 +1442,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionNoZeroButError) // Test with division by zero occurs and no error is raised (remove error flag) TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionByZeroNoError) { - trace_builder.call_data_copy(0, 1, 0, std::vector{ 15 }); + trace_builder.calldata_copy(0, 1, 0, std::vector{ 15 }); // Memory layout: [15,0,0,0,0,0,....] trace_builder.div(0, 1, 2, AvmMemoryTag::FF); // [15,0,0,0,0,0....] @@ -1477,7 +1479,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionZeroByZeroNoError) // the addition, subtraction, multiplication. TEST_F(AvmMiniArithmeticNegativeTestsFF, operationWithErrorFlag) { - trace_builder.call_data_copy(0, 3, 0, std::vector{ 37, 4, 11 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 37, 4, 11 }); // Memory layout: [37,4,11,0,0,0,....] trace_builder.add(0, 1, 4, AvmMemoryTag::FF); // [37,4,11,0,41,0,....] @@ -1495,7 +1497,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, operationWithErrorFlag) trace_builder.reset(); - trace_builder.call_data_copy(0, 3, 0, std::vector{ 8, 4, 17 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 8, 4, 17 }); // Memory layout: [8,4,17,0,0,0,....] trace_builder.sub(2, 0, 1, AvmMemoryTag::FF); // [8,9,17,0,0,0....] @@ -1512,7 +1514,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, operationWithErrorFlag) trace_builder.reset(); - trace_builder.call_data_copy(0, 3, 0, std::vector{ 5, 0, 20 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 5, 0, 20 }); // Memory layout: [5,0,20,0,0,0,....] trace_builder.mul(2, 0, 1, AvmMemoryTag::FF); // [5,100,20,0,0,0....] @@ -1675,4 +1677,6 @@ TEST_F(AvmMiniArithmeticNegativeTestsU128, multiplication) FF{ uint256_t::from_uint128(c) }, AvmMemoryTag::U128); EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "ALU_MULTIPLICATION_OUT_U128"); -} \ No newline at end of file +} + +} // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp index 657ae8e25b1..11ede61acfc 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp @@ -1,7 +1,7 @@ #include "AvmMini_common.test.hpp" +namespace tests_avm { using namespace avm_trace; -using namespace tests_avm; class AvmMiniControlFlowTests : public ::testing::Test { public: @@ -285,3 +285,4 @@ TEST_F(AvmMiniControlFlowTests, multipleCallsAndReturns) validate_trace_proof(std::move(trace)); } +} // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_execution.test.cpp new file mode 100644 index 00000000000..f74218e10f3 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_execution.test.cpp @@ -0,0 +1,304 @@ +#include "barretenberg/vm/avm_trace/AvmMini_execution.hpp" +#include "AvmMini_common.test.hpp" +#include "barretenberg/common/utils.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_common.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_helper.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_opcode.hpp" +#include "barretenberg/vm/tests/helpers.test.hpp" +#include +#include +#include +#include + +namespace tests_avm { +using namespace avm_trace; +using bb::utils::hex_to_bytes; + +class AvmMiniExecutionTests : public ::testing::Test { + public: + AvmMiniTraceBuilder trace_builder; + + protected: + // TODO(640): The Standard Honk on Grumpkin test suite fails unless the SRS is initialised for every test. + void SetUp() override + { + srs::init_crs_factory("../srs_db/ignition"); + trace_builder = AvmMiniTraceBuilder(); // Clean instance for every run. + }; +}; + +// Basic positive test with an ADD and RETURN opcode. +// Parsing, trace generation and proving is verified. +TEST_F(AvmMiniExecutionTests, basicAddReturn) +{ + std::string bytecode_hex = "00" // ADD + "01" // U8 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "34" // RETURN + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + auto bytecode = hex_to_bytes(bytecode_hex); + auto instructions = Execution::parse(bytecode); + + // 2 instructions + EXPECT_EQ(instructions.size(), 2); + + // ADD + EXPECT_EQ(instructions.at(0).op_code, OpCode::ADD); + EXPECT_EQ(instructions.at(0).operands.size(), 3); + EXPECT_EQ(instructions.at(0).operands.at(0), 7); + EXPECT_EQ(instructions.at(0).operands.at(1), 9); + EXPECT_EQ(instructions.at(0).operands.at(2), 1); + EXPECT_EQ(instructions.at(0).in_tag, AvmMemoryTag::U8); + + // RETURN + EXPECT_EQ(instructions.at(1).op_code, OpCode::RETURN); + EXPECT_EQ(instructions.at(1).operands.size(), 2); + EXPECT_EQ(instructions.at(1).operands.at(0), 0); + EXPECT_EQ(instructions.at(1).operands.at(0), 0); + + auto trace = Execution::gen_trace(instructions, std::vector{}); + auto trace_verif = trace; + validate_trace_proof(std::move(trace)); + + auto circuit_builder = AvmMiniCircuitBuilder(); + circuit_builder.set_trace(std::move(trace_verif)); + auto composer = honk::AvmMiniComposer(); + auto verifier = composer.create_verifier(circuit_builder); + + auto proof = Execution::run_and_prove(bytecode, std::vector{}); + + EXPECT_TRUE(verifier.verify_proof(proof)); +} + +// Positive test for SET and SUB opcodes +TEST_F(AvmMiniExecutionTests, setAndSubOpcodes) +{ + std::string bytecode_hex = "27" // SET 39 = 0x27 + "02" // U16 + "B813" // val 47123 + "000000AA" // dst_offset 170 + "27" // SET 39 = 0x27 + "02" // U16 + "9103" // val 37123 + "00000033" // dst_offset 51 + "01" // SUB + "02" // U16 + "000000AA" // addr a + "00000033" // addr b + "00000001" // addr c 1 + "34" // RETURN + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + auto bytecode = hex_to_bytes(bytecode_hex); + auto instructions = Execution::parse(bytecode); + + EXPECT_EQ(instructions.size(), 4); + + // SET + EXPECT_EQ(instructions.at(0).op_code, OpCode::SET); + EXPECT_EQ(instructions.at(0).operands.size(), 2); + EXPECT_EQ(instructions.at(0).operands.at(0), 47123); + EXPECT_EQ(instructions.at(0).operands.at(1), 170); + EXPECT_EQ(instructions.at(0).in_tag, AvmMemoryTag::U16); + + // SET + EXPECT_EQ(instructions.at(1).op_code, OpCode::SET); + EXPECT_EQ(instructions.at(1).operands.size(), 2); + EXPECT_EQ(instructions.at(1).operands.at(0), 37123); + EXPECT_EQ(instructions.at(1).operands.at(1), 51); + EXPECT_EQ(instructions.at(1).in_tag, AvmMemoryTag::U16); + + // SUB + EXPECT_EQ(instructions.at(2).op_code, OpCode::SUB); + EXPECT_EQ(instructions.at(2).operands.size(), 3); + EXPECT_EQ(instructions.at(2).operands.at(0), 170); + EXPECT_EQ(instructions.at(2).operands.at(1), 51); + EXPECT_EQ(instructions.at(2).in_tag, AvmMemoryTag::U16); + + auto trace = Execution::gen_trace(instructions, std::vector{}); + + // Find the first row enabling the subtraction selector + auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avmMini_sel_op_sub == 1; }); + EXPECT_EQ(row->avmMini_ic, 10000); // 47123 - 37123 = 10000 + + auto trace_verif = trace; + validate_trace_proof(std::move(trace)); + + auto circuit_builder = AvmMiniCircuitBuilder(); + circuit_builder.set_trace(std::move(trace_verif)); + auto composer = honk::AvmMiniComposer(); + auto verifier = composer.create_verifier(circuit_builder); + + auto proof = Execution::run_and_prove(bytecode, std::vector{}); + + EXPECT_TRUE(verifier.verify_proof(proof)); +} + +// Positive test for multiple MUL opcodes +// We compute 5^12 based on U64 multiplications +// 5 is stored at offset 0 and 1 at offset 1 +// Repeat 12 times a multiplication of value +// at offset 0 (5) with value at offset 1 and store +// the result at offset 1. +TEST_F(AvmMiniExecutionTests, powerWithMulOpcodes) +{ + std::string bytecode_hex = "27" // SET 39 = 0x27 + "04" // U64 + "00000000" // val 5 higher 32 bits + "00000005" // val 5 lower 32 bits + "00000000" // dst_offset 0 + "27" // SET 39 = 0x27 + "04" // U64 + "00000000" // val 1 higher 32 bits + "00000001" // val 1 lower 32 bits + "00000001"; // dst_offset 1 + + std::string const mul_hex = "02" // MUL + "04" // U64 + "00000000" // addr a + "00000001" // addr b + "00000001"; // addr c 1 + + std::string const ret_hex = "34" // RETURN + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + uint8_t num = 12; + while (num-- > 0) { + bytecode_hex.append(mul_hex); + } + + bytecode_hex.append(ret_hex); + + auto bytecode = hex_to_bytes(bytecode_hex); + auto instructions = Execution::parse(bytecode); + + EXPECT_EQ(instructions.size(), 15); + + // MUL first pos + EXPECT_EQ(instructions.at(2).op_code, OpCode::MUL); + EXPECT_EQ(instructions.at(2).operands.size(), 3); + EXPECT_EQ(instructions.at(2).operands.at(0), 0); + EXPECT_EQ(instructions.at(2).operands.at(1), 1); + EXPECT_EQ(instructions.at(2).operands.at(2), 1); + EXPECT_EQ(instructions.at(2).in_tag, AvmMemoryTag::U64); + + // MUL last pos + EXPECT_EQ(instructions.at(13).op_code, OpCode::MUL); + EXPECT_EQ(instructions.at(13).operands.size(), 3); + EXPECT_EQ(instructions.at(13).operands.at(0), 0); + EXPECT_EQ(instructions.at(13).operands.at(1), 1); + EXPECT_EQ(instructions.at(13).operands.at(2), 1); + EXPECT_EQ(instructions.at(13).in_tag, AvmMemoryTag::U64); + + // RETURN + EXPECT_EQ(instructions.at(14).op_code, OpCode::RETURN); + EXPECT_EQ(instructions.at(14).operands.size(), 2); + EXPECT_EQ(instructions.at(14).operands.at(0), 0); + EXPECT_EQ(instructions.at(14).operands.at(0), 0); + + auto trace = Execution::gen_trace(instructions, std::vector{}); + + // Find the first row enabling the multiplication selector and pc = 13 + auto row = std::ranges::find_if( + trace.begin(), trace.end(), [](Row r) { return r.avmMini_sel_op_mul == 1 && r.avmMini_pc == 13; }); + EXPECT_EQ(row->avmMini_ic, 244140625); // 5^12 = 244140625 + + auto trace_verif = trace; + validate_trace_proof(std::move(trace)); + + auto circuit_builder = AvmMiniCircuitBuilder(); + circuit_builder.set_trace(std::move(trace_verif)); + auto composer = honk::AvmMiniComposer(); + auto verifier = composer.create_verifier(circuit_builder); + + auto proof = Execution::run_and_prove(bytecode, std::vector{}); + + EXPECT_TRUE(verifier.verify_proof(proof)); +} + +// Negative test detecting an invalid opcode byte. +TEST_F(AvmMiniExecutionTests, invalidOpcode) +{ + std::string bytecode_hex = "00" // ADD + "02" // U16 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "AB" // Invalid opcode byte + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "opcode"); +} + +// Negative test detecting an invalid memmory instruction tag. +TEST_F(AvmMiniExecutionTests, invalidInstructionTag) +{ + std::string bytecode_hex = "00" // ADD + "00" // Wrong type + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "34" // RETURN + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "Instruction tag is invalid"); +} + +// Negative test detecting SET opcode with instruction memory tag set to FF. +TEST_F(AvmMiniExecutionTests, ffInstructionTagSetOpcode) +{ + std::string bytecode_hex = "00" // ADD + "05" // U128 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "27" // SET 39 = 0x27 + "06" // tag FF + "00002344"; // + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "Instruction tag for SET opcode is invalid"); +} + +// Negative test detecting an incomplete instruction: missing instruction tag +TEST_F(AvmMiniExecutionTests, truncatedInstructionNoTag) +{ + std::string bytecode_hex = "00" // ADD + "02" // U16 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "01"; // SUB + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "Instruction tag missing"); +} + +// Negative test detecting an incomplete instruction: instruction tag present but an operand is missing +TEST_F(AvmMiniExecutionTests, truncatedInstructionNoOperand) +{ + std::string bytecode_hex = "00" // ADD + "02" // U16 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "01" // SUB + "04" // U64 + "AB2373E7" // addr a + "FFFFFFBB"; // addr b and missing address for c = a-b + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "Operand is missing"); +} + +} // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp index a5ccfd1076a..f51e4139539 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp @@ -1,7 +1,7 @@ #include "AvmMini_common.test.hpp" - -using namespace tests_avm; +namespace tests_avm { using namespace avm_trace; + class AvmMiniMemoryTests : public ::testing::Test { public: AvmMiniTraceBuilder trace_builder; @@ -32,7 +32,7 @@ class AvmMiniMemoryTests : public ::testing::Test { // The proof must pass and we check that the AVM error is raised. TEST_F(AvmMiniMemoryTests, mismatchedTag) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 98, 12 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 98, 12 }); trace_builder.add(0, 1, 4, AvmMemoryTag::U8); trace_builder.halt(); @@ -79,7 +79,7 @@ TEST_F(AvmMiniMemoryTests, mismatchedTag) // in the memory trace TEST_F(AvmMiniMemoryTests, mLastAccessViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 4, 9 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 4, 9 }); // Memory layout: [4,9,0,0,0,0,....] trace_builder.sub(1, 0, 2, AvmMemoryTag::U8); // [4,9,5,0,0,0.....] @@ -109,7 +109,7 @@ TEST_F(AvmMiniMemoryTests, mLastAccessViolation) // written into memory TEST_F(AvmMiniMemoryTests, readWriteConsistencyValViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 4, 9 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 4, 9 }); // Memory layout: [4,9,0,0,0,0,....] trace_builder.mul(1, 0, 2, AvmMemoryTag::U8); // [4,9,36,0,0,0.....] @@ -139,7 +139,7 @@ TEST_F(AvmMiniMemoryTests, readWriteConsistencyValViolation) // written into memory TEST_F(AvmMiniMemoryTests, readWriteConsistencyTagViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 4, 9 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 4, 9 }); // Memory layout: [4,9,0,0,0,0,....] trace_builder.mul(1, 0, 2, AvmMemoryTag::U8); // [4,9,36,0,0,0.....] @@ -180,7 +180,7 @@ TEST_F(AvmMiniMemoryTests, readUninitializedMemoryViolation) // must raise a VM error. TEST_F(AvmMiniMemoryTests, mismatchedTagErrorViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 98, 12 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 98, 12 }); trace_builder.sub(0, 1, 4, AvmMemoryTag::U8); trace_builder.halt(); @@ -214,7 +214,7 @@ TEST_F(AvmMiniMemoryTests, mismatchedTagErrorViolation) // must not set a VM error. TEST_F(AvmMiniMemoryTests, consistentTagNoErrorViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 84, 7 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 84, 7 }); trace_builder.div(0, 1, 4, AvmMemoryTag::FF); trace_builder.halt(); @@ -236,3 +236,4 @@ TEST_F(AvmMiniMemoryTests, consistentTagNoErrorViolation) EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "MEM_IN_TAG_CONSISTENCY_1"); } +} // namespace tests_avm \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index 4878019b5f0..cccef0861fe 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -2,8 +2,8 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; -export const AVM_OPERAND_BYTE_LENGTH = 4; -export const AVM_OPCODE_BYTE_LENGTH = 1; +export const AVM_OPERAND_BYTE_LENGTH = 4; // Keep in sync with cpp code +export const AVM_OPCODE_BYTE_LENGTH = 1; // Keep in sync with cpp code /** * Opcode base class diff --git a/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts b/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts index 6f3f3709007..97bdfa3181d 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts @@ -1,5 +1,5 @@ /** - * All AVM opcodes. + * All AVM opcodes. (Keep in sync with cpp counterpart code AvmMini_opcode.hpp). * Source: https://yp-aztec.netlify.app/docs/public-vm/instruction-set */ export enum Opcode {