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 destination: None,
725 },
726 );
727
728 identifiers.insert(
729 String::from("__main__.main.SIZEOF_LOCALS"),
730 Identifier {
731 pc: None,
732 type_: Some(String::from("const")),
733 value: Some(Felt252::ZERO),
734 full_name: None,
735 members: None,
736 cairo_type: None,
737 size: None,
738 destination: None,
739 },
740 );
741
742 let program = Program::new(
743 builtins.clone(),
744 data.clone(),
745 None,
746 HashMap::new(),
747 reference_manager,
748 identifiers.clone(),
749 Vec::new(),
750 None,
751 )
752 .unwrap();
753
754 assert_eq!(program.builtins, builtins);
755 assert_eq!(program.shared_program_data.data, data);
756 assert_eq!(program.shared_program_data.main, None);
757 assert_eq!(program.shared_program_data.identifiers, identifiers);
758 assert_eq!(
759 program.constants,
760 [("__main__.main.SIZEOF_LOCALS", Felt252::ZERO)]
761 .into_iter()
762 .map(|(key, value)| (key.to_string(), value))
763 .collect::<HashMap<_, _>>(),
764 );
765 }
766
767 #[test]
768 fn extract_constants() {
769 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
770
771 identifiers.insert(
772 String::from("__main__.main"),
773 Identifier {
774 pc: Some(0),
775 type_: Some(String::from("function")),
776 value: None,
777 full_name: None,
778 members: None,
779 cairo_type: None,
780 size: None,
781 destination: None,
782 },
783 );
784
785 identifiers.insert(
786 String::from("__main__.main.SIZEOF_LOCALS"),
787 Identifier {
788 pc: None,
789 type_: Some(String::from("const")),
790 value: Some(Felt252::ZERO),
791 full_name: None,
792 members: None,
793 cairo_type: None,
794 size: None,
795 destination: None,
796 },
797 );
798
799 assert_eq!(
800 Program::extract_constants(&identifiers).unwrap(),
801 [("__main__.main.SIZEOF_LOCALS", Felt252::ZERO)]
802 .into_iter()
803 .map(|(key, value)| (key.to_string(), value))
804 .collect::<HashMap<_, _>>(),
805 );
806 }
807
808 #[test]
809 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
810 fn get_prime() {
811 let program = Program::default();
812 assert_eq!(PRIME_STR, program.prime());
813 }
814
815 #[test]
816 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
817 fn iter_builtins() {
818 let reference_manager = ReferenceManager {
819 references: Vec::new(),
820 };
821
822 let builtins: Vec<_> = vec![BuiltinName::range_check, BuiltinName::bitwise];
823 let data: Vec<_> = vec![
824 mayberelocatable!(5189976364521848832),
825 mayberelocatable!(1000),
826 mayberelocatable!(5189976364521848832),
827 mayberelocatable!(2000),
828 mayberelocatable!(5201798304953696256),
829 mayberelocatable!(2345108766317314046),
830 ];
831
832 let program = Program::new(
833 builtins.clone(),
834 data,
835 None,
836 HashMap::new(),
837 reference_manager,
838 HashMap::new(),
839 Vec::new(),
840 None,
841 )
842 .unwrap();
843
844 assert_eq!(
845 program.iter_builtins().cloned().collect::<Vec<_>>(),
846 builtins
847 );
848
849 assert_eq!(program.builtins_len(), 2);
850 }
851
852 #[test]
853 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
854 fn iter_data() {
855 let reference_manager = ReferenceManager {
856 references: Vec::new(),
857 };
858
859 let builtins: Vec<BuiltinName> = Vec::new();
860 let data: Vec<MaybeRelocatable> = vec![
861 mayberelocatable!(5189976364521848832),
862 mayberelocatable!(1000),
863 mayberelocatable!(5189976364521848832),
864 mayberelocatable!(2000),
865 mayberelocatable!(5201798304953696256),
866 mayberelocatable!(2345108766317314046),
867 ];
868
869 let program = Program::new(
870 builtins,
871 data.clone(),
872 None,
873 HashMap::new(),
874 reference_manager,
875 HashMap::new(),
876 Vec::new(),
877 None,
878 )
879 .unwrap();
880
881 assert_eq!(program.iter_data().cloned().collect::<Vec<_>>(), data);
882 }
883
884 #[test]
885 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
886 fn data_len() {
887 let reference_manager = ReferenceManager {
888 references: Vec::new(),
889 };
890
891 let builtins: Vec<BuiltinName> = Vec::new();
892 let data: Vec<MaybeRelocatable> = vec![
893 mayberelocatable!(5189976364521848832),
894 mayberelocatable!(1000),
895 mayberelocatable!(5189976364521848832),
896 mayberelocatable!(2000),
897 mayberelocatable!(5201798304953696256),
898 mayberelocatable!(2345108766317314046),
899 ];
900
901 let program = Program::new(
902 builtins,
903 data.clone(),
904 None,
905 HashMap::new(),
906 reference_manager,
907 HashMap::new(),
908 Vec::new(),
909 None,
910 )
911 .unwrap();
912
913 assert_eq!(program.data_len(), data.len());
914 }
915
916 #[test]
917 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
918 fn get_identifier() {
919 let reference_manager = ReferenceManager {
920 references: Vec::new(),
921 };
922
923 let builtins: Vec<BuiltinName> = Vec::new();
924
925 let data: Vec<MaybeRelocatable> = vec![
926 mayberelocatable!(5189976364521848832),
927 mayberelocatable!(1000),
928 mayberelocatable!(5189976364521848832),
929 mayberelocatable!(2000),
930 mayberelocatable!(5201798304953696256),
931 mayberelocatable!(2345108766317314046),
932 ];
933
934 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
935
936 identifiers.insert(
937 String::from("__main__.main"),
938 Identifier {
939 pc: Some(0),
940 type_: Some(String::from("function")),
941 value: None,
942 full_name: None,
943 members: None,
944 cairo_type: None,
945 size: None,
946 destination: None,
947 },
948 );
949
950 identifiers.insert(
951 String::from("__main__.main.SIZEOF_LOCALS"),
952 Identifier {
953 pc: None,
954 type_: Some(String::from("const")),
955 value: Some(Felt252::ZERO),
956 full_name: None,
957 members: None,
958 cairo_type: None,
959 size: None,
960 destination: None,
961 },
962 );
963
964 let program = Program::new(
965 builtins,
966 data,
967 None,
968 HashMap::new(),
969 reference_manager,
970 identifiers.clone(),
971 Vec::new(),
972 None,
973 )
974 .unwrap();
975
976 assert_eq!(
977 program.get_identifier("__main__.main"),
978 identifiers.get("__main__.main"),
979 );
980 assert_eq!(
981 program.get_identifier("__main__.main.SIZEOF_LOCALS"),
982 identifiers.get("__main__.main.SIZEOF_LOCALS"),
983 );
984 assert_eq!(
985 program.get_identifier("missing"),
986 identifiers.get("missing"),
987 );
988 }
989
990 #[test]
991 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
992 fn get_relocated_instruction_locations() {
993 fn build_instruction_location_for_test(start_line: u32) -> InstructionLocation {
994 InstructionLocation {
995 inst: Location {
996 end_line: 0,
997 end_col: 0,
998 input_file: InputFile {
999 filename: String::from("test"),
1000 },
1001 parent_location: None,
1002 start_line,
1003 start_col: 0,
1004 },
1005 hints: vec![],
1006 }
1007 }
1008
1009 let reference_manager = ReferenceManager {
1010 references: Vec::new(),
1011 };
1012 let builtins: Vec<BuiltinName> = Vec::new();
1013 let data: Vec<MaybeRelocatable> = vec![];
1014 let identifiers: HashMap<String, Identifier> = HashMap::new();
1015 let mut instruction_locations: HashMap<usize, InstructionLocation> = HashMap::new();
1016
1017 let il_1 = build_instruction_location_for_test(0);
1018 let il_2 = build_instruction_location_for_test(2);
1019 let il_3 = build_instruction_location_for_test(3);
1020 instruction_locations.insert(5, il_1.clone());
1021 instruction_locations.insert(10, il_2.clone());
1022 instruction_locations.insert(12, il_3.clone());
1023
1024 let program = Program::new(
1025 builtins,
1026 data,
1027 None,
1028 HashMap::new(),
1029 reference_manager,
1030 identifiers,
1031 Vec::new(),
1032 Some(instruction_locations),
1033 )
1034 .unwrap();
1035
1036 let relocated_instructions = program.get_relocated_instruction_locations(&[2]);
1037 assert!(relocated_instructions.is_some());
1038 let relocated_instructions = relocated_instructions.unwrap();
1039 assert_eq!(relocated_instructions.len(), 3);
1040 assert_eq!(relocated_instructions.get(&7), Some(&il_1));
1041 assert_eq!(relocated_instructions.get(&12), Some(&il_2));
1042 assert_eq!(relocated_instructions.get(&14), Some(&il_3));
1043 }
1044
1045 #[test]
1046 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1047 fn iter_identifiers() {
1048 let reference_manager = ReferenceManager {
1049 references: Vec::new(),
1050 };
1051
1052 let builtins: Vec<BuiltinName> = Vec::new();
1053
1054 let data: Vec<MaybeRelocatable> = vec![
1055 mayberelocatable!(5189976364521848832),
1056 mayberelocatable!(1000),
1057 mayberelocatable!(5189976364521848832),
1058 mayberelocatable!(2000),
1059 mayberelocatable!(5201798304953696256),
1060 mayberelocatable!(2345108766317314046),
1061 ];
1062
1063 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
1064
1065 identifiers.insert(
1066 String::from("__main__.main"),
1067 Identifier {
1068 pc: Some(0),
1069 type_: Some(String::from("function")),
1070 value: None,
1071 full_name: None,
1072 members: None,
1073 cairo_type: None,
1074 size: None,
1075 destination: None,
1076 },
1077 );
1078
1079 identifiers.insert(
1080 String::from("__main__.main.SIZEOF_LOCALS"),
1081 Identifier {
1082 pc: None,
1083 type_: Some(String::from("const")),
1084 value: Some(Felt252::ZERO),
1085 full_name: None,
1086 members: None,
1087 cairo_type: None,
1088 size: None,
1089 destination: None,
1090 },
1091 );
1092
1093 let program = Program::new(
1094 builtins,
1095 data,
1096 None,
1097 HashMap::new(),
1098 reference_manager,
1099 identifiers.clone(),
1100 Vec::new(),
1101 None,
1102 )
1103 .unwrap();
1104
1105 let collected_identifiers: HashMap<_, _> = program
1106 .iter_identifiers()
1107 .map(|(cairo_type, identifier)| (cairo_type.to_string(), identifier.clone()))
1108 .collect();
1109
1110 assert_eq!(collected_identifiers, identifiers);
1111 }
1112
1113 #[test]
1114 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1115 fn new_program_with_invalid_identifiers() {
1116 let reference_manager = ReferenceManager {
1117 references: Vec::new(),
1118 };
1119
1120 let builtins: Vec<BuiltinName> = Vec::new();
1121
1122 let data: Vec<MaybeRelocatable> = vec![
1123 mayberelocatable!(5189976364521848832),
1124 mayberelocatable!(1000),
1125 mayberelocatable!(5189976364521848832),
1126 mayberelocatable!(2000),
1127 mayberelocatable!(5201798304953696256),
1128 mayberelocatable!(2345108766317314046),
1129 ];
1130
1131 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
1132
1133 identifiers.insert(
1134 String::from("__main__.main"),
1135 Identifier {
1136 pc: Some(0),
1137 type_: Some(String::from("function")),
1138 value: None,
1139 full_name: None,
1140 members: None,
1141 cairo_type: None,
1142 size: None,
1143 destination: None,
1144 },
1145 );
1146
1147 identifiers.insert(
1148 String::from("__main__.main.SIZEOF_LOCALS"),
1149 Identifier {
1150 pc: None,
1151 type_: Some(String::from("const")),
1152 value: None,
1153 full_name: None,
1154 members: None,
1155 cairo_type: None,
1156 size: None,
1157 destination: None,
1158 },
1159 );
1160
1161 let program = Program::new(
1162 builtins,
1163 data,
1164 None,
1165 HashMap::new(),
1166 reference_manager,
1167 identifiers.clone(),
1168 Vec::new(),
1169 None,
1170 );
1171
1172 assert!(program.is_err());
1173 }
1174
1175 #[test]
1176 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1177 fn deserialize_program_test() {
1178 let program = Program::from_bytes(
1179 include_bytes!("../../../cairo_programs/manually_compiled/valid_program_a.json"),
1180 Some("main"),
1181 )
1182 .unwrap();
1183
1184 let builtins: Vec<BuiltinName> = Vec::new();
1185 let data: Vec<MaybeRelocatable> = vec![
1186 mayberelocatable!(5189976364521848832),
1187 mayberelocatable!(1000),
1188 mayberelocatable!(5189976364521848832),
1189 mayberelocatable!(2000),
1190 mayberelocatable!(5201798304953696256),
1191 mayberelocatable!(2345108766317314046),
1192 ];
1193
1194 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
1195
1196 identifiers.insert(
1197 String::from("__main__.main"),
1198 Identifier {
1199 pc: Some(0),
1200 type_: Some(String::from("function")),
1201 value: None,
1202 full_name: None,
1203 members: None,
1204 cairo_type: None,
1205 size: None,
1206 destination: None,
1207 },
1208 );
1209 identifiers.insert(
1210 String::from("__main__.main.Args"),
1211 Identifier {
1212 pc: None,
1213 type_: Some(String::from("struct")),
1214 value: None,
1215 full_name: Some("__main__.main.Args".to_string()),
1216 members: Some(HashMap::new()),
1217 cairo_type: None,
1218 size: Some(0),
1219 destination: None,
1220 },
1221 );
1222 identifiers.insert(
1223 String::from("__main__.main.ImplicitArgs"),
1224 Identifier {
1225 pc: None,
1226 type_: Some(String::from("struct")),
1227 value: None,
1228 full_name: Some("__main__.main.ImplicitArgs".to_string()),
1229 members: Some(HashMap::new()),
1230 cairo_type: None,
1231 size: Some(0),
1232 destination: None,
1233 },
1234 );
1235 identifiers.insert(
1236 String::from("__main__.main.Return"),
1237 Identifier {
1238 pc: None,
1239 type_: Some(String::from("struct")),
1240 value: None,
1241 full_name: Some("__main__.main.Return".to_string()),
1242 members: Some(HashMap::new()),
1243 cairo_type: None,
1244 size: Some(0),
1245 destination: None,
1246 },
1247 );
1248 identifiers.insert(
1249 String::from("__main__.main.SIZEOF_LOCALS"),
1250 Identifier {
1251 pc: None,
1252 type_: Some(String::from("const")),
1253 value: Some(Felt252::ZERO),
1254 full_name: None,
1255 members: None,
1256 cairo_type: None,
1257 size: None,
1258 destination: None,
1259 },
1260 );
1261
1262 assert_eq!(program.builtins, builtins);
1263 assert_eq!(program.shared_program_data.data, data);
1264 assert_eq!(program.shared_program_data.main, Some(0));
1265 assert_eq!(program.shared_program_data.identifiers, identifiers);
1266 }
1267
1268 #[test]
1270 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1271 fn deserialize_program_without_entrypoint_test() {
1272 let program = Program::from_bytes(
1273 include_bytes!("../../../cairo_programs/manually_compiled/valid_program_a.json"),
1274 None,
1275 )
1276 .unwrap();
1277
1278 let builtins: Vec<BuiltinName> = Vec::new();
1279
1280 let error_message_attributes: Vec<Attribute> = vec![Attribute {
1281 name: String::from("error_message"),
1282 start_pc: 379,
1283 end_pc: 381,
1284 value: String::from("SafeUint256: addition overflow"),
1285 flow_tracking_data: Some(FlowTrackingData {
1286 ap_tracking: ApTracking {
1287 group: 14,
1288 offset: 35,
1289 },
1290 reference_ids: HashMap::new(),
1291 }),
1292 }];
1293
1294 let data: Vec<MaybeRelocatable> = vec![
1295 mayberelocatable!(5189976364521848832),
1296 mayberelocatable!(1000),
1297 mayberelocatable!(5189976364521848832),
1298 mayberelocatable!(2000),
1299 mayberelocatable!(5201798304953696256),
1300 mayberelocatable!(2345108766317314046),
1301 ];
1302
1303 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
1304
1305 identifiers.insert(
1306 String::from("__main__.main"),
1307 Identifier {
1308 pc: Some(0),
1309 type_: Some(String::from("function")),
1310 value: None,
1311 full_name: None,
1312 members: None,
1313 cairo_type: None,
1314 size: None,
1315 destination: None,
1316 },
1317 );
1318 identifiers.insert(
1319 String::from("__main__.main.Args"),
1320 Identifier {
1321 pc: None,
1322 type_: Some(String::from("struct")),
1323 value: None,
1324 full_name: Some("__main__.main.Args".to_string()),
1325 members: Some(HashMap::new()),
1326 cairo_type: None,
1327 size: Some(0),
1328 destination: None,
1329 },
1330 );
1331 identifiers.insert(
1332 String::from("__main__.main.ImplicitArgs"),
1333 Identifier {
1334 pc: None,
1335 type_: Some(String::from("struct")),
1336 value: None,
1337 full_name: Some("__main__.main.ImplicitArgs".to_string()),
1338 members: Some(HashMap::new()),
1339 cairo_type: None,
1340 size: Some(0),
1341 destination: None,
1342 },
1343 );
1344 identifiers.insert(
1345 String::from("__main__.main.Return"),
1346 Identifier {
1347 pc: None,
1348 type_: Some(String::from("struct")),
1349 value: None,
1350 full_name: Some("__main__.main.Return".to_string()),
1351 members: Some(HashMap::new()),
1352 cairo_type: None,
1353 size: Some(0),
1354 destination: None,
1355 },
1356 );
1357 identifiers.insert(
1358 String::from("__main__.main.SIZEOF_LOCALS"),
1359 Identifier {
1360 pc: None,
1361 type_: Some(String::from("const")),
1362 value: Some(Felt252::ZERO),
1363 full_name: None,
1364 members: None,
1365 cairo_type: None,
1366 size: None,
1367 destination: None,
1368 },
1369 );
1370
1371 assert_eq!(program.builtins, builtins);
1372 assert_eq!(program.shared_program_data.data, data);
1373 assert_eq!(program.shared_program_data.main, None);
1374 assert_eq!(program.shared_program_data.identifiers, identifiers);
1375 assert_eq!(
1376 program.shared_program_data.error_message_attributes,
1377 error_message_attributes
1378 )
1379 }
1380
1381 #[test]
1382 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1383 fn deserialize_program_constants_test() {
1384 let program = Program::from_bytes(
1385 include_bytes!(
1386 "../../../cairo_programs/manually_compiled/deserialize_constant_test.json"
1387 ),
1388 Some("main"),
1389 )
1390 .unwrap();
1391
1392 let constants = [
1393 ("__main__.compare_abs_arrays.SIZEOF_LOCALS", Felt252::ZERO),
1394 (
1395 "starkware.cairo.common.cairo_keccak.packed_keccak.ALL_ONES",
1396 felt_hex!("0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
1397 ),
1398 (
1399 "starkware.cairo.common.cairo_keccak.packed_keccak.BLOCK_SIZE",
1400 Felt252::from(3),
1401 ),
1402 (
1403 "starkware.cairo.common.alloc.alloc.SIZEOF_LOCALS",
1404 felt_hex!("0x800000000000011000000000000000000000000000000000000000000000001")
1405 .neg(),
1406 ),
1407 (
1408 "starkware.cairo.common.uint256.SHIFT",
1409 felt_hex!("0x100000000000000000000000000000000"),
1410 ),
1411 ]
1412 .into_iter()
1413 .map(|(key, value)| (key.to_string(), value))
1414 .collect::<HashMap<_, _>>();
1415
1416 assert_eq!(program.constants, constants);
1417 }
1418
1419 #[test]
1420 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1421 fn default_program() {
1422 let hints_collection = HintsCollection {
1423 hints: Vec::new(),
1424 hints_ranges: Default::default(),
1425 };
1426
1427 let shared_program_data = SharedProgramData {
1428 data: Vec::new(),
1429 hints_collection,
1430 main: None,
1431 start: None,
1432 end: None,
1433 error_message_attributes: Vec::new(),
1434 instruction_locations: None,
1435 identifiers: HashMap::new(),
1436 reference_manager: Program::get_reference_list(&ReferenceManager {
1437 references: Vec::new(),
1438 }),
1439 };
1440 let program = Program {
1441 shared_program_data: Arc::new(shared_program_data),
1442 constants: HashMap::new(),
1443 builtins: Vec::new(),
1444 };
1445
1446 assert_eq!(program, Program::default());
1447 }
1448
1449 #[test]
1450 fn get_stripped_program() {
1451 let program_content = include_bytes!("../../../cairo_programs/pedersen_test.json");
1452 let program = Program::from_bytes(program_content, Some("main")).unwrap();
1453 let stripped_program = program.get_stripped_program().unwrap();
1454 assert_eq!(stripped_program.builtins, program.builtins);
1455 assert_eq!(stripped_program.data, program.shared_program_data.data);
1456 assert_eq!(
1457 stripped_program.main,
1458 program.shared_program_data.main.unwrap()
1459 );
1460 }
1461
1462 #[test]
1463 fn get_stripped_no_main() {
1464 let program_content =
1465 include_bytes!("../../../cairo_programs/proof_programs/fibonacci.json");
1466 let program = Program::from_bytes(program_content, None).unwrap();
1467 assert_matches!(
1468 program.get_stripped_program(),
1469 Err(ProgramError::StrippedProgramNoMain)
1470 );
1471 }
1472}