Skip to content

Commit 153d3a8

Browse files
implement Blake2s opcode in runner (#1927)
1 parent 47ff7a9 commit 153d3a8

File tree

7 files changed

+475
-5
lines changed

7 files changed

+475
-5
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 `Blake2s` opcode in VM [#1927](https://github.com/lambdaclass/cairo-vm/pull/1927)
6+
57
* feat: remove `NonZeroReservedBits` from `VirtualMachineError` [#1948](https://github.com/lambdaclass/cairo-vm/pull/1948)
68

79
* feat: set `encoded_instruction` to be u128 for opcode_extensions to come [#1940](https://github.com/lambdaclass/cairo-vm/pull/1940)
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
%builtins range_check bitwise
2+
3+
from starkware.cairo.common.alloc import alloc
4+
from starkware.cairo.common.cairo_blake2s.blake2s import STATE_SIZE_FELTS, INPUT_BLOCK_FELTS, _get_sigma
5+
from starkware.cairo.common.cairo_blake2s.packed_blake2s import N_PACKED_INSTANCES, blake2s_compress
6+
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin
7+
8+
const COUNTER = 64;
9+
const U32_MASK = 0xffffffff;
10+
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.
13+
// Both the opcode and the reference implementation are run on the same inputs and then their outputs are compared.
14+
// Before comparing the outputs, it is verified that the opcode runner has written the output to the correct location.
15+
func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
16+
alloc_locals;
17+
18+
let (local random_message) = alloc();
19+
assert random_message[0] = 930933030;
20+
assert random_message[1] = 1766240503;
21+
assert random_message[2] = 3660871006;
22+
assert random_message[3] = 388409270;
23+
assert random_message[4] = 1948594622;
24+
assert random_message[5] = 3119396969;
25+
assert random_message[6] = 3924579183;
26+
assert random_message[7] = 2089920034;
27+
assert random_message[8] = 3857888532;
28+
assert random_message[9] = 929304360;
29+
assert random_message[10] = 1810891574;
30+
assert random_message[11] = 860971754;
31+
assert random_message[12] = 1822893775;
32+
assert random_message[13] = 2008495810;
33+
assert random_message[14] = 2958962335;
34+
assert random_message[15] = 2340515744;
35+
36+
let (local input_state) = alloc();
37+
// Set the initial state to IV (IV[0] is modified).
38+
assert input_state[0] = 0x6B08E647; // IV[0] ^ 0x01010020 (config: no key, 32 bytes output).
39+
assert input_state[1] = 0xBB67AE85;
40+
assert input_state[2] = 0x3C6EF372;
41+
assert input_state[3] = 0xA54FF53A;
42+
assert input_state[4] = 0x510E527F;
43+
assert input_state[5] = 0x9B05688C;
44+
assert input_state[6] = 0x1F83D9AB;
45+
assert input_state[7] = 0x5BE0CD19;
46+
static_assert STATE_SIZE_FELTS == 8;
47+
48+
// Use the packed blake2s_compress to compute the output of the first instance.
49+
let (sigma) = _get_sigma();
50+
let (local cairo_output) = alloc();
51+
blake2s_compress(
52+
h=input_state,
53+
message=random_message,
54+
t0=COUNTER,
55+
f0=0,
56+
sigma=sigma,
57+
output=cairo_output,
58+
);
59+
60+
// Unpack the first instance of the blake2s_compress output (extract the first 32 bits).
61+
assert bitwise_ptr[0].x = cairo_output[0];
62+
assert bitwise_ptr[0].y = U32_MASK;
63+
assert bitwise_ptr[1].x = cairo_output[1];
64+
assert bitwise_ptr[1].y = U32_MASK;
65+
assert bitwise_ptr[2].x = cairo_output[2];
66+
assert bitwise_ptr[2].y = U32_MASK;
67+
assert bitwise_ptr[3].x = cairo_output[3];
68+
assert bitwise_ptr[3].y = U32_MASK;
69+
assert bitwise_ptr[4].x = cairo_output[4];
70+
assert bitwise_ptr[4].y = U32_MASK;
71+
assert bitwise_ptr[5].x = cairo_output[5];
72+
assert bitwise_ptr[5].y = U32_MASK;
73+
assert bitwise_ptr[6].x = cairo_output[6];
74+
assert bitwise_ptr[6].y = U32_MASK;
75+
assert bitwise_ptr[7].x = cairo_output[7];
76+
assert bitwise_ptr[7].y = U32_MASK;
77+
78+
// Run the blake2s opcode runner on the same inputs and store its output.
79+
let vm_output = run_blake2s(
80+
dst=COUNTER,
81+
op0=input_state,
82+
op1=random_message,
83+
);
84+
85+
// Verify that the opcode runner has written the 8 felts to the correct location.
86+
tempvar check_nonempty = vm_output[0];
87+
tempvar check_nonempty = vm_output[1];
88+
tempvar check_nonempty = vm_output[2];
89+
tempvar check_nonempty = vm_output[3];
90+
tempvar check_nonempty = vm_output[4];
91+
tempvar check_nonempty = vm_output[5];
92+
tempvar check_nonempty = vm_output[6];
93+
tempvar check_nonempty = vm_output[7];
94+
95+
// Compare the vm_output to the blake2s_compress first instance output.
96+
assert vm_output[0] = bitwise_ptr[0].x_and_y;
97+
assert vm_output[1] = bitwise_ptr[1].x_and_y;
98+
assert vm_output[2] = bitwise_ptr[2].x_and_y;
99+
assert vm_output[3] = bitwise_ptr[3].x_and_y;
100+
assert vm_output[4] = bitwise_ptr[4].x_and_y;
101+
assert vm_output[5] = bitwise_ptr[5].x_and_y;
102+
assert vm_output[6] = bitwise_ptr[6].x_and_y;
103+
assert vm_output[7] = bitwise_ptr[7].x_and_y;
104+
105+
let bitwise_ptr = bitwise_ptr + BitwiseBuiltin.SIZE * STATE_SIZE_FELTS;
106+
107+
return ();
108+
}
109+
110+
// Forces the runner to execute the Blake2s with the given operands.
111+
// op0 is a pointer to an array of 8 felts as u32 integers of the state.
112+
// op1 is a pointer to an array of 16 felts as u32 integers of the messsage.
113+
// dst is a felt representing a u32 of the counter.
114+
// ap contains a pointer to an array of 8 felts as u32 integers of the output state.
115+
// Those values are stored within addresses fp-5, fp-4 and fp-3 respectively.
116+
// An instruction encoding is built from offsets -5, -4, -3 and flags which are all 0 except for
117+
// those denoting uses of fp as the base for operand addresses and flag_opcode_blake (16th flag).
118+
// The instruction is then written to [pc] and the runner is forced to execute Blake2s.
119+
func run_blake2s(
120+
dst: felt,
121+
op0: felt*,
122+
op1: felt*,
123+
) -> felt* {
124+
alloc_locals;
125+
126+
// Set the offsets for the operands.
127+
let offset0 = (2**15)-5;
128+
let offset1 = (2**15)-4;
129+
let offset2 = (2**15)-3;
130+
static_assert dst == [fp -5];
131+
static_assert op0 == [fp -4];
132+
static_assert op1 == [fp -3];
133+
134+
// Set the flags for the instruction.
135+
let flag_dst_base_fp = 1;
136+
let flag_op0_base_fp = 1;
137+
let flag_op1_imm = 0;
138+
let flag_op1_base_fp = 1;
139+
let flag_op1_base_ap = 0;
140+
let flag_res_add = 0;
141+
let flag_res_mul = 0;
142+
let flag_PC_update_jump = 0;
143+
let flag_PC_update_jump_rel = 0;
144+
let flag_PC_update_jnz = 0;
145+
let flag_ap_update_add = 0;
146+
let flag_ap_update_add_1 = 0;
147+
let flag_opcode_call = 0;
148+
let flag_opcode_ret = 0;
149+
let flag_opcode_assert_eq = 0;
150+
let flag_opcode_blake2s = 1;
151+
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;
156+
157+
// Write the instruction to [pc] and point [ap] to the designated output.
158+
let (local vm_output) = alloc();
159+
assert [ap] = cast(vm_output, felt);
160+
dw 9226608988349300731;
161+
162+
return cast([ap], felt*);
163+
}

vm/src/tests/cairo_run_test.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,14 @@ fn blake2s_integration_tests() {
568568
run_program_simple(program_data.as_slice());
569569
}
570570

571+
#[test]
572+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
573+
fn blake2s_opcode_test() {
574+
let program_data =
575+
include_bytes!("../../../cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.json");
576+
run_program_simple(program_data.as_slice());
577+
}
578+
571579
#[test]
572580
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
573581
fn relocate_segments() {

vm/src/types/instruction.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub enum Opcode {
8080
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
8181
pub enum OpcodeExtension {
8282
Stone,
83+
Blake,
8384
}
8485

8586
impl Instruction {

vm/src/vm/decoding/decoder.rs

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ 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+
}
105116
_ => {
106117
return Err(VirtualMachineError::InvalidOpcodeExtension(
107118
opcode_extension_num,
@@ -412,15 +423,61 @@ mod decoder_test {
412423
assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(1)));
413424
}
414425

426+
#[test]
427+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
428+
fn decode_opcode_extension_clash() {
429+
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
430+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
431+
// Blake| CALL| REGULAR| REGULAR| Op1| FP| AP| AP
432+
// 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0
433+
// 1001 0000 0000 1000 = 0x9008; off0 = 1, off1 = 1
434+
let error = decode_instruction(0x9008800180018001);
435+
assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4104)));
436+
}
437+
438+
#[test]
439+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
440+
fn decode_blake_imm() {
441+
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
442+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
443+
// Blake| NOP| REGULAR| REGULAR| Op1| IMM| AP| AP
444+
// 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
445+
// 1000 0000 0000 0100 = 0x8004; off0 = 1, off1 = 1
446+
let error = decode_instruction(0x8004800180018001);
447+
assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4)));
448+
}
449+
450+
#[test]
451+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
452+
fn decode_blake() {
453+
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
454+
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
455+
// Blake| NOP| ADD1| REGULAR| Op1| AP| FP| FP
456+
// 1 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1
457+
// 1000 1000 0001 0011 = 0x8813; off0 = 1, off1 = 1
458+
let inst = decode_instruction(0x8813800180018001).unwrap();
459+
assert_matches!(inst.opcode, Opcode::NOp);
460+
assert_matches!(inst.off0, 1);
461+
assert_matches!(inst.off1, 1);
462+
assert_matches!(inst.dst_register, Register::FP);
463+
assert_matches!(inst.op0_register, Register::FP);
464+
assert_matches!(inst.op1_addr, Op1Addr::AP);
465+
assert_matches!(inst.res, Res::Op1);
466+
assert_matches!(inst.pc_update, PcUpdate::Regular);
467+
assert_matches!(inst.ap_update, ApUpdate::Add1);
468+
assert_matches!(inst.fp_update, FpUpdate::Regular);
469+
assert_matches!(inst.opcode_extension, OpcodeExtension::Blake);
470+
}
471+
415472
#[test]
416473
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
417474
fn decode_invalid_opcode_extension_error() {
418475
// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
419476
// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0
420477
// ???| CALL| Add2| JumpRel| Op1| IMM| FP| FP
421-
// 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
422-
// 1001 0001 0000 0100 = 0x9104; off0 = 0, off1 = 1
423-
let error = decode_instruction(0x9104800180018000);
424-
assert_matches!(error, Err(VirtualMachineError::InvalidOpcodeExtension(1)));
478+
// 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0
479+
// 0001 1001 0001 0000 0100 = 0x39104; off0 = 0, off1 = 1
480+
let error = decode_instruction(0x19104800180018000);
481+
assert_matches!(error, Err(VirtualMachineError::InvalidOpcodeExtension(3)));
425482
}
426483
}

vm/src/vm/errors/vm_errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ pub enum VirtualMachineError {
136136
RelocationNotFound(usize),
137137
#[error("{} batch size is not {}", (*.0).0, (*.0).1)]
138138
ModBuiltinBatchSize(Box<(BuiltinName, usize)>),
139+
#[error("Blake2s opcode invalid operand: op{0} does not point to {1} u32 numbers.")]
140+
Blake2sInvalidOperand(u8, u8),
141+
#[error("Blake2s opcode invalid flags {0}")]
142+
InvalidBlake2sFlags(u128),
139143
}
140144

141145
#[cfg(test)]

0 commit comments

Comments
 (0)