Skip to main content

cairo_vm/serde/
deserialize_program.rs

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