From 7b7d956403e3c8ebcc4332bed64649121fa45f56 Mon Sep 17 00:00:00 2001 From: ohad nir Date: Wed, 5 Feb 2025 11:38:58 +0200 Subject: [PATCH] implement Blake2sLastBlock opcode in runner --- CHANGELOG.md | 2 + cairo-vm-tracer/src/tracer_data.rs | 2 +- .../blake2s_opcode_test.cairo | 54 ++++--- vm/src/tests/cairo_run_test.rs | 9 ++ vm/src/types/instruction.rs | 7 +- vm/src/vm/decoding/decoder.rs | 132 ++++++++++-------- vm/src/vm/errors/vm_errors.rs | 16 ++- vm/src/vm/vm_core.rs | 26 +++- 8 files changed, 154 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 933e0eae9c..0d1748a914 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: 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/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo b/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo index b92c4c6ddf..546ccc21e4 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,11 +9,16 @@ 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. +// Tests the Blake2s and Blake2sLastBlock opcode runners using a preexisting implementation within the repo as reference. +// The initial state, a random message of 64 or 56 bytes respectively (the zeros are treated as padding) and counter are used as input. +// Both the opcode and the reference implementation are run on the same inputs and 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(); @@ -30,8 +36,8 @@ func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { 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[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_last_block( + 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_last_block( + 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/tests/cairo_run_test.rs b/vm/src/tests/cairo_run_test.rs index cbce327c08..760f71dd90 100644 --- a/vm/src/tests/cairo_run_test.rs +++ b/vm/src/tests/cairo_run_test.rs @@ -576,6 +576,15 @@ fn blake2s_opcode_test() { run_program_simple(program_data.as_slice()); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn blake2s_last_block_opcode_test() { + let program_data = include_bytes!( + "../../../cairo_programs/stwo_exclusive_programs/blake2s_last_block_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 9bed2f42e3..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 { @@ -94,11 +95,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, }; @@ -141,7 +142,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 cb5fdbfc54..6c3efb40bc 100644 --- a/vm/src/vm/decoding/decoder.rs +++ b/vm/src/vm/decoding/decoder.rs @@ -5,34 +5,39 @@ 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 +// 31 ... 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 64 to 48. -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::InstructionNonZeroHighBits); + } // Grab offsets and convert them from little endian format. let off0 = decode_offset(encoded_instr >> OFF0_OFF & OFFX_MASK); @@ -100,21 +105,8 @@ pub fn decode_instruction(encoded_instr: u64) -> Result OpcodeExtension::Stone, - 1 => { - if opcode != Opcode::NOp { - return Err(VirtualMachineError::OpcodeExtensionClash( - opcode_num, - opcode_extension_num, - )); - }; - if (op1_addr != Op1Addr::FP && op1_addr != Op1Addr::AP) - || res != Res::Op1 - || pc_update != PcUpdate::Regular - { - return Err(VirtualMachineError::InvalidBlake2sFlags(flags)); - }; - OpcodeExtension::Blake - } + 1 => OpcodeExtension::Blake, + 2 => OpcodeExtension::BlakeFinalize, _ => { return Err(VirtualMachineError::InvalidOpcodeExtension( opcode_extension_num, @@ -122,6 +114,22 @@ pub fn decode_instruction(encoded_instr: u64) -> Result ApUpdate::Add2, (0, false) => ApUpdate::Regular, @@ -173,8 +181,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); @@ -190,6 +198,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(), + "Instruction bits 65 to 127 should be 0", + ) + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn invalid_op1_reg() { @@ -237,7 +255,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 + // 31 ... 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 @@ -256,7 +274,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 + // 31 ... 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 @@ -275,7 +293,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 + // 31 ... 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 @@ -294,7 +312,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 + // 31 ... 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 @@ -313,7 +331,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 + // 31 ... 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 @@ -332,7 +350,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 + // 31 ... 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 @@ -346,7 +364,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 + // 31 ... 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 @@ -367,7 +385,7 @@ 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 + // 31 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| CALL| Add2| 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 @@ -388,7 +406,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 + // 31 ... 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 @@ -400,7 +418,7 @@ 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 + // 31 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 // Stone| CALL| Add2| 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 = 1, off1 = 1 @@ -412,11 +430,11 @@ mod decoder_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 - // 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // Blake| CALL| Add2| JumpRel| Op1| FP| FP| FP - // 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 - // 1001 0001 0000 0100 = 0x9104; off0 = 1, off1 = 1 - let error = decode_instruction(0x9104800180018001); - assert_matches!(error, Err(VirtualMachineError::OpcodeExtensionClash(1, 1))); + // 31 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // BlakeFinalize| CALL| Add2| JumpRel| Op1| FP| FP| FP + // 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 + // 0001 0001 0001 0000 0100 = 0x11104; off0 = 1, off1 = 1 + let error = decode_instruction(0x11104800180018001); + assert_matches!(error, Err(VirtualMachineError::OpcodeExtensionClash(1, 2))); } } diff --git a/vm/src/vm/errors/vm_errors.rs b/vm/src/vm/errors/vm_errors.rs index c49b00f218..a4d35ea88f 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("Instruction bits 65 to 127 should be 0")] + InstructionNonZeroHighBits, #[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("Nonzero opcode value: {0} and nonzero opcode extension value: {1}")] OpcodeExtensionClash(u64, u64), #[error("This is not implemented")] @@ -141,7 +143,7 @@ pub enum VirtualMachineError { #[error("Blake2s opcode invalid operand: op{0} does not point to {1} u32 numbers.")] Blake2sInvalidOperand(u8, u8), #[error("Blake2s opcode invalid flags {0}")] - InvalidBlake2sFlags(u64), + InvalidBlake2sFlags(u128), } #[cfg(test)] diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index 87f3aa05ba..e87283f9b7 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,8 +460,7 @@ impl VirtualMachine { Ok(()) } - /// Executes a Blake2s instruction. - /// Expects operands to be RelocatableValue and to point to segments of memory. + /// Executes a Blake2s or Blake2sLastBlock instruction. /// 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. @@ -469,6 +473,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 +498,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( @@ -510,7 +522,7 @@ impl VirtualMachine { .segments .memory .get_integer(self.run_context.pc)? - .to_u64() + .to_u128() .ok_or(VirtualMachineError::InvalidInstructionEncoding)?; decode_instruction(instruction) } @@ -4244,7 +4256,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)