diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/control_flow.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/control_flow.cpp index 6ebd989dfd5a..92216247e7af 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/control_flow.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/control_flow.cpp @@ -145,6 +145,21 @@ void ControlFlow::process_finalize_with_return(FinalizeWithReturn instruction) current_block = non_terminated_blocks.at(0); } +void ControlFlow::process_finalize_with_revert(FinalizeWithRevert instruction) +{ + if (this->current_block->terminator_type != TerminatorType::NONE) { + return; + } + current_block->finalize_with_revert(instruction.revert_options.return_size, + instruction.revert_options.return_value_tag, + instruction.revert_options.return_value_offset_index); + std::vector non_terminated_blocks = get_non_terminated_blocks(); + if (non_terminated_blocks.size() == 0) { + return; + } + current_block = non_terminated_blocks.at(0); +} + void ControlFlow::process_switch_to_non_terminated_block(SwitchToNonTerminatedBlock instruction) { std::vector non_terminated_blocks = get_non_terminated_blocks(); @@ -214,6 +229,7 @@ void ControlFlow::process_cfg_instruction(CFGInstruction instruction) [&](JumpToBlock arg) { process_jump_to_block(arg); }, [&](JumpIfToBlock arg) { process_jump_if_to_block(arg); }, [&](FinalizeWithReturn arg) { process_finalize_with_return(arg); }, + [&](FinalizeWithRevert arg) { process_finalize_with_revert(arg); }, [&](SwitchToNonTerminatedBlock arg) { process_switch_to_non_terminated_block(arg); }, [&](InsertInternalCall arg) { process_insert_internal_call(arg); } }, instruction); @@ -239,7 +255,8 @@ int predict_block_size(ProgramBlock* block) auto bytecode_length = static_cast(create_bytecode(block->get_instructions()).size()); switch (block->terminator_type) { case TerminatorType::RETURN: - return bytecode_length; // finalized with return, already counted + case TerminatorType::REVERT: + return bytecode_length; // finalized with return/revert, already counted case TerminatorType::JUMP: return bytecode_length + JMP_SIZE; // finalized with jump case TerminatorType::JUMP_IF: { @@ -263,9 +280,9 @@ int predict_block_size(ProgramBlock* block) return bytecode_length + JMP_IF_SIZE + JMP_SIZE; // finalized with jumpi } default: - throw std::runtime_error("Predict block size: Every block should be terminated with return, jump, or jumpi, " - "got " + - std::to_string(static_cast(block->terminator_type))); + throw std::runtime_error( + "Predict block size: Every block should be terminated with return, revert, jump, or jumpi, got " + + std::to_string(static_cast(block->terminator_type))); } throw std::runtime_error("Unreachable"); } @@ -315,6 +332,9 @@ std::vector ControlFlow::build_bytecode(const ReturnOptions& return_opt case TerminatorType::RETURN: // finalized with return // already terminated with return break; + case TerminatorType::REVERT: // finalized with revert + // already terminated with revert + break; case TerminatorType::JUMP: { // finalized with jump ProgramBlock* target_block = block->successors.at(0); size_t target_block_idx = find_block_idx(target_block, blocks); @@ -347,7 +367,7 @@ std::vector ControlFlow::build_bytecode(const ReturnOptions& return_opt } default: throw std::runtime_error( - "Inject terminators: Every block should be terminated with return, jump, or jumpi"); + "Inject terminators: Every block should be terminated with return, revert, jump, or jumpi"); } block_bytecodes.push_back(create_bytecode(instructions)); } diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/control_flow.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/control_flow.hpp index 09707e2c5ace..d646affcf2aa 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/control_flow.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/control_flow.hpp @@ -64,6 +64,12 @@ struct FinalizeWithReturn { MSGPACK_FIELDS(return_options); }; +/// @brief finalizes the current block with Revert and switches to the first non-terminated block +struct FinalizeWithRevert { + ReturnOptions revert_options; + MSGPACK_FIELDS(revert_options); +}; + /// @brief switches to the non-terminated block with the chosen index struct SwitchToNonTerminatedBlock { uint16_t non_terminated_block_idx; @@ -83,6 +89,7 @@ using CFGInstruction = std::variant; template struct overloaded_cfg_instruction : Ts... { @@ -111,6 +118,10 @@ inline std::ostream& operator<<(std::ostream& os, const CFGInstruction& instruct os << "FinalizeWithReturn " << arg.return_options.return_size << " " << arg.return_options.return_value_tag << " " << arg.return_options.return_value_offset_index; }, + [&](FinalizeWithRevert arg) { + os << "FinalizeWithRevert " << arg.revert_options.return_size << " " + << arg.revert_options.return_value_tag << " " << arg.revert_options.return_value_offset_index; + }, [&](SwitchToNonTerminatedBlock arg) { os << "SwitchToNonTerminatedBlock " << arg.non_terminated_block_idx; }, @@ -160,6 +171,10 @@ class ControlFlow { /// @param instruction the instruction to process void process_finalize_with_return(FinalizeWithReturn instruction); + /// @brief terminates the current block with Revert and switches to the first non-terminated block + /// @param instruction the instruction to process + void process_finalize_with_revert(FinalizeWithRevert instruction); + /// @brief switches to the non-terminated block with the chosen index /// @param instruction the instruction to process void process_switch_to_non_terminated_block(SwitchToNonTerminatedBlock instruction); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp index 270882952cea..c04940d9897a 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp @@ -1586,6 +1586,34 @@ void ProgramBlock::finalize_with_return(uint8_t return_size, instructions.push_back(return_instruction); } +void ProgramBlock::finalize_with_revert(uint8_t revert_size, + MemoryTagWrapper revert_value_tag, + uint16_t revert_value_offset_index) +{ + this->terminator_type = TerminatorType::REVERT; + + auto revert_addr = memory_manager.get_memory_offset_16(revert_value_tag.value, revert_value_offset_index); + if (!revert_addr.has_value()) { + revert_addr = std::optional(0); + } + + // Once we do more of the randomness in Instruction selection, revert_size_offset we shouldnt need to hardcode + uint16_t revert_size_offset = 5U; + // Ensure operands are created as U16 to match wire format (UINT16) + auto set_size_instruction = bb::avm2::testing::InstructionBuilder(bb::avm2::WireOpCode::SET_16) + .operand(revert_size_offset) + .operand(bb::avm2::MemoryTag::U32) + .operand(static_cast(revert_size)) + .build(); + instructions.push_back(set_size_instruction); + // REVERT_16 expects UINT16 operands, ensure we cast to uint16_t explicitly + auto revert_instruction = bb::avm2::testing::InstructionBuilder(bb::avm2::WireOpCode::REVERT_16) + .operand(static_cast(revert_size_offset)) + .operand(revert_addr.value()) + .build(); + instructions.push_back(revert_instruction); +} + void ProgramBlock::finalize_with_jump(ProgramBlock* target_block, bool copy_memory_manager) { this->terminator_type = TerminatorType::JUMP; diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.hpp index a442b4e75408..360b03e0bdde 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.hpp @@ -29,6 +29,7 @@ enum class TerminatorType { RETURN, + REVERT, JUMP, JUMP_IF, NONE, @@ -139,6 +140,13 @@ class ProgramBlock { MemoryTagWrapper return_value_tag, uint16_t return_value_offset_index); + /// @brief finalize the program block with a revert instruction + /// Similar to finalize_with_return but uses REVERT opcode instead. + /// Sets the terminator type to REVERT. + void finalize_with_revert(uint8_t revert_size, + MemoryTagWrapper revert_value_tag, + uint16_t revert_value_offset_index); + /// @brief finalize the block with a jump /// Sets the terminator type to JUMP, adds the target block to the successors and the current block to the /// predecessors. diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp index 5807264211b2..13ae8b732692 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp @@ -555,11 +555,12 @@ enum class CFGInstructionGenerationOptions { JumpToBlock, JumpIfToBlock, FinalizeWithReturn, + FinalizeWithRevert, SwitchToNonTerminatedBlock, InsertInternalCall, }; -using CFGInstructionGenerationConfig = WeightedSelectionConfig; +using CFGInstructionGenerationConfig = WeightedSelectionConfig; constexpr CFGInstructionGenerationConfig BASIC_CFG_INSTRUCTION_GENERATION_CONFIGURATION = CFGInstructionGenerationConfig({ @@ -569,6 +570,7 @@ constexpr CFGInstructionGenerationConfig BASIC_CFG_INSTRUCTION_GENERATION_CONFIG { CFGInstructionGenerationOptions::JumpToBlock, 15 }, { CFGInstructionGenerationOptions::JumpIfToBlock, 15 }, { CFGInstructionGenerationOptions::FinalizeWithReturn, 7 }, + { CFGInstructionGenerationOptions::FinalizeWithRevert, 3 }, { CFGInstructionGenerationOptions::SwitchToNonTerminatedBlock, 8 }, { CFGInstructionGenerationOptions::InsertInternalCall, 3 }, }); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/control_flow/control_flow_vec.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/control_flow/control_flow_vec.cpp index be0ed13e0ea6..2143ab12cc75 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/control_flow/control_flow_vec.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/control_flow/control_flow_vec.cpp @@ -58,6 +58,11 @@ void mutate_finalize_with_return(FinalizeWithReturn& instr, std::mt19937_64& rng mutate_return_options(instr.return_options, rng, BASIC_RETURN_OPTIONS_MUTATION_CONFIGURATION); } +void mutate_finalize_with_revert(FinalizeWithRevert& instr, std::mt19937_64& rng) +{ + mutate_return_options(instr.revert_options, rng, BASIC_RETURN_OPTIONS_MUTATION_CONFIGURATION); +} + void mutate_switch_to_non_terminated_block(SwitchToNonTerminatedBlock& instr, std::mt19937_64& rng) { mutate_uint16_t(instr.non_terminated_block_idx, rng, BASIC_UINT16_T_MUTATION_CONFIGURATION); @@ -86,6 +91,10 @@ CFGInstruction generate_cfg_instruction(std::mt19937_64& rng) return FinalizeWithReturn(ReturnOptions(generate_random_uint8(rng), generate_memory_tag(rng, BASIC_MEMORY_TAG_GENERATION_CONFIGURATION), generate_random_uint16(rng))); + case CFGInstructionGenerationOptions::FinalizeWithRevert: + return FinalizeWithRevert(ReturnOptions(generate_random_uint8(rng), + generate_memory_tag(rng, BASIC_MEMORY_TAG_GENERATION_CONFIGURATION), + generate_random_uint16(rng))); case CFGInstructionGenerationOptions::SwitchToNonTerminatedBlock: return SwitchToNonTerminatedBlock(generate_random_uint16(rng)); case CFGInstructionGenerationOptions::InsertInternalCall: @@ -102,6 +111,7 @@ void mutate_cfg_instruction(CFGInstruction& cfg_instruction, std::mt19937_64& rn [&](JumpToBlock& instr) { mutate_jump_to_block(instr, rng); }, [&](JumpIfToBlock& instr) { mutate_jump_if_to_block(instr, rng); }, [&](FinalizeWithReturn& instr) { mutate_finalize_with_return(instr, rng); }, + [&](FinalizeWithRevert& instr) { mutate_finalize_with_revert(instr, rng); }, [&](SwitchToNonTerminatedBlock& instr) { mutate_switch_to_non_terminated_block(instr, rng); }, [&](InsertInternalCall& instr) { mutate_insert_internal_call(instr, rng); } }, cfg_instruction);