1use crate::{
2 serde::{
3 deserialize_program::{parse_program_json, ProgramJson},
4 serialize_program::ProgramSerializer,
5 },
6 stdlib::{
7 collections::{BTreeMap, HashMap},
8 prelude::*,
9 sync::Arc,
10 },
11 vm::runners::cairo_pie::StrippedProgram,
12};
13
14#[cfg(feature = "cairo-1-hints")]
15use crate::serde::deserialize_program::{ApTracking, FlowTrackingData};
16use crate::utils::PRIME_STR;
17use crate::Felt252;
18use crate::{
19 hint_processor::hint_processor_definition::HintReference,
20 serde::deserialize_program::{
21 deserialize_and_parse_program, Attribute, HintParams, Identifier, InstructionLocation,
22 OffsetValue, ReferenceManager,
23 },
24 types::{
25 errors::program_errors::ProgramError, instruction::Register, relocatable::MaybeRelocatable,
26 },
27};
28#[cfg(feature = "cairo-1-hints")]
29use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass;
30use core::num::NonZeroUsize;
31
32#[cfg(feature = "std")]
33use std::path::Path;
34
35use super::builtin_name::BuiltinName;
36#[cfg(feature = "extensive_hints")]
37use super::relocatable::Relocatable;
38#[cfg(feature = "test_utils")]
39use arbitrary::{Arbitrary, Unstructured};
40
41#[derive(Clone, Default, Debug, PartialEq, Eq)]
63pub struct SharedProgramData {
64 pub(crate) data: Vec<MaybeRelocatable>,
65 pub hints_collection: HintsCollection,
66 pub(crate) main: Option<usize>,
67 pub(crate) start: Option<usize>,
69 pub(crate) end: Option<usize>,
70 pub(crate) error_message_attributes: Vec<Attribute>,
71 pub(crate) instruction_locations: Option<HashMap<usize, InstructionLocation>>,
72 pub(crate) identifiers: HashMap<String, Identifier>,
73 pub reference_manager: Vec<HintReference>,
74}
75
76#[cfg(feature = "test_utils")]
77impl<'a> Arbitrary<'a> for SharedProgramData {
78 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
81 let mut data = Vec::new();
82 let len = usize::arbitrary(u)?;
83 for i in 0..len {
84 let instruction = u64::arbitrary(u)?;
85 data.push(MaybeRelocatable::from(Felt252::from(instruction)));
86 if instruction & 0x0004000000000000 != 0 && i < len - 1 {
88 data.push(MaybeRelocatable::from(Felt252::arbitrary(u)?));
89 }
90 }
91
92 let raw_hints = BTreeMap::<usize, Vec<HintParams>>::arbitrary(u)?;
93 let hints_collection = HintsCollection::new(&raw_hints, data.len())
94 .map_err(|_| arbitrary::Error::IncorrectFormat)?;
95 Ok(SharedProgramData {
96 data,
97 hints_collection,
98 main: Option::<usize>::arbitrary(u)?,
99 start: Option::<usize>::arbitrary(u)?,
100 end: Option::<usize>::arbitrary(u)?,
101 error_message_attributes: Vec::<Attribute>::arbitrary(u)?,
102 instruction_locations: Option::<HashMap<usize, InstructionLocation>>::arbitrary(u)?,
103 identifiers: HashMap::<String, Identifier>::arbitrary(u)?,
104 reference_manager: Vec::<HintReference>::arbitrary(u)?,
105 })
106 }
107}
108
109#[derive(Clone, Default, Debug, PartialEq, Eq)]
110pub struct HintsCollection {
111 pub hints: Vec<HintParams>,
112 #[cfg(not(feature = "extensive_hints"))]
114 pub(crate) hints_ranges: Vec<HintRange>,
115 #[cfg(feature = "extensive_hints")]
116 pub hints_ranges: HashMap<Relocatable, HintRange>,
117}
118
119impl HintsCollection {
120 pub(crate) fn new(
121 hints: &BTreeMap<usize, Vec<HintParams>>,
122 program_length: usize,
123 ) -> Result<Self, ProgramError> {
124 let bounds = hints
125 .iter()
126 .map(|(pc, hs)| (*pc, hs.len()))
127 .reduce(|(max_hint_pc, full_len), (pc, len)| (max_hint_pc.max(pc), full_len + len));
128
129 let Some((max_hint_pc, full_len)) = bounds else {
130 return Ok(HintsCollection {
131 hints: Vec::new(),
132 hints_ranges: Default::default(),
133 });
134 };
135
136 if max_hint_pc >= program_length {
137 return Err(ProgramError::InvalidHintPc(max_hint_pc, program_length));
138 }
139
140 let mut hints_values = Vec::with_capacity(full_len);
141 #[cfg(not(feature = "extensive_hints"))]
142 let mut hints_ranges = vec![None; max_hint_pc + 1];
143 #[cfg(feature = "extensive_hints")]
144 let mut hints_ranges = HashMap::default();
145 for (pc, hs) in hints.iter().filter(|(_, hs)| !hs.is_empty()) {
146 let range = (
147 hints_values.len(),
148 NonZeroUsize::new(hs.len()).expect("empty vecs already filtered"),
149 );
150 #[cfg(not(feature = "extensive_hints"))]
151 {
152 hints_ranges[*pc] = Some(range);
153 }
154 #[cfg(feature = "extensive_hints")]
155 hints_ranges.insert(Relocatable::from((0_isize, *pc)), range);
156 hints_values.extend_from_slice(&hs[..]);
157 }
158
159 Ok(HintsCollection {
160 hints: hints_values,
161 hints_ranges,
162 })
163 }
164
165 pub fn iter_hints(&self) -> impl Iterator<Item = &HintParams> {
166 self.hints.iter()
167 }
168
169 #[cfg(not(feature = "extensive_hints"))]
170 pub fn get_hint_range_for_pc(&self, pc: usize) -> Option<HintRange> {
171 self.hints_ranges.get(pc).cloned()
172 }
173}
174
175impl From<&HintsCollection> for BTreeMap<usize, Vec<HintParams>> {
176 fn from(hc: &HintsCollection) -> Self {
177 let mut hint_map = BTreeMap::new();
178 #[cfg(not(feature = "extensive_hints"))]
179 for (i, r) in hc.hints_ranges.iter().enumerate() {
180 let Some(r) = r else {
181 continue;
182 };
183 hint_map.insert(i, hc.hints[r.0..r.0 + r.1.get()].to_owned());
184 }
185 #[cfg(feature = "extensive_hints")]
186 for (pc, r) in hc.hints_ranges.iter() {
187 hint_map.insert(pc.offset, hc.hints[r.0..r.0 + r.1.get()].to_owned());
188 }
189 hint_map
190 }
191}
192
193#[cfg(not(feature = "extensive_hints"))]
195type HintRange = Option<(usize, NonZeroUsize)>;
197#[cfg(feature = "extensive_hints")]
198pub type HintRange = (usize, NonZeroUsize);
199
200#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
201#[derive(Clone, Debug, PartialEq, Eq)]
202pub struct Program {
203 pub shared_program_data: Arc<SharedProgramData>,
204 pub constants: HashMap<String, Felt252>,
205 pub(crate) builtins: Vec<BuiltinName>,
206}
207
208impl Program {
209 #[allow(clippy::too_many_arguments)]
210 pub fn new(
211 builtins: Vec<BuiltinName>,
212 data: Vec<MaybeRelocatable>,
213 main: Option<usize>,
214 hints: HashMap<usize, Vec<HintParams>>,
215 reference_manager: ReferenceManager,
216 identifiers: HashMap<String, Identifier>,
217 error_message_attributes: Vec<Attribute>,
218 instruction_locations: Option<HashMap<usize, InstructionLocation>>,
219 ) -> Result<Program, ProgramError> {
220 let constants = Self::extract_constants(&identifiers)?;
221
222 let hints: BTreeMap<_, _> = hints.into_iter().collect();
223 let hints_collection = HintsCollection::new(&hints, data.len())?;
224
225 let shared_program_data = SharedProgramData {
226 data,
227 main,
228 start: None,
229 end: None,
230 hints_collection,
231 error_message_attributes,
232 instruction_locations,
233 identifiers,
234 reference_manager: Self::get_reference_list(&reference_manager),
235 };
236 Ok(Self {
237 shared_program_data: Arc::new(shared_program_data),
238 constants,
239 builtins,
240 })
241 }
242 #[allow(clippy::too_many_arguments)]
243 pub fn new_for_proof(
244 builtins: Vec<BuiltinName>,
245 data: Vec<MaybeRelocatable>,
246 start: usize,
247 end: usize,
248 hints: HashMap<usize, Vec<HintParams>>,
249 reference_manager: ReferenceManager,
250 identifiers: HashMap<String, Identifier>,
251 error_message_attributes: Vec<Attribute>,
252 instruction_locations: Option<HashMap<usize, InstructionLocation>>,
253 ) -> Result<Program, ProgramError> {
254 let constants = Self::extract_constants(&identifiers)?;
255
256 let hints: BTreeMap<_, _> = hints.into_iter().collect();
257 let hints_collection = HintsCollection::new(&hints, data.len())?;
258
259 let shared_program_data = SharedProgramData {
260 data,
261 main: None,
262 start: Some(start),
263 end: Some(end),
264 hints_collection,
265 error_message_attributes,
266 instruction_locations,
267 identifiers,
268 reference_manager: Self::get_reference_list(&reference_manager),
269 };
270 Ok(Self {
271 shared_program_data: Arc::new(shared_program_data),
272 constants,
273 builtins,
274 })
275 }
276
277 #[cfg(feature = "std")]
278 pub fn from_file(path: &Path, entrypoint: Option<&str>) -> Result<Program, ProgramError> {
279 let file_content = std::fs::read(path)?;
280 deserialize_and_parse_program(&file_content, entrypoint)
281 }
282
283 pub fn from_bytes(bytes: &[u8], entrypoint: Option<&str>) -> Result<Program, ProgramError> {
284 deserialize_and_parse_program(bytes, entrypoint)
285 }
286
287 pub fn prime(&self) -> &str {
288 _ = self;
289 PRIME_STR
290 }
291
292 pub fn iter_builtins(&self) -> impl Iterator<Item = &BuiltinName> {
293 self.builtins.iter()
294 }
295
296 pub fn iter_data(&self) -> impl Iterator<Item = &MaybeRelocatable> {
297 self.shared_program_data.data.iter()
298 }
299
300 pub fn data_len(&self) -> usize {
301 self.shared_program_data.data.len()
302 }
303
304 pub fn builtins_len(&self) -> usize {
305 self.builtins.len()
306 }
307
308 pub fn get_identifier(&self, id: &str) -> Option<&Identifier> {
309 self.shared_program_data.identifiers.get(id)
310 }
311
312 pub fn get_relocated_instruction_locations(
313 &self,
314 relocation_table: &[usize],
315 ) -> Option<HashMap<usize, InstructionLocation>> {
316 self.shared_program_data.instruction_locations.as_ref()?;
317 let relocated_instructions = self
318 .shared_program_data
319 .instruction_locations
320 .as_ref()
321 .unwrap()
322 .iter()
323 .map(|(k, v)| (k + relocation_table[0], v.clone()))
324 .collect();
325 Some(relocated_instructions)
326 }
327
328 pub fn iter_identifiers(&self) -> impl Iterator<Item = (&str, &Identifier)> {
329 self.shared_program_data
330 .identifiers
331 .iter()
332 .map(|(cairo_type, identifier)| (cairo_type.as_str(), identifier))
333 }
334
335 pub(crate) fn get_reference_list(reference_manager: &ReferenceManager) -> Vec<HintReference> {
336 reference_manager
337 .references
338 .iter()
339 .map(|r| {
340 HintReference {
341 offset1: r.value_address.offset1.clone(),
342 offset2: r.value_address.offset2.clone(),
343 outer_dereference: r.value_address.outer_dereference,
344 inner_dereference: r.value_address.inner_dereference,
345 ap_tracking_data: match (&r.value_address.offset1, &r.value_address.offset2) {
347 (OffsetValue::Reference(Register::AP, _, _, _), _)
348 | (_, OffsetValue::Reference(Register::AP, _, _, _)) => {
349 Some(r.ap_tracking_data.clone())
350 }
351 _ => None,
352 },
353 cairo_type: Some(r.value_address.value_type.clone()),
354 }
355 })
356 .collect()
357 }
358
359 pub(crate) fn extract_constants(
360 identifiers: &HashMap<String, Identifier>,
361 ) -> Result<HashMap<String, Felt252>, ProgramError> {
362 let mut constants = HashMap::new();
363 for (key, value) in identifiers.iter() {
364 if value.type_.as_deref() == Some("const") {
365 let value = value
366 .value
367 .ok_or_else(|| ProgramError::ConstWithoutValue(key.clone()))?;
368 constants.insert(key.clone(), value);
369 }
370 }
371 Ok(constants)
372 }
373
374 pub fn get_stripped_program(&self) -> Result<StrippedProgram, ProgramError> {
378 Ok(StrippedProgram {
379 data: self.shared_program_data.data.clone(),
380 builtins: self.builtins.clone(),
381 main: self
382 .shared_program_data
383 .main
384 .ok_or(ProgramError::StrippedProgramNoMain)?,
385 prime: (),
386 })
387 }
388
389 pub fn from_stripped_program(stripped: &StrippedProgram) -> Program {
390 Program {
391 shared_program_data: Arc::new(SharedProgramData {
392 data: stripped.data.clone(),
393 main: Some(stripped.main),
394 ..Default::default()
395 }),
396 constants: Default::default(),
397 builtins: stripped.builtins.clone(),
398 }
399 }
400
401 pub fn serialize(&self) -> Result<Vec<u8>, ProgramError> {
402 let program_serializer: ProgramSerializer = ProgramSerializer::from(self);
403 let bytes: Vec<u8> = serde_json::to_vec(&program_serializer)?;
404 Ok(bytes)
405 }
406
407 pub fn deserialize(
408 program_serializer_bytes: &[u8],
409 entrypoint: Option<&str>,
410 ) -> Result<Program, ProgramError> {
411 let program_serializer: ProgramSerializer =
412 serde_json::from_slice(program_serializer_bytes)?;
413 let program_json = ProgramJson::from(program_serializer);
414 let program = parse_program_json(program_json, entrypoint)?;
415 Ok(program)
416 }
417}
418
419impl Default for Program {
420 fn default() -> Self {
421 Self {
422 shared_program_data: Arc::new(SharedProgramData::default()),
423 constants: HashMap::new(),
424 builtins: Vec::new(),
425 }
426 }
427}
428
429#[cfg(feature = "cairo-1-hints")]
430impl TryFrom<CasmContractClass> for Program {
432 type Error = ProgramError;
433 fn try_from(value: CasmContractClass) -> Result<Self, ProgramError> {
434 let data = value
435 .bytecode
436 .iter()
437 .map(|x| MaybeRelocatable::from(Felt252::from(&x.value)))
438 .collect();
439 let hints = value
442 .hints
443 .iter()
444 .map(|(x, _)| {
445 (
446 *x,
447 vec![HintParams {
448 code: x.to_string(),
449 accessible_scopes: Vec::new(),
450 flow_tracking_data: FlowTrackingData {
451 ap_tracking: ApTracking::default(),
452 reference_ids: HashMap::new(),
453 },
454 }],
455 )
456 })
457 .collect();
458 let error_message_attributes = Vec::new();
459 let reference_manager = ReferenceManager {
460 references: Vec::new(),
461 };
462 Self::new(
463 vec![],
464 data,
465 None,
466 hints,
467 reference_manager,
468 HashMap::new(),
469 error_message_attributes,
470 None,
471 )
472 }
473}
474
475#[cfg(test)]
476impl HintsCollection {
477 pub fn iter(&self) -> impl Iterator<Item = (usize, &[HintParams])> {
478 #[cfg(not(feature = "extensive_hints"))]
479 let iter = self
480 .hints_ranges
481 .iter()
482 .enumerate()
483 .filter_map(|(pc, range)| {
484 range.and_then(|(start, len)| {
485 let end = start + len.get();
486 if end <= self.hints.len() {
487 Some((pc, &self.hints[start..end]))
488 } else {
489 None
490 }
491 })
492 });
493 #[cfg(feature = "extensive_hints")]
494 let iter = self.hints_ranges.iter().filter_map(|(pc, (start, len))| {
495 let end = start + len.get();
496 if end <= self.hints.len() {
497 Some((pc.offset, &self.hints[*start..end]))
498 } else {
499 None
500 }
501 });
502 iter
503 }
504}
505
506#[cfg(test)]
507mod tests {
508 use core::ops::Neg;
509
510 use super::*;
511 use crate::felt_hex;
512 use crate::serde::deserialize_program::{ApTracking, FlowTrackingData, InputFile, Location};
513 use crate::utils::test_utils::*;
514
515 use assert_matches::assert_matches;
516
517 #[cfg(target_arch = "wasm32")]
518 use wasm_bindgen_test::*;
519
520 #[test]
521 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
522 fn new() {
523 let reference_manager = ReferenceManager {
524 references: Vec::new(),
525 };
526
527 let builtins: Vec<BuiltinName> = Vec::new();
528 let data: Vec<MaybeRelocatable> = vec![
529 mayberelocatable!(5189976364521848832),
530 mayberelocatable!(1000),
531 mayberelocatable!(5189976364521848832),
532 mayberelocatable!(2000),
533 mayberelocatable!(5201798304953696256),
534 mayberelocatable!(2345108766317314046),
535 ];
536
537 let program = Program::new(
538 builtins.clone(),
539 data.clone(),
540 None,
541 HashMap::new(),
542 reference_manager,
543 HashMap::new(),
544 Vec::new(),
545 None,
546 )
547 .unwrap();
548
549 assert_eq!(program.builtins, builtins);
550 assert_eq!(program.shared_program_data.data, data);
551 assert_eq!(program.shared_program_data.main, None);
552 assert_eq!(program.shared_program_data.identifiers, HashMap::new());
553 assert_eq!(
554 program.shared_program_data.hints_collection.hints,
555 Vec::new()
556 );
557 assert!(program
558 .shared_program_data
559 .hints_collection
560 .hints_ranges
561 .is_empty());
562 }
563
564 #[test]
565 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
566 fn new_for_proof() {
567 let reference_manager = ReferenceManager {
568 references: Vec::new(),
569 };
570
571 let builtins: Vec<BuiltinName> = Vec::new();
572 let data: Vec<MaybeRelocatable> = vec![
573 mayberelocatable!(5189976364521848832),
574 mayberelocatable!(1000),
575 mayberelocatable!(5189976364521848832),
576 mayberelocatable!(2000),
577 mayberelocatable!(5201798304953696256),
578 mayberelocatable!(2345108766317314046),
579 ];
580
581 let program = Program::new_for_proof(
582 builtins.clone(),
583 data.clone(),
584 0,
585 1,
586 HashMap::new(),
587 reference_manager,
588 HashMap::new(),
589 Vec::new(),
590 None,
591 )
592 .unwrap();
593
594 assert_eq!(program.builtins, builtins);
595 assert_eq!(program.shared_program_data.data, data);
596 assert_eq!(program.shared_program_data.main, None);
597 assert_eq!(program.shared_program_data.start, Some(0));
598 assert_eq!(program.shared_program_data.end, Some(1));
599 assert_eq!(program.shared_program_data.identifiers, HashMap::new());
600 assert_eq!(
601 program.shared_program_data.hints_collection.hints,
602 Vec::new()
603 );
604 assert!(program
605 .shared_program_data
606 .hints_collection
607 .hints_ranges
608 .is_empty());
609 }
610
611 #[test]
612 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
613 fn new_program_with_hints() {
614 let reference_manager = ReferenceManager {
615 references: Vec::new(),
616 };
617
618 let builtins: Vec<BuiltinName> = Vec::new();
619 let data: Vec<MaybeRelocatable> = vec![
620 mayberelocatable!(5189976364521848832),
621 mayberelocatable!(1000),
622 mayberelocatable!(5189976364521848832),
623 mayberelocatable!(2000),
624 mayberelocatable!(5201798304953696256),
625 mayberelocatable!(2345108766317314046),
626 ];
627
628 let str_to_hint_param = |s: &str| HintParams {
629 code: s.to_string(),
630 accessible_scopes: vec![],
631 flow_tracking_data: FlowTrackingData {
632 ap_tracking: ApTracking {
633 group: 0,
634 offset: 0,
635 },
636 reference_ids: HashMap::new(),
637 },
638 };
639
640 let hints = HashMap::from([
641 (5, vec![str_to_hint_param("c"), str_to_hint_param("d")]),
642 (1, vec![str_to_hint_param("a")]),
643 (4, vec![str_to_hint_param("b")]),
644 ]);
645
646 let program = Program::new(
647 builtins.clone(),
648 data.clone(),
649 None,
650 hints.clone(),
651 reference_manager,
652 HashMap::new(),
653 Vec::new(),
654 None,
655 )
656 .unwrap();
657
658 assert_eq!(program.builtins, builtins);
659 assert_eq!(program.shared_program_data.data, data);
660 assert_eq!(program.shared_program_data.main, None);
661 assert_eq!(program.shared_program_data.identifiers, HashMap::new());
662
663 #[cfg(not(feature = "extensive_hints"))]
664 let program_hints: HashMap<_, _> = program
665 .shared_program_data
666 .hints_collection
667 .hints_ranges
668 .iter()
669 .enumerate()
670 .filter_map(|(pc, r)| r.map(|(s, l)| (pc, (s, s + l.get()))))
671 .map(|(pc, (s, e))| {
672 (
673 pc,
674 program.shared_program_data.hints_collection.hints[s..e].to_vec(),
675 )
676 })
677 .collect();
678 #[cfg(feature = "extensive_hints")]
679 let program_hints: HashMap<_, _> = program
680 .shared_program_data
681 .hints_collection
682 .hints_ranges
683 .iter()
684 .map(|(pc, (s, l))| {
685 (
686 pc.offset,
687 program.shared_program_data.hints_collection.hints[*s..(s + l.get())].to_vec(),
688 )
689 })
690 .collect();
691 assert_eq!(program_hints, hints);
692 }
693
694 #[test]
695 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
696 fn new_program_with_identifiers() {
697 let reference_manager = ReferenceManager {
698 references: Vec::new(),
699 };
700
701 let builtins: Vec<BuiltinName> = Vec::new();
702
703 let data: Vec<MaybeRelocatable> = vec![
704 mayberelocatable!(5189976364521848832),
705 mayberelocatable!(1000),
706 mayberelocatable!(5189976364521848832),
707 mayberelocatable!(2000),
708 mayberelocatable!(5201798304953696256),
709 mayberelocatable!(2345108766317314046),
710 ];
711
712 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
713
714 identifiers.insert(
715 String::from("__main__.main"),
716 Identifier {
717 pc: Some(0),
718 type_: Some(String::from("function")),
719 value: None,
720 full_name: None,
721 members: None,
722 cairo_type: None,
723 size: None,
724 },
725 );
726
727 identifiers.insert(
728 String::from("__main__.main.SIZEOF_LOCALS"),
729 Identifier {
730 pc: None,
731 type_: Some(String::from("const")),
732 value: Some(Felt252::ZERO),
733 full_name: None,
734 members: None,
735 cairo_type: None,
736 size: None,
737 },
738 );
739
740 let program = Program::new(
741 builtins.clone(),
742 data.clone(),
743 None,
744 HashMap::new(),
745 reference_manager,
746 identifiers.clone(),
747 Vec::new(),
748 None,
749 )
750 .unwrap();
751
752 assert_eq!(program.builtins, builtins);
753 assert_eq!(program.shared_program_data.data, data);
754 assert_eq!(program.shared_program_data.main, None);
755 assert_eq!(program.shared_program_data.identifiers, identifiers);
756 assert_eq!(
757 program.constants,
758 [("__main__.main.SIZEOF_LOCALS", Felt252::ZERO)]
759 .into_iter()
760 .map(|(key, value)| (key.to_string(), value))
761 .collect::<HashMap<_, _>>(),
762 );
763 }
764
765 #[test]
766 fn extract_constants() {
767 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
768
769 identifiers.insert(
770 String::from("__main__.main"),
771 Identifier {
772 pc: Some(0),
773 type_: Some(String::from("function")),
774 value: None,
775 full_name: None,
776 members: None,
777 cairo_type: None,
778 size: None,
779 },
780 );
781
782 identifiers.insert(
783 String::from("__main__.main.SIZEOF_LOCALS"),
784 Identifier {
785 pc: None,
786 type_: Some(String::from("const")),
787 value: Some(Felt252::ZERO),
788 full_name: None,
789 members: None,
790 cairo_type: None,
791 size: None,
792 },
793 );
794
795 assert_eq!(
796 Program::extract_constants(&identifiers).unwrap(),
797 [("__main__.main.SIZEOF_LOCALS", Felt252::ZERO)]
798 .into_iter()
799 .map(|(key, value)| (key.to_string(), value))
800 .collect::<HashMap<_, _>>(),
801 );
802 }
803
804 #[test]
805 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
806 fn get_prime() {
807 let program = Program::default();
808 assert_eq!(PRIME_STR, program.prime());
809 }
810
811 #[test]
812 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
813 fn iter_builtins() {
814 let reference_manager = ReferenceManager {
815 references: Vec::new(),
816 };
817
818 let builtins: Vec<_> = vec![BuiltinName::range_check, BuiltinName::bitwise];
819 let data: Vec<_> = vec![
820 mayberelocatable!(5189976364521848832),
821 mayberelocatable!(1000),
822 mayberelocatable!(5189976364521848832),
823 mayberelocatable!(2000),
824 mayberelocatable!(5201798304953696256),
825 mayberelocatable!(2345108766317314046),
826 ];
827
828 let program = Program::new(
829 builtins.clone(),
830 data,
831 None,
832 HashMap::new(),
833 reference_manager,
834 HashMap::new(),
835 Vec::new(),
836 None,
837 )
838 .unwrap();
839
840 assert_eq!(
841 program.iter_builtins().cloned().collect::<Vec<_>>(),
842 builtins
843 );
844
845 assert_eq!(program.builtins_len(), 2);
846 }
847
848 #[test]
849 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
850 fn iter_data() {
851 let reference_manager = ReferenceManager {
852 references: Vec::new(),
853 };
854
855 let builtins: Vec<BuiltinName> = Vec::new();
856 let data: Vec<MaybeRelocatable> = vec![
857 mayberelocatable!(5189976364521848832),
858 mayberelocatable!(1000),
859 mayberelocatable!(5189976364521848832),
860 mayberelocatable!(2000),
861 mayberelocatable!(5201798304953696256),
862 mayberelocatable!(2345108766317314046),
863 ];
864
865 let program = Program::new(
866 builtins,
867 data.clone(),
868 None,
869 HashMap::new(),
870 reference_manager,
871 HashMap::new(),
872 Vec::new(),
873 None,
874 )
875 .unwrap();
876
877 assert_eq!(program.iter_data().cloned().collect::<Vec<_>>(), data);
878 }
879
880 #[test]
881 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
882 fn data_len() {
883 let reference_manager = ReferenceManager {
884 references: Vec::new(),
885 };
886
887 let builtins: Vec<BuiltinName> = Vec::new();
888 let data: Vec<MaybeRelocatable> = vec![
889 mayberelocatable!(5189976364521848832),
890 mayberelocatable!(1000),
891 mayberelocatable!(5189976364521848832),
892 mayberelocatable!(2000),
893 mayberelocatable!(5201798304953696256),
894 mayberelocatable!(2345108766317314046),
895 ];
896
897 let program = Program::new(
898 builtins,
899 data.clone(),
900 None,
901 HashMap::new(),
902 reference_manager,
903 HashMap::new(),
904 Vec::new(),
905 None,
906 )
907 .unwrap();
908
909 assert_eq!(program.data_len(), data.len());
910 }
911
912 #[test]
913 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
914 fn get_identifier() {
915 let reference_manager = ReferenceManager {
916 references: Vec::new(),
917 };
918
919 let builtins: Vec<BuiltinName> = Vec::new();
920
921 let data: Vec<MaybeRelocatable> = vec![
922 mayberelocatable!(5189976364521848832),
923 mayberelocatable!(1000),
924 mayberelocatable!(5189976364521848832),
925 mayberelocatable!(2000),
926 mayberelocatable!(5201798304953696256),
927 mayberelocatable!(2345108766317314046),
928 ];
929
930 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
931
932 identifiers.insert(
933 String::from("__main__.main"),
934 Identifier {
935 pc: Some(0),
936 type_: Some(String::from("function")),
937 value: None,
938 full_name: None,
939 members: None,
940 cairo_type: None,
941 size: None,
942 },
943 );
944
945 identifiers.insert(
946 String::from("__main__.main.SIZEOF_LOCALS"),
947 Identifier {
948 pc: None,
949 type_: Some(String::from("const")),
950 value: Some(Felt252::ZERO),
951 full_name: None,
952 members: None,
953 cairo_type: None,
954 size: None,
955 },
956 );
957
958 let program = Program::new(
959 builtins,
960 data,
961 None,
962 HashMap::new(),
963 reference_manager,
964 identifiers.clone(),
965 Vec::new(),
966 None,
967 )
968 .unwrap();
969
970 assert_eq!(
971 program.get_identifier("__main__.main"),
972 identifiers.get("__main__.main"),
973 );
974 assert_eq!(
975 program.get_identifier("__main__.main.SIZEOF_LOCALS"),
976 identifiers.get("__main__.main.SIZEOF_LOCALS"),
977 );
978 assert_eq!(
979 program.get_identifier("missing"),
980 identifiers.get("missing"),
981 );
982 }
983
984 #[test]
985 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
986 fn get_relocated_instruction_locations() {
987 fn build_instruction_location_for_test(start_line: u32) -> InstructionLocation {
988 InstructionLocation {
989 inst: Location {
990 end_line: 0,
991 end_col: 0,
992 input_file: InputFile {
993 filename: String::from("test"),
994 },
995 parent_location: None,
996 start_line,
997 start_col: 0,
998 },
999 hints: vec![],
1000 }
1001 }
1002
1003 let reference_manager = ReferenceManager {
1004 references: Vec::new(),
1005 };
1006 let builtins: Vec<BuiltinName> = Vec::new();
1007 let data: Vec<MaybeRelocatable> = vec![];
1008 let identifiers: HashMap<String, Identifier> = HashMap::new();
1009 let mut instruction_locations: HashMap<usize, InstructionLocation> = HashMap::new();
1010
1011 let il_1 = build_instruction_location_for_test(0);
1012 let il_2 = build_instruction_location_for_test(2);
1013 let il_3 = build_instruction_location_for_test(3);
1014 instruction_locations.insert(5, il_1.clone());
1015 instruction_locations.insert(10, il_2.clone());
1016 instruction_locations.insert(12, il_3.clone());
1017
1018 let program = Program::new(
1019 builtins,
1020 data,
1021 None,
1022 HashMap::new(),
1023 reference_manager,
1024 identifiers,
1025 Vec::new(),
1026 Some(instruction_locations),
1027 )
1028 .unwrap();
1029
1030 let relocated_instructions = program.get_relocated_instruction_locations(&[2]);
1031 assert!(relocated_instructions.is_some());
1032 let relocated_instructions = relocated_instructions.unwrap();
1033 assert_eq!(relocated_instructions.len(), 3);
1034 assert_eq!(relocated_instructions.get(&7), Some(&il_1));
1035 assert_eq!(relocated_instructions.get(&12), Some(&il_2));
1036 assert_eq!(relocated_instructions.get(&14), Some(&il_3));
1037 }
1038
1039 #[test]
1040 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1041 fn iter_identifiers() {
1042 let reference_manager = ReferenceManager {
1043 references: Vec::new(),
1044 };
1045
1046 let builtins: Vec<BuiltinName> = Vec::new();
1047
1048 let data: Vec<MaybeRelocatable> = vec![
1049 mayberelocatable!(5189976364521848832),
1050 mayberelocatable!(1000),
1051 mayberelocatable!(5189976364521848832),
1052 mayberelocatable!(2000),
1053 mayberelocatable!(5201798304953696256),
1054 mayberelocatable!(2345108766317314046),
1055 ];
1056
1057 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
1058
1059 identifiers.insert(
1060 String::from("__main__.main"),
1061 Identifier {
1062 pc: Some(0),
1063 type_: Some(String::from("function")),
1064 value: None,
1065 full_name: None,
1066 members: None,
1067 cairo_type: None,
1068 size: None,
1069 },
1070 );
1071
1072 identifiers.insert(
1073 String::from("__main__.main.SIZEOF_LOCALS"),
1074 Identifier {
1075 pc: None,
1076 type_: Some(String::from("const")),
1077 value: Some(Felt252::ZERO),
1078 full_name: None,
1079 members: None,
1080 cairo_type: None,
1081 size: None,
1082 },
1083 );
1084
1085 let program = Program::new(
1086 builtins,
1087 data,
1088 None,
1089 HashMap::new(),
1090 reference_manager,
1091 identifiers.clone(),
1092 Vec::new(),
1093 None,
1094 )
1095 .unwrap();
1096
1097 let collected_identifiers: HashMap<_, _> = program
1098 .iter_identifiers()
1099 .map(|(cairo_type, identifier)| (cairo_type.to_string(), identifier.clone()))
1100 .collect();
1101
1102 assert_eq!(collected_identifiers, identifiers);
1103 }
1104
1105 #[test]
1106 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1107 fn new_program_with_invalid_identifiers() {
1108 let reference_manager = ReferenceManager {
1109 references: Vec::new(),
1110 };
1111
1112 let builtins: Vec<BuiltinName> = Vec::new();
1113
1114 let data: Vec<MaybeRelocatable> = vec![
1115 mayberelocatable!(5189976364521848832),
1116 mayberelocatable!(1000),
1117 mayberelocatable!(5189976364521848832),
1118 mayberelocatable!(2000),
1119 mayberelocatable!(5201798304953696256),
1120 mayberelocatable!(2345108766317314046),
1121 ];
1122
1123 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
1124
1125 identifiers.insert(
1126 String::from("__main__.main"),
1127 Identifier {
1128 pc: Some(0),
1129 type_: Some(String::from("function")),
1130 value: None,
1131 full_name: None,
1132 members: None,
1133 cairo_type: None,
1134 size: None,
1135 },
1136 );
1137
1138 identifiers.insert(
1139 String::from("__main__.main.SIZEOF_LOCALS"),
1140 Identifier {
1141 pc: None,
1142 type_: Some(String::from("const")),
1143 value: None,
1144 full_name: None,
1145 members: None,
1146 cairo_type: None,
1147 size: None,
1148 },
1149 );
1150
1151 let program = Program::new(
1152 builtins,
1153 data,
1154 None,
1155 HashMap::new(),
1156 reference_manager,
1157 identifiers.clone(),
1158 Vec::new(),
1159 None,
1160 );
1161
1162 assert!(program.is_err());
1163 }
1164
1165 #[test]
1166 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1167 fn deserialize_program_test() {
1168 let program = Program::from_bytes(
1169 include_bytes!("../../../cairo_programs/manually_compiled/valid_program_a.json"),
1170 Some("main"),
1171 )
1172 .unwrap();
1173
1174 let builtins: Vec<BuiltinName> = Vec::new();
1175 let data: Vec<MaybeRelocatable> = vec![
1176 mayberelocatable!(5189976364521848832),
1177 mayberelocatable!(1000),
1178 mayberelocatable!(5189976364521848832),
1179 mayberelocatable!(2000),
1180 mayberelocatable!(5201798304953696256),
1181 mayberelocatable!(2345108766317314046),
1182 ];
1183
1184 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
1185
1186 identifiers.insert(
1187 String::from("__main__.main"),
1188 Identifier {
1189 pc: Some(0),
1190 type_: Some(String::from("function")),
1191 value: None,
1192 full_name: None,
1193 members: None,
1194 cairo_type: None,
1195 size: None,
1196 },
1197 );
1198 identifiers.insert(
1199 String::from("__main__.main.Args"),
1200 Identifier {
1201 pc: None,
1202 type_: Some(String::from("struct")),
1203 value: None,
1204 full_name: Some("__main__.main.Args".to_string()),
1205 members: Some(HashMap::new()),
1206 cairo_type: None,
1207 size: Some(0),
1208 },
1209 );
1210 identifiers.insert(
1211 String::from("__main__.main.ImplicitArgs"),
1212 Identifier {
1213 pc: None,
1214 type_: Some(String::from("struct")),
1215 value: None,
1216 full_name: Some("__main__.main.ImplicitArgs".to_string()),
1217 members: Some(HashMap::new()),
1218 cairo_type: None,
1219 size: Some(0),
1220 },
1221 );
1222 identifiers.insert(
1223 String::from("__main__.main.Return"),
1224 Identifier {
1225 pc: None,
1226 type_: Some(String::from("struct")),
1227 value: None,
1228 full_name: Some("__main__.main.Return".to_string()),
1229 members: Some(HashMap::new()),
1230 cairo_type: None,
1231 size: Some(0),
1232 },
1233 );
1234 identifiers.insert(
1235 String::from("__main__.main.SIZEOF_LOCALS"),
1236 Identifier {
1237 pc: None,
1238 type_: Some(String::from("const")),
1239 value: Some(Felt252::ZERO),
1240 full_name: None,
1241 members: None,
1242 cairo_type: None,
1243 size: None,
1244 },
1245 );
1246
1247 assert_eq!(program.builtins, builtins);
1248 assert_eq!(program.shared_program_data.data, data);
1249 assert_eq!(program.shared_program_data.main, Some(0));
1250 assert_eq!(program.shared_program_data.identifiers, identifiers);
1251 }
1252
1253 #[test]
1255 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1256 fn deserialize_program_without_entrypoint_test() {
1257 let program = Program::from_bytes(
1258 include_bytes!("../../../cairo_programs/manually_compiled/valid_program_a.json"),
1259 None,
1260 )
1261 .unwrap();
1262
1263 let builtins: Vec<BuiltinName> = Vec::new();
1264
1265 let error_message_attributes: Vec<Attribute> = vec![Attribute {
1266 name: String::from("error_message"),
1267 start_pc: 379,
1268 end_pc: 381,
1269 value: String::from("SafeUint256: addition overflow"),
1270 flow_tracking_data: Some(FlowTrackingData {
1271 ap_tracking: ApTracking {
1272 group: 14,
1273 offset: 35,
1274 },
1275 reference_ids: HashMap::new(),
1276 }),
1277 }];
1278
1279 let data: Vec<MaybeRelocatable> = vec![
1280 mayberelocatable!(5189976364521848832),
1281 mayberelocatable!(1000),
1282 mayberelocatable!(5189976364521848832),
1283 mayberelocatable!(2000),
1284 mayberelocatable!(5201798304953696256),
1285 mayberelocatable!(2345108766317314046),
1286 ];
1287
1288 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
1289
1290 identifiers.insert(
1291 String::from("__main__.main"),
1292 Identifier {
1293 pc: Some(0),
1294 type_: Some(String::from("function")),
1295 value: None,
1296 full_name: None,
1297 members: None,
1298 cairo_type: None,
1299 size: None,
1300 },
1301 );
1302 identifiers.insert(
1303 String::from("__main__.main.Args"),
1304 Identifier {
1305 pc: None,
1306 type_: Some(String::from("struct")),
1307 value: None,
1308 full_name: Some("__main__.main.Args".to_string()),
1309 members: Some(HashMap::new()),
1310 cairo_type: None,
1311 size: Some(0),
1312 },
1313 );
1314 identifiers.insert(
1315 String::from("__main__.main.ImplicitArgs"),
1316 Identifier {
1317 pc: None,
1318 type_: Some(String::from("struct")),
1319 value: None,
1320 full_name: Some("__main__.main.ImplicitArgs".to_string()),
1321 members: Some(HashMap::new()),
1322 cairo_type: None,
1323 size: Some(0),
1324 },
1325 );
1326 identifiers.insert(
1327 String::from("__main__.main.Return"),
1328 Identifier {
1329 pc: None,
1330 type_: Some(String::from("struct")),
1331 value: None,
1332 full_name: Some("__main__.main.Return".to_string()),
1333 members: Some(HashMap::new()),
1334 cairo_type: None,
1335 size: Some(0),
1336 },
1337 );
1338 identifiers.insert(
1339 String::from("__main__.main.SIZEOF_LOCALS"),
1340 Identifier {
1341 pc: None,
1342 type_: Some(String::from("const")),
1343 value: Some(Felt252::ZERO),
1344 full_name: None,
1345 members: None,
1346 cairo_type: None,
1347 size: None,
1348 },
1349 );
1350
1351 assert_eq!(program.builtins, builtins);
1352 assert_eq!(program.shared_program_data.data, data);
1353 assert_eq!(program.shared_program_data.main, None);
1354 assert_eq!(program.shared_program_data.identifiers, identifiers);
1355 assert_eq!(
1356 program.shared_program_data.error_message_attributes,
1357 error_message_attributes
1358 )
1359 }
1360
1361 #[test]
1362 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1363 fn deserialize_program_constants_test() {
1364 let program = Program::from_bytes(
1365 include_bytes!(
1366 "../../../cairo_programs/manually_compiled/deserialize_constant_test.json"
1367 ),
1368 Some("main"),
1369 )
1370 .unwrap();
1371
1372 let constants = [
1373 ("__main__.compare_abs_arrays.SIZEOF_LOCALS", Felt252::ZERO),
1374 (
1375 "starkware.cairo.common.cairo_keccak.packed_keccak.ALL_ONES",
1376 felt_hex!("0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
1377 ),
1378 (
1379 "starkware.cairo.common.cairo_keccak.packed_keccak.BLOCK_SIZE",
1380 Felt252::from(3),
1381 ),
1382 (
1383 "starkware.cairo.common.alloc.alloc.SIZEOF_LOCALS",
1384 felt_hex!("0x800000000000011000000000000000000000000000000000000000000000001")
1385 .neg(),
1386 ),
1387 (
1388 "starkware.cairo.common.uint256.SHIFT",
1389 felt_hex!("0x100000000000000000000000000000000"),
1390 ),
1391 ]
1392 .into_iter()
1393 .map(|(key, value)| (key.to_string(), value))
1394 .collect::<HashMap<_, _>>();
1395
1396 assert_eq!(program.constants, constants);
1397 }
1398
1399 #[test]
1400 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1401 fn default_program() {
1402 let hints_collection = HintsCollection {
1403 hints: Vec::new(),
1404 hints_ranges: Default::default(),
1405 };
1406
1407 let shared_program_data = SharedProgramData {
1408 data: Vec::new(),
1409 hints_collection,
1410 main: None,
1411 start: None,
1412 end: None,
1413 error_message_attributes: Vec::new(),
1414 instruction_locations: None,
1415 identifiers: HashMap::new(),
1416 reference_manager: Program::get_reference_list(&ReferenceManager {
1417 references: Vec::new(),
1418 }),
1419 };
1420 let program = Program {
1421 shared_program_data: Arc::new(shared_program_data),
1422 constants: HashMap::new(),
1423 builtins: Vec::new(),
1424 };
1425
1426 assert_eq!(program, Program::default());
1427 }
1428
1429 #[test]
1430 fn get_stripped_program() {
1431 let program_content = include_bytes!("../../../cairo_programs/pedersen_test.json");
1432 let program = Program::from_bytes(program_content, Some("main")).unwrap();
1433 let stripped_program = program.get_stripped_program().unwrap();
1434 assert_eq!(stripped_program.builtins, program.builtins);
1435 assert_eq!(stripped_program.data, program.shared_program_data.data);
1436 assert_eq!(
1437 stripped_program.main,
1438 program.shared_program_data.main.unwrap()
1439 );
1440 }
1441
1442 #[test]
1443 fn get_stripped_no_main() {
1444 let program_content =
1445 include_bytes!("../../../cairo_programs/proof_programs/fibonacci.json");
1446 let program = Program::from_bytes(program_content, None).unwrap();
1447 assert_matches!(
1448 program.get_stripped_program(),
1449 Err(ProgramError::StrippedProgramNoMain)
1450 );
1451 }
1452}