From 974e62185f359050d142ab2c3c95963385975f89 Mon Sep 17 00:00:00 2001 From: Ohad Nir <141617878+ohad-nir-starkware@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:35:59 +0200 Subject: [PATCH 1/3] add get_u32_range to impl VirtualMachine add get_u32 and get_u32_range to impl Memory (#1936) --- CHANGELOG.md | 2 ++ vm/src/vm/vm_core.rs | 39 +++++++++++++++++++++ vm/src/vm/vm_memory/memory.rs | 66 +++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95cfd2d722..c8b21c95a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: add `get_u32_range` to `impl VirtualMachine` add `get_u32` and `get_u32_range` to `impl Memory` [#1936](https://github.com/lambdaclass/cairo-vm/pull/1936) + * feat: add the field `opcode_extension` to the structure of `Instruction` [#1933](https://github.com/lambdaclass/cairo-vm/pull/1933) * fix(BREAKING): Fix no trace padding flow in proof mode [#1909](https://github.com/lambdaclass/cairo-vm/pull/1909) diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index 0a87ef026f..38fb82d29b 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -929,6 +929,12 @@ impl VirtualMachine { self.segments.memory.get_integer_range(addr, size) } + /// Gets n u32 values from memory starting from addr (n being size). + /// Returns an error if any of the values inside the range is missing (memory gap) or is not a u32. + pub fn get_u32_range(&self, addr: Relocatable, size: usize) -> Result, MemoryError> { + self.segments.memory.get_u32_range(addr, size) + } + pub fn get_range_check_builtin( &self, ) -> Result<&RangeCheckBuiltinRunner, VirtualMachineError> { @@ -4373,6 +4379,39 @@ mod tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_ok() { + let mut vm = vm!(); + vm.segments.memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967295), ((0, 3), 3)]; + let expected_vector = vec![1, 4294967295]; + assert_eq!(vm.get_u32_range((0, 1).into(), 2), Ok(expected_vector)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_relocatable() { + let mut vm = vm!(); + vm.segments.memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), (0, 0)), ((0, 3), 3)]; + assert_matches!(vm.get_u32_range((0, 1).into(), 2), Err(MemoryError::ExpectedInteger(bx)) if *bx == (0, 2).into()); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_over_32_bits() { + let mut vm = vm!(); + vm.segments.memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967296), ((0, 3), 3)]; + assert_matches!(vm.get_u32_range((0, 1).into(), 2), Err(MemoryError::Math(MathError::Felt252ToU32Conversion(bx))) if *bx == Felt252::from(4294967296_u64)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_memory_gap() { + let mut vm = vm!(); + vm.segments.memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 3), 3)]; + assert_matches!(vm.get_u32_range((0, 1).into(), 3), Err(MemoryError::UnknownMemoryCell(bx)) if *bx == (0, 2).into()); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_traceback_entries_bad_usort() { diff --git a/vm/src/vm/vm_memory/memory.rs b/vm/src/vm/vm_memory/memory.rs index affd0a06d4..9a20b60d05 100644 --- a/vm/src/vm/vm_memory/memory.rs +++ b/vm/src/vm/vm_memory/memory.rs @@ -414,6 +414,14 @@ impl Memory { } } + /// Gets a u32 value from memory address. + /// Returns an Error if the value at the memory address is missing or not a u32. + pub fn get_u32(&self, key: Relocatable) -> Result { + let felt = self.get_integer(key)?.into_owned(); + felt.to_u32() + .ok_or_else(|| MemoryError::Math(MathError::Felt252ToU32Conversion(Box::new(felt)))) + } + /// Gets the value from memory address as a usize. /// Returns an Error if the value at the memory address is missing not a Felt252, or can't be converted to usize. pub fn get_usize(&self, key: Relocatable) -> Result { @@ -623,6 +631,18 @@ impl Memory { Ok(values) } + /// Gets a range of u32 memory values from addr to addr + size + /// Fails if any of the values inside the range is missing (memory gap) or is not a u32 + pub fn get_u32_range(&self, addr: Relocatable, size: usize) -> Result, MemoryError> { + let mut values = Vec::new(); + + for i in 0..size { + values.push(self.get_u32((addr + i)?)?); + } + + Ok(values) + } + pub fn mark_as_accessed(&mut self, addr: Relocatable) { let (i, j) = from_relocatable_to_indexes(addr); let data = if addr.segment_index < 0 { @@ -1135,6 +1155,23 @@ mod memory_tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_too_big() { + let mut segments = MemorySegmentManager::new(); + segments.add(); + segments + .memory + .insert(Relocatable::from((0, 0)), &Felt252::from(1_u64 << 32)) + .unwrap(); + assert_matches!( + segments.memory.get_u32(Relocatable::from((0, 0))), + Err(MemoryError::Math(MathError::Felt252ToU32Conversion( + bx + ))) if *bx == Felt252::from(1_u64 << 32) + ); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn default_memory() { @@ -1350,6 +1387,35 @@ mod memory_tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_ok() { + let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967295), ((0, 3), 3)]; + let expected_vector = vec![1, 4294967295]; + assert_eq!(memory.get_u32_range((0, 1).into(), 2), Ok(expected_vector)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_relocatable() { + let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), (0, 0)), ((0, 3), 3)]; + assert_matches!(memory.get_u32_range((0, 1).into(), 2), Err(MemoryError::ExpectedInteger(bx)) if *bx == (0, 2).into()); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_over_32_bits() { + let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967296), ((0, 3), 3)]; + assert_matches!(memory.get_u32_range((0, 1).into(), 2), Err(MemoryError::Math(MathError::Felt252ToU32Conversion(bx))) if *bx == Felt252::from(4294967296_u64)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_memory_gap() { + let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 3), 3)]; + assert_matches!(memory.get_u32_range((0, 1).into(), 3), Err(MemoryError::UnknownMemoryCell(bx)) if *bx == (0, 2).into()); + } + /// Test that relocate_memory() works when there are no relocation rules. #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] From 1a135db1d3359be07cb9c59119c331e89b5aa407 Mon Sep 17 00:00:00 2001 From: Ohad Nir <141617878+ohad-nir-starkware@users.noreply.github.com> Date: Tue, 11 Feb 2025 21:57:47 +0200 Subject: [PATCH 2/3] prepare rust.yml and MakeFile for the folder stwo_exclusive_programs which will contain programs that will be run but not yet proven (#1939) --- .github/workflows/rust.yml | 10 ++++++++++ Makefile | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6a76308239..a88de28b9c 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/stwo_exclusive_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/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) From 54209080931c6ce945b3eb9018b585fb7f6aef48 Mon Sep 17 00:00:00 2001 From: Ohad Nir <141617878+ohad-nir-starkware@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:44:07 +0200 Subject: [PATCH 3/3] set the encoded instruction to be u128 for opcode_extensions to come (#1940) --- CHANGELOG.md | 2 + cairo-vm-tracer/src/tracer_data.rs | 2 +- vm/src/types/instruction.rs | 6 +- vm/src/vm/decoding/decoder.rs | 98 +++++++++++++++++------------- vm/src/vm/errors/vm_errors.rs | 14 +++-- vm/src/vm/vm_core.rs | 4 +- 6 files changed, 73 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8b21c95a6..0497cac9cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: set `encoded_instruction` to be u128 for opcode_extensions to come [#1940](https://github.com/lambdaclass/cairo-vm/pull/1940) + * feat: add `get_u32_range` to `impl VirtualMachine` add `get_u32` and `get_u32_range` to `impl Memory` [#1936](https://github.com/lambdaclass/cairo-vm/pull/1936) * feat: add the field `opcode_extension` to the structure of `Instruction` [#1933](https://github.com/lambdaclass/cairo-vm/pull/1933) diff --git a/cairo-vm-tracer/src/tracer_data.rs b/cairo-vm-tracer/src/tracer_data.rs index e2b02cfb23..6b739c3ba3 100644 --- a/cairo-vm-tracer/src/tracer_data.rs +++ b/cairo-vm-tracer/src/tracer_data.rs @@ -143,7 +143,7 @@ impl TracerData { let (instruction_encoding, _) = get_instruction_encoding(entry.pc, &memory, program.prime())?; - let instruction_encoding = instruction_encoding.to_u64(); + let instruction_encoding = instruction_encoding.to_u128(); if instruction_encoding.is_none() { return Err(TraceDataError::FailedToConvertInstructionEncoding); } diff --git a/vm/src/types/instruction.rs b/vm/src/types/instruction.rs index 698f6e0500..8f598ccd6e 100644 --- a/vm/src/types/instruction.rs +++ b/vm/src/types/instruction.rs @@ -93,11 +93,11 @@ impl Instruction { // Returns True if the given instruction looks like a call instruction pub(crate) fn is_call_instruction(encoded_instruction: &Felt252) -> bool { - let encoded_i64_instruction = match encoded_instruction.to_u64() { + let encoded_u128_instruction = match encoded_instruction.to_u128() { Some(num) => num, None => return false, }; - let instruction = match decode_instruction(encoded_i64_instruction) { + let instruction = match decode_instruction(encoded_u128_instruction) { Ok(inst) => inst, Err(_) => return false, }; @@ -140,7 +140,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn instruction_size() { let encoded_instruction = Felt252::from(1226245742482522112_i64); - let instruction = decode_instruction(encoded_instruction.to_u64().unwrap()).unwrap(); + let instruction = decode_instruction(encoded_instruction.to_u128().unwrap()).unwrap(); assert_eq!(instruction.size(), 2); } } diff --git a/vm/src/vm/decoding/decoder.rs b/vm/src/vm/decoding/decoder.rs index 794fd95031..70049cc2f5 100644 --- a/vm/src/vm/decoding/decoder.rs +++ b/vm/src/vm/decoding/decoder.rs @@ -5,36 +5,42 @@ use crate::{ vm::errors::vm_errors::VirtualMachineError, }; -// opcode_extension| 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_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg +// 79 ... 17 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. -/// The bits 64 and beyond are reserved for the opcode extension. +/// Decodes an instruction. The encoding is little endian, so flags go from bit 127 to 48. +/// The bits 63 and beyond are reserved for the opcode extension. /// opcode_extension_num=0 means the instruction is a Stone instruction. -/// opcode_extension_num>1 is for new Stwo opcodes. -pub fn decode_instruction(encoded_instr: u64) -> Result { - const DST_REG_MASK: u64 = 0x0001; - const DST_REG_OFF: u64 = 0; - const OP0_REG_MASK: u64 = 0x0002; - const OP0_REG_OFF: u64 = 1; - const OP1_SRC_MASK: u64 = 0x001C; - const OP1_SRC_OFF: u64 = 2; - const RES_LOGIC_MASK: u64 = 0x0060; - const RES_LOGIC_OFF: u64 = 5; - const PC_UPDATE_MASK: u64 = 0x0380; - const PC_UPDATE_OFF: u64 = 7; - const AP_UPDATE_MASK: u64 = 0x0C00; - const AP_UPDATE_OFF: u64 = 10; - const OPCODE_MASK: u64 = 0x7000; - const OPCODE_OFF: u64 = 12; - const OPCODE_EXTENSION_OFF: u64 = 63; +/// opcode_extension_num>0 is for new Stwo opcodes. +pub fn decode_instruction(encoded_instr: u128) -> Result { + // HIGH_BITS_MASK is a mask to extract the high bits that are yet to be used in any opcode extension. + const HIGH_BITS_MASK: u128 = ((1 << 127) - (1 << 64)) << 1; + const DST_REG_MASK: u128 = 0x0001; + const DST_REG_OFF: u128 = 0; + const OP0_REG_MASK: u128 = 0x0002; + const OP0_REG_OFF: u128 = 1; + const OP1_SRC_MASK: u128 = 0x001C; + const OP1_SRC_OFF: u128 = 2; + const RES_LOGIC_MASK: u128 = 0x0060; + const RES_LOGIC_OFF: u128 = 5; + const PC_UPDATE_MASK: u128 = 0x0380; + const PC_UPDATE_OFF: u128 = 7; + const AP_UPDATE_MASK: u128 = 0x0C00; + const AP_UPDATE_OFF: u128 = 10; + const OPCODE_MASK: u128 = 0x7000; + const OPCODE_OFF: u128 = 12; + const OPCODE_EXTENSION_OFF: u128 = 63; // Flags start on the 48th bit. - const FLAGS_OFFSET: u64 = 48; - const OFF0_OFF: u64 = 0; - const OFF1_OFF: u64 = 16; - const OFF2_OFF: u64 = 32; - const OFFX_MASK: u64 = 0xFFFF; + const FLAGS_OFFSET: u128 = 48; + const OFF0_OFF: u128 = 0; + const OFF1_OFF: u128 = 16; + const OFF2_OFF: u128 = 32; + const OFFX_MASK: u128 = 0xFFFF; + + if (encoded_instr & HIGH_BITS_MASK) != 0 { + return Err(VirtualMachineError::NonZeroReservedBits); + } // Grab offsets and convert them from little endian format. let off0 = decode_offset(encoded_instr >> OFF0_OFF & OFFX_MASK); @@ -160,8 +166,8 @@ pub fn decode_instruction(encoded_instr: u64) -> Result isize { - let vectorized_offset: [u8; 8] = offset.to_le_bytes(); +fn decode_offset(offset: u128) -> isize { + let vectorized_offset: [u8; 16] = offset.to_le_bytes(); let offset_16b_encoded = u16::from_le_bytes([vectorized_offset[0], vectorized_offset[1]]); let complement_const = 0x8000u16; let (offset_16b, _) = offset_16b_encoded.overflowing_sub(complement_const); @@ -177,6 +183,16 @@ mod decoder_test { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn non_zero_high_bits() { + let error = decode_instruction(0x214a7800080008000); + assert_eq!( + error.unwrap_err().to_string(), + "Reserved instruction bits must be 0", + ) + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn invalid_op1_reg() { @@ -224,7 +240,7 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_nop_add_jmp_add_imm_fp_fp() { // opcode_extension| 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 + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| NOp| ADD| JUMP| ADD| IMM| FP| FP // 0 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1 // 0000 0100 1010 0111 = 0x04A7; offx = 0 @@ -244,7 +260,7 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_nop_add1_jmp_rel_mul_fp_ap_ap() { // opcode_extension| 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 + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| NOp| ADD1| JUMP_REL| MUL| FP| AP| AP // 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 // 0000 1001 0100 1000 = 0x0948; offx = 0 @@ -264,7 +280,7 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_assrt_add_regular_mul_ap_ap_ap() { // opcode_extension| 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 + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| ASSRT_EQ| ADD| REGULAR| MUL| AP| AP| AP // 0 1 0 0 1 0 0 0 0 1 0 1 0 0 0 0 // 0100 1000 0101 0000 = 0x4850; offx = 0 @@ -284,7 +300,7 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_assrt_add2_jnz_uncon_op0_ap_ap() { // opcode_extension| 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 + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| ASSRT_EQ| ADD2| JNZ|UNCONSTRD| OP0| AP| AP // 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 // 0100 0010 0000 0000 = 0x4200; offx = 0 @@ -304,7 +320,7 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_nop_regu_regu_op1_op0_ap_ap() { // opcode_extension| 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 + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| NOP| REGULAR| REGULAR| OP1| OP0| AP| AP // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 // 0000 0000 0000 0000 = 0x0000; offx = 0 @@ -324,7 +340,7 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_offset_negative() { // opcode_extension| 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 + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| NOP| REGULAR| REGULAR| OP1| OP0| AP| AP // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 // 0000 0000 0000 0000 = 0x0000; offx = 0 @@ -338,7 +354,7 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_ret_cairo_standard() { // opcode_extension| 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 + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| RET| REGULAR| JUMP| Op1| FP| FP| FP // 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 // 0010 0000 1000 1011 = 0x208b; off0 = -2, off1 = -1 @@ -360,8 +376,8 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_call_cairo_standard() { // opcode_extension| 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 - // Stone| CALL| Add2| JumpRel| Op1| IMM| FP| FP + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| CALL| Regular| JumpRel| Op1| FP| FP| FP // 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 // 0001 0001 0000 0100 = 0x1104; off0 = 0, off1 = 1 let inst = decode_instruction(0x1104800180018000).unwrap(); @@ -382,7 +398,7 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_ret_opcode_error() { // opcode_extension| 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 + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| RET| REGULAR| JUMP| Op1| FP| FP| FP // 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 // 0010 0000 1000 1011 = 0x208b; off0 = -1, off1 = -1 @@ -394,8 +410,8 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_call_opcode_error() { // opcode_extension| 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 - // Stone| CALL| Add2| JumpRel| Op1| FP| FP| FP + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| CALL| REGULAR| JumpRel| Op1| IMM| AP| AP // 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 // 0001 0001 0000 0100 = 0x1104; off0 = 1, off1 = 1 let error = decode_instruction(0x1104800180018001); @@ -406,7 +422,7 @@ mod decoder_test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_invalid_opcode_extension_error() { // opcode_extension| 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 + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // ???| CALL| Add2| JumpRel| Op1| IMM| FP| FP // 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 // 1001 0001 0000 0100 = 0x9104; off0 = 0, off1 = 1 diff --git a/vm/src/vm/errors/vm_errors.rs b/vm/src/vm/errors/vm_errors.rs index 2ba54a7d84..3733c12270 100644 --- a/vm/src/vm/errors/vm_errors.rs +++ b/vm/src/vm/errors/vm_errors.rs @@ -34,18 +34,20 @@ pub enum VirtualMachineError { MainScopeError(#[from] ExecScopeError), #[error(transparent)] Other(anyhow::Error), + #[error("Reserved instruction bits must be 0")] + NonZeroReservedBits, #[error("Instruction should be an int")] InvalidInstructionEncoding, #[error("Invalid op1_register value: {0}")] - InvalidOp1Reg(u64), + InvalidOp1Reg(u128), #[error("In immediate mode, off2 should be 1")] ImmShouldBe1, #[error("op0 must be known in double dereference")] UnknownOp0, #[error("Invalid ap_update value: {0}")] - InvalidApUpdate(u64), + InvalidApUpdate(u128), #[error("Invalid pc_update value: {0}")] - InvalidPcUpdate(u64), + InvalidPcUpdate(u128), #[error("Res.UNCONSTRAINED cannot be used with ApUpdate.ADD")] UnconstrainedResAdd, #[error("Res.UNCONSTRAINED cannot be used with PcUpdate.JUMP")] @@ -71,11 +73,11 @@ pub enum VirtualMachineError { #[error("Couldn't get or load dst")] NoDst, #[error("Invalid res value: {0}")] - InvalidRes(u64), + InvalidRes(u128), #[error("Invalid opcode value: {0}")] - InvalidOpcode(u64), + InvalidOpcode(u128), #[error("Invalid opcode extension value: {0}")] - InvalidOpcodeExtension(u64), + InvalidOpcodeExtension(u128), #[error("This is not implemented")] NotImplemented, #[error("Inconsistent auto-deduction for {}, expected {}, got {:?}", (*.0).0, (*.0).1, (*.0).2)] diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index 38fb82d29b..878953b9ea 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -452,7 +452,7 @@ impl VirtualMachine { .segments .memory .get_integer(self.run_context.pc)? - .to_u64() + .to_u128() .ok_or(VirtualMachineError::InvalidInstructionEncoding)?; decode_instruction(instruction) } @@ -4187,7 +4187,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_current_instruction_invalid_encoding() { let mut vm = vm!(); - vm.segments = segments![((0, 0), ("112233445566778899", 16))]; + vm.segments = segments![((0, 0), ("112233445566778899112233445566778899", 16))]; assert_matches!( vm.decode_current_instruction(), Err(VirtualMachineError::InvalidInstructionEncoding)