1use crate::{
10 stdlib::{
11 collections::{BTreeMap, HashMap},
12 fmt,
13 prelude::*,
14 sync::Arc,
15 },
16 types::builtin_name::BuiltinName,
17 utils::CAIRO_PRIME,
18};
19
20use crate::utils::PRIME_STR;
21use crate::Felt252;
22use crate::{
23 serde::deserialize_utils,
24 types::{
25 errors::program_errors::ProgramError,
26 instruction::Register,
27 program::{HintsCollection, Program, SharedProgramData},
28 relocatable::MaybeRelocatable,
29 },
30};
31use num_bigint::BigInt;
32use num_traits::{float::FloatCore, Num};
33use serde::{de, de::MapAccess, de::SeqAccess, Deserialize, Deserializer, Serialize};
34use serde_json::Number;
35
36#[cfg(feature = "test_utils")]
37use arbitrary::{self, Arbitrary, Unstructured};
38
39#[cfg_attr(feature = "test_utils", derive(Arbitrary, Clone))]
40#[derive(Deserialize, Debug)]
41pub struct ProgramJson {
42 pub prime: String,
43 pub builtins: Vec<BuiltinName>,
44 #[serde(deserialize_with = "deserialize_array_of_bigint_hex")]
45 pub data: Vec<MaybeRelocatable>,
46 pub identifiers: HashMap<String, Identifier>,
47 pub hints: BTreeMap<usize, Vec<HintParams>>,
48 pub reference_manager: ReferenceManager,
49 #[serde(default)]
50 pub attributes: Vec<Attribute>,
51 pub debug_info: Option<DebugInfo>,
52}
53
54#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
55#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
56pub struct HintParams {
57 pub code: String,
58 pub accessible_scopes: Vec<String>,
59 pub flow_tracking_data: FlowTrackingData,
60}
61
62#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
63#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
64pub struct FlowTrackingData {
65 pub ap_tracking: ApTracking,
66 #[serde(deserialize_with = "deserialize_map_to_string_and_usize_hashmap")]
67 pub reference_ids: HashMap<String, usize>,
68}
69
70#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
71#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
72pub struct ApTracking {
73 pub group: usize,
74 pub offset: usize,
75}
76
77impl ApTracking {
78 pub fn new() -> ApTracking {
79 ApTracking {
80 group: 0,
81 offset: 0,
82 }
83 }
84}
85
86impl Default for ApTracking {
87 fn default() -> Self {
88 Self::new()
89 }
90}
91
92#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
93#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
94pub struct Identifier {
95 pub pc: Option<usize>,
96 #[serde(rename(deserialize = "type"))]
97 pub type_: Option<String>,
98 #[serde(default)]
99 #[serde(deserialize_with = "felt_from_number")]
100 pub value: Option<Felt252>,
101
102 pub full_name: Option<String>,
103 pub members: Option<HashMap<String, Member>>,
104 pub cairo_type: Option<String>,
105 pub size: Option<usize>,
106 pub destination: Option<String>,
108}
109
110#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
111#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
112pub struct Member {
113 pub cairo_type: String,
114 pub offset: usize,
115}
116
117#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
118#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
119pub struct Attribute {
120 pub name: String,
121 pub start_pc: usize,
122 pub end_pc: usize,
123 pub value: String,
124 #[cfg_attr(feature = "test_utils", serde(skip_serializing_if = "Option::is_none"))]
125 pub flow_tracking_data: Option<FlowTrackingData>,
126}
127
128#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
129pub struct Location {
130 pub end_line: u32,
131 pub end_col: u32,
132 pub input_file: InputFile,
133 pub parent_location: Option<(Box<Location>, String)>,
134 pub start_line: u32,
135 pub start_col: u32,
136}
137
138#[cfg(feature = "test_utils")]
139impl<'a> Arbitrary<'a> for Location {
140 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
141 arbitrary_parent_location(u, 20)
142 }
143}
144
145#[cfg(feature = "test_utils")]
146fn arbitrary_parent_location(u: &mut Unstructured, depth: u8) -> arbitrary::Result<Location> {
147 let parent_location = if depth > 0 {
148 Some((
149 Box::new(arbitrary_parent_location(u, depth - 1)?),
150 String::arbitrary(u)?,
151 ))
152 } else {
153 None
154 };
155 Ok(Location {
156 end_line: u32::arbitrary(u)?,
157 end_col: u32::arbitrary(u)?,
158 input_file: InputFile::arbitrary(u)?,
159 parent_location,
160 start_line: u32::arbitrary(u)?,
161 start_col: u32::arbitrary(u)?,
162 })
163}
164
165#[cfg_attr(feature = "test_utils", derive(Arbitrary, Clone))]
166#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
167pub struct DebugInfo {
168 pub(crate) instruction_locations: HashMap<usize, InstructionLocation>,
169}
170
171impl DebugInfo {
172 pub fn new(instruction_locations: HashMap<usize, InstructionLocation>) -> Self {
173 Self {
174 instruction_locations,
175 }
176 }
177 pub fn get_instruction_locations(&self) -> HashMap<usize, InstructionLocation> {
178 self.instruction_locations.clone()
179 }
180}
181
182#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
183#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
184pub struct InstructionLocation {
185 pub inst: Location,
186 pub hints: Vec<HintLocation>,
187}
188
189#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
190#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
191pub struct InputFile {
192 pub filename: String,
193}
194
195impl InputFile {
196 #[cfg(feature = "std")]
197 pub fn get_content(&self) -> Result<String, String> {
198 let content = std::fs::read_to_string(self.filename.clone());
199 if let Ok(content) = content {
200 return Ok(content);
201 }
202 Err(format!("Failed to read file {}", self.filename.clone()))
203 }
204}
205
206#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
207#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
208pub struct HintLocation {
209 pub location: Location,
210 pub n_prefix_newlines: u32,
211}
212
213fn felt_from_number<'de, D>(deserializer: D) -> Result<Option<Felt252>, D::Error>
214where
215 D: Deserializer<'de>,
216{
217 let n = Number::deserialize(deserializer)?;
218 match Felt252::from_dec_str(&n.to_string()).ok() {
219 Some(x) => Ok(Some(x)),
220 None => {
221 let felt = deserialize_scientific_notation(n);
224 if felt.is_some() {
225 return Ok(felt);
226 }
227
228 Err(de::Error::custom(String::from(
229 "felt_from_number parse error",
230 )))
231 }
232 }
233}
234
235fn deserialize_scientific_notation(n: Number) -> Option<Felt252> {
236 match n.as_f64() {
237 None => {
238 let str = n.to_string();
239 let list: [&str; 2] = str.split('e').collect::<Vec<&str>>().try_into().ok()?;
240 let exponent = list[1].parse::<u128>().ok()?;
241
242 let prime_bigint = BigInt::from_biguint(num_bigint::Sign::Plus, CAIRO_PRIME.clone());
244 let base_bigint = BigInt::from_str_radix(list[0], 10).ok()? % prime_bigint;
245 let base = Felt252::from_dec_str(&base_bigint.to_string()).ok()?;
246
247 Some(base * Felt252::from(10).pow(exponent))
248 }
249 Some(float) => {
250 let prime_bigint = BigInt::from_biguint(num_bigint::Sign::Plus, CAIRO_PRIME.clone());
252 let number = BigInt::from_str_radix(&FloatCore::round(float).to_string(), 10).ok()?
253 % prime_bigint;
254 Felt252::from_dec_str(&number.to_string()).ok()
255 }
256 }
257}
258
259#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
260#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
261pub struct ReferenceManager {
262 pub references: Vec<Reference>,
263}
264
265#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
266#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
267pub struct Reference {
268 pub ap_tracking_data: ApTracking,
269 pub pc: Option<usize>,
270 #[serde(deserialize_with = "deserialize_value_address")]
271 #[serde(rename(deserialize = "value"))]
272 pub value_address: ValueAddress,
273}
274
275#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
276#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
277pub enum OffsetValue {
278 Immediate(Felt252),
279 Value(i32),
280 Reference(Register, i32, bool, bool),
281}
282
283#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
284#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
285pub struct ValueAddress {
286 pub offset1: OffsetValue, pub offset2: OffsetValue, pub outer_dereference: bool, pub inner_dereference: bool, pub value_type: String, }
292
293impl ValueAddress {
294 pub fn no_hint_reference_default() -> ValueAddress {
302 ValueAddress {
303 offset1: OffsetValue::Value(99),
304 offset2: OffsetValue::Value(99),
305 outer_dereference: false,
306 inner_dereference: false,
307 value_type: String::from("felt"),
308 }
309 }
310}
311
312struct Felt252Visitor;
313
314impl de::Visitor<'_> for Felt252Visitor {
315 type Value = Felt252;
316
317 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
318 formatter.write_str("Could not deserialize hexadecimal string")
319 }
320
321 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
322 where
323 E: de::Error,
324 {
325 let value = deserialize_utils::maybe_add_padding(value.to_string());
327 Felt252::from_hex(&value).map_err(de::Error::custom)
328 }
329}
330
331struct MaybeRelocatableVisitor;
332
333impl<'de> de::Visitor<'de> for MaybeRelocatableVisitor {
334 type Value = Vec<MaybeRelocatable>;
335
336 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
337 formatter.write_str("Could not deserialize array of hexadecimal")
338 }
339
340 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
341 where
342 A: SeqAccess<'de>,
343 {
344 let mut data: Vec<MaybeRelocatable> = vec![];
345
346 while let Some(value) = seq.next_element::<String>()? {
347 let value = deserialize_utils::maybe_add_padding(value.to_string());
349 data.push(MaybeRelocatable::Int(
350 Felt252::from_hex(&value).map_err(de::Error::custom)?,
351 ));
352 }
353 Ok(data)
354 }
355}
356
357struct ReferenceIdsVisitor;
358
359impl<'de> de::Visitor<'de> for ReferenceIdsVisitor {
360 type Value = HashMap<String, usize>;
361
362 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
363 formatter.write_str("a map with string keys and integer values")
364 }
365
366 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
367 where
368 A: MapAccess<'de>,
369 {
370 let mut data: HashMap<String, usize> = HashMap::new();
371
372 while let Some((key, value)) = map.next_entry::<String, usize>()? {
373 data.insert(key, value);
374 }
375
376 Ok(data)
377 }
378}
379
380struct ValueAddressVisitor;
381
382impl de::Visitor<'_> for ValueAddressVisitor {
383 type Value = ValueAddress;
384
385 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
386 formatter.write_str("a string representing the address in memory of a variable")
387 }
388
389 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
390 where
391 E: de::Error,
392 {
393 let parse_res = deserialize_utils::parse_value(value);
394
395 if let Ok((_, res)) = parse_res {
396 return Ok(res);
397 }
398
399 Ok(ValueAddress::no_hint_reference_default())
400 }
401}
402
403pub fn deserialize_felt_hex<'de, D: Deserializer<'de>>(d: D) -> Result<Felt252, D::Error> {
404 d.deserialize_str(Felt252Visitor)
405}
406
407pub fn deserialize_array_of_bigint_hex<'de, D: Deserializer<'de>>(
408 d: D,
409) -> Result<Vec<MaybeRelocatable>, D::Error> {
410 d.deserialize_seq(MaybeRelocatableVisitor)
411}
412
413pub fn deserialize_map_to_string_and_usize_hashmap<'de, D: Deserializer<'de>>(
414 d: D,
415) -> Result<HashMap<String, usize>, D::Error> {
416 d.deserialize_map(ReferenceIdsVisitor)
417}
418
419pub fn deserialize_value_address<'de, D: Deserializer<'de>>(
420 d: D,
421) -> Result<ValueAddress, D::Error> {
422 d.deserialize_str(ValueAddressVisitor)
423}
424
425pub fn deserialize_program_json(reader: &[u8]) -> Result<ProgramJson, ProgramError> {
426 let program_json = serde_json::from_slice(reader)?;
427 Ok(program_json)
428}
429pub fn deserialize_and_parse_program(
430 reader: &[u8],
431 entrypoint: Option<&str>,
432) -> Result<Program, ProgramError> {
433 let program_json: ProgramJson = deserialize_program_json(reader)?;
434 parse_program_json(program_json, entrypoint)
435}
436
437pub fn parse_program_json(
438 program_json: ProgramJson,
439 entrypoint: Option<&str>,
440) -> Result<Program, ProgramError> {
441 if PRIME_STR != program_json.prime {
442 return Err(ProgramError::PrimeDiffers(program_json.prime));
443 }
444
445 let entrypoint_pc = match entrypoint {
446 Some(entrypoint) => match program_json
447 .identifiers
448 .get(&format!("__main__.{entrypoint}"))
449 {
450 Some(entrypoint_identifier) => entrypoint_identifier.pc,
451 None => return Err(ProgramError::EntrypointNotFound(entrypoint.to_string())),
452 },
453 None => None,
454 };
455
456 let start = match program_json.identifiers.get("__main__.__start__") {
457 Some(identifier) => identifier.pc,
458 None => None,
459 };
460 let end = match program_json.identifiers.get("__main__.__end__") {
461 Some(identifier) => identifier.pc,
462 None => None,
463 };
464
465 let mut constants = HashMap::new();
466 for (key, value) in program_json.identifiers.iter() {
467 if value.type_.as_deref() == Some("const") {
468 let value = value
469 .value
470 .ok_or_else(|| ProgramError::ConstWithoutValue(key.clone()))?;
471 constants.insert(key.clone(), value);
472 }
473 }
474
475 let hints_collection = HintsCollection::new(&program_json.hints, program_json.data.len())?;
476
477 let shared_program_data = SharedProgramData {
478 data: program_json.data,
479 hints_collection,
480 main: entrypoint_pc,
481 start,
482 end,
483 error_message_attributes: program_json
484 .attributes
485 .into_iter()
486 .filter(|attr| attr.name == "error_message")
487 .collect(),
488 instruction_locations: program_json
489 .debug_info
490 .map(|debug_info| debug_info.instruction_locations),
491 identifiers: program_json.identifiers,
492 reference_manager: Program::get_reference_list(&program_json.reference_manager),
493 };
494 Ok(Program {
495 shared_program_data: Arc::new(shared_program_data),
496 constants: Arc::new(constants),
497 builtins: program_json.builtins,
498 })
499}
500
501#[cfg(test)]
502mod tests {
503 use super::*;
504 use crate::felt_str;
505 use assert_matches::assert_matches;
506 use core::num::NonZeroUsize;
507
508 #[cfg(target_arch = "wasm32")]
509 use wasm_bindgen_test::*;
510
511 #[test]
512 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
513 fn deserialize_bigint_from_string_json_gives_error() {
514 let invalid_even_length_hex_json = r#"
515 {
516 "prime": "0bx000A"
517 }"#;
518
519 let even_result: Result<ProgramJson, _> =
521 serde_json::from_str(invalid_even_length_hex_json);
522
523 assert!(even_result.is_err());
524
525 let invalid_odd_length_hex_json = r#"
526 {
527 "prime": "0bx00A"
528 }"#;
529
530 let odd_result: Result<ProgramJson, _> = serde_json::from_str(invalid_odd_length_hex_json);
532
533 assert!(odd_result.is_err());
534 }
535
536 #[test]
537 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
538 fn deserialize_bigint_invalid_char_error() {
539 let invalid_char = r#"
540 {
541 "prime": "0xlambda"
542 }"#;
543
544 let invalid_char_error: Result<ProgramJson, _> = serde_json::from_str(invalid_char);
545
546 assert!(invalid_char_error.is_err());
547 }
548
549 #[test]
550 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
551 fn deserialize_bigint_no_prefix_error() {
552 let no_prefix = r#"
553 {
554 "prime": "00A"
555 }"#;
556
557 let no_prefix_error: Result<ProgramJson, _> = serde_json::from_str(no_prefix);
559
560 assert!(no_prefix_error.is_err());
561 }
562
563 #[test]
564 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
565 fn deserialize_from_string_json() {
566 let valid_json = r#"
567 {
568 "prime": "0x800000000000011000000000000000000000000000000000000000000000001",
569 "attributes": [],
570 "debug_info": {
571 "instruction_locations": {}
572 },
573 "builtins": [],
574 "data": [
575 "0x480680017fff8000",
576 "0x3e8",
577 "0x480680017fff8000",
578 "0x7d0",
579 "0x48307fff7ffe8000",
580 "0x208b7fff7fff7ffe"
581 ],
582 "identifiers": {
583 "__main__.main": {
584 "decorators": [],
585 "pc": 0,
586 "type": "function"
587 },
588 "__main__.main.Args": {
589 "full_name": "__main__.main.Args",
590 "members": {},
591 "size": 0,
592 "type": "struct"
593 },
594 "__main__.main.ImplicitArgs": {
595 "full_name": "__main__.main.ImplicitArgs",
596 "members": {},
597 "size": 0,
598 "type": "struct"
599 }
600 },
601 "hints": {
602 "0": [
603 {
604 "accessible_scopes": [
605 "starkware.cairo.common.alloc",
606 "starkware.cairo.common.alloc.alloc"
607 ],
608 "code": "memory[ap] = segments.add()",
609 "flow_tracking_data": {
610 "ap_tracking": {
611 "group": 0,
612 "offset": 0
613 },
614 "reference_ids": {
615 "starkware.cairo.common.math.split_felt.high": 0,
616 "starkware.cairo.common.math.split_felt.low": 14,
617 "starkware.cairo.common.math.split_felt.range_check_ptr": 16,
618 "starkware.cairo.common.math.split_felt.value": 12
619 }
620 }
621 }
622 ]
623 },
624 "reference_manager": {
625 "references": [
626 {
627 "ap_tracking_data": {
628 "group": 0,
629 "offset": 0
630 },
631 "pc": 0,
632 "value": "[cast(fp + (-4), felt*)]"
633 },
634 {
635 "ap_tracking_data": {
636 "group": 0,
637 "offset": 0
638 },
639 "pc": 0,
640 "value": "[cast(fp + (-3), felt*)]"
641 },
642 {
643 "ap_tracking_data": {
644 "group": 0,
645 "offset": 0
646 },
647 "pc": 0,
648 "value": "cast([fp + (-3)] + 2, felt)"
649 },
650 {
651 "ap_tracking_data": {
652 "group": 0,
653 "offset": 0
654 },
655 "pc": 0,
656 "value": "[cast(fp, felt**)]"
657 }
658 ]
659 }
660 }"#;
661
662 let program_json: ProgramJson = serde_json::from_str(valid_json).unwrap();
664
665 let data: Vec<MaybeRelocatable> = vec![
666 MaybeRelocatable::Int(Felt252::from(5189976364521848832_i64)),
667 MaybeRelocatable::Int(Felt252::from(1000_i64)),
668 MaybeRelocatable::Int(Felt252::from(5189976364521848832_i64)),
669 MaybeRelocatable::Int(Felt252::from(2000_i64)),
670 MaybeRelocatable::Int(Felt252::from(5201798304953696256_i64)),
671 MaybeRelocatable::Int(Felt252::from(2345108766317314046_i64)),
672 ];
673
674 let mut hints = BTreeMap::new();
675 hints.insert(
676 0,
677 vec![HintParams {
678 code: "memory[ap] = segments.add()".to_string(),
679 accessible_scopes: vec![
680 String::from("starkware.cairo.common.alloc"),
681 String::from("starkware.cairo.common.alloc.alloc"),
682 ],
683 flow_tracking_data: FlowTrackingData {
684 ap_tracking: ApTracking {
685 group: 0,
686 offset: 0,
687 },
688 reference_ids: HashMap::from([
689 (
690 String::from("starkware.cairo.common.math.split_felt.high"),
691 0,
692 ),
693 (
694 String::from("starkware.cairo.common.math.split_felt.low"),
695 14,
696 ),
697 (
698 String::from("starkware.cairo.common.math.split_felt.range_check_ptr"),
699 16,
700 ),
701 (
702 String::from("starkware.cairo.common.math.split_felt.value"),
703 12,
704 ),
705 ]),
706 },
707 }],
708 );
709
710 let reference_manager = ReferenceManager {
711 references: vec![
712 Reference {
713 ap_tracking_data: ApTracking {
714 group: 0,
715 offset: 0,
716 },
717 pc: Some(0),
718 value_address: ValueAddress {
719 offset1: OffsetValue::Reference(Register::FP, -4, false, true),
720 offset2: OffsetValue::Value(0),
721 outer_dereference: true,
722 inner_dereference: false,
723 value_type: "felt".to_string(),
724 },
725 },
726 Reference {
727 ap_tracking_data: ApTracking {
728 group: 0,
729 offset: 0,
730 },
731 pc: Some(0),
732 value_address: ValueAddress {
733 offset1: OffsetValue::Reference(Register::FP, -3, false, true),
734 offset2: OffsetValue::Value(0),
735 outer_dereference: true,
736 inner_dereference: false,
737 value_type: "felt".to_string(),
738 },
739 },
740 Reference {
741 ap_tracking_data: ApTracking {
742 group: 0,
743 offset: 0,
744 },
745 pc: Some(0),
746 value_address: ValueAddress {
747 offset1: OffsetValue::Reference(Register::FP, -3, true, true),
748 offset2: OffsetValue::Immediate(Felt252::from(2)),
749 outer_dereference: false,
750 inner_dereference: false,
751 value_type: "felt".to_string(),
752 },
753 },
754 Reference {
755 ap_tracking_data: ApTracking {
756 group: 0,
757 offset: 0,
758 },
759 pc: Some(0),
760 value_address: ValueAddress {
761 offset1: OffsetValue::Reference(Register::FP, 0, false, true),
762 offset2: OffsetValue::Value(0),
763 outer_dereference: true,
764 inner_dereference: false,
765 value_type: "felt*".to_string(),
766 },
767 },
768 ],
769 };
770
771 assert_eq!(
772 program_json.prime,
773 "0x800000000000011000000000000000000000000000000000000000000000001"
774 );
775 assert!(program_json.builtins.is_empty());
776 assert_eq!(program_json.data, data);
777 assert_eq!(program_json.identifiers["__main__.main"].pc, Some(0));
778 assert_eq!(program_json.hints, hints);
779 assert_eq!(program_json.reference_manager, reference_manager);
780 }
781
782 #[test]
783 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
784 fn deserialize_program_json_from_json_file_a() {
785 let reader =
787 include_bytes!("../../../cairo_programs/manually_compiled/valid_program_a.json");
788
789 let program_json: ProgramJson = serde_json::from_slice(reader).unwrap();
790
791 assert_eq!(
792 program_json.prime,
793 "0x800000000000011000000000000000000000000000000000000000000000001"
794 );
795 assert!(program_json.builtins.is_empty());
796 assert_eq!(program_json.data.len(), 6);
797 assert_eq!(program_json.identifiers["__main__.main"].pc, Some(0));
798 }
799
800 #[test]
801 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
802 fn deserialize_program_json_from_json_file_b() {
803 let reader =
805 include_bytes!("../../../cairo_programs/manually_compiled/valid_program_b.json");
806
807 let program_json: ProgramJson = serde_json::from_slice(reader).unwrap();
808 let builtins: Vec<BuiltinName> = vec![BuiltinName::output, BuiltinName::range_check];
809
810 assert_eq!(
811 program_json.prime,
812 "0x800000000000011000000000000000000000000000000000000000000000001"
813 );
814 assert_eq!(program_json.builtins, builtins);
815 assert_eq!(program_json.data.len(), 24);
816 assert_eq!(program_json.identifiers["__main__.main"].pc, Some(13));
817 }
818
819 #[test]
820 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
821 fn deserialize_program_json_from_json_file_gives_error() {
822 let reader = include_bytes!(
824 "../../../cairo_programs/manually_compiled/invalid_even_length_hex.json"
825 );
826
827 let even_result: Result<ProgramJson, _> = serde_json::from_slice(reader);
828
829 assert!(even_result.is_err());
830
831 let reader =
833 include_bytes!("../../../cairo_programs/manually_compiled/invalid_odd_length_hex.json");
834
835 let odd_result: Result<ProgramJson, _> = serde_json::from_slice(reader);
836
837 assert!(odd_result.is_err());
838 }
839
840 #[test]
841 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
842 fn deserialize_missing_entrypoint_gives_error() {
843 let reader =
844 include_bytes!("../../../cairo_programs/manually_compiled/valid_program_a.json");
845
846 let deserialization_result =
847 deserialize_and_parse_program(reader, Some("missing_function"));
848 assert!(deserialization_result.is_err());
849 assert_matches!(
850 deserialization_result,
851 Err(ProgramError::EntrypointNotFound(_))
852 );
853 }
854
855 fn get_hints_as_map(program: &Program) -> HashMap<usize, Vec<HintParams>> {
856 let hints_collection = &program.shared_program_data.hints_collection;
857 let hints_map: HashMap<usize, Vec<HintParams>> = hints_collection
858 .iter()
859 .map(|(pc, hints)| (pc, hints.to_vec()))
860 .collect();
861
862 hints_map
863 }
864
865 #[test]
866 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
867 fn deserialize_program_test() {
868 let reader =
869 include_bytes!("../../../cairo_programs/manually_compiled/valid_program_a.json");
870
871 let program: Program = deserialize_and_parse_program(reader, Some("main"))
872 .expect("Failed to deserialize program");
873
874 let builtins: Vec<BuiltinName> = Vec::new();
875 let data: Vec<MaybeRelocatable> = vec![
876 MaybeRelocatable::Int(Felt252::from(5189976364521848832_i64)),
877 MaybeRelocatable::Int(Felt252::from(1000)),
878 MaybeRelocatable::Int(Felt252::from(5189976364521848832_i64)),
879 MaybeRelocatable::Int(Felt252::from(2000)),
880 MaybeRelocatable::Int(Felt252::from(5201798304953696256_i64)),
881 MaybeRelocatable::Int(Felt252::from(2345108766317314046_i64)),
882 ];
883
884 let hints: HashMap<_, _> = [
885 (
886 0,
887 vec![HintParams {
888 code: "memory[ap] = segments.add()".to_string(),
889 accessible_scopes: vec![
890 String::from("starkware.cairo.common.alloc"),
891 String::from("starkware.cairo.common.alloc.alloc"),
892 ],
893 flow_tracking_data: FlowTrackingData {
894 ap_tracking: ApTracking {
895 group: 0,
896 offset: 0,
897 },
898 reference_ids: HashMap::new(),
899 },
900 }],
901 ),
902 (
903 4,
904 vec![HintParams {
905 code: "import math".to_string(),
906 accessible_scopes: vec![
907 String::from("__main__"),
908 String::from("__main__.main"),
909 ],
910 flow_tracking_data: FlowTrackingData {
911 ap_tracking: ApTracking {
912 group: 5,
913 offset: 0,
914 },
915 reference_ids: HashMap::new(),
916 },
917 }],
918 ),
919 ]
920 .into();
921 let mut hints_ranges = vec![None; 47];
922 hints_ranges[0] = Some((0, NonZeroUsize::new(1).unwrap()));
923 hints_ranges[46] = Some((1, NonZeroUsize::new(1).unwrap()));
924
925 assert_eq!(program.builtins, builtins);
926 assert_eq!(program.shared_program_data.data, data);
927 assert_eq!(program.shared_program_data.main, Some(0));
928
929 let program_hints = get_hints_as_map(&program);
930 assert_eq!(program_hints, hints);
931 }
932
933 #[test]
935 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
936 fn deserialize_program_without_entrypoint() {
937 let reader =
938 include_bytes!("../../../cairo_programs/manually_compiled/valid_program_a.json");
939
940 let program: Program =
941 deserialize_and_parse_program(reader, None).expect("Failed to deserialize program");
942
943 let builtins: Vec<BuiltinName> = Vec::new();
944 let data: Vec<MaybeRelocatable> = vec![
945 MaybeRelocatable::Int(Felt252::from(5189976364521848832_i64)),
946 MaybeRelocatable::Int(Felt252::from(1000)),
947 MaybeRelocatable::Int(Felt252::from(5189976364521848832_i64)),
948 MaybeRelocatable::Int(Felt252::from(2000)),
949 MaybeRelocatable::Int(Felt252::from(5201798304953696256_i64)),
950 MaybeRelocatable::Int(Felt252::from(2345108766317314046_i64)),
951 ];
952
953 let hints: HashMap<_, _> = [
954 (
955 0,
956 vec![HintParams {
957 code: "memory[ap] = segments.add()".to_string(),
958 accessible_scopes: vec![
959 String::from("starkware.cairo.common.alloc"),
960 String::from("starkware.cairo.common.alloc.alloc"),
961 ],
962 flow_tracking_data: FlowTrackingData {
963 ap_tracking: ApTracking {
964 group: 0,
965 offset: 0,
966 },
967 reference_ids: HashMap::new(),
968 },
969 }],
970 ),
971 (
972 4,
973 vec![HintParams {
974 code: "import math".to_string(),
975 accessible_scopes: vec![
976 String::from("__main__"),
977 String::from("__main__.main"),
978 ],
979 flow_tracking_data: FlowTrackingData {
980 ap_tracking: ApTracking {
981 group: 5,
982 offset: 0,
983 },
984 reference_ids: HashMap::new(),
985 },
986 }],
987 ),
988 ]
989 .into();
990
991 assert_eq!(program.builtins, builtins);
992 assert_eq!(program.shared_program_data.data, data);
993 assert_eq!(program.shared_program_data.main, None);
994
995 let program_hints = get_hints_as_map(&program);
996 assert_eq!(program_hints, hints);
997 }
998
999 #[test]
1000 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1001 fn deserialize_constant() {
1002 let reader = include_bytes!(
1003 "../../../cairo_programs/manually_compiled/deserialize_constant_test.json"
1004 );
1005
1006 let program_json: ProgramJson = serde_json::from_slice(reader).unwrap();
1007 let mut identifiers: HashMap<String, Identifier> = HashMap::new();
1008
1009 identifiers.insert(
1010 String::from("__main__.main"),
1011 Identifier {
1012 pc: Some(0),
1013 type_: Some(String::from("function")),
1014 value: None,
1015 full_name: None,
1016 members: None,
1017 cairo_type: None,
1018 size: None,
1019 destination: None,
1020 },
1021 );
1022 identifiers.insert(
1023 String::from("__main__.compare_abs_arrays.SIZEOF_LOCALS"),
1024 Identifier {
1025 pc: None,
1026 type_: Some(String::from("const")),
1027 value: Some(felt_str!(
1028 "-3618502788666131213697322783095070105623107215331596699973092056135872020481"
1029 )),
1030 full_name: None,
1031 members: None,
1032 cairo_type: None,
1033 size: None,
1034 destination: None,
1035 },
1036 );
1037 identifiers.insert(
1038 String::from("starkware.cairo.common.cairo_keccak.keccak.unsigned_div_rem"),
1039 Identifier {
1040 pc: None,
1041 type_: Some(String::from("alias")),
1042 value: None,
1043 full_name: None,
1044 members: None,
1045 cairo_type: None,
1046 size: None,
1047 destination: Some(String::from("starkware.cairo.common.math.unsigned_div_rem")),
1048 },
1049 );
1050 identifiers.insert(
1051 String::from("starkware.cairo.common.cairo_keccak.packed_keccak.ALL_ONES"),
1052 Identifier {
1053 pc: None,
1054 type_: Some(String::from("const")),
1055 value: Some(felt_str!(
1056 "-106710729501573572985208420194530329073740042555888586719234"
1057 )),
1058 full_name: None,
1059 members: None,
1060 cairo_type: None,
1061 size: None,
1062 destination: None,
1063 },
1064 );
1065 identifiers.insert(
1066 String::from("starkware.cairo.common.cairo_keccak.packed_keccak.BLOCK_SIZE"),
1067 Identifier {
1068 pc: None,
1069 type_: Some(String::from("const")),
1070 value: Some(Felt252::from(3)),
1071 full_name: None,
1072 members: None,
1073 cairo_type: None,
1074 size: None,
1075 destination: None,
1076 },
1077 );
1078 identifiers.insert(
1079 String::from("starkware.cairo.common.alloc.alloc.SIZEOF_LOCALS"),
1080 Identifier {
1081 pc: None,
1082 type_: Some(String::from("const")),
1083 value: Some(Felt252::ZERO),
1084 full_name: None,
1085 members: None,
1086 cairo_type: None,
1087 size: None,
1088 destination: None,
1089 },
1090 );
1091 identifiers.insert(
1092 String::from("starkware.cairo.common.uint256.SHIFT"),
1093 Identifier {
1094 pc: None,
1095 type_: Some(String::from("const")),
1096 value: Some(felt_str!("340282366920938463463374607431768211456")),
1097 full_name: None,
1098 members: None,
1099 cairo_type: None,
1100 size: None,
1101 destination: None,
1102 },
1103 );
1104
1105 assert_eq!(program_json.identifiers, identifiers);
1106 }
1107
1108 #[test]
1109 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1110 fn value_address_no_hint_reference_default_test() {
1111 let valid_json = r#"
1112 {
1113 "prime": "0x800000000000011000000000000000000000000000000000000000000000001",
1114 "attributes": [],
1115 "debug_info": {
1116 "instruction_locations": {}
1117 },
1118 "builtins": [],
1119 "data": [
1120 ],
1121 "identifiers": {
1122 },
1123 "hints": {
1124 },
1125 "reference_manager": {
1126 "references": [
1127 {
1128 "ap_tracking_data": {
1129 "group": 0,
1130 "offset": 0
1131 },
1132 "pc": 0,
1133 "value": ""
1134 }
1135 ]
1136 }
1137 }"#;
1138
1139 let program_json: ProgramJson = serde_json::from_str(valid_json).unwrap();
1140
1141 let reference_manager = ReferenceManager {
1142 references: vec![Reference {
1143 ap_tracking_data: ApTracking {
1144 group: 0,
1145 offset: 0,
1146 },
1147 pc: Some(0),
1148 value_address: ValueAddress::no_hint_reference_default(),
1149 }],
1150 };
1151
1152 assert_eq!(program_json.reference_manager, reference_manager);
1153 }
1154
1155 #[test]
1156 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1157 fn deserialize_attributes_test() {
1158 let valid_json = r#"
1159 {
1160 "prime": "0x800000000000011000000000000000000000000000000000000000000000001",
1161 "attributes": [
1162 {
1163 "accessible_scopes": [
1164 "openzeppelin.security.safemath.library",
1165 "openzeppelin.security.safemath.library.SafeUint256",
1166 "openzeppelin.security.safemath.library.SafeUint256.add"
1167 ],
1168 "end_pc": 381,
1169 "flow_tracking_data": {
1170 "ap_tracking": {
1171 "group": 14,
1172 "offset": 35
1173 },
1174 "reference_ids": {}
1175 },
1176 "name": "error_message",
1177 "start_pc": 379,
1178 "value": "SafeUint256: addition overflow"
1179 },
1180 {
1181 "accessible_scopes": [
1182 "openzeppelin.security.safemath.library",
1183 "openzeppelin.security.safemath.library.SafeUint256",
1184 "openzeppelin.security.safemath.library.SafeUint256.sub_le"
1185 ],
1186 "end_pc": 404,
1187 "flow_tracking_data": {
1188 "ap_tracking": {
1189 "group": 15,
1190 "offset": 60
1191 },
1192 "reference_ids": {}
1193 },
1194 "name": "error_message",
1195 "start_pc": 402,
1196 "value": "SafeUint256: subtraction overflow"
1197 }
1198 ],
1199 "debug_info": {
1200 "instruction_locations": {}
1201 },
1202 "builtins": [],
1203 "data": [
1204 ],
1205 "identifiers": {
1206 },
1207 "hints": {
1208 },
1209 "reference_manager": {
1210 "references": [
1211 ]
1212 }
1213 }"#;
1214
1215 let program_json: ProgramJson = serde_json::from_str(valid_json).unwrap();
1216
1217 let attributes: Vec<Attribute> = vec![
1218 Attribute {
1219 name: String::from("error_message"),
1220 start_pc: 379,
1221 end_pc: 381,
1222 value: String::from("SafeUint256: addition overflow"),
1223 flow_tracking_data: Some(FlowTrackingData {
1224 ap_tracking: ApTracking {
1225 group: 14,
1226 offset: 35,
1227 },
1228 reference_ids: HashMap::new(),
1229 }),
1230 },
1231 Attribute {
1232 name: String::from("error_message"),
1233 start_pc: 402,
1234 end_pc: 404,
1235 value: String::from("SafeUint256: subtraction overflow"),
1236 flow_tracking_data: Some(FlowTrackingData {
1237 ap_tracking: ApTracking {
1238 group: 15,
1239 offset: 60,
1240 },
1241 reference_ids: HashMap::new(),
1242 }),
1243 },
1244 ];
1245
1246 assert_eq!(program_json.attributes, attributes);
1247 }
1248
1249 #[test]
1250 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1251 fn deserialize_instruction_locations_test_no_parent() {
1252 let valid_json = r#"
1253 {
1254 "prime": "0x800000000000011000000000000000000000000000000000000000000000001",
1255 "attributes": [],
1256 "debug_info": {
1257 "file_contents": {},
1258 "instruction_locations": {
1259 "0": {
1260 "accessible_scopes": [
1261 "starkware.cairo.lang.compiler.lib.registers",
1262 "starkware.cairo.lang.compiler.lib.registers.get_fp_and_pc"
1263 ],
1264 "flow_tracking_data": {
1265 "ap_tracking": {
1266 "group": 0,
1267 "offset": 0
1268 },
1269 "reference_ids": {}
1270 },
1271 "hints": [],
1272 "inst": {
1273 "end_col": 73,
1274 "end_line": 7,
1275 "input_file": {
1276 "filename": "/Users/user/test/env/lib/python3.9/site-packages/starkware/cairo/lang/compiler/lib/registers.cairo"
1277 },
1278 "start_col": 5,
1279 "start_line": 7
1280 }
1281 },
1282 "3": {
1283 "accessible_scopes": [
1284 "starkware.cairo.common.alloc",
1285 "starkware.cairo.common.alloc.alloc"
1286 ],
1287 "flow_tracking_data": {
1288 "ap_tracking": {
1289 "group": 1,
1290 "offset": 1
1291 },
1292 "reference_ids": {}
1293 },
1294 "hints": [],
1295 "inst": {
1296 "end_col": 40,
1297 "end_line": 5,
1298 "input_file": {
1299 "filename": "/Users/user/test/env/lib/python3.9/site-packages/starkware/cairo/common/alloc.cairo"
1300 },
1301 "start_col": 5,
1302 "start_line": 5
1303 }
1304 }
1305 }
1306 },
1307 "builtins": [],
1308 "data": [
1309 ],
1310 "identifiers": {
1311 },
1312 "hints": {
1313 },
1314 "reference_manager": {
1315 "references": [
1316 ]
1317 }
1318 }"#;
1319
1320 let program_json: ProgramJson = serde_json::from_str(valid_json).unwrap();
1321
1322 let debug_info: DebugInfo = DebugInfo {
1323 instruction_locations: HashMap::from([
1324 (
1325 0,
1326 InstructionLocation {
1327 inst: Location {
1328 end_line: 7,
1329 end_col: 73,
1330 input_file: InputFile { filename: String::from("/Users/user/test/env/lib/python3.9/site-packages/starkware/cairo/lang/compiler/lib/registers.cairo") },
1331 parent_location: None,
1332 start_line: 7,
1333 start_col: 5,
1334 },
1335 hints: vec![],
1336 },
1337 ),
1338 (
1339 3,
1340 InstructionLocation {
1341 inst: Location {
1342 end_line: 5,
1343 end_col: 40,
1344 input_file: InputFile { filename: String::from("/Users/user/test/env/lib/python3.9/site-packages/starkware/cairo/common/alloc.cairo") },
1345 parent_location: None,
1346 start_line: 5,
1347 start_col: 5,
1348 },
1349 hints: vec![],
1350 },
1351 ),
1352 ]),
1353 };
1354
1355 assert_eq!(program_json.debug_info, Some(debug_info));
1356 }
1357
1358 #[test]
1359 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1360 fn deserialize_instruction_locations_test_with_parent() {
1361 let valid_json = r#"
1362 {
1363 "prime": "0x800000000000011000000000000000000000000000000000000000000000001",
1364 "attributes": [],
1365 "debug_info": {
1366 "file_contents": {},
1367 "instruction_locations": {
1368 "4": {
1369 "accessible_scopes": [
1370 "__main__",
1371 "__main__",
1372 "__main__.constructor"
1373 ],
1374 "flow_tracking_data": null,
1375 "hints": [],
1376 "inst": {
1377 "end_col": 36,
1378 "end_line": 9,
1379 "input_file": {
1380 "filename": "test/contracts/cairo/always_fail.cairo"
1381 },
1382 "parent_location": [
1383 {
1384 "end_col": 36,
1385 "end_line": 9,
1386 "input_file": {
1387 "filename": "test/contracts/cairo/always_fail.cairo"
1388 },
1389 "parent_location": [
1390 {
1391 "end_col": 15,
1392 "end_line": 11,
1393 "input_file": {
1394 "filename": "test/contracts/cairo/always_fail.cairo"
1395 },
1396 "start_col": 5,
1397 "start_line": 11
1398 },
1399 "While trying to retrieve the implicit argument 'syscall_ptr' in:"
1400 ],
1401 "start_col": 18,
1402 "start_line": 9
1403 },
1404 "While expanding the reference 'syscall_ptr' in:"
1405 ],
1406 "start_col": 18,
1407 "start_line": 9
1408 }
1409 }
1410 }
1411 },
1412 "builtins": [],
1413 "data": [
1414 ],
1415 "identifiers": {
1416 },
1417 "hints": {
1418 },
1419 "reference_manager": {
1420 "references": [
1421 ]
1422 }
1423 }"#;
1424
1425 let program_json: ProgramJson = serde_json::from_str(valid_json).unwrap();
1426
1427 let debug_info: DebugInfo = DebugInfo { instruction_locations: HashMap::from(
1428 [
1429 (4, InstructionLocation {
1430 inst: Location { end_line: 9, end_col: 36,input_file: InputFile { filename: String::from("test/contracts/cairo/always_fail.cairo") }, parent_location: Some(
1431 (Box::new(Location {
1432 end_line: 9,
1433 end_col: 36,
1434 input_file: InputFile { filename: String::from("test/contracts/cairo/always_fail.cairo") },
1435 parent_location: Some(
1436 ( Box::new(Location {
1437 end_line: 11,
1438 end_col: 15,
1439 input_file: InputFile { filename: String::from("test/contracts/cairo/always_fail.cairo") },
1440 parent_location: None,
1441 start_line: 11,
1442 start_col: 5,
1443 })
1444 , String::from("While trying to retrieve the implicit argument 'syscall_ptr' in:")
1445 )
1446 ),
1447 start_line: 9,
1448 start_col: 18,
1449 }), String::from( "While expanding the reference 'syscall_ptr' in:"))
1450 ), start_line: 9, start_col: 18 },
1451 hints: vec![],
1452 }),
1453 ]
1454 ) };
1455
1456 assert_eq!(program_json.debug_info, Some(debug_info));
1457 }
1458
1459 #[test]
1460 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1461 fn deserialize_program_with_type_definition() {
1462 let reader = include_bytes!("../../../cairo_programs/uint256_integration_tests.json");
1463
1464 let program_json: ProgramJson = serde_json::from_slice(reader).unwrap();
1465
1466 assert_eq!(
1467 program_json.identifiers["starkware.cairo.common.alloc.alloc.Return"]
1468 .cairo_type
1469 .as_ref()
1470 .expect("key not found"),
1471 "(ptr: felt*)"
1472 );
1473 assert_eq!(
1474 program_json.identifiers["starkware.cairo.common.uint256.uint256_add.Return"]
1475 .cairo_type
1476 .as_ref()
1477 .expect("key not found"),
1478 "(res: starkware.cairo.common.uint256.Uint256, carry: felt)"
1479 );
1480 assert_eq!(
1481 program_json.identifiers["__main__.test_unsigned_div_rem.Return"]
1482 .cairo_type
1483 .as_ref()
1484 .expect("key not found"),
1485 "()"
1486 );
1487 }
1488
1489 #[test]
1490 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1491 fn deserialize_nonbase10_number_errors() {
1492 let valid_json = r#"
1493 {
1494 "value" : 0x123
1495 }"#;
1496
1497 let iden: Result<Identifier, serde_json::Error> = serde_json::from_str(valid_json);
1498 assert!(iden.err().is_some());
1499 }
1500
1501 #[test]
1502 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1503 fn test_felt_from_number_with_scientific_notation() {
1504 let n = Number::deserialize(serde_json::Value::from(1e27)).unwrap();
1505 assert_eq!(n.to_string(), "1e27".to_owned());
1506
1507 assert_matches!(
1508 felt_from_number(n),
1509 Ok(x) if x == Some(Felt252::ONE * Felt252::from(10).pow(27_u32))
1510 );
1511 }
1512
1513 #[test]
1514 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1515 fn test_felt_from_number_with_scientific_notation_with_fractional_part() {
1516 let n = serde_json::Value::Number(Number::from_f64(64e+74).unwrap());
1517
1518 assert_matches!(
1519 felt_from_number(n),
1520 Ok(x) if x == Some(Felt252::from_dec_str("64").unwrap() * Felt252::from(10).pow(74_u32))
1521 );
1522 }
1523
1524 #[test]
1525 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1526 fn test_felt_from_number_with_scientific_notation_with_fractional_part_f64_max() {
1527 let n = serde_json::Value::Number(Number::from_f64(f64::MAX).unwrap());
1528 assert_eq!(
1529 felt_from_number(n).unwrap(),
1530 Some(
1531 Felt252::from_dec_str(
1532 "2082797363194934431336897723140298717588791783575467744530053896730196177808",
1533 )
1534 .unwrap()
1535 )
1536 );
1537 }
1538
1539 #[test]
1540 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1541 fn test_felt_from_number_with_scientific_notation_big_exponent() {
1542 #[derive(Deserialize, Debug, PartialEq)]
1543 struct Test {
1544 #[serde(deserialize_with = "felt_from_number")]
1545 f: Option<Felt252>,
1546 }
1547 let malicious_input = &format!(
1548 "{{ \"f\": {}e{} }}",
1549 String::from_utf8(vec![b'9'; 1000]).unwrap(),
1550 u32::MAX
1551 );
1552 let f = serde_json::from_str::<Test>(malicious_input)
1553 .unwrap()
1554 .f
1555 .unwrap();
1556 assert_eq!(
1557 f,
1558 Felt252::from_dec_str(
1559 "2471602022505793130446032259107029522557827898253184929958153020344968292412",
1560 )
1561 .unwrap()
1562 );
1563 }
1564
1565 #[test]
1566 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1567 fn test_felt_from_number_with_scientific_notation_negative() {
1568 let n = Number::deserialize(serde_json::Value::from(-1e27)).unwrap();
1569 assert_eq!(n.to_string(), "-1e27".to_owned());
1570
1571 let felt = felt_from_number(n).unwrap().unwrap();
1572
1573 assert_eq!(felt, Felt252::from(-1) * Felt252::from(10).pow(27_u32));
1574 }
1575
1576 #[test]
1577 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1578 fn deserialize_program_with_invalid_hint_pc() {
1579 let reader = br#"{
1580 "attributes": [],
1581 "builtins": [],
1582 "compiler_version": "0.11.0",
1583 "data": [
1584 "0x41241"
1585 ],
1586 "debug_info": {
1587 "instruction_locations": {}
1588 },
1589 "hints": {
1590 "1": [
1591 {
1592 "accessible_scopes": [],
1593 "code": "",
1594 "flow_tracking_data": {
1595 "ap_tracking": {
1596 "group": 0,
1597 "offset": 0
1598 },
1599 "reference_ids": {}
1600 }
1601 }
1602 ]
1603 },
1604 "identifiers": {
1605 "__main__.main": {}
1606 },
1607 "main_scope": "",
1608 "prime": "0x800000000000011000000000000000000000000000000000000000000000001",
1609 "reference_manager": {
1610 "references": []
1611 }
1612 }"#;
1613
1614 let deserialization_result = deserialize_and_parse_program(reader, Some("main"));
1615
1616 assert!(deserialization_result.is_err());
1617 assert_matches!(
1618 deserialization_result.unwrap_err(),
1619 ProgramError::InvalidHintPc(1, 1)
1620 );
1621 }
1622
1623 #[test]
1624 fn parse_without_program_attributes() {
1625 let program = include_bytes!(concat!(
1627 env!("CARGO_MANIFEST_DIR"),
1628 "/../cairo_programs/manually_compiled/program_without_attributes.json",
1629 ));
1630 _ = deserialize_and_parse_program(program, None).expect("should be able to read file");
1631 }
1632
1633 #[test]
1634 fn parse_without_program_attributes_2() {
1635 let program = include_bytes!(concat!(
1637 env!("CARGO_MANIFEST_DIR"),
1638 "/../cairo_programs/manually_compiled/program_without_attributes_2.json",
1639 ));
1640 _ = deserialize_and_parse_program(program, None).expect("should be able to read file");
1641 }
1642}