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 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 }
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 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 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 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}