Skip to main content

cairo_vm/vm/runners/builtin_runner/
hash.rs

1use crate::air_private_input::{PrivateInput, PrivateInputPair};
2use crate::types::builtin_name::BuiltinName;
3use crate::types::instance_definitions::pedersen_instance_def::CELLS_PER_HASH;
4use crate::types::relocatable::{MaybeRelocatable, Relocatable};
5use crate::vm::errors::memory_errors::MemoryError;
6use crate::vm::errors::runner_errors::RunnerError;
7use crate::vm::runners::cairo_pie::BuiltinAdditionalData;
8use crate::vm::vm_memory::memory::Memory;
9use crate::vm::vm_memory::memory_segments::MemorySegmentManager;
10use num_integer::{div_ceil, Integer};
11use starknet_types_core::hash::StarkHash;
12use std::cell::RefCell;
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    #[test]
182    fn get_used_instances() {
183        let builtin = HashBuiltinRunner::new(Some(10), true);
184
185        let mut vm = vm!();
186        vm.segments.segment_used_sizes = Some(vec![1]);
187
188        assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
189    }
190
191    #[test]
192    fn final_stack() {
193        let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
194
195        let mut vm = vm!();
196
197        vm.segments = segments![
198            ((0, 0), (0, 0)),
199            ((0, 1), (0, 1)),
200            ((2, 0), (0, 0)),
201            ((2, 1), (0, 0))
202        ];
203
204        vm.segments.segment_used_sizes = Some(vec![0]);
205
206        let pointer = Relocatable::from((2, 2));
207
208        assert_eq!(
209            builtin.final_stack(&vm.segments, pointer).unwrap(),
210            Relocatable::from((2, 1))
211        );
212    }
213
214    #[test]
215    fn final_stack_error_stop_pointer() {
216        let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
217
218        let mut vm = vm!();
219
220        vm.segments = segments![
221            ((0, 0), (0, 0)),
222            ((0, 1), (0, 1)),
223            ((2, 0), (0, 0)),
224            ((2, 1), (0, 0))
225        ];
226
227        vm.segments.segment_used_sizes = Some(vec![999]);
228
229        let pointer = Relocatable::from((2, 2));
230
231        assert_eq!(
232            builtin.final_stack(&vm.segments, pointer),
233            Err(RunnerError::InvalidStopPointer(Box::new((
234                BuiltinName::pedersen,
235                relocatable!(0, 999),
236                relocatable!(0, 0)
237            ))))
238        );
239    }
240
241    #[test]
242    fn final_stack_error_when_not_included() {
243        let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), false).into();
244
245        let mut vm = vm!();
246
247        vm.segments = segments![
248            ((0, 0), (0, 0)),
249            ((0, 1), (0, 1)),
250            ((2, 0), (0, 0)),
251            ((2, 1), (0, 0))
252        ];
253
254        vm.segments.segment_used_sizes = Some(vec![0]);
255
256        let pointer = Relocatable::from((2, 2));
257
258        assert_eq!(
259            builtin.final_stack(&vm.segments, pointer).unwrap(),
260            Relocatable::from((2, 2))
261        );
262    }
263
264    #[test]
265    fn final_stack_error_non_relocatable() {
266        let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
267
268        let mut vm = vm!();
269
270        vm.segments = segments![
271            ((0, 0), (0, 0)),
272            ((0, 1), (0, 1)),
273            ((2, 0), (0, 0)),
274            ((2, 1), 2)
275        ];
276
277        vm.segments.segment_used_sizes = Some(vec![0]);
278
279        let pointer = Relocatable::from((2, 2));
280
281        assert_eq!(
282            builtin.final_stack(&vm.segments, pointer),
283            Err(RunnerError::NoStopPointer(Box::new(BuiltinName::pedersen)))
284        );
285    }
286
287    #[test]
288    fn get_used_cells_and_allocated_size_test() {
289        let builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
290
291        let program = program!(
292            builtins = vec![BuiltinName::ec_op],
293            data = vec_data!(
294                (4612671182993129469_i64),
295                (5189976364521848832_i64),
296                (18446744073709551615_i128),
297                (5199546496550207487_i64),
298                (4612389712311386111_i64),
299                (5198983563776393216_i64),
300                (2),
301                (2345108766317314046_i64),
302                (5191102247248822272_i64),
303                (5189976364521848832_i64),
304                (7),
305                (1226245742482522112_i64),
306                ((
307                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
308                    10
309                )),
310                (2345108766317314046_i64)
311            ),
312            main = Some(8),
313        );
314
315        let mut cairo_runner = cairo_runner!(program);
316
317        cairo_runner.vm.segments.segment_used_sizes = Some(vec![0]);
318
319        let mut hint_processor = BuiltinHintProcessor::new_empty();
320
321        let address = cairo_runner.initialize(false).unwrap();
322
323        cairo_runner
324            .run_until_pc(address, &mut hint_processor)
325            .unwrap();
326
327        assert_eq!(
328            builtin.get_used_cells_and_allocated_size(&cairo_runner.vm),
329            Ok((0, 3))
330        );
331    }
332
333    #[test]
334    fn get_allocated_memory_units() {
335        let builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into();
336
337        let program = program!(
338            builtins = vec![BuiltinName::ec_op],
339            data = vec_data!(
340                (4612671182993129469_i64),
341                (5189976364521848832_i64),
342                (18446744073709551615_i128),
343                (5199546496550207487_i64),
344                (4612389712311386111_i64),
345                (5198983563776393216_i64),
346                (2),
347                (2345108766317314046_i64),
348                (5191102247248822272_i64),
349                (5189976364521848832_i64),
350                (7),
351                (1226245742482522112_i64),
352                ((
353                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
354                    10
355                )),
356                (2345108766317314046_i64)
357            ),
358            main = Some(8),
359        );
360
361        let mut cairo_runner = cairo_runner!(program);
362
363        let mut hint_processor = BuiltinHintProcessor::new_empty();
364
365        let address = cairo_runner.initialize(false).unwrap();
366
367        cairo_runner
368            .run_until_pc(address, &mut hint_processor)
369            .unwrap();
370
371        assert_eq!(builtin.get_allocated_memory_units(&cairo_runner.vm), Ok(3));
372    }
373
374    #[test]
375    fn deduce_memory_cell_pedersen_for_preset_memory_valid() {
376        let memory = memory![((0, 3), 32), ((0, 4), 72), ((0, 5), 0)];
377        let builtin = HashBuiltinRunner::new(Some(8), true);
378
379        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
380        assert_eq!(
381            result,
382            Ok(Some(MaybeRelocatable::from(felt_hex!(
383                "0x73b3ec210cccbb970f80c6826fb1c40ae9f487617696234ff147451405c339f"
384            ))))
385        );
386        assert_eq!(
387            builtin.verified_addresses.into_inner(),
388            vec![false, false, false, false, false, true],
389        );
390    }
391
392    #[test]
393    fn deduce_memory_cell_pedersen_for_preset_memory_incorrect_offset() {
394        let memory = memory![((0, 4), 32), ((0, 5), 72), ((0, 6), 0)];
395        let builtin = HashBuiltinRunner::new(Some(8), true);
396        let result = builtin.deduce_memory_cell(Relocatable::from((0, 6)), &memory);
397        assert_eq!(result, Ok(None));
398    }
399
400    #[test]
401    fn deduce_memory_cell_pedersen_for_preset_memory_no_values_to_hash() {
402        let memory = memory![((0, 4), 72), ((0, 5), 0)];
403        let builtin = HashBuiltinRunner::new(Some(8), true);
404        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
405        assert_eq!(result, Ok(None));
406    }
407
408    #[test]
409    fn deduce_memory_cell_pedersen_for_preset_memory_already_computed() {
410        let memory = memory![((0, 3), 32), ((0, 4), 72), ((0, 5), 0)];
411        let mut builtin = HashBuiltinRunner::new(Some(8), true);
412        builtin.verified_addresses = RefCell::new(vec![false, false, false, false, false, true]);
413        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
414        assert_eq!(result, Ok(None));
415    }
416
417    #[test]
418    fn get_used_cells_missing_segment_used_sizes() {
419        let builtin = BuiltinRunner::Hash(HashBuiltinRunner::new(Some(256), true));
420        let vm = vm!();
421
422        assert_eq!(
423            builtin.get_used_cells(&vm.segments),
424            Err(MemoryError::MissingSegmentUsedSizes)
425        );
426    }
427
428    #[test]
429    fn get_used_cells_empty() {
430        let builtin = BuiltinRunner::Hash(HashBuiltinRunner::new(Some(256), true));
431        let mut vm = vm!();
432
433        vm.segments.segment_used_sizes = Some(vec![0]);
434        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(0));
435    }
436
437    #[test]
438    fn get_used_cells() {
439        let builtin = BuiltinRunner::Hash(HashBuiltinRunner::new(Some(256), true));
440        let mut vm = vm!();
441
442        vm.segments.segment_used_sizes = Some(vec![4]);
443        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(4));
444    }
445
446    #[test]
447    fn get_additional_data() {
448        let mut builtin = HashBuiltinRunner::new(Some(1), true);
449        let verified_addresses = vec![Relocatable::from((0, 3)), Relocatable::from((0, 6))];
450        builtin.verified_addresses =
451            RefCell::new(vec![false, false, false, true, false, false, true]);
452        assert_eq!(
453            builtin.get_additional_data(),
454            BuiltinAdditionalData::Hash(verified_addresses)
455        )
456    }
457
458    #[test]
459    fn get_and_extend_additional_data() {
460        let mut builtin_a = HashBuiltinRunner::new(Some(1), true);
461        builtin_a.verified_addresses =
462            RefCell::new(vec![false, false, false, true, false, false, true]);
463        let additional_data = builtin_a.get_additional_data();
464        let mut builtin_b = HashBuiltinRunner::new(Some(1), true);
465        builtin_b.extend_additional_data(&additional_data).unwrap();
466        assert_eq!(builtin_a.verified_addresses, builtin_b.verified_addresses);
467    }
468
469    #[test]
470    fn get_air_private_input() {
471        let builtin: BuiltinRunner = HashBuiltinRunner::new(None, true).into();
472
473        let segments = segments![
474            ((0, 0), 0),
475            ((0, 1), 1),
476            ((0, 2), 2),
477            ((0, 3), 3),
478            ((0, 4), 4),
479            ((0, 5), 5),
480            ((0, 6), 6),
481            ((0, 7), 7),
482            ((0, 8), 8),
483            ((0, 9), 9)
484        ];
485        assert_eq!(
486            builtin.air_private_input(&segments),
487            (vec![
488                PrivateInput::Pair(PrivateInputPair {
489                    index: 0,
490                    x: 0.into(),
491                    y: 1.into()
492                }),
493                PrivateInput::Pair(PrivateInputPair {
494                    index: 1,
495                    x: 3.into(),
496                    y: 4.into()
497                }),
498                PrivateInput::Pair(PrivateInputPair {
499                    index: 2,
500                    x: 6.into(),
501                    y: 7.into()
502                }),
503            ]),
504        );
505    }
506}