|
| 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 | +} |
0 commit comments