cairo_vm/vm/runners/builtin_runner/
hash.rs

1use crate::air_private_input::{PrivateInput, PrivateInputPair};
2use crate::stdlib::{cell::RefCell, prelude::*};
3use crate::types::builtin_name::BuiltinName;
4use crate::types::instance_definitions::pedersen_instance_def::CELLS_PER_HASH;
5use crate::types::relocatable::{MaybeRelocatable, Relocatable};
6use crate::vm::errors::memory_errors::MemoryError;
7use crate::vm::errors::runner_errors::RunnerError;
8use crate::vm::runners::cairo_pie::BuiltinAdditionalData;
9use crate::vm::vm_memory::memory::Memory;
10use crate::vm::vm_memory::memory_segments::MemorySegmentManager;
11use num_integer::{div_ceil, Integer};
12use starknet_types_core::hash::StarkHash;
13
14#[derive(Debug, Clone)]
15pub struct HashBuiltinRunner {
16    pub base: usize,
17    ratio: Option<u32>,
18    pub(crate) stop_ptr: Option<usize>,
19    pub(crate) included: bool,
20    // This act as a cache to optimize calls to deduce_memory_cell
21    // Therefore need interior mutability
22    // 1 at position 'n' means offset 'n' relative to base pointer
23    // has been verified
24    pub(self) verified_addresses: RefCell<Vec<bool>>,
25}
26
27impl HashBuiltinRunner {
28    pub fn new(ratio: Option<u32>, included: bool) -> Self {
29        HashBuiltinRunner {
30            base: 0,
31            ratio,
32            stop_ptr: None,
33            verified_addresses: RefCell::new(Vec::new()),
34            included,
35        }
36    }
37
38    pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) {
39        self.base = segments.add().segment_index as usize // segments.add() always returns a positive index
40    }
41
42    pub fn initial_stack(&self) -> Vec<MaybeRelocatable> {
43        if self.included {
44            vec![MaybeRelocatable::from((self.base as isize, 0))]
45        } else {
46            vec![]
47        }
48    }
49
50    pub fn base(&self) -> usize {
51        self.base
52    }
53
54    pub fn ratio(&self) -> Option<u32> {
55        self.ratio
56    }
57
58    pub fn deduce_memory_cell(
59        &self,
60        address: Relocatable,
61        memory: &Memory,
62    ) -> Result<Option<MaybeRelocatable>, RunnerError> {
63        if address.offset.mod_floor(&(CELLS_PER_HASH as usize)) != 2
64            || *self
65                .verified_addresses
66                .borrow()
67                .get(address.offset)
68                .unwrap_or(&false)
69        {
70            return Ok(None);
71        };
72
73        let num_a = memory.get(&MaybeRelocatable::RelocatableValue(Relocatable {
74            segment_index: address.segment_index,
75            offset: address.offset - 1,
76        }));
77        let num_b = memory.get(&MaybeRelocatable::RelocatableValue(Relocatable {
78            segment_index: address.segment_index,
79            offset: address.offset - 2,
80        }));
81        if let (Some(MaybeRelocatable::Int(num_a)), Some(MaybeRelocatable::Int(num_b))) = (
82            num_a.as_ref().map(|x| x.as_ref()),
83            num_b.as_ref().map(|x| x.as_ref()),
84        ) {
85            if self.verified_addresses.borrow().len() <= address.offset {
86                self.verified_addresses
87                    .borrow_mut()
88                    .resize(address.offset + 1, false);
89            }
90            self.verified_addresses.borrow_mut()[address.offset] = true;
91            //Compute pedersen Hash
92            let result = starknet_types_core::hash::Pedersen::hash(num_b, num_a);
93            return Ok(Some(MaybeRelocatable::from(result)));
94        }
95        Ok(None)
96    }
97
98    pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result<usize, MemoryError> {
99        segments
100            .get_segment_used_size(self.base())
101            .ok_or(MemoryError::MissingSegmentUsedSizes)
102    }
103
104    pub fn get_used_instances(
105        &self,
106        segments: &MemorySegmentManager,
107    ) -> Result<usize, MemoryError> {
108        let used_cells = self.get_used_cells(segments)?;
109        Ok(div_ceil(used_cells, CELLS_PER_HASH as usize))
110    }
111
112    pub fn get_additional_data(&self) -> BuiltinAdditionalData {
113        let mut verified_addresses = Vec::new();
114        for (offset, is_verified) in self.verified_addresses.borrow().iter().enumerate() {
115            if *is_verified {
116                verified_addresses.push(Relocatable::from((self.base as isize, offset)));
117            }
118        }
119        BuiltinAdditionalData::Hash(verified_addresses)
120    }
121
122    pub fn extend_additional_data(
123        &mut self,
124        additional_data: &BuiltinAdditionalData,
125    ) -> Result<(), RunnerError> {
126        let additional_data = match additional_data {
127            BuiltinAdditionalData::Hash(d) => d,
128            BuiltinAdditionalData::Empty(_) => return Ok(()),
129            _ => return Err(RunnerError::InvalidAdditionalData(BuiltinName::pedersen)),
130        };
131        let mut verified_addresses = self.verified_addresses.borrow_mut();
132        for addr in additional_data {
133            if addr.segment_index != self.base as isize {
134                return Err(RunnerError::InvalidAdditionalData(BuiltinName::pedersen));
135            }
136            // Mark offset as verified
137            if addr.offset > verified_addresses.len() {
138                verified_addresses.resize(addr.offset, false);
139            }
140            verified_addresses.insert(addr.offset, true)
141        }
142        Ok(())
143    }
144
145    pub fn air_private_input(&self, memory: &Memory) -> Vec<PrivateInput> {
146        let mut private_inputs = vec![];
147        if let Some(segment) = memory.data.get(self.base) {
148            let segment_len = segment.len();
149            for (index, off) in (0..segment_len)
150                .step_by(CELLS_PER_HASH as usize)
151                .enumerate()
152            {
153                // Add the input cells of each hash instance to the private inputs
154                if let (Ok(x), Ok(y)) = (
155                    memory.get_integer((self.base as isize, off).into()),
156                    memory.get_integer((self.base as isize, off + 1).into()),
157                ) {
158                    private_inputs.push(PrivateInput::Pair(PrivateInputPair {
159                        index,
160                        x: *x,
161                        y: *y,
162                    }))
163                }
164            }
165        }
166        private_inputs
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
174    use crate::types::builtin_name::BuiltinName;
175    use crate::types::program::Program;
176    use crate::utils::test_utils::*;
177    use crate::{felt_hex, relocatable};
178
179    use crate::vm::{errors::memory_errors::MemoryError, runners::builtin_runner::BuiltinRunner};
180
181    #[cfg(target_arch = "wasm32")]
182    use wasm_bindgen_test::*;
183
184    #[test]
185    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
186    fn get_used_instances() {
187        let builtin = HashBuiltinRunner::new(Some(10), true);
188
189        let mut vm = vm!();
190        vm.segments.segment_used_sizes = Some(vec![1]);
191
192        assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
193    }
194
195    #[test]
196    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
197    fn final_stack() {
198        let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
199
200        let mut vm = vm!();
201
202        vm.segments = segments![
203            ((0, 0), (0, 0)),
204            ((0, 1), (0, 1)),
205            ((2, 0), (0, 0)),
206            ((2, 1), (0, 0))
207        ];
208
209        vm.segments.segment_used_sizes = Some(vec![0]);
210
211        let pointer = Relocatable::from((2, 2));
212
213        assert_eq!(
214            builtin.final_stack(&vm.segments, pointer).unwrap(),
215            Relocatable::from((2, 1))
216        );
217    }
218
219    #[test]
220    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
221    fn final_stack_error_stop_pointer() {
222        let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
223
224        let mut vm = vm!();
225
226        vm.segments = segments![
227            ((0, 0), (0, 0)),
228            ((0, 1), (0, 1)),
229            ((2, 0), (0, 0)),
230            ((2, 1), (0, 0))
231        ];
232
233        vm.segments.segment_used_sizes = Some(vec![999]);
234
235        let pointer = Relocatable::from((2, 2));
236
237        assert_eq!(
238            builtin.final_stack(&vm.segments, pointer),
239            Err(RunnerError::InvalidStopPointer(Box::new((
240                BuiltinName::pedersen,
241                relocatable!(0, 999),
242                relocatable!(0, 0)
243            ))))
244        );
245    }
246
247    #[test]
248    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
249    fn final_stack_error_when_not_included() {
250        let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), false).into();
251
252        let mut vm = vm!();
253
254        vm.segments = segments![
255            ((0, 0), (0, 0)),
256            ((0, 1), (0, 1)),
257            ((2, 0), (0, 0)),
258            ((2, 1), (0, 0))
259        ];
260
261        vm.segments.segment_used_sizes = Some(vec![0]);
262
263        let pointer = Relocatable::from((2, 2));
264
265        assert_eq!(
266            builtin.final_stack(&vm.segments, pointer).unwrap(),
267            Relocatable::from((2, 2))
268        );
269    }
270
271    #[test]
272    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
273    fn final_stack_error_non_relocatable() {
274        let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
275
276        let mut vm = vm!();
277
278        vm.segments = segments![
279            ((0, 0), (0, 0)),
280            ((0, 1), (0, 1)),
281            ((2, 0), (0, 0)),
282            ((2, 1), 2)
283        ];
284
285        vm.segments.segment_used_sizes = Some(vec![0]);
286
287        let pointer = Relocatable::from((2, 2));
288
289        assert_eq!(
290            builtin.final_stack(&vm.segments, pointer),
291            Err(RunnerError::NoStopPointer(Box::new(BuiltinName::pedersen)))
292        );
293    }
294
295    #[test]
296    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
297    fn get_used_cells_and_allocated_size_test() {
298        let builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
299
300        let program = program!(
301            builtins = vec![BuiltinName::ec_op],
302            data = vec_data!(
303                (4612671182993129469_i64),
304                (5189976364521848832_i64),
305                (18446744073709551615_i128),
306                (5199546496550207487_i64),
307                (4612389712311386111_i64),
308                (5198983563776393216_i64),
309                (2),
310                (2345108766317314046_i64),
311                (5191102247248822272_i64),
312                (5189976364521848832_i64),
313                (7),
314                (1226245742482522112_i64),
315                ((
316                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
317                    10
318                )),
319                (2345108766317314046_i64)
320            ),
321            main = Some(8),
322        );
323
324        let mut cairo_runner = cairo_runner!(program);
325
326        cairo_runner.vm.segments.segment_used_sizes = Some(vec![0]);
327
328        let mut hint_processor = BuiltinHintProcessor::new_empty();
329
330        let address = cairo_runner.initialize(false).unwrap();
331
332        cairo_runner
333            .run_until_pc(address, &mut hint_processor)
334            .unwrap();
335
336        assert_eq!(
337            builtin.get_used_cells_and_allocated_size(&cairo_runner.vm),
338            Ok((0, 3))
339        );
340    }
341
342    #[test]
343    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
344    fn get_allocated_memory_units() {
345        let builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
346
347        let program = program!(
348            builtins = vec![BuiltinName::ec_op],
349            data = vec_data!(
350                (4612671182993129469_i64),
351                (5189976364521848832_i64),
352                (18446744073709551615_i128),
353                (5199546496550207487_i64),
354                (4612389712311386111_i64),
355                (5198983563776393216_i64),
356                (2),
357                (2345108766317314046_i64),
358                (5191102247248822272_i64),
359                (5189976364521848832_i64),
360                (7),
361                (1226245742482522112_i64),
362                ((
363                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
364                    10
365                )),
366                (2345108766317314046_i64)
367            ),
368            main = Some(8),
369        );
370
371        let mut cairo_runner = cairo_runner!(program);
372
373        let mut hint_processor = BuiltinHintProcessor::new_empty();
374
375        let address = cairo_runner.initialize(false).unwrap();
376
377        cairo_runner
378            .run_until_pc(address, &mut hint_processor)
379            .unwrap();
380
381        assert_eq!(builtin.get_allocated_memory_units(&cairo_runner.vm), Ok(3));
382    }
383
384    #[test]
385    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
386    fn deduce_memory_cell_pedersen_for_preset_memory_valid() {
387        let memory = memory![((0, 3), 32), ((0, 4), 72), ((0, 5), 0)];
388        let builtin = HashBuiltinRunner::new(Some(8), true);
389
390        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
391        assert_eq!(
392            result,
393            Ok(Some(MaybeRelocatable::from(felt_hex!(
394                "0x73b3ec210cccbb970f80c6826fb1c40ae9f487617696234ff147451405c339f"
395            ))))
396        );
397        assert_eq!(
398            builtin.verified_addresses.into_inner(),
399            vec![false, false, false, false, false, true],
400        );
401    }
402
403    #[test]
404    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
405    fn deduce_memory_cell_pedersen_for_preset_memory_incorrect_offset() {
406        let memory = memory![((0, 4), 32), ((0, 5), 72), ((0, 6), 0)];
407        let builtin = HashBuiltinRunner::new(Some(8), true);
408        let result = builtin.deduce_memory_cell(Relocatable::from((0, 6)), &memory);
409        assert_eq!(result, Ok(None));
410    }
411
412    #[test]
413    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
414    fn deduce_memory_cell_pedersen_for_preset_memory_no_values_to_hash() {
415        let memory = memory![((0, 4), 72), ((0, 5), 0)];
416        let builtin = HashBuiltinRunner::new(Some(8), true);
417        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
418        assert_eq!(result, Ok(None));
419    }
420
421    #[test]
422    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
423    fn deduce_memory_cell_pedersen_for_preset_memory_already_computed() {
424        let memory = memory![((0, 3), 32), ((0, 4), 72), ((0, 5), 0)];
425        let mut builtin = HashBuiltinRunner::new(Some(8), true);
426        builtin.verified_addresses = RefCell::new(vec![false, false, false, false, false, true]);
427        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
428        assert_eq!(result, Ok(None));
429    }
430
431    #[test]
432    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
433    fn get_used_cells_missing_segment_used_sizes() {
434        let builtin = BuiltinRunner::Hash(HashBuiltinRunner::new(Some(256), true));
435        let vm = vm!();
436
437        assert_eq!(
438            builtin.get_used_cells(&vm.segments),
439            Err(MemoryError::MissingSegmentUsedSizes)
440        );
441    }
442
443    #[test]
444    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
445    fn get_used_cells_empty() {
446        let builtin = BuiltinRunner::Hash(HashBuiltinRunner::new(Some(256), true));
447        let mut vm = vm!();
448
449        vm.segments.segment_used_sizes = Some(vec![0]);
450        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(0));
451    }
452
453    #[test]
454    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
455    fn get_used_cells() {
456        let builtin = BuiltinRunner::Hash(HashBuiltinRunner::new(Some(256), true));
457        let mut vm = vm!();
458
459        vm.segments.segment_used_sizes = Some(vec![4]);
460        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(4));
461    }
462
463    #[test]
464    fn get_additional_data() {
465        let mut builtin = HashBuiltinRunner::new(Some(1), true);
466        let verified_addresses = vec![Relocatable::from((0, 3)), Relocatable::from((0, 6))];
467        builtin.verified_addresses =
468            RefCell::new(vec![false, false, false, true, false, false, true]);
469        assert_eq!(
470            builtin.get_additional_data(),
471            BuiltinAdditionalData::Hash(verified_addresses)
472        )
473    }
474
475    #[test]
476    fn get_and_extend_additional_data() {
477        let mut builtin_a = HashBuiltinRunner::new(Some(1), true);
478        builtin_a.verified_addresses =
479            RefCell::new(vec![false, false, false, true, false, false, true]);
480        let additional_data = builtin_a.get_additional_data();
481        let mut builtin_b = HashBuiltinRunner::new(Some(1), true);
482        builtin_b.extend_additional_data(&additional_data).unwrap();
483        assert_eq!(builtin_a.verified_addresses, builtin_b.verified_addresses);
484    }
485
486    #[test]
487    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
488    fn get_air_private_input() {
489        let builtin: BuiltinRunner = HashBuiltinRunner::new(None, true).into();
490
491        let segments = segments![
492            ((0, 0), 0),
493            ((0, 1), 1),
494            ((0, 2), 2),
495            ((0, 3), 3),
496            ((0, 4), 4),
497            ((0, 5), 5),
498            ((0, 6), 6),
499            ((0, 7), 7),
500            ((0, 8), 8),
501            ((0, 9), 9)
502        ];
503        assert_eq!(
504            builtin.air_private_input(&segments),
505            (vec![
506                PrivateInput::Pair(PrivateInputPair {
507                    index: 0,
508                    x: 0.into(),
509                    y: 1.into()
510                }),
511                PrivateInput::Pair(PrivateInputPair {
512                    index: 1,
513                    x: 3.into(),
514                    y: 4.into()
515                }),
516                PrivateInput::Pair(PrivateInputPair {
517                    index: 2,
518                    x: 6.into(),
519                    y: 7.into()
520                }),
521            ]),
522        );
523    }
524}