1use crate::air_private_input::{PrivateInput, PrivateInputKeccakState};
2use crate::math_utils::safe_div_usize;
3use crate::stdlib::{cell::RefCell, collections::HashMap, prelude::*};
4use crate::types::builtin_name::BuiltinName;
5use crate::types::instance_definitions::keccak_instance_def::{
6 CELLS_PER_KECCAK, INPUT_CELLS_PER_KECCAK,
7};
8use crate::types::relocatable::{MaybeRelocatable, Relocatable};
9use crate::vm::errors::memory_errors::MemoryError;
10use crate::vm::errors::runner_errors::RunnerError;
11use crate::vm::vm_memory::memory::Memory;
12use crate::vm::vm_memory::memory_segments::MemorySegmentManager;
13use crate::Felt252;
14use lazy_static::lazy_static;
15use num_bigint::BigUint;
16use num_integer::div_ceil;
17
18const KECCAK_FELT_BYTE_SIZE: usize = 25; const BITS: u32 = 200;
20lazy_static! {
21 static ref KECCAK_INPUT_MAX: Felt252 = Felt252::TWO.pow(BITS);
22}
23
24#[derive(Debug, Clone)]
25pub struct KeccakBuiltinRunner {
26 ratio: Option<u32>,
27 pub base: usize,
28 pub(crate) stop_ptr: Option<usize>,
29 pub(crate) included: bool,
30 cache: RefCell<HashMap<Relocatable, Felt252>>,
31}
32
33impl KeccakBuiltinRunner {
34 pub(crate) fn new(ratio: Option<u32>, included: bool) -> Self {
35 KeccakBuiltinRunner {
36 base: 0,
37 ratio,
38 stop_ptr: None,
39 included,
40 cache: RefCell::new(HashMap::new()),
41 }
42 }
43
44 pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) {
45 self.base = segments.add().segment_index as usize }
47
48 pub fn initial_stack(&self) -> Vec<MaybeRelocatable> {
49 if self.included {
50 vec![MaybeRelocatable::from((self.base as isize, 0))]
51 } else {
52 vec![]
53 }
54 }
55
56 pub fn base(&self) -> usize {
57 self.base
58 }
59
60 pub fn ratio(&self) -> Option<u32> {
61 self.ratio
62 }
63
64 pub fn deduce_memory_cell(
65 &self,
66 address: Relocatable,
67 memory: &Memory,
68 ) -> Result<Option<MaybeRelocatable>, RunnerError> {
69 let index = address.offset % CELLS_PER_KECCAK as usize;
70 if index < INPUT_CELLS_PER_KECCAK as usize {
71 return Ok(None);
72 }
73 if let Some(felt) = self.cache.borrow().get(&address) {
74 return Ok(Some(felt.into()));
75 }
76 let first_input_addr = (address - index)?;
77 let first_output_addr = (first_input_addr + INPUT_CELLS_PER_KECCAK as usize)?;
78
79 let mut input_felts = vec![];
80
81 for i in 0..INPUT_CELLS_PER_KECCAK as usize {
82 let m_index = (first_input_addr + i)?;
83 let val = match memory.get(&m_index) {
84 Some(value) => {
85 let num = value
86 .get_int_ref()
87 .ok_or(RunnerError::BuiltinExpectedInteger(Box::new((
88 BuiltinName::keccak,
89 (first_input_addr + i)?,
90 ))))?;
91 if num >= &KECCAK_INPUT_MAX {
92 return Err(RunnerError::IntegerBiggerThanPowerOfTwo(Box::new((
93 (first_input_addr + i)?,
94 BITS,
95 *num,
96 ))));
97 }
98 *num
99 }
100 _ => return Ok(None),
101 };
102 input_felts.push(val)
103 }
104 let input_message: Vec<u8> = input_felts
105 .iter()
106 .flat_map(|x| {
107 let mut bytes = x.to_bytes_le().to_vec();
108 bytes.resize(KECCAK_FELT_BYTE_SIZE, 0);
109 bytes
110 })
111 .collect();
112 let keccak_result = Self::keccak_f(&input_message)?;
113
114 let mut start_index = 0_usize;
115 for i in 0..INPUT_CELLS_PER_KECCAK {
116 let end_index = start_index + BITS as usize / 8;
117 self.cache.borrow_mut().insert((first_output_addr + i)?, {
118 let mut bytes = keccak_result[start_index..end_index].to_vec();
119 bytes.resize(32, 0);
120 Felt252::from_bytes_le_slice(&bytes)
121 });
122 start_index = end_index;
123 }
124 Ok(self.cache.borrow().get(&address).map(|x| x.into()))
125 }
126
127 pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result<usize, MemoryError> {
128 segments
129 .get_segment_used_size(self.base())
130 .ok_or(MemoryError::MissingSegmentUsedSizes)
131 }
132
133 pub fn get_used_instances(
134 &self,
135 segments: &MemorySegmentManager,
136 ) -> Result<usize, MemoryError> {
137 let used_cells = self.get_used_cells(segments)?;
138 Ok(div_ceil(used_cells, CELLS_PER_KECCAK as usize))
139 }
140
141 pub fn get_used_diluted_check_units(&self, diluted_n_bits: u32) -> usize {
142 safe_div_usize(262144_usize, diluted_n_bits as usize).unwrap_or(0)
153 }
154
155 fn keccak_f(input_message: &[u8]) -> Result<Vec<u8>, RunnerError> {
156 let bigint = BigUint::from_bytes_le(input_message);
157 let mut keccak_input = bigint.to_u64_digits();
158 keccak_input.resize(25, 0);
159 let mut keccak_input: [u64; 25] = keccak_input.try_into().unwrap();
161 keccak::f1600(&mut keccak_input);
162 Ok(keccak_input.iter().flat_map(|x| x.to_le_bytes()).collect())
163 }
164
165 pub fn air_private_input(&self, memory: &Memory) -> Vec<PrivateInput> {
166 let mut private_inputs = vec![];
167 if let Some(segment) = memory.data.get(self.base) {
168 let segment_len = segment.len();
169 for (index, off) in (0..segment_len)
170 .step_by(CELLS_PER_KECCAK as usize)
171 .enumerate()
172 {
173 if let (
175 Ok(input_s0),
176 Ok(input_s1),
177 Ok(input_s2),
178 Ok(input_s3),
179 Ok(input_s4),
180 Ok(input_s5),
181 Ok(input_s6),
182 Ok(input_s7),
183 ) = (
184 memory.get_integer((self.base as isize, off).into()),
185 memory.get_integer((self.base as isize, off + 1).into()),
186 memory.get_integer((self.base as isize, off + 2).into()),
187 memory.get_integer((self.base as isize, off + 3).into()),
188 memory.get_integer((self.base as isize, off + 4).into()),
189 memory.get_integer((self.base as isize, off + 5).into()),
190 memory.get_integer((self.base as isize, off + 6).into()),
191 memory.get_integer((self.base as isize, off + 7).into()),
192 ) {
193 private_inputs.push(PrivateInput::KeccakState(PrivateInputKeccakState {
194 index,
195 input_s0: *input_s0,
196 input_s1: *input_s1,
197 input_s2: *input_s2,
198 input_s3: *input_s3,
199 input_s4: *input_s4,
200 input_s5: *input_s5,
201 input_s6: *input_s6,
202 input_s7: *input_s7,
203 }))
204 }
205 }
206 }
207 private_inputs
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
215 use crate::types::program::Program;
216 use crate::utils::test_utils::*;
217 use crate::{felt_hex, relocatable};
218
219 use crate::vm::{
220 errors::{memory_errors::MemoryError, runner_errors::RunnerError},
221 runners::builtin_runner::BuiltinRunner,
222 };
223
224 #[cfg(target_arch = "wasm32")]
225 use wasm_bindgen_test::*;
226
227 #[test]
228 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
229 fn get_used_instances() {
230 let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into();
231
232 let mut vm = vm!();
233 vm.segments.segment_used_sizes = Some(vec![1]);
234
235 assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
236 }
237
238 #[test]
239 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
240 fn final_stack() {
241 let mut builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into();
242
243 let mut vm = vm!();
244
245 vm.segments = segments![
246 ((0, 0), (0, 0)),
247 ((0, 1), (0, 1)),
248 ((2, 0), (0, 0)),
249 ((2, 1), (0, 0))
250 ];
251
252 vm.segments.segment_used_sizes = Some(vec![0]);
253
254 let pointer = Relocatable::from((2, 2));
255
256 assert_eq!(
257 builtin.final_stack(&vm.segments, pointer).unwrap(),
258 Relocatable::from((2, 1))
259 );
260 }
261
262 #[test]
263 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
264 fn final_stack_error_stop_pointer() {
265 let mut builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into();
266
267 let mut vm = vm!();
268
269 vm.segments = segments![
270 ((0, 0), (0, 0)),
271 ((0, 1), (0, 1)),
272 ((2, 0), (0, 0)),
273 ((2, 1), (0, 0))
274 ];
275
276 vm.segments.segment_used_sizes = Some(vec![992]);
277
278 let pointer = Relocatable::from((2, 2));
279 assert_eq!(
280 builtin.final_stack(&vm.segments, pointer),
281 Err(RunnerError::InvalidStopPointer(Box::new((
282 BuiltinName::keccak,
283 relocatable!(0, 992),
284 relocatable!(0, 0)
285 ))))
286 );
287 }
288
289 #[test]
290 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
291 fn final_stack_error_when_not_included() {
292 let mut builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), false).into();
293
294 let mut vm = vm!();
295
296 vm.segments = segments![
297 ((0, 0), (0, 0)),
298 ((0, 1), (0, 1)),
299 ((2, 0), (0, 0)),
300 ((2, 1), (0, 0))
301 ];
302
303 vm.segments.segment_used_sizes = Some(vec![0]);
304
305 let pointer = Relocatable::from((2, 2));
306
307 assert_eq!(
308 builtin.final_stack(&vm.segments, pointer).unwrap(),
309 Relocatable::from((2, 2))
310 );
311 }
312
313 #[test]
314 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
315 fn final_stack_error_non_relocatable() {
316 let mut builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into();
317
318 let mut vm = vm!();
319
320 vm.segments = segments![
321 ((0, 0), (0, 0)),
322 ((0, 1), (0, 1)),
323 ((2, 0), (0, 0)),
324 ((2, 1), 2)
325 ];
326
327 vm.segments.segment_used_sizes = Some(vec![0]);
328
329 let pointer = Relocatable::from((2, 2));
330
331 assert_eq!(
332 builtin.final_stack(&vm.segments, pointer),
333 Err(RunnerError::NoStopPointer(Box::new(BuiltinName::keccak)))
334 );
335 }
336
337 #[test]
338 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
339 fn get_used_cells_and_allocated_size_test() {
340 let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into();
341
342 let program = Program::from_bytes(
343 include_bytes!("../../../../../cairo_programs/keccak.json"),
344 Some("main"),
345 )
346 .unwrap();
347
348 let mut cairo_runner = cairo_runner!(program);
349 cairo_runner.vm.segments.segment_used_sizes = Some(vec![0]);
350
351 let mut hint_processor = BuiltinHintProcessor::new_empty();
352
353 let address = cairo_runner.initialize(false).unwrap();
354
355 cairo_runner
356 .run_until_pc(address, &mut hint_processor)
357 .unwrap();
358
359 assert_eq!(
360 builtin.get_used_cells_and_allocated_size(&cairo_runner.vm),
361 Ok((0, 1072))
362 );
363 }
364
365 #[test]
366 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
367 fn get_allocated_memory_units() {
368 let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into();
369
370 let mut vm = vm!();
371 vm.current_step = 160;
372
373 assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(256));
374 }
375
376 #[test]
377 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
378 fn get_used_cells_missing_segment_used_sizes() {
379 let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(2048), true).into();
380 let vm = vm!();
381
382 assert_eq!(
383 builtin.get_used_cells(&vm.segments),
384 Err(MemoryError::MissingSegmentUsedSizes)
385 );
386 }
387
388 #[test]
389 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
390 fn get_used_cells_empty() {
391 let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(2048), true).into();
392 let mut vm = vm!();
393
394 vm.segments.segment_used_sizes = Some(vec![0]);
395 assert_eq!(builtin.get_used_cells(&vm.segments), Ok(0));
396 }
397
398 #[test]
399 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
400 fn get_used_cells() {
401 let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(2048), true).into();
402 let mut vm = vm!();
403
404 vm.segments.segment_used_sizes = Some(vec![4]);
405 assert_eq!(builtin.get_used_cells(&vm.segments), Ok(4));
406 }
407
408 #[test]
409 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
410 fn initial_stackincluded_test() {
411 let keccak_builtin = KeccakBuiltinRunner::new(Some(2048), true);
412 assert_eq!(
413 keccak_builtin.initial_stack(),
414 vec![mayberelocatable!(0, 0)]
415 )
416 }
417
418 #[test]
419 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
420 fn initial_stack_notincluded_test() {
421 let keccak_builtin = KeccakBuiltinRunner::new(Some(2048), false);
422 assert_eq!(keccak_builtin.initial_stack(), Vec::new())
423 }
424
425 #[test]
426 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
427 fn deduce_memory_cell_memory_valid() {
428 let memory = memory![
429 ((0, 16), 43),
430 ((0, 17), 199),
431 ((0, 18), 0),
432 ((0, 19), 0),
433 ((0, 20), 0),
434 ((0, 21), 0),
435 ((0, 22), 0),
436 ((0, 23), 1),
437 ((0, 24), 0),
438 ((0, 25), 0),
439 ((0, 26), 43),
440 ((0, 27), 199),
441 ((0, 28), 0),
442 ((0, 29), 0),
443 ((0, 30), 0),
444 ((0, 31), 0),
445 ((0, 32), 0),
446 ((0, 33), 1),
447 ((0, 34), 0),
448 ((0, 35), 0)
449 ];
450 let builtin = KeccakBuiltinRunner::new(Some(2048), true);
451
452 let result = builtin.deduce_memory_cell(Relocatable::from((0, 25)), &memory);
453 assert_eq!(
454 result,
455 Ok(Some(MaybeRelocatable::from(felt_hex!(
456 "0xa06bd018ba91b93146f53563cff2efba46fee2eabe9d89b4cb"
457 ))))
458 );
459 }
460
461 #[test]
462 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
463 fn deduce_memory_cell_non_reloc_address_err() {
464 let memory = memory![
465 ((0, 4), 32),
466 ((0, 5), 72),
467 ((0, 6), 0),
468 ((0, 7), 120),
469 ((0, 8), 52)
470 ];
471 let builtin = KeccakBuiltinRunner::new(Some(2048), true);
472 let result = builtin.deduce_memory_cell(Relocatable::from((0, 1)), &memory);
473 assert_eq!(result, Ok(None));
474 }
475
476 #[test]
477 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
478 fn deduce_memory_cell_offset_lt_input_cell_length_none() {
479 let memory = memory![((0, 4), 32)];
480 let builtin = KeccakBuiltinRunner::new(Some(2048), true);
481 let result = builtin.deduce_memory_cell(Relocatable::from((0, 2)), &memory);
482 assert_eq!(result, Ok(None));
483 }
484
485 #[test]
486 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
487 fn deduce_memory_cell_expected_integer() {
488 let memory = memory![((0, 0), (1, 2))];
489
490 let builtin = KeccakBuiltinRunner::new(Some(2048), true);
491
492 let result = builtin.deduce_memory_cell(Relocatable::from((0, 9)), &memory);
493
494 assert_eq!(
495 result,
496 Err(RunnerError::BuiltinExpectedInteger(Box::new((
497 BuiltinName::keccak,
498 (0, 0).into()
499 ))))
500 );
501 }
502
503 #[test]
504 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
505 fn deduce_memory_cell_missing_input_cells() {
506 let memory = memory![((0, 1), (1, 2))];
507
508 let builtin = KeccakBuiltinRunner::new(Some(2048), true);
509
510 let result = builtin.deduce_memory_cell(Relocatable::from((0, 1)), &memory);
511
512 assert_eq!(result, Ok(None));
513 }
514
515 #[test]
516 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
517 fn deduce_memory_cell_get_memory_err() {
518 let memory = memory![((0, 35), 0)];
519
520 let builtin = KeccakBuiltinRunner::new(Some(2048), true);
521
522 let result = builtin.deduce_memory_cell(Relocatable::from((0, 15)), &memory);
523
524 assert_eq!(result, Ok(None));
525 }
526
527 #[test]
528 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
529 fn deduce_memory_cell_memory_int_larger_than_bits() {
530 let mut memory = memory![
531 ((0, 17), 199),
532 ((0, 18), 0),
533 ((0, 19), 0),
534 ((0, 20), 0),
535 ((0, 21), 0),
536 ((0, 22), 0),
537 ((0, 23), 1),
538 ((0, 24), 0),
539 ((0, 25), 0),
540 ((0, 26), 43),
541 ((0, 27), 199),
542 ((0, 28), 0),
543 ((0, 29), 0),
544 ((0, 30), 0),
545 ((0, 31), 0),
546 ((0, 32), 0),
547 ((0, 33), 1),
548 ((0, 34), 0),
549 ((0, 35), 0)
550 ];
551
552 memory.insert((0, 16).into(), Felt252::MAX).unwrap();
553
554 let builtin = KeccakBuiltinRunner::new(Some(2048), true);
555
556 let result = builtin.deduce_memory_cell(Relocatable::from((0, 25)), &memory);
557
558 assert_eq!(
559 result,
560 Err(RunnerError::IntegerBiggerThanPowerOfTwo(Box::new((
561 (0, 16).into(),
562 BITS,
563 Felt252::MAX
564 ))))
565 );
566 }
567
568 #[test]
569 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
570 fn get_used_diluted_check_units_result() {
571 let builtin = KeccakBuiltinRunner::new(Some(2048), true);
572
573 let result: usize = builtin.get_used_diluted_check_units(16);
574
575 assert_eq!(result, 16384);
576 }
577
578 #[test]
579 fn keccak_f() {
580 let input_bytes = b"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
581 let expected_output_bytes = b"\xf6\x98\x81\xe1\x00!\x1f.\xc4*\x8c\x0c\x7fF\xc8q8\xdf\xb9\xbe\x07H\xca7T1\xab\x16\x17\xa9\x11\xff-L\x87\xb2iY.\x96\x82x\xde\xbb\\up?uz:0\xee\x08\x1b\x15\xd6\n\xab\r\x0b\x87T:w\x0fH\xe7!f},\x08a\xe5\xbe8\x16\x13\x9a?\xad~<9\xf7\x03`\x8b\xd8\xa3F\x8aQ\xf9\n9\xcdD\xb7.X\xf7\x8e\x1f\x17\x9e \xe5i\x01rr\xdf\xaf\x99k\x9f\x8e\x84\\\xday`\xf1``\x02q+\x8e\xad\x96\xd8\xff\xff3<\xb6\x01o\xd7\xa6\x86\x9d\xea\xbc\xfb\x08\xe1\xa3\x1c\x06z\xab@\xa1\xc1\xb1xZ\x92\x96\xc0.\x01\x13g\x93\x87!\xa6\xa8z\x9c@\x0bY'\xe7\xa7Qr\xe5\xc1\xa3\xa6\x88H\xa5\xc0@9k:y\xd1Kw\xd5";
582 let output_bytes = KeccakBuiltinRunner::keccak_f(input_bytes);
583 assert_eq!(output_bytes, Ok(expected_output_bytes.to_vec()));
584 }
585
586 #[test]
587 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
588 fn get_air_private_input() {
589 let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(2048), true).into();
590
591 let segments = segments![
592 ((0, 0), 0),
593 ((0, 1), 1),
594 ((0, 2), 2),
595 ((0, 3), 3),
596 ((0, 4), 4),
597 ((0, 5), 5),
598 ((0, 6), 6),
599 ((0, 7), 7)
600 ];
601 assert_eq!(
602 builtin.air_private_input(&segments),
603 (vec![PrivateInput::KeccakState(PrivateInputKeccakState {
604 index: 0,
605 input_s0: 0.into(),
606 input_s1: 1.into(),
607 input_s2: 2.into(),
608 input_s3: 3.into(),
609 input_s4: 4.into(),
610 input_s5: 5.into(),
611 input_s6: 6.into(),
612 input_s7: 7.into()
613 }),]),
614 );
615 }
616}