diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6a76308239..16c9242bc1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -46,6 +46,7 @@ jobs: cairo_bench_programs, cairo_proof_programs, cairo_test_programs, + cairo_stwo_exclusive_programs, cairo_1_test_contracts, cairo_2_test_contracts, ] @@ -124,6 +125,12 @@ jobs: path: ${{ env.CAIRO_PROGRAMS_PATH }} key: cairo_test_programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} fail-on-cache-miss: true + - name: Fetch cairo stwo exclusive programs + uses: actions/cache/restore@v3 + with: + path: ${{ env.CAIRO_PROGRAMS_PATH }} + key: cairo_stwo_exclusive_programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} + fail-on-cache-miss: true - name: Fetch proof programs uses: actions/cache/restore@v3 with: @@ -486,6 +493,9 @@ jobs: - program-target: cairo_test_programs programs-dir: cairo_programs extra-args: '--cairo_pie_output {program}.rs.pie.zip' + - program-target: cairo_stwo_exclusive_programs + programs-dir: cairo_programs + extra-args: '--cairo_pie_output {program}.rs.pie.zip' name: Compute memory and execution traces with cairo-vm needs: [ build-programs, build-release ] runs-on: ubuntu-22.04 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f40a256a9..5291a4a615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: implement `Blake2s` opcode in VM [#1927](https://github.com/lambdaclass/cairo-vm/pull/1927) + * refactor: Limit ret opcode decodeing to Cairo0's standards. [#1925](https://github.com/lambdaclass/cairo-vm/pull/1925) #### [2.0.0-rc4] - 2025-01-23 diff --git a/Makefile b/Makefile index 9b60105ab3..5d772dbb40 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,10 @@ CAIRO_RS_MEM:=$(patsubst $(TEST_DIR)/%.json, $(TEST_DIR)/%.rs.memory, $(COMPILED CAIRO_RS_TRACE:=$(patsubst $(TEST_DIR)/%.json, $(TEST_DIR)/%.rs.trace, $(COMPILED_TESTS)) CAIRO_RS_PIE:=$(patsubst $(TEST_DIR)/%.json, $(TEST_DIR)/%.rs.pie.zip, $(COMPILED_TESTS)) +STWO_EXCLUSIVE_DIR=cairo_programs/stwo_exclusive_programs +STWO_EXCLUSIVE_FILES:=$(wildcard $(STWO_EXCLUSIVE_DIR)/*.cairo) +COMPILED_STWO_EXCLUSIVE_TESTS:=$(patsubst $(STWO_EXCLUSIVE_DIR)/%.cairo, $(STWO_EXCLUSIVE_DIR)/%.json, $(STWO_EXCLUSIVE_FILES)) + BENCH_DIR=cairo_programs/benchmarks BENCH_FILES:=$(wildcard $(BENCH_DIR)/*.cairo) COMPILED_BENCHES:=$(patsubst $(BENCH_DIR)/%.cairo, $(BENCH_DIR)/%.json, $(BENCH_FILES)) @@ -249,6 +253,7 @@ check: cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) $(COMPILED_PRINT_TESTS) $(COMPILED_MOD_BUILTIN_TESTS) $(COMPILED_SECP_CAIRO0_HINTS) $(COMPILED_KZG_DA_CAIRO0_HINTS) cairo_proof_programs: $(COMPILED_PROOF_TESTS) $(COMPILED_MOD_BUILTIN_PROOF_TESTS) +cairo_stwo_exclusive_programs: $(COMPILED_STWO_EXCLUSIVE_TESTS) cairo_bench_programs: $(COMPILED_BENCHES) cairo_1_test_contracts: $(CAIRO_1_COMPILED_CASM_CONTRACTS) cairo_2_test_contracts: $(CAIRO_2_COMPILED_CASM_CONTRACTS) diff --git a/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo b/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo new file mode 100644 index 0000000000..0e996d82fd --- /dev/null +++ b/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo @@ -0,0 +1,137 @@ +%builtins range_check + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.cairo_blake2s.blake2s import blake2s_inner, INPUT_BLOCK_BYTES, STATE_SIZE_FELTS, INPUT_BLOCK_FELTS + +const COUNTER = 128; + +// Tests the Blake2s opcode runner using a preexisting implementation within the repo as reference. +// The initial state, a random message of 68 bytes and counter are used as input. +// Both the opcode and the reference implementation are run on the same inputs and then their outputs are compared. +// Before comparing the outputs, it is verified that the opcode runner has written the output to the correct location. +func main{range_check_ptr}() { + alloc_locals; + + let (local random_message) = alloc(); + assert random_message[0] = 930933030; + assert random_message[1] = 1766240503; + assert random_message[2] = 3660871006; + assert random_message[3] = 388409270; + assert random_message[4] = 1948594622; + assert random_message[5] = 3119396969; + assert random_message[6] = 3924579183; + assert random_message[7] = 2089920034; + assert random_message[8] = 3857888532; + assert random_message[9] = 929304360; + assert random_message[10] = 1810891574; + assert random_message[11] = 860971754; + assert random_message[12] = 1822893775; + assert random_message[13] = 2008495810; + assert random_message[14] = 2958962335; + assert random_message[15] = 2340515744; + assert random_message[16] = 1111307871; + + let (local blake2s_ptr_start) = alloc(); + let blake2s_ptr = blake2s_ptr_start; + // Set the initial state to IV (IV[0] is modified). + assert blake2s_ptr[0] = 0x6B08E647; // IV[0] ^ 0x01010020 (config: no key, 32 bytes output). + assert blake2s_ptr[1] = 0xBB67AE85; + assert blake2s_ptr[2] = 0x3C6EF372; + assert blake2s_ptr[3] = 0xA54FF53A; + assert blake2s_ptr[4] = 0x510E527F; + assert blake2s_ptr[5] = 0x9B05688C; + assert blake2s_ptr[6] = 0x1F83D9AB; + assert blake2s_ptr[7] = 0x5BE0CD19; + static_assert STATE_SIZE_FELTS == 8; + let blake2s_ptr = blake2s_ptr + STATE_SIZE_FELTS; + + let (cairo_output) = blake2s_inner{range_check_ptr=range_check_ptr, blake2s_ptr=blake2s_ptr}(data=random_message, n_bytes=INPUT_BLOCK_BYTES+4, counter=COUNTER); + + let (local initial_state) = alloc(); + assert initial_state[0] = 0x6B08E647; + assert initial_state[1] = 0xBB67AE85; + assert initial_state[2] = 0x3C6EF372; + assert initial_state[3] = 0xA54FF53A; + assert initial_state[4] = 0x510E527F; + assert initial_state[5] = 0x9B05688C; + assert initial_state[6] = 0x1F83D9AB; + assert initial_state[7] = 0x5BE0CD19; + assert initial_state[8] = COUNTER; + + let (local vm_output_start) = alloc(); + + run_blake2s( + dst=vm_output_start, + op0=initial_state, + op1=random_message, + ); + + tempvar check_nonempty = vm_output_start[0]; + tempvar check_nonempty = vm_output_start[1]; + tempvar check_nonempty = vm_output_start[2]; + tempvar check_nonempty = vm_output_start[3]; + tempvar check_nonempty = vm_output_start[4]; + tempvar check_nonempty = vm_output_start[5]; + tempvar check_nonempty = vm_output_start[6]; + tempvar check_nonempty = vm_output_start[7]; + tempvar check_nonempty = vm_output_start[8]; + + let relevant_output_start = blake2s_ptr_start+INPUT_BLOCK_FELTS+2+STATE_SIZE_FELTS; + + assert vm_output_start[0] = relevant_output_start[0]; + assert vm_output_start[1] = relevant_output_start[1]; + assert vm_output_start[2] = relevant_output_start[2]; + assert vm_output_start[3] = relevant_output_start[3]; + assert vm_output_start[4] = relevant_output_start[4]; + assert vm_output_start[5] = relevant_output_start[5]; + assert vm_output_start[6] = relevant_output_start[6]; + assert vm_output_start[7] = relevant_output_start[7]; + assert vm_output_start[8] = [relevant_output_start-2]; + + return (); +} + +// Forces the runner to execute the Blake2s with the given operands. +// op0 is a pointer to an array of 9 felts, 8 as u32 integers of the state and 1 as a u32 of the counter. +// op1 is a pointer to an array of 16 felts as u32 integers of the messsage. +// dst is a pointer to an array of 9 felts, 8 as u32 integers of the output state and 1 as a u32 of the updated counter. +// The values of said pointers are stored within addresses fp-5, fp-4 and fp-3 respectively. +// An instruction encoding is built from offsets -5, -4, -3 and flags which are all 0 except for +// those denoting uses of fp as the base for operand addresses and flag_opcode_blake (16th flag). +// The instruction is then written to [pc] and the runner is forced to execute Blake2s. +func run_blake2s( + dst: felt*, + op0: felt*, + op1: felt*, +) { + let offset0 = (2**15)-5; + let offset1 = (2**15)-4; + let offset2 = (2**15)-3; + + static_assert dst == [fp -5]; + static_assert op0 == [fp -4]; + static_assert op1 == [fp -3]; + + let flag_dst_base_fp = 1; + let flag_op0_base_fp = 1; + let flag_op1_imm = 0; + let flag_op1_base_fp = 1; + let flag_op1_base_ap = 0; + let flag_res_add = 0; + let flag_res_mul = 0; + let flag_PC_update_jump = 0; + let flag_PC_update_jump_rel = 0; + let flag_PC_update_jnz = 0; + let flag_ap_update_add = 0; + let flag_ap_update_add_1 = 0; + let flag_opcode_call = 0; + let flag_opcode_ret = 0; + let flag_opcode_assert_eq = 0; + let flag_opcode_blake2s = 1; + + let flag_num = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3)+flag_opcode_blake2s*(2**15); + let instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num*(2**48); + static_assert instruction_num==9226608988349300731; + dw 9226608988349300731; + return (); +} diff --git a/vm/src/tests/cairo_run_test.rs b/vm/src/tests/cairo_run_test.rs index d847852929..c757217416 100644 --- a/vm/src/tests/cairo_run_test.rs +++ b/vm/src/tests/cairo_run_test.rs @@ -568,6 +568,14 @@ fn blake2s_integration_tests() { run_program_simple(program_data.as_slice()); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn blake2s_opcode_test() { + let program_data = + include_bytes!("../../../cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.json"); + run_program_simple(program_data.as_slice()); +} + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn relocate_segments() { diff --git a/vm/src/types/instruction.rs b/vm/src/types/instruction.rs index 133c691302..9bed2f42e3 100644 --- a/vm/src/types/instruction.rs +++ b/vm/src/types/instruction.rs @@ -27,6 +27,7 @@ pub struct Instruction { pub ap_update: ApUpdate, pub fp_update: FpUpdate, pub opcode: Opcode, + pub opcode_extension: OpcodeExtension, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -76,6 +77,12 @@ pub enum Opcode { Ret, } +#[derive(Clone, Debug, Copy, PartialEq, Eq)] +pub enum OpcodeExtension { + Stone, + Blake, +} + impl Instruction { pub fn size(&self) -> usize { match self.op1_addr { diff --git a/vm/src/vm/context/run_context.rs b/vm/src/vm/context/run_context.rs index d5de5d41f5..e2fdb9e633 100644 --- a/vm/src/vm/context/run_context.rs +++ b/vm/src/vm/context/run_context.rs @@ -106,7 +106,7 @@ mod tests { use super::*; use crate::relocatable; use crate::stdlib::string::ToString; - use crate::types::instruction::{ApUpdate, FpUpdate, Opcode, PcUpdate, Res}; + use crate::types::instruction::{ApUpdate, FpUpdate, Opcode, OpcodeExtension, PcUpdate, Res}; use crate::utils::test_utils::mayberelocatable; use crate::vm::errors::memory_errors::MemoryError; use crate::Felt252; @@ -130,6 +130,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -158,6 +159,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -187,6 +189,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -215,6 +218,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -243,6 +247,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -271,6 +276,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -299,6 +305,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -327,6 +334,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -358,6 +366,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -388,6 +397,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -420,6 +430,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { diff --git a/vm/src/vm/decoding/decoder.rs b/vm/src/vm/decoding/decoder.rs index 8a018980f4..ff8361b0a4 100644 --- a/vm/src/vm/decoding/decoder.rs +++ b/vm/src/vm/decoding/decoder.rs @@ -1,16 +1,15 @@ use crate::{ types::instruction::{ - ApUpdate, FpUpdate, Instruction, Op1Addr, Opcode, PcUpdate, Register, Res, + ApUpdate, FpUpdate, Instruction, Op1Addr, Opcode, OpcodeExtension, PcUpdate, Register, Res, }, vm::errors::vm_errors::VirtualMachineError, }; -// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg -// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 +// opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg +// 16 15 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 -/// Decodes an instruction. The encoding is little endian, so flags go from bit 63 to 48. +/// Decodes an instruction. The encoding is little endian, so flags go from bit 64 to 48. pub fn decode_instruction(encoded_instr: u64) -> Result { - const HIGH_BIT: u64 = 1u64 << 63; const DST_REG_MASK: u64 = 0x0001; const DST_REG_OFF: u64 = 0; const OP0_REG_MASK: u64 = 0x0002; @@ -25,6 +24,8 @@ pub fn decode_instruction(encoded_instr: u64) -> Result Result> OFF0_OFF & OFFX_MASK); let off1 = decode_offset(encoded_instr >> OFF1_OFF & OFFX_MASK); @@ -52,6 +49,7 @@ pub fn decode_instruction(encoded_instr: u64) -> Result> PC_UPDATE_OFF; let ap_update_num = (flags & AP_UPDATE_MASK) >> AP_UPDATE_OFF; let opcode_num = (flags & OPCODE_MASK) >> OPCODE_OFF; + let opcode_extension_num = (flags & OPCODE_EXTENSION_MASK) >> OPCODE_EXTENSION_OFF; // Match each flag to its corresponding enum value let dst_register = if dst_reg_num == 1 { @@ -98,6 +96,19 @@ pub fn decode_instruction(encoded_instr: u64) -> Result return Err(VirtualMachineError::InvalidOpcode(opcode_num)), }; + let opcode_extension = match opcode_extension_num { + 0 => OpcodeExtension::Stone, + _ => { + if opcode != Opcode::NOp { + return Err(VirtualMachineError::OpcodeExtensionClash( + opcode_num, + opcode_extension_num, + )); + }; + OpcodeExtension::Blake + } + }; + let ap_update = match (ap_update_num, opcode == Opcode::Call) { (0, true) => ApUpdate::Add2, (0, false) => ApUpdate::Regular, @@ -145,6 +156,7 @@ pub fn decode_instruction(encoded_instr: u64) -> Result), } #[derive(Debug, PartialEq, Eq, Error)] diff --git a/vm/src/vm/errors/vm_errors.rs b/vm/src/vm/errors/vm_errors.rs index c3698eda02..f2598d9e59 100644 --- a/vm/src/vm/errors/vm_errors.rs +++ b/vm/src/vm/errors/vm_errors.rs @@ -34,8 +34,6 @@ pub enum VirtualMachineError { MainScopeError(#[from] ExecScopeError), #[error(transparent)] Other(anyhow::Error), - #[error("Instruction MSB should be 0")] - InstructionNonZeroHighBit, #[error("Instruction should be an int")] InvalidInstructionEncoding, #[error("Invalid op1_register value: {0}")] @@ -76,6 +74,8 @@ pub enum VirtualMachineError { InvalidRes(u64), #[error("Invalid opcode value: {0}")] InvalidOpcode(u64), + #[error("Nonzero opcode value: {0} and nonzero opcode extension value: {1}")] + OpcodeExtensionClash(u64, u64), #[error("This is not implemented")] NotImplemented, #[error("Inconsistent auto-deduction for {}, expected {}, got {:?}", (*.0).0, (*.0).1, (*.0).2)] @@ -136,6 +136,8 @@ pub enum VirtualMachineError { RelocationNotFound(usize), #[error("{} batch size is not {}", (*.0).0, (*.0).1)] ModBuiltinBatchSize(Box<(BuiltinName, usize)>), + #[error("Blake2s opcode invalid operand: op{0} does not point to {1} u32 numbers.")] + Blake2sInvalidOperand(u8, u8), } #[cfg(test)] diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index 5862aee093..eb06095a5c 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -1,24 +1,28 @@ use crate::math_utils::signed_felt; use crate::stdlib::{any::Any, borrow::Cow, collections::HashMap, prelude::*}; use crate::types::builtin_name::BuiltinName; +use crate::types::instruction::OpcodeExtension; #[cfg(feature = "extensive_hints")] use crate::types::program::HintRange; use crate::{ - hint_processor::hint_processor_definition::HintProcessor, + hint_processor::{ + builtin_hint_processor::blake2s_hash::blake2s_compress, + hint_processor_definition::HintProcessor, + }, types::{ errors::math_errors::MathError, exec_scope::ExecutionScopes, instruction::{ is_call_instruction, ApUpdate, FpUpdate, Instruction, Opcode, PcUpdate, Res, }, - relocatable::{MaybeRelocatable, Relocatable}, + relocatable::{MaybeRelocatable, MaybeRelocatable::RelocatableValue, Relocatable}, }, vm::{ context::run_context::RunContext, decoding::decoder::decode_instruction, errors::{ exec_scope_errors::ExecScopeError, memory_errors::MemoryError, - vm_errors::VirtualMachineError, + memory_errors::MemoryError::AddressNotRelocatable, vm_errors::VirtualMachineError, }, runners::builtin_runner::{ BuiltinRunner, OutputBuiltinRunner, RangeCheckBuiltinRunner, SignatureBuiltinRunner, @@ -39,6 +43,7 @@ use super::runners::builtin_runner::{ModBuiltinRunner, RC_N_PARTS_STANDARD}; use super::runners::cairo_pie::CairoPie; const MAX_TRACEBACK_ENTRIES: u32 = 20; +const BLAKE2S_BYTES_PER_BLOCK: u32 = 64; #[derive(PartialEq, Eq, Debug)] pub struct Operands { @@ -438,12 +443,63 @@ impl VirtualMachine { .memory .mark_as_accessed(operands_addresses.op1_addr); + if instruction.opcode_extension == OpcodeExtension::Blake { + self.handle_blake2s_instruction(&operands)?; + } + self.update_registers(instruction, operands)?; self.current_step += 1; Ok(()) } + /// Executes a Blake2s instruction. + /// Expects operands to be RelocatableValue and to point to segments of memory. + /// op0 is expected to point to a sequence of 9 u32 values (state and counter t0). + /// op1 is expected to point to a sequence of 16 u32 values (message). + /// dst is expected to point to a sequence of 9 cells with the first 8 cells each + /// being either unitialised or containing the Blake2s compression output at that index + /// and the last cell being unitialised or containing . + /// Deviation from the aforementioned expectations will result in an error. + /// The instruction will update the dst memory segment with the new state. + /// Note: the byte counter should count the number of message bytes processed so far including + /// the current portion of the message (i.e. it starts at 64, not 0). + fn handle_blake2s_instruction( + &mut self, + operands: &Operands, + ) -> Result<(), VirtualMachineError> { + let dst: Relocatable = match operands.dst { + RelocatableValue(relocatable) => relocatable, + _ => return Err(VirtualMachineError::Memory(AddressNotRelocatable)), + }; + + let state_and_t0: [u32; 9] = (self.get_u32_range(&operands.op0, 9)?) + .try_into() + .map_err(|_| VirtualMachineError::Blake2sInvalidOperand(0, 9))?; + let message = (self.get_u32_range(&operands.op1, 16)?) + .try_into() + .map_err(|_| VirtualMachineError::Blake2sInvalidOperand(1, 16))?; + + let next_counter = state_and_t0[8] + BLAKE2S_BYTES_PER_BLOCK; + let mut new_state_and_t0 = blake2s_compress( + state_and_t0[0..8].try_into().unwrap(), + &message, + next_counter, + 0, + 0, + 0, + ); + new_state_and_t0.push(next_counter); + + for (i, &val) in new_state_and_t0.iter().enumerate() { + self.segments + .memory + .insert_as_accessed((dst + i)?, MaybeRelocatable::Int(Felt252::from(val)))?; + } + + Ok(()) + } + fn decode_current_instruction(&self) -> Result { let instruction = self .segments @@ -926,6 +982,33 @@ impl VirtualMachine { self.segments.memory.get_integer_range(addr, size) } + /// Gets n u32 values from memory starting from addr (n being size), + /// Verifies that addr is &RelocatableValue and that the values are u32. + pub fn get_u32_range( + &self, + addr: &MaybeRelocatable, + size: usize, + ) -> Result, MemoryError> { + let addr_relocatable: Relocatable = match addr { + &RelocatableValue(relocatable) => relocatable, + _ => return Err(MemoryError::AddressNotRelocatable), + }; + let res_cow = self.get_integer_range(addr_relocatable, size)?; + let mut res = vec![]; + for (i, x) in res_cow.iter().enumerate() { + let le_digits = x.clone().into_owned().to_le_digits(); + if le_digits[0] >= (1 << 32) + || le_digits[1] != 0 + || le_digits[2] != 0 + || le_digits[3] != 0 + { + return Err(MemoryError::ExpectedU32(Box::new((addr_relocatable + i)?))); + } + res.push(le_digits[0] as u32); + } + Ok(res) + } + pub fn get_range_check_builtin( &self, ) -> Result<&RangeCheckBuiltinRunner, VirtualMachineError> { @@ -1302,6 +1385,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1335,6 +1419,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Dst, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1368,6 +1453,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1401,6 +1487,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Dst, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1435,6 +1522,7 @@ mod tests { ap_update: ApUpdate::Add, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1471,6 +1559,7 @@ mod tests { ap_update: ApUpdate::Add, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1506,6 +1595,7 @@ mod tests { ap_update: ApUpdate::Add1, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1542,6 +1632,7 @@ mod tests { ap_update: ApUpdate::Add2, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1578,6 +1669,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1614,6 +1706,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1647,6 +1740,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1680,6 +1774,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1713,6 +1808,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1748,6 +1844,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1782,6 +1879,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1814,6 +1912,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1845,6 +1944,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1878,6 +1978,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1911,6 +2012,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1949,6 +2051,7 @@ mod tests { ap_update: ApUpdate::Add2, fp_update: FpUpdate::Dst, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1999,6 +2102,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2027,6 +2131,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2059,6 +2164,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2086,6 +2192,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2118,6 +2225,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2147,6 +2255,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2176,6 +2285,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Ret, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2206,6 +2316,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2233,6 +2344,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2264,6 +2376,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2290,6 +2403,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2321,6 +2435,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2350,6 +2465,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2378,6 +2494,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2408,6 +2525,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2437,6 +2555,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2466,6 +2585,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2495,6 +2615,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2522,6 +2643,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2549,6 +2671,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2575,6 +2698,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2597,6 +2721,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2622,6 +2747,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Ret, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2644,6 +2770,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let mut vm = vm!(); @@ -2704,6 +2831,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let mut vm = vm!(); //Create program and execution segments @@ -2763,6 +2891,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let mut vm = vm!(); @@ -2818,6 +2947,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let mut vm = vm!(); @@ -2843,6 +2973,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -2873,6 +3004,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -2907,6 +3039,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -2939,6 +3072,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -2972,6 +3106,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -3339,6 +3474,7 @@ mod tests { ap_update: ApUpdate::Add1, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let mut builtin = HashBuiltinRunner::new(Some(8), true); builtin.base = 3; @@ -3427,6 +3563,7 @@ mod tests { ap_update: ApUpdate::Add1, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let mut builtin = BitwiseBuiltinRunner::new(Some(256), true); @@ -4314,6 +4451,43 @@ mod tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_not_relocatable() { + let vm = vm!(); + assert_matches!( + vm.get_u32_range(&MaybeRelocatable::Int(Felt252::from(1)), 1), + Err(MemoryError::AddressNotRelocatable) + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_not_u32() { + let mut vm = vm!(); + vm.segments.memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967296)]; + assert_matches!( + vm.get_u32_range(&MaybeRelocatable::RelocatableValue((0, 0).into()), 3), + Err(MemoryError::ExpectedU32(bx)) if *bx == (0, 2).into() + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn handle_blake2s_instruction_dst_not_relocatable() { + let mut vm = vm!(); + let operands = Operands { + dst: MaybeRelocatable::Int(Felt252::from(1)), + res: None, + op0: (0, 0).into(), + op1: (0, 1).into(), + }; + assert_matches!( + vm.handle_blake2s_instruction(&operands), + Err(VirtualMachineError::Memory(AddressNotRelocatable)) + ); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_traceback_entries_bad_usort() {