1use crate::air_private_input::{PrivateInput, PrivateInputPoseidonState};
2use crate::stdlib::{cell::RefCell, collections::HashMap, prelude::*};
3use crate::types::builtin_name::BuiltinName;
4use crate::types::instance_definitions::poseidon_instance_def::{
5 CELLS_PER_POSEIDON, INPUT_CELLS_PER_POSEIDON,
6};
7use crate::types::relocatable::{MaybeRelocatable, Relocatable};
8use crate::vm::errors::memory_errors::MemoryError;
9use crate::vm::errors::runner_errors::RunnerError;
10use crate::vm::vm_memory::memory::Memory;
11use crate::vm::vm_memory::memory_segments::MemorySegmentManager;
12use crate::Felt252;
13use num_integer::div_ceil;
14use starknet_types_core::hash::Poseidon;
15
16#[derive(Debug, Clone)]
17pub struct PoseidonBuiltinRunner {
18 pub base: usize,
19 ratio: Option<u32>,
20 pub(crate) stop_ptr: Option<usize>,
21 pub(crate) included: bool,
22 cache: RefCell<HashMap<Relocatable, Felt252>>,
23}
24
25impl PoseidonBuiltinRunner {
26 pub fn new(ratio: Option<u32>, included: bool) -> Self {
27 PoseidonBuiltinRunner {
28 base: 0,
29 ratio,
30 stop_ptr: None,
31 included,
32 cache: RefCell::new(HashMap::new()),
33 }
34 }
35
36 pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) {
37 self.base = segments.add().segment_index as usize }
39
40 pub fn initial_stack(&self) -> Vec<MaybeRelocatable> {
41 if self.included {
42 vec![MaybeRelocatable::from((self.base as isize, 0))]
43 } else {
44 vec![]
45 }
46 }
47
48 pub fn base(&self) -> usize {
49 self.base
50 }
51
52 pub fn ratio(&self) -> Option<u32> {
53 self.ratio
54 }
55
56 pub fn add_validation_rule(&self, _memory: &mut Memory) {}
57
58 pub fn deduce_memory_cell(
59 &self,
60 address: Relocatable,
61 memory: &Memory,
62 ) -> Result<Option<MaybeRelocatable>, RunnerError> {
63 let index = address.offset % CELLS_PER_POSEIDON as usize;
64 if index < INPUT_CELLS_PER_POSEIDON as usize {
65 return Ok(None);
66 }
67 if let Some(felt) = self.cache.borrow().get(&address) {
68 return Ok(Some(felt.into()));
69 }
70 let first_input_addr = (address - index)?;
71 let first_output_addr = (first_input_addr + INPUT_CELLS_PER_POSEIDON as usize)?;
72
73 let mut input_felts = vec![];
74
75 for i in 0..INPUT_CELLS_PER_POSEIDON as usize {
76 let m_index = (first_input_addr + i)?;
77 let val = match memory.get(&m_index) {
78 Some(value) => *value
79 .get_int_ref()
80 .ok_or(RunnerError::BuiltinExpectedInteger(Box::new((
81 BuiltinName::poseidon,
82 m_index,
83 ))))?,
84 _ => return Ok(None),
85 };
86 input_felts.push(val)
87 }
88 let mut poseidon_state: [Felt252; 3] = input_felts.try_into().unwrap();
90 Poseidon::hades_permutation(&mut poseidon_state);
91 for (i, elem) in poseidon_state.iter().enumerate() {
92 self.cache
93 .borrow_mut()
94 .insert((first_output_addr + i)?, *elem);
95 }
96
97 Ok(self.cache.borrow().get(&address).map(|x| x.into()))
98 }
99
100 pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result<usize, MemoryError> {
101 segments
102 .get_segment_used_size(self.base())
103 .ok_or(MemoryError::MissingSegmentUsedSizes)
104 }
105
106 pub fn get_used_instances(
107 &self,
108 segments: &MemorySegmentManager,
109 ) -> Result<usize, MemoryError> {
110 let used_cells = self.get_used_cells(segments)?;
111 Ok(div_ceil(used_cells, CELLS_PER_POSEIDON as usize))
112 }
113
114 pub fn air_private_input(&self, memory: &Memory) -> Vec<PrivateInput> {
115 let mut private_inputs = vec![];
116 if let Some(segment) = memory.data.get(self.base) {
117 let segment_len = segment.len();
118 for (index, off) in (0..segment_len)
119 .step_by(CELLS_PER_POSEIDON as usize)
120 .enumerate()
121 {
122 if let (Ok(input_s0), Ok(input_s1), Ok(input_s2)) = (
124 memory.get_integer((self.base as isize, off).into()),
125 memory.get_integer((self.base as isize, off + 1).into()),
126 memory.get_integer((self.base as isize, off + 2).into()),
127 ) {
128 private_inputs.push(PrivateInput::PoseidonState(PrivateInputPoseidonState {
129 index,
130 input_s0: *input_s0,
131 input_s1: *input_s1,
132 input_s2: *input_s2,
133 }))
134 }
135 }
136 }
137 private_inputs
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
145 use crate::relocatable;
146 use crate::types::builtin_name::BuiltinName;
147 use crate::types::program::Program;
148 use crate::utils::test_utils::*;
149
150 use crate::vm::runners::builtin_runner::BuiltinRunner;
151 #[cfg(target_arch = "wasm32")]
152 use wasm_bindgen_test::*;
153
154 #[test]
155 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
156 fn get_used_instances() {
157 let builtin = PoseidonBuiltinRunner::new(Some(10), true);
158
159 let mut vm = vm!();
160 vm.segments.segment_used_sizes = Some(vec![1]);
161
162 assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
163 }
164
165 #[test]
166 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
167 fn get_used_instances_enum() {
168 let builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), true).into();
169
170 let mut vm = vm!();
171 vm.segments.segment_used_sizes = Some(vec![1]);
172
173 assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
174 }
175
176 #[test]
177 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
178 fn final_stack() {
179 let mut builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), true).into();
180
181 let mut vm = vm!();
182
183 vm.segments = segments![
184 ((0, 0), (0, 0)),
185 ((0, 1), (0, 1)),
186 ((2, 0), (0, 0)),
187 ((2, 1), (0, 0))
188 ];
189
190 vm.segments.segment_used_sizes = Some(vec![0]);
191
192 let pointer = Relocatable::from((2, 2));
193
194 assert_eq!(
195 builtin.final_stack(&vm.segments, pointer).unwrap(),
196 Relocatable::from((2, 1))
197 );
198 }
199
200 #[test]
201 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
202 fn final_stack_error_stop_pointer() {
203 let mut builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), true).into();
204
205 let mut vm = vm!();
206
207 vm.segments = segments![
208 ((0, 0), (0, 0)),
209 ((0, 1), (0, 1)),
210 ((2, 0), (0, 0)),
211 ((2, 1), (0, 0))
212 ];
213
214 vm.segments.segment_used_sizes = Some(vec![999]);
215
216 let pointer = Relocatable::from((2, 2));
217
218 assert_eq!(
219 builtin.final_stack(&vm.segments, pointer),
220 Err(RunnerError::InvalidStopPointer(Box::new((
221 BuiltinName::poseidon,
222 relocatable!(0, 1002),
223 relocatable!(0, 0)
224 ))))
225 );
226 }
227
228 #[test]
229 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
230 fn final_stack_error_when_not_included() {
231 let mut builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), false).into();
232
233 let mut vm = vm!();
234
235 vm.segments = segments![
236 ((0, 0), (0, 0)),
237 ((0, 1), (0, 1)),
238 ((2, 0), (0, 0)),
239 ((2, 1), (0, 0))
240 ];
241
242 vm.segments.segment_used_sizes = Some(vec![0]);
243
244 let pointer = Relocatable::from((2, 2));
245
246 assert_eq!(
247 builtin.final_stack(&vm.segments, pointer).unwrap(),
248 Relocatable::from((2, 2))
249 );
250 }
251
252 #[test]
253 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
254 fn final_stack_error_non_relocatable() {
255 let mut builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), true).into();
256
257 let mut vm = vm!();
258
259 vm.segments = segments![
260 ((0, 0), (0, 0)),
261 ((0, 1), (0, 1)),
262 ((2, 0), (0, 0)),
263 ((2, 1), 2)
264 ];
265
266 vm.segments.segment_used_sizes = Some(vec![0]);
267
268 let pointer = Relocatable::from((2, 2));
269
270 assert_eq!(
271 builtin.final_stack(&vm.segments, pointer),
272 Err(RunnerError::NoStopPointer(Box::new(BuiltinName::poseidon)))
273 );
274 }
275
276 #[test]
277 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
278 fn get_used_cells_and_allocated_size_test() {
279 let builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), true).into();
280
281 let program = program!(
282 builtins = vec![BuiltinName::poseidon],
283 data = vec_data!(
284 (4612671182993129469_i64),
285 (5189976364521848832_i64),
286 (18446744073709551615_i128),
287 (5199546496550207487_i64),
288 (4612389712311386111_i64),
289 (5198983563776393216_i64),
290 (2),
291 (2345108766317314046_i64),
292 (5191102247248822272_i64),
293 (5189976364521848832_i64),
294 (7),
295 (1226245742482522112_i64),
296 ((
297 "3618502788666131213697322783095070105623107215331596699973092056135872020470",
298 10
299 )),
300 (2345108766317314046_i64)
301 ),
302 main = Some(8),
303 );
304
305 let mut cairo_runner = cairo_runner!(program);
306 cairo_runner.vm.segments.segment_used_sizes = Some(vec![0]);
307
308 let mut hint_processor = BuiltinHintProcessor::new_empty();
309
310 let address = cairo_runner.initialize(false).unwrap();
311
312 cairo_runner
313 .run_until_pc(address, &mut hint_processor)
314 .unwrap();
315
316 assert_eq!(
317 builtin.get_used_cells_and_allocated_size(&cairo_runner.vm),
318 Ok((0, 6))
319 );
320 }
321
322 #[test]
323 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
324 fn get_allocated_memory_units() {
325 let builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), true).into();
326
327 let program = program!(
328 builtins = vec![BuiltinName::poseidon],
329 data = vec_data!(
330 (4612671182993129469_i64),
331 (5189976364521848832_i64),
332 (18446744073709551615_i128),
333 (5199546496550207487_i64),
334 (4612389712311386111_i64),
335 (5198983563776393216_i64),
336 (2),
337 (2345108766317314046_i64),
338 (5191102247248822272_i64),
339 (5189976364521848832_i64),
340 (7),
341 (1226245742482522112_i64),
342 ((
343 "3618502788666131213697322783095070105623107215331596699973092056135872020470",
344 10
345 )),
346 (2345108766317314046_i64)
347 ),
348 main = Some(8),
349 );
350
351 let mut cairo_runner = cairo_runner!(program);
352
353 let mut hint_processor = BuiltinHintProcessor::new_empty();
354
355 let address = cairo_runner.initialize(false).unwrap();
356
357 cairo_runner
358 .run_until_pc(address, &mut hint_processor)
359 .unwrap();
360
361 assert_eq!(builtin.get_allocated_memory_units(&cairo_runner.vm), Ok(6));
362 }
363
364 #[test]
365 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
366 fn deduce_memory_cell_missing_input_cells_ok() {
367 let builtin = PoseidonBuiltinRunner::new(Some(10), false);
368
369 let mut vm = vm!();
370
371 vm.segments = segments![
372 ((0, 0), (0, 0)),
373 ((0, 1), (0, 1)),
374 ((2, 0), (0, 0)),
375 ((2, 1), (0, 0))
376 ];
377
378 assert_eq!(
379 builtin.deduce_memory_cell(relocatable!(0, 1), &vm.segments.memory),
380 Ok(None)
381 );
382 }
383
384 #[test]
385 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
386 fn get_air_private_input() {
387 let builtin: BuiltinRunner = PoseidonBuiltinRunner::new(None, true).into();
388
389 let segments = segments![
390 ((0, 0), 0),
391 ((0, 1), 1),
392 ((0, 2), 2),
393 ((0, 3), 3),
394 ((0, 4), 4),
395 ((0, 5), 5),
396 ((0, 6), 6),
397 ((0, 7), 7),
398 ((0, 8), 8),
399 ((0, 9), 9),
400 ((0, 10), 10),
401 ((0, 11), 11)
402 ];
403 assert_eq!(
404 builtin.air_private_input(&segments),
405 (vec![
406 PrivateInput::PoseidonState(PrivateInputPoseidonState {
407 index: 0,
408 input_s0: 0.into(),
409 input_s1: 1.into(),
410 input_s2: 2.into(),
411 }),
412 PrivateInput::PoseidonState(PrivateInputPoseidonState {
413 index: 1,
414 input_s0: 6.into(),
415 input_s1: 7.into(),
416 input_s2: 8.into(),
417 }),
418 ]),
419 );
420 }
421}