From 4c6820f6359a2db4863502d36b188dd52d2d32b1 Mon Sep 17 00:00:00 2001 From: Ilyas Ridhuan Date: Tue, 5 Mar 2024 20:30:10 +0000 Subject: [PATCH] feat: indirect mem flag deserialisation (#4877) Brings the cpp avm code in sync with the instruction layout + serialisation format of the ts simulator by adding the Indirect flag. Note: The flag isnt yet used in the witgen/prover or in generating the trace --- .../vm/avm_trace/avm_deserialization.cpp | 40 +++++---- .../vm/avm_trace/avm_deserialization.hpp | 5 +- .../vm/avm_trace/avm_execution.cpp | 54 ++++++------ .../vm/avm_trace/avm_instructions.hpp | 2 +- .../barretenberg/vm/avm_trace/avm_opcode.hpp | 2 +- .../vm/tests/avm_execution.test.cpp | 85 ++++++++++++++----- 6 files changed, 120 insertions(+), 68 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_deserialization.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_deserialization.cpp index d2a7daf9bfa..00efac80474 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_deserialization.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_deserialization.cpp @@ -14,10 +14,7 @@ namespace bb::avm_trace { namespace { const std::vector three_operand_format = { - OperandType::TAG, - OperandType::UINT32, - OperandType::UINT32, - OperandType::UINT32, + OperandType::INDIRECT, OperandType::TAG, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, }; // Contrary to TS, the format does not contain the opcode byte which prefixes any instruction. @@ -32,9 +29,9 @@ const std::unordered_map> OPCODE_WIRE_FORMAT = // Compute - Comparators { OpCode::EQ, three_operand_format }, // Compute - Bitwise - { OpCode::NOT, { OperandType::TAG, OperandType::UINT32, OperandType::UINT32 } }, + { OpCode::NOT, { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT32, OperandType::UINT32 } }, // Execution Environment - Calldata - { OpCode::CALLDATACOPY, { OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } }, + { OpCode::CALLDATACOPY, { OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } }, // Machine State - Internal Control Flow { OpCode::JUMP, { OperandType::UINT32 } }, { OpCode::INTERNALCALL, { OperandType::UINT32 } }, @@ -42,12 +39,12 @@ const std::unordered_map> OPCODE_WIRE_FORMAT = // Machine State - Memory // OpCode::SET is handled differently // Control Flow - Contract Calls - { OpCode::RETURN, { OperandType::UINT32, OperandType::UINT32 } }, + { OpCode::RETURN, { OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32 } }, }; const std::unordered_map OPERAND_TYPE_SIZE = { - { OperandType::TAG, 1 }, { OperandType::UINT8, 1 }, { OperandType::UINT16, 2 }, - { OperandType::UINT32, 4 }, { OperandType::UINT64, 8 }, { OperandType::UINT128, 16 }, + { OperandType::INDIRECT, 1 }, { OperandType::TAG, 1 }, { OperandType::UINT8, 1 }, { OperandType::UINT16, 2 }, + { OperandType::UINT32, 4 }, { OperandType::UINT64, 8 }, { OperandType::UINT128, 16 }, }; } // Anonymous namespace @@ -80,7 +77,11 @@ std::vector Deserialization::parse(std::vector const& byte std::vector inst_format; if (opcode == OpCode::SET) { - if (pos == length) { + // Small hack here because of the structure of SET (where Indirect is the first flag). + // Right now pos is pointing to the indirect flag, but we want it to point to the memory tag. + // We cannot increment pos again because we need to read from pos later when parsing the SET opcode + // So we effectively peek at the next pos + if (pos + 1 == length) { throw_or_abort("Operand for SET opcode is missing at position " + std::to_string(pos)); } @@ -89,29 +90,30 @@ std::vector Deserialization::parse(std::vector const& byte static_cast(AvmMemoryTag::U32), static_cast(AvmMemoryTag::U64), static_cast(AvmMemoryTag::U128) }; - uint8_t set_tag_u8 = bytecode.at(pos); + // Peek again here for the mem tag + uint8_t set_tag_u8 = bytecode.at(pos + 1); if (!valid_tags.contains(set_tag_u8)) { - throw_or_abort("Instruction tag for SET opcode is invalid at position " + std::to_string(pos) + + throw_or_abort("Instruction tag for SET opcode is invalid at position " + std::to_string(pos + 1) + " value: " + std::to_string(set_tag_u8)); } auto in_tag = static_cast(set_tag_u8); switch (in_tag) { case AvmMemoryTag::U8: - inst_format = { OperandType::TAG, OperandType::UINT8, OperandType::UINT32 }; + inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT8, OperandType::UINT32 }; break; case AvmMemoryTag::U16: - inst_format = { OperandType::TAG, OperandType::UINT16, OperandType::UINT32 }; + inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT16, OperandType::UINT32 }; break; case AvmMemoryTag::U32: - inst_format = { OperandType::TAG, OperandType::UINT32, OperandType::UINT32 }; + inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT32, OperandType::UINT32 }; break; case AvmMemoryTag::U64: - inst_format = { OperandType::TAG, OperandType::UINT64, OperandType::UINT32 }; + inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT64, OperandType::UINT32 }; break; case AvmMemoryTag::U128: - inst_format = { OperandType::TAG, OperandType::UINT128, OperandType::UINT32 }; + inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT128, OperandType::UINT32 }; break; default: // This branch is guarded above. std::cerr << "This code branch must have been guarded by the tag validation. \n"; @@ -130,6 +132,10 @@ std::vector Deserialization::parse(std::vector const& byte } switch (opType) { + case OperandType::INDIRECT: { + operands.emplace_back(bytecode.at(pos)); + break; + } case OperandType::TAG: { uint8_t tag_u8 = bytecode.at(pos); if (tag_u8 == static_cast(AvmMemoryTag::U0) || tag_u8 > MAX_MEM_TAG) { diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_deserialization.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_deserialization.hpp index 6b58fa299f7..3f25bd9ed82 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_deserialization.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_deserialization.hpp @@ -15,7 +15,8 @@ namespace bb::avm_trace { // Possible types for an instruction's operand in its wire format. (Keep in sync with TS code. // See avm/serialization/instruction_serialization.ts). // Note that the TAG enum value is not supported in TS and is parsed as UINT8. -enum class OperandType : uint8_t { TAG, UINT8, UINT16, UINT32, UINT64, UINT128 }; +// INDIRECT is parsed as UINT8 where the bits represent the operands that have indirect mem access. +enum class OperandType : uint8_t { INDIRECT, TAG, UINT8, UINT16, UINT32, UINT64, UINT128 }; class Deserialization { public: @@ -24,4 +25,4 @@ class Deserialization { static std::vector parse(std::vector const& bytecode); }; -} // namespace bb::avm_trace \ No newline at end of file +} // namespace bb::avm_trace diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp index 81c738a4691..dbd142fb2fd 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp @@ -58,44 +58,46 @@ std::vector Execution::gen_trace(std::vector const& instructio while ((pc = trace_builder.getPc()) < inst_size) { auto inst = instructions.at(pc); + // TODO: We do not yet support the indirect flag. Therefore we do not extract + // inst.operands(0) (i.e. the indirect flag) when processiing the instructions. switch (inst.op_code) { // Compute // Compute - Arithmetic case OpCode::ADD: - trace_builder.op_add(std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), + trace_builder.op_add(std::get(inst.operands.at(2)), std::get(inst.operands.at(3)), - std::get(inst.operands.at(0))); + std::get(inst.operands.at(4)), + std::get(inst.operands.at(1))); break; case OpCode::SUB: - trace_builder.op_sub(std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), + trace_builder.op_sub(std::get(inst.operands.at(2)), std::get(inst.operands.at(3)), - std::get(inst.operands.at(0))); + std::get(inst.operands.at(4)), + std::get(inst.operands.at(1))); break; case OpCode::MUL: - trace_builder.op_mul(std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), + trace_builder.op_mul(std::get(inst.operands.at(2)), std::get(inst.operands.at(3)), - std::get(inst.operands.at(0))); + std::get(inst.operands.at(4)), + std::get(inst.operands.at(1))); break; case OpCode::DIV: - trace_builder.op_div(std::get(inst.operands.at(1)), - std::get(inst.operands.at(2)), + trace_builder.op_div(std::get(inst.operands.at(2)), std::get(inst.operands.at(3)), - std::get(inst.operands.at(0))); + std::get(inst.operands.at(4)), + std::get(inst.operands.at(1))); break; // Compute - Bitwise case OpCode::NOT: - trace_builder.op_not(std::get(inst.operands.at(1)), - std::get(inst.operands.at(3)), - std::get(inst.operands.at(0))); + trace_builder.op_not(std::get(inst.operands.at(2)), + std::get(inst.operands.at(4)), + std::get(inst.operands.at(1))); break; // Execution Environment - Calldata case OpCode::CALLDATACOPY: - trace_builder.calldata_copy(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), + trace_builder.calldata_copy(std::get(inst.operands.at(1)), std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), calldata); break; // Machine State - Internal Control Flow @@ -112,24 +114,25 @@ std::vector Execution::gen_trace(std::vector const& instructio case OpCode::SET: { uint32_t dst_offset = 0; uint128_t val = 0; - AvmMemoryTag in_tag = std::get(inst.operands.at(0)); - dst_offset = std::get(inst.operands.at(2)); + // Skip the indirect flag at index 0; + AvmMemoryTag in_tag = std::get(inst.operands.at(1)); + dst_offset = std::get(inst.operands.at(3)); switch (in_tag) { case AvmMemoryTag::U8: - val = std::get(inst.operands.at(1)); + val = std::get(inst.operands.at(2)); break; case AvmMemoryTag::U16: - val = std::get(inst.operands.at(1)); + val = std::get(inst.operands.at(2)); break; case AvmMemoryTag::U32: - val = std::get(inst.operands.at(1)); + val = std::get(inst.operands.at(2)); break; case AvmMemoryTag::U64: - val = std::get(inst.operands.at(1)); + val = std::get(inst.operands.at(2)); break; case AvmMemoryTag::U128: - val = std::get(inst.operands.at(1)); + val = std::get(inst.operands.at(2)); break; default: break; @@ -140,7 +143,8 @@ std::vector Execution::gen_trace(std::vector const& instructio } // Control Flow - Contract Calls case OpCode::RETURN: - trace_builder.return_op(std::get(inst.operands.at(0)), std::get(inst.operands.at(1))); + // Skip indirect at index 0 + trace_builder.return_op(std::get(inst.operands.at(1)), std::get(inst.operands.at(2))); break; default: break; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_instructions.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_instructions.hpp index 751b7fd3990..c738081367c 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_instructions.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_instructions.hpp @@ -21,4 +21,4 @@ class Instruction { , operands(std::move(operands)){}; }; -} // namespace bb::avm_trace \ No newline at end of file +} // namespace bb::avm_trace diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp index 53fc087b26d..21df8c4ca97 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp @@ -108,4 +108,4 @@ class Bytecode { std::string to_hex(OpCode opcode); -} // namespace bb::avm_trace \ No newline at end of file +} // namespace bb::avm_trace diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/avm_execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/avm_execution.test.cpp index 8658b461255..b180ef83a75 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/avm_execution.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/avm_execution.test.cpp @@ -53,11 +53,13 @@ class AvmExecutionTests : public ::testing::Test { TEST_F(AvmExecutionTests, basicAddReturn) { std::string bytecode_hex = to_hex(OpCode::ADD) + // opcode ADD + "00" // Indirect flag "01" // U8 "00000007" // addr a 7 "00000009" // addr b 9 "00000001" // addr c 1 + to_hex(OpCode::RETURN) + // opcode RETURN + "00" // Indirect flag "00000000" // ret offset 0 "00000000"; // ret size 0 @@ -71,7 +73,8 @@ TEST_F(AvmExecutionTests, basicAddReturn) EXPECT_THAT(instructions.at(0), AllOf(Field(&Instruction::op_code, OpCode::ADD), Field(&Instruction::operands, - ElementsAre(VariantWith(AvmMemoryTag::U8), + ElementsAre(VariantWith(0), + VariantWith(AvmMemoryTag::U8), VariantWith(7), VariantWith(9), VariantWith(1))))); @@ -79,7 +82,8 @@ TEST_F(AvmExecutionTests, basicAddReturn) // RETURN EXPECT_THAT(instructions.at(1), AllOf(Field(&Instruction::op_code, OpCode::RETURN), - Field(&Instruction::operands, ElementsAre(VariantWith(0), VariantWith(0))))); + Field(&Instruction::operands, + ElementsAre(VariantWith(0), VariantWith(0), VariantWith(0))))); auto trace = Execution::gen_trace(instructions); gen_proof_and_validate(bytecode, std::move(trace), {}); @@ -89,19 +93,23 @@ TEST_F(AvmExecutionTests, basicAddReturn) TEST_F(AvmExecutionTests, setAndSubOpcodes) { std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + "00" // Indirect flag "02" // U16 "B813" // val 47123 "000000AA" // dst_offset 170 + to_hex(OpCode::SET) + // opcode SET + "00" // Indirect flag "02" // U16 "9103" // val 37123 "00000033" // dst_offset 51 + to_hex(OpCode::SUB) + // opcode SUB + "00" // Indirect flag "02" // U16 "000000AA" // addr a "00000033" // addr b "00000001" // addr c 1 + to_hex(OpCode::RETURN) + // opcode RETURN + "00" // Indirect flag "00000000" // ret offset 0 "00000000"; // ret size 0 @@ -114,7 +122,8 @@ TEST_F(AvmExecutionTests, setAndSubOpcodes) EXPECT_THAT(instructions.at(0), AllOf(Field(&Instruction::op_code, OpCode::SET), Field(&Instruction::operands, - ElementsAre(VariantWith(AvmMemoryTag::U16), + ElementsAre(VariantWith(0), + VariantWith(AvmMemoryTag::U16), VariantWith(47123), VariantWith(170))))); @@ -122,7 +131,8 @@ TEST_F(AvmExecutionTests, setAndSubOpcodes) EXPECT_THAT(instructions.at(1), AllOf(Field(&Instruction::op_code, OpCode::SET), Field(&Instruction::operands, - ElementsAre(VariantWith(AvmMemoryTag::U16), + ElementsAre(VariantWith(0), + VariantWith(AvmMemoryTag::U16), VariantWith(37123), VariantWith(51))))); @@ -130,7 +140,8 @@ TEST_F(AvmExecutionTests, setAndSubOpcodes) EXPECT_THAT(instructions.at(2), AllOf(Field(&Instruction::op_code, OpCode::SUB), Field(&Instruction::operands, - ElementsAre(VariantWith(AvmMemoryTag::U16), + ElementsAre(VariantWith(0), + VariantWith(AvmMemoryTag::U16), VariantWith(170), VariantWith(51), VariantWith(1))))); @@ -153,23 +164,27 @@ TEST_F(AvmExecutionTests, setAndSubOpcodes) TEST_F(AvmExecutionTests, powerWithMulOpcodes) { std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + "00" // Indirect flag "04" // U64 "00000000" // val 5 higher 32 bits "00000005" // val 5 lower 32 bits "00000000" // dst_offset 0 + to_hex(OpCode::SET) + // opcode SET + "00" // Indirect flag "04" // U64 "00000000" // val 1 higher 32 bits "00000001" // val 1 lower 32 bits "00000001"; // dst_offset 1 std::string const mul_hex = to_hex(OpCode::MUL) + // opcode MUL + "00" // Indirect flag "04" // U64 "00000000" // addr a "00000001" // addr b "00000001"; // addr c 1 std::string const ret_hex = to_hex(OpCode::RETURN) + // opcode RETURN + "00" // Indirect flag "00000000" // ret offset 0 "00000000"; // ret size 0 @@ -188,7 +203,8 @@ TEST_F(AvmExecutionTests, powerWithMulOpcodes) EXPECT_THAT(instructions.at(2), AllOf(Field(&Instruction::op_code, OpCode::MUL), Field(&Instruction::operands, - ElementsAre(VariantWith(AvmMemoryTag::U64), + ElementsAre(VariantWith(0), + VariantWith(AvmMemoryTag::U64), VariantWith(0), VariantWith(1), VariantWith(1))))); @@ -197,7 +213,8 @@ TEST_F(AvmExecutionTests, powerWithMulOpcodes) EXPECT_THAT(instructions.at(13), AllOf(Field(&Instruction::op_code, OpCode::MUL), Field(&Instruction::operands, - ElementsAre(VariantWith(AvmMemoryTag::U64), + ElementsAre(VariantWith(0), + VariantWith(AvmMemoryTag::U64), VariantWith(0), VariantWith(1), VariantWith(1))))); @@ -205,7 +222,8 @@ TEST_F(AvmExecutionTests, powerWithMulOpcodes) // RETURN EXPECT_THAT(instructions.at(14), AllOf(Field(&Instruction::op_code, OpCode::RETURN), - Field(&Instruction::operands, ElementsAre(VariantWith(0), VariantWith(0))))); + Field(&Instruction::operands, + ElementsAre(VariantWith(0), VariantWith(0), VariantWith(0))))); auto trace = Execution::gen_trace(instructions); @@ -229,20 +247,24 @@ TEST_F(AvmExecutionTests, powerWithMulOpcodes) TEST_F(AvmExecutionTests, simpleInternalCall) { std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + "00" // Indirect flag "03" // U32 "0D3D2518" // val 222111000 = 0xD3D2518 "00000004" // dst_offset 4 "25" // INTERNALCALL 37 "00000004" // jmp_dest + to_hex(OpCode::ADD) + // opcode ADD + "00" // Indirect flag "03" // U32 "00000004" // addr a 4 "00000007" // addr b 7 "00000009" // addr c9 + to_hex(OpCode::RETURN) + // opcode RETURN + "00" // Indirect flag "00000000" // ret offset 0 "00000000" // ret size 0 + to_hex(OpCode::SET) + // opcode SET + "00" // Indirect flag "03" // U32 "075BCD15" // val 123456789 = 0x75BCD15 "00000007" // dst_offset 7 @@ -301,17 +323,20 @@ TEST_F(AvmExecutionTests, nestedInternalCalls) auto setInstructionHex = [](std::string const& val, std::string const& dst_offset) { return to_hex(OpCode::SET) // opcode SET + + "00" // Indirect flag + "01" // U8 + val + "000000" + dst_offset; }; - const std::string tag_address_arguments = "01" // U8 + const std::string tag_address_arguments = "00" // Indirect Flag + "01" // U8 "00000002" // addr a 2 "00000003" // addr b 3 "00000002"; // addr c 2 const std::string return_instruction_hex = to_hex(OpCode::RETURN) // opcode RETURN - + "00000000" // ret offset 0 + + "00" // Indirect flag + "00000000" // ret offset 0 "00000000"; // ret size 0 const std::string bytecode_f1 = to_hex(OpCode::ADD) + tag_address_arguments + to_hex(OpCode::INTERNALRETURN); @@ -366,22 +391,26 @@ TEST_F(AvmExecutionTests, nestedInternalCalls) TEST_F(AvmExecutionTests, jumpAndCalldatacopy) { std::string bytecode_hex = to_hex(OpCode::CALLDATACOPY) + // opcode CALLDATACOPY (no in tag) + "00" // Indirect flag "00000000" // cd_offset "00000002" // copy_size "0000000A" // dst_offset // M[10] = 13, M[11] = 156 + to_hex(OpCode::JUMP) + // opcode JUMP "00000003" // jmp_dest (DIV located at 3) + to_hex(OpCode::SUB) + // opcode SUB + "00" // Indirect flag "06" // FF "0000000B" // addr 11 "0000000A" // addr 10 "00000001" // addr c 1 (If executed would be 156 - 13 = 143) + to_hex(OpCode::DIV) + // opcode DIV + "00" // Indirect flag "06" // FF "0000000B" // addr 11 "0000000A" // addr 10 "00000001" // addr c 1 (156 / 13 = 12) + to_hex(OpCode::RETURN) + // opcode RETURN + "00" // Indirect flag "00000000" // ret offset 0 "00000000" // ret size 0 ; @@ -394,11 +423,13 @@ TEST_F(AvmExecutionTests, jumpAndCalldatacopy) // We test parsing steps for CALLDATACOPY and JUMP. // CALLDATACOPY - EXPECT_THAT( - instructions.at(0), - AllOf(Field(&Instruction::op_code, OpCode::CALLDATACOPY), - Field(&Instruction::operands, - ElementsAre(VariantWith(0), VariantWith(2), VariantWith(10))))); + EXPECT_THAT(instructions.at(0), + AllOf(Field(&Instruction::op_code, OpCode::CALLDATACOPY), + Field(&Instruction::operands, + ElementsAre(VariantWith(0), + VariantWith(0), + VariantWith(2), + VariantWith(10))))); // JUMP EXPECT_THAT(instructions.at(1), @@ -430,6 +461,7 @@ TEST_F(AvmExecutionTests, jumpAndCalldatacopy) TEST_F(AvmExecutionTests, invalidOpcode) { std::string bytecode_hex = to_hex(OpCode::ADD) + // opcode ADD + "00" // Indirect flag "02" // U16 "00000007" // addr a 7 "00000009" // addr b 9 @@ -446,11 +478,13 @@ TEST_F(AvmExecutionTests, invalidOpcode) TEST_F(AvmExecutionTests, invalidInstructionTag) { std::string bytecode_hex = to_hex(OpCode::ADD) + // opcode ADD + "00" // Indirect flag "00" // Wrong type "00000007" // addr a 7 "00000009" // addr b 9 "00000001" // addr c 1 + to_hex(OpCode::RETURN) + // opcode RETURN + "00" // Indirect flag "00000000" // ret offset 0 "00000000"; // ret size 0 @@ -462,11 +496,13 @@ TEST_F(AvmExecutionTests, invalidInstructionTag) TEST_F(AvmExecutionTests, ffInstructionTagSetOpcode) { std::string bytecode_hex = "00" // ADD + "00" // Indirect flag "05" // U128 "00000007" // addr a 7 "00000009" // addr b 9 "00000001" // addr c 1 + to_hex(OpCode::SET) + // opcode SET + "00" // Indirect flag "06" // tag FF "00002344"; // @@ -477,12 +513,14 @@ TEST_F(AvmExecutionTests, ffInstructionTagSetOpcode) // Negative test detecting SET opcode without any operand. TEST_F(AvmExecutionTests, SetOpcodeNoOperand) { - std::string bytecode_hex = "00" // ADD - "05" // U128 - "00000007" // addr a 7 - "00000009" // addr b 9 - "00000001" // addr c 1 - + to_hex(OpCode::SET); // opcode SET + std::string bytecode_hex = "00" // ADD + "00" // Indirect flag + "05" // U128 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + + to_hex(OpCode::SET) + // opcode SET + "00"; // Indirect flag auto bytecode = hex_to_bytes(bytecode_hex); EXPECT_THROW_WITH_MESSAGE(Deserialization::parse(bytecode), "Operand for SET opcode is missing"); @@ -492,6 +530,7 @@ TEST_F(AvmExecutionTests, SetOpcodeNoOperand) TEST_F(AvmExecutionTests, truncatedInstructionNoTag) { std::string bytecode_hex = to_hex(OpCode::ADD) + // opcode ADD + "00" // Indirect flag "02" // U16 "00000007" // addr a 7 "00000009" // addr b 9 @@ -506,11 +545,13 @@ TEST_F(AvmExecutionTests, truncatedInstructionNoTag) TEST_F(AvmExecutionTests, truncatedInstructionNoOperand) { std::string bytecode_hex = to_hex(OpCode::ADD) + // opcode ADD + "00" // Indirect flag "02" // U16 "00000007" // addr a 7 "00000009" // addr b 9 "00000001" // addr c 1 + to_hex(OpCode::SUB) + // opcode SUB + "00" // Indirect flag "04" // U64 "AB2373E7" // addr a "FFFFFFBB"; // addr b and missing address for c = a-b @@ -519,4 +560,4 @@ TEST_F(AvmExecutionTests, truncatedInstructionNoOperand) EXPECT_THROW_WITH_MESSAGE(Deserialization::parse(bytecode), "Operand is missing"); } -} // namespace tests_avm \ No newline at end of file +} // namespace tests_avm