From dfd3f66fd01295aaa859ee8483bbdfc18d1d7318 Mon Sep 17 00:00:00 2001 From: ohad nir Date: Tue, 4 Feb 2025 12:15:18 +0200 Subject: [PATCH 1/6] introduce opcode_extension to the structure of Instruction. --- CHANGELOG.md | 2 + vm/src/types/instruction.rs | 6 ++ vm/src/vm/context/run_context.rs | 13 ++- vm/src/vm/decoding/decoder.rs | 141 ++++++++++++++++++------------- vm/src/vm/errors/vm_errors.rs | 4 +- vm/src/vm/vm_core.rs | 55 ++++++++++++ 6 files changed, 158 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be252bc857..95cfd2d722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* 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) * refactor: Limit ret opcode decodeing to Cairo0's standards. [#1925](https://github.com/lambdaclass/cairo-vm/pull/1925) diff --git a/vm/src/types/instruction.rs b/vm/src/types/instruction.rs index 133c691302..698f6e0500 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,11 @@ pub enum Opcode { Ret, } +#[derive(Clone, Debug, Copy, PartialEq, Eq)] +pub enum OpcodeExtension { + Stone, +} + 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..794fd95031 100644 --- a/vm/src/vm/decoding/decoder.rs +++ b/vm/src/vm/decoding/decoder.rs @@ -1,16 +1,18 @@ 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_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 /// 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. +/// 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 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 +27,7 @@ pub fn decode_instruction(encoded_instr: u64) -> Result Result> OFF0_OFF & OFFX_MASK); let off1 = decode_offset(encoded_instr >> OFF1_OFF & OFFX_MASK); @@ -53,6 +52,9 @@ pub fn decode_instruction(encoded_instr: u64) -> Result> AP_UPDATE_OFF; let opcode_num = (flags & OPCODE_MASK) >> OPCODE_OFF; + // Grab opcode_extension + let opcode_extension_num = encoded_instr >> OPCODE_EXTENSION_OFF; + // Match each flag to its corresponding enum value let dst_register = if dst_reg_num == 1 { Register::FP @@ -98,6 +100,15 @@ pub fn decode_instruction(encoded_instr: u64) -> Result return Err(VirtualMachineError::InvalidOpcode(opcode_num)), }; + let opcode_extension = match opcode_extension_num { + 0 => OpcodeExtension::Stone, + _ => { + return Err(VirtualMachineError::InvalidOpcodeExtension( + opcode_extension_num, + )) + } + }; + 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 Date: Tue, 11 Feb 2025 10:32:42 +0200 Subject: [PATCH 2/6] add get_u32_range to impl VirtualMachine add get_u32 and get_u32_range to impl Memory --- CHANGELOG.md | 2 ++ vm/src/vm/decoding/decoder.rs | 3 +- vm/src/vm/vm_core.rs | 39 +++++++++++++++++++++ vm/src/vm/vm_memory/memory.rs | 66 +++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 1 deletion(-) 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/decoding/decoder.rs b/vm/src/vm/decoding/decoder.rs index 794fd95031..93d14a87b9 100644 --- a/vm/src/vm/decoding/decoder.rs +++ b/vm/src/vm/decoding/decoder.rs @@ -31,6 +31,7 @@ pub fn decode_instruction(encoded_instr: u64) -> Result Result> OFF2_OFF & OFFX_MASK); // Grab flags - let flags = encoded_instr >> FLAGS_OFFSET; + let flags = (encoded_instr >> FLAGS_OFFSET) & FLAGS_MASK; // Grab individual flags let dst_reg_num = (flags & DST_REG_MASK) >> DST_REG_OFF; let op0_reg_num = (flags & OP0_REG_MASK) >> OP0_REG_OFF; 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 4f53823e708a2ebf9181d86ec588f6a46549208d Mon Sep 17 00:00:00 2001 From: ohad nir Date: Tue, 11 Feb 2025 11:27:14 +0200 Subject: [PATCH 3/6] prepare rust.yml and MakeFile for the folder stwo_exclusive_programs which will contain programs that will be run but not yet proven --- .github/workflows/rust.yml | 10 ++++++++++ CHANGELOG.md | 2 ++ Makefile | 5 +++++ 3 files changed, 17 insertions(+) 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 c8b21c95a6..7c44d0d26b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: prepare `rust.yml` and `MakeFile` for the folder `stwo_exclusive_programs` [#1939](https://github.com/lambdaclass/cairo-vm/pull/1939) + * 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/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 afcbcea44a4ff8999497d2f0d400c1678d842a11 Mon Sep 17 00:00:00 2001 From: ohad nir Date: Tue, 11 Feb 2025 11:32:49 +0200 Subject: [PATCH 4/6] set the encoded instruction to be u128 for opcode_extensions to come --- CHANGELOG.md | 2 + cairo-vm-tracer/src/tracer_data.rs | 2 +- vm/src/types/instruction.rs | 6 +- vm/src/vm/decoding/decoder.rs | 93 +++++++++++++++++------------- vm/src/vm/errors/vm_errors.rs | 14 +++-- vm/src/vm/vm_core.rs | 4 +- 6 files changed, 70 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c44d0d26b..0079e8c947 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: prepare `rust.yml` and `MakeFile` for the folder `stwo_exclusive_programs` [#1939](https://github.com/lambdaclass/cairo-vm/pull/1939) * 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) 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 93d14a87b9..12879f9235 100644 --- a/vm/src/vm/decoding/decoder.rs +++ b/vm/src/vm/decoding/decoder.rs @@ -5,37 +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. /// 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; +pub fn decode_instruction(encoded_instr: u128) -> Result { + const HIGH_BITS: 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 FLAGS_MASK: u64 = 0x7FFF; - 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 FLAGS_MASK: u128 = 0x7FFF; + 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) != 0 { + return Err(VirtualMachineError::NonZeroReservedBits); + } // Grab offsets and convert them from little endian format. let off0 = decode_offset(encoded_instr >> OFF0_OFF & OFFX_MASK); @@ -161,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; 8] = (offset as u64).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); @@ -178,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() { @@ -225,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 @@ -245,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 @@ -265,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 @@ -285,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 @@ -305,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 @@ -325,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 @@ -339,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 @@ -361,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(); @@ -383,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 @@ -395,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); @@ -407,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) From a6456454fb0b3ed040e959792b369cb4558d7e2c Mon Sep 17 00:00:00 2001 From: ohad nir Date: Tue, 11 Feb 2025 11:56:02 +0200 Subject: [PATCH 5/6] implement Blake2s opcode in runner --- CHANGELOG.md | 2 + .../blake2s_opcode_test.cairo | 163 ++++++++++++++++++ vm/src/tests/cairo_run_test.rs | 8 + vm/src/types/instruction.rs | 1 + vm/src/vm/decoding/decoder.rs | 43 ++++- vm/src/vm/errors/vm_errors.rs | 4 + vm/src/vm/vm_core.rs | 163 +++++++++++++++++- 7 files changed, 379 insertions(+), 5 deletions(-) create mode 100644 cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo diff --git a/CHANGELOG.md b/CHANGELOG.md index 0079e8c947..4e7961c125 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) + * feat: set `encoded_instruction` to be u128 for opcode_extensions to come [#1940](https://github.com/lambdaclass/cairo-vm/pull/1940) * feat: prepare `rust.yml` and `MakeFile` for the folder `stwo_exclusive_programs` [#1939](https://github.com/lambdaclass/cairo-vm/pull/1939) 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..b92c4c6ddf --- /dev/null +++ b/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo @@ -0,0 +1,163 @@ +%builtins range_check bitwise + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.cairo_blake2s.blake2s import STATE_SIZE_FELTS, INPUT_BLOCK_FELTS, _get_sigma +from starkware.cairo.common.cairo_blake2s.packed_blake2s import N_PACKED_INSTANCES, blake2s_compress +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin + +const COUNTER = 128; +const U32_MASK = 0xffffffff; + +// Tests the Blake2s opcode runner using a preexisting implementation within the repo as reference. +// The initial state, a random message of 64 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, bitwise_ptr: BitwiseBuiltin*}() { + 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; + + let (local input_state) = alloc(); + // Set the initial state to IV (IV[0] is modified). + assert input_state[0] = 0x6B08E647; // IV[0] ^ 0x01010020 (config: no key, 32 bytes output). + assert input_state[1] = 0xBB67AE85; + assert input_state[2] = 0x3C6EF372; + assert input_state[3] = 0xA54FF53A; + assert input_state[4] = 0x510E527F; + assert input_state[5] = 0x9B05688C; + assert input_state[6] = 0x1F83D9AB; + assert input_state[7] = 0x5BE0CD19; + static_assert STATE_SIZE_FELTS == 8; + + // Use the packed blake2s_compress to compute the output of the first instance. + let (sigma) = _get_sigma(); + let (local cairo_output) = alloc(); + blake2s_compress( + h=input_state, + message=random_message, + t0=COUNTER, + f0=0, + sigma=sigma, + output=cairo_output, + ); + + // Unpack the first instance of the blake2s_compress output (extract the first 32 bits). + assert bitwise_ptr[0].x = cairo_output[0]; + assert bitwise_ptr[0].y = U32_MASK; + assert bitwise_ptr[1].x = cairo_output[1]; + assert bitwise_ptr[1].y = U32_MASK; + assert bitwise_ptr[2].x = cairo_output[2]; + assert bitwise_ptr[2].y = U32_MASK; + assert bitwise_ptr[3].x = cairo_output[3]; + assert bitwise_ptr[3].y = U32_MASK; + assert bitwise_ptr[4].x = cairo_output[4]; + assert bitwise_ptr[4].y = U32_MASK; + assert bitwise_ptr[5].x = cairo_output[5]; + assert bitwise_ptr[5].y = U32_MASK; + assert bitwise_ptr[6].x = cairo_output[6]; + assert bitwise_ptr[6].y = U32_MASK; + assert bitwise_ptr[7].x = cairo_output[7]; + assert bitwise_ptr[7].y = U32_MASK; + + // Run the blake2s opcode runner on the same inputs and store its output. + let vm_output = run_blake2s( + dst=COUNTER, + op0=input_state, + op1=random_message, + ); + + // Verify that the opcode runner has written the 8 felts to the correct location. + tempvar check_nonempty = vm_output[0]; + tempvar check_nonempty = vm_output[1]; + tempvar check_nonempty = vm_output[2]; + tempvar check_nonempty = vm_output[3]; + tempvar check_nonempty = vm_output[4]; + tempvar check_nonempty = vm_output[5]; + tempvar check_nonempty = vm_output[6]; + tempvar check_nonempty = vm_output[7]; + + // Compare the vm_output to the blake2s_compress first instance output. + assert vm_output[0] = bitwise_ptr[0].x_and_y; + assert vm_output[1] = bitwise_ptr[1].x_and_y; + assert vm_output[2] = bitwise_ptr[2].x_and_y; + assert vm_output[3] = bitwise_ptr[3].x_and_y; + assert vm_output[4] = bitwise_ptr[4].x_and_y; + assert vm_output[5] = bitwise_ptr[5].x_and_y; + assert vm_output[6] = bitwise_ptr[6].x_and_y; + assert vm_output[7] = bitwise_ptr[7].x_and_y; + + let bitwise_ptr = bitwise_ptr + BitwiseBuiltin.SIZE * STATE_SIZE_FELTS; + + return (); +} + +// Forces the runner to execute the Blake2s with the given operands. +// op0 is a pointer to an array of 8 felts as u32 integers of the state. +// op1 is a pointer to an array of 16 felts as u32 integers of the messsage. +// dst is a felt representing a u32 of the counter. +// ap contains a pointer to an array of 8 felts as u32 integers of the output state. +// Those values 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*, +) -> felt* { + alloc_locals; + + // Set the offsets for the operands. + 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]; + + // Set the flags for the instruction. + 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; + + // Build the instruction encoding. + 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; + + // Write the instruction to [pc] and point [ap] to the designated output. + let (local vm_output) = alloc(); + assert [ap] = cast(vm_output, felt); + dw 9226608988349300731; + + return cast([ap], felt*); +} diff --git a/vm/src/tests/cairo_run_test.rs b/vm/src/tests/cairo_run_test.rs index c2dea2f4b2..cbce327c08 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 8f598ccd6e..45cfab1f6f 100644 --- a/vm/src/types/instruction.rs +++ b/vm/src/types/instruction.rs @@ -80,6 +80,7 @@ pub enum Opcode { #[derive(Clone, Debug, Copy, PartialEq, Eq)] pub enum OpcodeExtension { Stone, + Blake, } impl Instruction { diff --git a/vm/src/vm/decoding/decoder.rs b/vm/src/vm/decoding/decoder.rs index 12879f9235..84013e9225 100644 --- a/vm/src/vm/decoding/decoder.rs +++ b/vm/src/vm/decoding/decoder.rs @@ -108,6 +108,17 @@ pub fn decode_instruction(encoded_instr: u128) -> Result OpcodeExtension::Stone, + 1 => { + if opcode != Opcode::NOp + || (op1_addr != Op1Addr::FP && op1_addr != Op1Addr::AP) + || res != Res::Op1 + || pc_update != PcUpdate::Regular + || (ap_update_num != 0 && ap_update_num != 2) + { + return Err(VirtualMachineError::InvalidBlake2sFlags(flags)); + }; + OpcodeExtension::Blake + } _ => { return Err(VirtualMachineError::InvalidOpcodeExtension( opcode_extension_num, @@ -418,15 +429,39 @@ mod decoder_test { assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(1))); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn decode_opcode_extension_clash() { + // 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 + // Blake| CALL| REGULAR| REGULAR| Op1| FP| AP| AP + // 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 + // 1001 0000 0000 1000 = 0x9008; off0 = 1, off1 = 1 + let error = decode_instruction(0x9008800180018001); + assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4104))); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn decode_blake_imm() { + // 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 + // Blake| NOP| REGULAR| REGULAR| Op1| IMM| AP| AP + // 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 + // 1000 0000 0000 0100 = 0x8004; off0 = 1, off1 = 1 + let error = decode_instruction(0x8004800180018001); + assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4))); + } + #[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 // 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 - let error = decode_instruction(0x9104800180018000); - assert_matches!(error, Err(VirtualMachineError::InvalidOpcodeExtension(1))); + // 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 + // 0001 1001 0001 0000 0100 = 0x39104; off0 = 0, off1 = 1 + let error = decode_instruction(0x19104800180018000); + assert_matches!(error, Err(VirtualMachineError::InvalidOpcodeExtension(3))); } } diff --git a/vm/src/vm/errors/vm_errors.rs b/vm/src/vm/errors/vm_errors.rs index 3733c12270..930a11bd25 100644 --- a/vm/src/vm/errors/vm_errors.rs +++ b/vm/src/vm/errors/vm_errors.rs @@ -138,6 +138,10 @@ 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), + #[error("Blake2s opcode invalid flags {0}")] + InvalidBlake2sFlags(u128), } #[cfg(test)] diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index 878953b9ea..5ff5738f23 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -1,10 +1,14 @@ 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, @@ -441,12 +445,66 @@ impl VirtualMachine { .memory .mark_as_accessed(operands_addresses.op1_addr); + if instruction.opcode_extension == OpcodeExtension::Blake { + self.handle_blake2s_instruction(&operands_addresses)?; + } + 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 8 u32 values (state). + /// op1 is expected to point to a sequence of 16 u32 values (message). + /// dst is expected hold the u32 value of the counter (t). + /// [ap] is expected to point to a sequence of 8 cells each being either unitialised or + /// containing the Blake2s compression output at that index. + /// Deviation from the aforementioned expectations will result in an error. + /// The instruction will update the memory segment pointed by [ap] 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_addresses: &OperandsAddresses, + ) -> Result<(), VirtualMachineError> { + let counter = self.segments.memory.get_u32(operands_addresses.dst_addr)?; + + let state: [u32; 8] = (self.get_u32_range( + self.segments + .memory + .get_relocatable(operands_addresses.op0_addr)?, + 8, + )?) + .try_into() + .map_err(|_| VirtualMachineError::Blake2sInvalidOperand(0, 8))?; + + let message: [u32; 16] = (self.get_u32_range( + self.segments + .memory + .get_relocatable(operands_addresses.op1_addr)?, + 16, + )?) + .try_into() + .map_err(|_| VirtualMachineError::Blake2sInvalidOperand(1, 16))?; + + let ap = self.run_context.get_ap(); + let output_address = self.segments.memory.get_relocatable(ap)?; + + let new_state = blake2s_compress(&state, &message, counter, 0, 0, 0); + + for (i, &val) in new_state.iter().enumerate() { + self.segments.memory.insert_as_accessed( + (output_address + i)?, + MaybeRelocatable::Int(Felt252::from(val)), + )?; + } + + Ok(()) + } + fn decode_current_instruction(&self) -> Result { let instruction = self .segments @@ -4412,6 +4470,109 @@ mod tests { 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 handle_blake2s_instruction_state_too_short() { + let mut vm = vm!(); + vm.segments.memory = memory![ + ((0, 0), 0), + ((0, 1), 0), + ((0, 2), 0), + ((0, 3), 0), + ((0, 4), 0), + ((0, 5), 0), + ((0, 6), 0), + ((2, 0), (0, 0)) + ]; + let operands_addresses = OperandsAddresses { + dst_addr: (0, 0).into(), + op0_addr: (2, 0).into(), + op1_addr: (2, 0).into(), + }; + vm.run_context = RunContext { + pc: (0, 0).into(), + ap: 0, + fp: 0, + }; + + assert_matches!( + vm.handle_blake2s_instruction(&operands_addresses), + Err(VirtualMachineError::Memory(MemoryError::UnknownMemoryCell(bx))) if *bx == (0, 7).into() + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn handle_blake2s_instruction_message_too_short() { + let mut vm = vm!(); + vm.segments.memory = memory![ + ((0, 0), 0), + ((0, 1), 0), + ((0, 2), 0), + ((0, 3), 0), + ((0, 4), 0), + ((0, 5), 0), + ((0, 6), 0), + ((0, 7), 0), + ((2, 0), (0, 0)) + ]; + let operands_addresses = OperandsAddresses { + dst_addr: (0, 0).into(), + op0_addr: (2, 0).into(), + op1_addr: (2, 0).into(), + }; + vm.run_context = RunContext { + pc: (0, 0).into(), + ap: 0, + fp: 0, + }; + + assert_matches!( + vm.handle_blake2s_instruction(&operands_addresses), + Err(VirtualMachineError::Memory(MemoryError::UnknownMemoryCell(bx))) if *bx == (0, 8).into() + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn handle_blake2s_instruction_ap_points_to_inconsistent_memory() { + let mut vm = vm!(); + vm.segments.memory = memory![ + ((0, 0), 0), + ((0, 1), 0), + ((0, 2), 0), + ((0, 3), 0), + ((0, 4), 0), + ((0, 5), 0), + ((0, 6), 0), + ((0, 7), 0), + ((0, 8), 0), + ((0, 9), 0), + ((0, 10), 0), + ((0, 11), 0), + ((0, 12), 0), + ((0, 13), 0), + ((0, 14), 0), + ((0, 15), 0), + ((1, 0), (0, 0)) + ]; + let operands_addresses = OperandsAddresses { + dst_addr: (0, 0).into(), + op0_addr: (1, 0).into(), + op1_addr: (1, 0).into(), + }; + vm.run_context = RunContext { + pc: (0, 0).into(), + ap: 0, + fp: 0, + }; + + assert_matches!( + vm.handle_blake2s_instruction(&operands_addresses), + Err(VirtualMachineError::Memory(MemoryError::InconsistentMemory(bx))) if *bx == ((0, 0).into(),0.into(),1848029226.into()) + ); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_traceback_entries_bad_usort() { From 0e3fb2c966cf99bc8480530220da69a7316be278 Mon Sep 17 00:00:00 2001 From: ohad nir Date: Tue, 11 Feb 2025 12:54:19 +0200 Subject: [PATCH 6/6] implement Blake2sLastBlock opcode in runner --- CHANGELOG.md | 2 + .../blake2s_opcode_test.cairo | 80 +++++++++++-------- vm/src/types/instruction.rs | 1 + vm/src/vm/decoding/decoder.rs | 24 +++--- vm/src/vm/vm_core.rs | 27 +++++-- 5 files changed, 84 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e7961c125..c3d8a1bdbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: implement `Blake2sLastBlock` opcode in VM [#1932](https://github.com/lambdaclass/cairo-vm/pull/1932) + * feat: implement `Blake2s` opcode in VM [#1927](https://github.com/lambdaclass/cairo-vm/pull/1927) * feat: set `encoded_instruction` to be u128 for opcode_extensions to come [#1940](https://github.com/lambdaclass/cairo-vm/pull/1940) diff --git a/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo b/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo index b92c4c6ddf..843a5c77bf 100644 --- a/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo +++ b/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo @@ -1,6 +1,7 @@ %builtins range_check bitwise from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.bool import FALSE, TRUE from starkware.cairo.common.cairo_blake2s.blake2s import STATE_SIZE_FELTS, INPUT_BLOCK_FELTS, _get_sigma from starkware.cairo.common.cairo_blake2s.packed_blake2s import N_PACKED_INSTANCES, blake2s_compress from starkware.cairo.common.cairo_builtins import BitwiseBuiltin @@ -8,30 +9,35 @@ from starkware.cairo.common.cairo_builtins import BitwiseBuiltin const COUNTER = 128; const U32_MASK = 0xffffffff; -// Tests the Blake2s opcode runner using a preexisting implementation within the repo as reference. -// The initial state, a random message of 64 bytes and counter are used as input. +// Tests the Blake2s and Blake2sLastBlock opcode runners using a preexisting implementation within the repo as reference. +// The initial state, a random message of 64 bytes and a 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, bitwise_ptr: BitwiseBuiltin*}() { + run_blake_test(is_last_block=FALSE); + run_blake_test(is_last_block=TRUE); + return (); +} +func run_blake_test{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(is_last_block: felt) { 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[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; let (local input_state) = alloc(); // Set the initial state to IV (IV[0] is modified). @@ -52,7 +58,7 @@ func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { h=input_state, message=random_message, t0=COUNTER, - f0=0, + f0=is_last_block * U32_MASK, sigma=sigma, output=cairo_output, ); @@ -76,7 +82,8 @@ func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { assert bitwise_ptr[7].y = U32_MASK; // Run the blake2s opcode runner on the same inputs and store its output. - let vm_output = run_blake2s( + let vm_output = run_blake2s_opcode( + is_last_block = is_last_block, dst=COUNTER, op0=input_state, op1=random_message, @@ -107,7 +114,7 @@ func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { return (); } -// Forces the runner to execute the Blake2s with the given operands. +// Forces the runner to execute the Blake2s or Blake2sLastBlock opcode with the given operands. // op0 is a pointer to an array of 8 felts as u32 integers of the state. // op1 is a pointer to an array of 16 felts as u32 integers of the messsage. // dst is a felt representing a u32 of the counter. @@ -116,7 +123,8 @@ func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { // 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( +func run_blake2s_opcode( + is_last_block: felt, dst: felt, op0: felt*, op1: felt*, @@ -147,17 +155,25 @@ func run_blake2s( let flag_opcode_call = 0; let flag_opcode_ret = 0; let flag_opcode_assert_eq = 0; - let flag_opcode_blake2s = 1; - // Build the instruction encoding. - 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; + let flag_num = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3); + let blake2s_opcode_extension_num = 1; + let blake2s_last_block_opcode_extension_num =2; + let blake2s_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num*(2**48) + blake2s_opcode_extension_num*(2**63); + let blake2s_last_block_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num*(2**48) + blake2s_last_block_opcode_extension_num*(2**63); + static_assert blake2s_instruction_num==9226608988349300731; + static_assert blake2s_last_block_instruction_num==18449981025204076539; - // Write the instruction to [pc] and point [ap] to the designated output. + // Write the instruction to [pc] and point [ap+1] to the designated output. let (local vm_output) = alloc(); - assert [ap] = cast(vm_output, felt); - dw 9226608988349300731; - - return cast([ap], felt*); + assert [ap+1] = cast(vm_output, felt); + if (is_last_block == TRUE) { + dw 18449981025204076539; + let vm_output = cast([ap], felt*); + } else { + dw 9226608988349300731; + let vm_output = cast([ap], felt*); + } + + return vm_output; } diff --git a/vm/src/types/instruction.rs b/vm/src/types/instruction.rs index 45cfab1f6f..439130b83c 100644 --- a/vm/src/types/instruction.rs +++ b/vm/src/types/instruction.rs @@ -81,6 +81,7 @@ pub enum Opcode { pub enum OpcodeExtension { Stone, Blake, + BlakeFinalize, } impl Instruction { diff --git a/vm/src/vm/decoding/decoder.rs b/vm/src/vm/decoding/decoder.rs index 84013e9225..f51ccb432e 100644 --- a/vm/src/vm/decoding/decoder.rs +++ b/vm/src/vm/decoding/decoder.rs @@ -108,17 +108,8 @@ pub fn decode_instruction(encoded_instr: u128) -> Result OpcodeExtension::Stone, - 1 => { - if opcode != Opcode::NOp - || (op1_addr != Op1Addr::FP && op1_addr != Op1Addr::AP) - || res != Res::Op1 - || pc_update != PcUpdate::Regular - || (ap_update_num != 0 && ap_update_num != 2) - { - return Err(VirtualMachineError::InvalidBlake2sFlags(flags)); - }; - OpcodeExtension::Blake - } + 1 => OpcodeExtension::Blake, + 2 => OpcodeExtension::BlakeFinalize, _ => { return Err(VirtualMachineError::InvalidOpcodeExtension( opcode_extension_num, @@ -126,6 +117,17 @@ pub fn decode_instruction(encoded_instr: u128) -> Result ApUpdate::Add2, (0, false) => ApUpdate::Regular, diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index 5ff5738f23..fb690419ef 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -445,8 +445,13 @@ impl VirtualMachine { .memory .mark_as_accessed(operands_addresses.op1_addr); - if instruction.opcode_extension == OpcodeExtension::Blake { - self.handle_blake2s_instruction(&operands_addresses)?; + if instruction.opcode_extension == OpcodeExtension::Blake + || instruction.opcode_extension == OpcodeExtension::BlakeFinalize + { + self.handle_blake2s_instruction( + &operands_addresses, + instruction.opcode_extension == OpcodeExtension::BlakeFinalize, + )?; } self.update_registers(instruction, operands)?; @@ -455,7 +460,7 @@ impl VirtualMachine { Ok(()) } - /// Executes a Blake2s instruction. + /// Executes a Blake2s or Blake2sLastBlock instruction. /// Expects operands to be RelocatableValue and to point to segments of memory. /// op0 is expected to point to a sequence of 8 u32 values (state). /// op1 is expected to point to a sequence of 16 u32 values (message). @@ -469,6 +474,7 @@ impl VirtualMachine { fn handle_blake2s_instruction( &mut self, operands_addresses: &OperandsAddresses, + is_last_block: bool, ) -> Result<(), VirtualMachineError> { let counter = self.segments.memory.get_u32(operands_addresses.dst_addr)?; @@ -493,7 +499,14 @@ impl VirtualMachine { let ap = self.run_context.get_ap(); let output_address = self.segments.memory.get_relocatable(ap)?; - let new_state = blake2s_compress(&state, &message, counter, 0, 0, 0); + let new_state = blake2s_compress( + &state, + &message, + counter, + 0, + if !is_last_block { 0 } else { 0xffffffff }, + 0, + ); for (i, &val) in new_state.iter().enumerate() { self.segments.memory.insert_as_accessed( @@ -4496,7 +4509,7 @@ mod tests { }; assert_matches!( - vm.handle_blake2s_instruction(&operands_addresses), + vm.handle_blake2s_instruction(&operands_addresses, false), Err(VirtualMachineError::Memory(MemoryError::UnknownMemoryCell(bx))) if *bx == (0, 7).into() ); } @@ -4528,7 +4541,7 @@ mod tests { }; assert_matches!( - vm.handle_blake2s_instruction(&operands_addresses), + vm.handle_blake2s_instruction(&operands_addresses, false), Err(VirtualMachineError::Memory(MemoryError::UnknownMemoryCell(bx))) if *bx == (0, 8).into() ); } @@ -4568,7 +4581,7 @@ mod tests { }; assert_matches!( - vm.handle_blake2s_instruction(&operands_addresses), + vm.handle_blake2s_instruction(&operands_addresses, false), Err(VirtualMachineError::Memory(MemoryError::InconsistentMemory(bx))) if *bx == ((0, 0).into(),0.into(),1848029226.into()) ); }