Skip to content

Commit abe5f44

Browse files
implement Blake2sLastBlock opcode in runner (#1932)
1 parent 153d3a8 commit abe5f44

File tree

5 files changed

+144
-33
lines changed

5 files changed

+144
-33
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#### Upcoming Changes
44

5+
* feat: implement `Blake2sLastBlock` opcode in VM [#1932](https://github.com/lambdaclass/cairo-vm/pull/1932)
6+
57
* feat: implement `Blake2s` opcode in VM [#1927](https://github.com/lambdaclass/cairo-vm/pull/1927)
68

79
* feat: remove `NonZeroReservedBits` from `VirtualMachineError` [#1948](https://github.com/lambdaclass/cairo-vm/pull/1948)

cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
%builtins range_check bitwise
22

33
from starkware.cairo.common.alloc import alloc
4+
from starkware.cairo.common.bool import FALSE, TRUE
45
from starkware.cairo.common.cairo_blake2s.blake2s import STATE_SIZE_FELTS, INPUT_BLOCK_FELTS, _get_sigma
56
from starkware.cairo.common.cairo_blake2s.packed_blake2s import N_PACKED_INSTANCES, blake2s_compress
67
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin
78

89
const COUNTER = 64;
910
const U32_MASK = 0xffffffff;
1011

11-
// Tests the Blake2s opcode runner using a preexisting implementation within the repo as reference.
12-
// The initial state, a random message of 64 bytes and counter are used as input.
12+
// Tests the Blake2s and Blake2sLastBlock opcode runners using a preexisting implementation within the repo as reference.
13+
// The initial state, a random message of 64 bytes and a counter are used as input.
1314
// Both the opcode and the reference implementation are run on the same inputs and then their outputs are compared.
1415
// Before comparing the outputs, it is verified that the opcode runner has written the output to the correct location.
1516
func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
17+
run_blake_test(is_last_block=FALSE);
18+
run_blake_test(is_last_block=TRUE);
19+
return ();
20+
}
21+
func run_blake_test{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(is_last_block: felt) {
1622
alloc_locals;
1723

1824
let (local random_message) = alloc();
@@ -52,7 +58,7 @@ func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
5258
h=input_state,
5359
message=random_message,
5460
t0=COUNTER,
55-
f0=0,
61+
f0=is_last_block * U32_MASK,
5662
sigma=sigma,
5763
output=cairo_output,
5864
);
@@ -76,7 +82,8 @@ func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
7682
assert bitwise_ptr[7].y = U32_MASK;
7783

7884
// Run the blake2s opcode runner on the same inputs and store its output.
79-
let vm_output = run_blake2s(
85+
let vm_output = run_blake2s_opcode(
86+
is_last_block = is_last_block,
8087
dst=COUNTER,
8188
op0=input_state,
8289
op1=random_message,
@@ -107,7 +114,7 @@ func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
107114
return ();
108115
}
109116

110-
// Forces the runner to execute the Blake2s with the given operands.
117+
// Forces the runner to execute the Blake2s or Blake2sLastBlock opcode with the given operands.
111118
// op0 is a pointer to an array of 8 felts as u32 integers of the state.
112119
// op1 is a pointer to an array of 16 felts as u32 integers of the messsage.
113120
// dst is a felt representing a u32 of the counter.
@@ -116,7 +123,8 @@ func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
116123
// An instruction encoding is built from offsets -5, -4, -3 and flags which are all 0 except for
117124
// those denoting uses of fp as the base for operand addresses and flag_opcode_blake (16th flag).
118125
// The instruction is then written to [pc] and the runner is forced to execute Blake2s.
119-
func run_blake2s(
126+
func run_blake2s_opcode(
127+
is_last_block: felt,
120128
dst: felt,
121129
op0: felt*,
122130
op1: felt*,
@@ -127,9 +135,9 @@ func run_blake2s(
127135
let offset0 = (2**15)-5;
128136
let offset1 = (2**15)-4;
129137
let offset2 = (2**15)-3;
130-
static_assert dst == [fp -5];
131-
static_assert op0 == [fp -4];
132-
static_assert op1 == [fp -3];
138+
static_assert dst == [fp - 5];
139+
static_assert op0 == [fp - 4];
140+
static_assert op1 == [fp - 3];
133141
134142
// Set the flags for the instruction.
135143
let flag_dst_base_fp = 1;
@@ -147,17 +155,24 @@ func run_blake2s(
147155
let flag_opcode_call = 0;
148156
let flag_opcode_ret = 0;
149157
let flag_opcode_assert_eq = 0;
150-
let flag_opcode_blake2s = 1;
151158
152-
// Build the instruction encoding.
153-
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);
154-
let instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num*(2**48);
155-
static_assert instruction_num==9226608988349300731;
159+
let flag_num = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3);
160+
let blake2s_opcode_extension_num = 1;
161+
let blake2s_last_block_opcode_extension_num = 2;
162+
let blake2s_instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num*(2**48) + blake2s_opcode_extension_num*(2**63);
163+
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);
164+
static_assert blake2s_instruction_num==9226608988349300731;
165+
static_assert blake2s_last_block_instruction_num==18449981025204076539;
156166
157167
// Write the instruction to [pc] and point [ap] to the designated output.
158168
let (local vm_output) = alloc();
159169
assert [ap] = cast(vm_output, felt);
170+
171+
jmp last_block if is_last_block!=0;
160172
dw 9226608988349300731;
173+
return cast([ap], felt*);
161174
175+
last_block:
176+
dw 18449981025204076539;
162177
return cast([ap], felt*);
163178
}

vm/src/types/instruction.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub enum Opcode {
8181
pub enum OpcodeExtension {
8282
Stone,
8383
Blake,
84+
BlakeFinalize,
8485
}
8586

8687
impl Instruction {

vm/src/vm/decoding/decoder.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,24 +102,28 @@ pub fn decode_instruction(encoded_instr: u128) -> Result<Instruction, VirtualMac
102102

103103
let opcode_extension = match opcode_extension_num {
104104
0 => OpcodeExtension::Stone,
105-
1 => {
106-
if opcode != Opcode::NOp
107-
|| (op1_addr != Op1Addr::FP && op1_addr != Op1Addr::AP)
108-
|| res != Res::Op1
109-
|| pc_update != PcUpdate::Regular
110-
|| (ap_update_num != 0 && ap_update_num != 2)
111-
{
112-
return Err(VirtualMachineError::InvalidBlake2sFlags(flags & 0x7FFF));
113-
};
114-
OpcodeExtension::Blake
115-
}
105+
1 => OpcodeExtension::Blake,
106+
2 => OpcodeExtension::BlakeFinalize,
116107
_ => {
117108
return Err(VirtualMachineError::InvalidOpcodeExtension(
118109
opcode_extension_num,
119110
))
120111
}
121112
};
122113

114+
let are_blake_flags_invalid = opcode != Opcode::NOp
115+
|| (op1_addr != Op1Addr::FP && op1_addr != Op1Addr::AP)
116+
|| res != Res::Op1
117+
|| pc_update != PcUpdate::Regular
118+
|| (ap_update_num != 0 && ap_update_num != 2);
119+
120+
if (opcode_extension == OpcodeExtension::Blake
121+
|| opcode_extension == OpcodeExtension::BlakeFinalize)
122+
&& are_blake_flags_invalid
123+
{
124+
return Err(VirtualMachineError::InvalidBlake2sFlags(flags & 0x7FFF));
125+
}
126+
123127
let ap_update = match (ap_update_num, opcode == Opcode::Call) {
124128
(0, true) => ApUpdate::Add2,
125129
(0, false) => ApUpdate::Regular,

vm/src/vm/vm_core.rs

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,13 @@ impl VirtualMachine {
445445
.memory
446446
.mark_as_accessed(operands_addresses.op1_addr);
447447

448-
if instruction.opcode_extension == OpcodeExtension::Blake {
449-
self.handle_blake2s_instruction(&operands_addresses)?;
448+
if instruction.opcode_extension == OpcodeExtension::Blake
449+
|| instruction.opcode_extension == OpcodeExtension::BlakeFinalize
450+
{
451+
self.handle_blake2s_instruction(
452+
&operands_addresses,
453+
instruction.opcode_extension == OpcodeExtension::BlakeFinalize,
454+
)?;
450455
}
451456

452457
self.update_registers(instruction, operands)?;
@@ -455,7 +460,7 @@ impl VirtualMachine {
455460
Ok(())
456461
}
457462

458-
/// Executes a Blake2s instruction.
463+
/// Executes a Blake2s or Blake2sLastBlock instruction.
459464
/// Expects operands to be RelocatableValue and to point to segments of memory.
460465
/// op0 is expected to point to a sequence of 8 u32 values (state).
461466
/// op1 is expected to point to a sequence of 16 u32 values (message).
@@ -469,6 +474,7 @@ impl VirtualMachine {
469474
fn handle_blake2s_instruction(
470475
&mut self,
471476
operands_addresses: &OperandsAddresses,
477+
is_last_block: bool,
472478
) -> Result<(), VirtualMachineError> {
473479
let counter = self.segments.memory.get_u32(operands_addresses.dst_addr)?;
474480

@@ -490,10 +496,12 @@ impl VirtualMachine {
490496
.try_into()
491497
.map_err(|_| VirtualMachineError::Blake2sInvalidOperand(1, 16))?;
492498

499+
let f0 = if is_last_block { 0xffffffff } else { 0 };
500+
493501
let ap = self.run_context.get_ap();
494502
let output_address = self.segments.memory.get_relocatable(ap)?;
495503

496-
let new_state = blake2s_compress(&state, &message, counter, 0, 0, 0);
504+
let new_state = blake2s_compress(&state, &message, counter, 0, f0, 0);
497505

498506
for (i, &val) in new_state.iter().enumerate() {
499507
self.segments.memory.insert_as_accessed(
@@ -4496,7 +4504,7 @@ mod tests {
44964504
};
44974505

44984506
assert_matches!(
4499-
vm.handle_blake2s_instruction(&operands_addresses),
4507+
vm.handle_blake2s_instruction(&operands_addresses, false),
45004508
Err(VirtualMachineError::Memory(MemoryError::UnknownMemoryCell(bx))) if *bx == (0, 7).into()
45014509
);
45024510
}
@@ -4528,7 +4536,7 @@ mod tests {
45284536
};
45294537

45304538
assert_matches!(
4531-
vm.handle_blake2s_instruction(&operands_addresses),
4539+
vm.handle_blake2s_instruction(&operands_addresses, false),
45324540
Err(VirtualMachineError::Memory(MemoryError::UnknownMemoryCell(bx))) if *bx == (0, 8).into()
45334541
);
45344542
}
@@ -4568,7 +4576,7 @@ mod tests {
45684576
};
45694577

45704578
assert_matches!(
4571-
vm.handle_blake2s_instruction(&operands_addresses),
4579+
vm.handle_blake2s_instruction(&operands_addresses, false),
45724580
Err(VirtualMachineError::Memory(MemoryError::InconsistentMemory(bx))) if *bx == ((0, 0).into(),0.into(),1848029226.into())
45734581
);
45744582
}
@@ -4621,7 +4629,10 @@ mod tests {
46214629
ap: 0,
46224630
fp: 0,
46234631
};
4624-
assert_matches!(vm.handle_blake2s_instruction(&operands_addresses), Ok(()));
4632+
assert_matches!(
4633+
vm.handle_blake2s_instruction(&operands_addresses, false),
4634+
Ok(())
4635+
);
46254636

46264637
let state: [u32; 8] = vm
46274638
.get_u32_range((0, 0).into(), 8)
@@ -4647,6 +4658,84 @@ mod tests {
46474658
assert_eq!(new_state, expected_new_state);
46484659
}
46494660

4661+
#[test]
4662+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
4663+
fn handle_blake2s_last_block_instruction_ok() {
4664+
let mut vm = vm!();
4665+
vm.segments.memory = memory![
4666+
// State
4667+
((0, 0), 0x6B08E647),
4668+
((0, 1), 0xBB67AE85),
4669+
((0, 2), 0x3C6EF372),
4670+
((0, 3), 0xA54FF53A),
4671+
((0, 4), 0x510E527F),
4672+
((0, 5), 0x9B05688C),
4673+
((0, 6), 0x1F83D9AB),
4674+
((0, 7), 0x5BE0CD19),
4675+
// Message
4676+
((0, 8), 930933030),
4677+
((0, 9), 1766240503),
4678+
((0, 10), 3660871006),
4679+
((0, 11), 388409270),
4680+
((0, 12), 1948594622),
4681+
((0, 13), 3119396969),
4682+
((0, 14), 3924579183),
4683+
((0, 15), 2089920034),
4684+
((0, 16), 3857888532),
4685+
((0, 17), 929304360),
4686+
((0, 18), 1810891574),
4687+
((0, 19), 860971754),
4688+
((0, 20), 1822893775),
4689+
((0, 21), 2008495810),
4690+
((0, 22), 2958962335),
4691+
((0, 23), 2340515744),
4692+
// Counter
4693+
((0, 24), 64),
4694+
// AP
4695+
((1, 0), (0, 25)),
4696+
((2, 0), (0, 0)),
4697+
((2, 1), (0, 8))
4698+
];
4699+
let operands_addresses = OperandsAddresses {
4700+
dst_addr: (0, 24).into(),
4701+
op0_addr: (2, 0).into(),
4702+
op1_addr: (2, 1).into(),
4703+
};
4704+
vm.run_context = RunContext {
4705+
pc: (0, 0).into(),
4706+
ap: 0,
4707+
fp: 0,
4708+
};
4709+
assert_matches!(
4710+
vm.handle_blake2s_instruction(&operands_addresses, true),
4711+
Ok(())
4712+
);
4713+
4714+
let state: [u32; 8] = vm
4715+
.get_u32_range((0, 0).into(), 8)
4716+
.unwrap()
4717+
.try_into()
4718+
.unwrap();
4719+
let message: [u32; 16] = vm
4720+
.get_u32_range((0, 8).into(), 16)
4721+
.unwrap()
4722+
.try_into()
4723+
.unwrap();
4724+
let counter = vm.segments.memory.get_u32((0, 24).into()).unwrap();
4725+
4726+
let expected_new_state: [u32; 8] =
4727+
blake2s_compress(&state, &message, counter, 0, 0xffffffff, 0)
4728+
.try_into()
4729+
.unwrap();
4730+
4731+
let new_state: [u32; 8] = vm
4732+
.get_u32_range((0, 25).into(), 8)
4733+
.unwrap()
4734+
.try_into()
4735+
.unwrap();
4736+
assert_eq!(new_state, expected_new_state);
4737+
}
4738+
46504739
#[test]
46514740
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
46524741
fn get_traceback_entries_bad_usort() {

0 commit comments

Comments
 (0)