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 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    // In case of an alias - resolves to the original identifier
107    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            // Handle de Number with scientific notation cases
222            // e.g.: n = Number(1e27)
223            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            // Apply % CAIRO_PRIME, BECAUSE Felt252::from_dec_str fails with big numbers
243            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            // Apply % CAIRO_PRIME, BECAUSE Felt252::from_dec_str fails with big numbers
251            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,    // A in cast(A + B, type)
287    pub offset2: OffsetValue,    // B in cast(A + B, type)
288    pub outer_dereference: bool, // [] in [cast(A + B, type)]
289    pub inner_dereference: bool, // [] in cast([A + B], type)
290    pub value_type: String,      // type in cast(A + B, type)
291}
292
293impl ValueAddress {
294    // The parsing functionality is focused on the string formats that appear in the
295    // references used by hints. Errors may occur when parsing references not used by hints.
296    // When this happens, this default ValueAddress is returned to make explicit that the value was not
297    // parsed correctly.
298    // In case an incorrectly parsed reference is used by a hint, an error will be raised (IdNotFound) in the
299    // get_address_from_reference function call to notify this, and the parsing functionality should be
300    // extended to contemplate this new case.
301    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        // Add padding if necessary
326        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            // Add padding if necessary
348            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        // ProgramJson result instance for the json with an even length encoded hex.
520        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        // ProgramJson result instance for the json with an odd length encoded hex.
531        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        // ProgramJson result instance for the json with an odd length encoded hex.
558        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        // ProgramJson instance for the json with an even length encoded hex.
663        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        // Open json file with (valid) even length encoded hex
786        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        // Open json file with (valid) odd length encoded hex
804        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        // Open json file with (invalid) even length encoded hex
823        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        // Open json file with (invalid) odd length encoded hex
832        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    /// Deserialize a program without an entrypoint.
934    #[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        // Extracted from: https://testnet.starkscan.co/class/0x068dd0dd8a54ebdaa10563fbe193e6be1e0f7c423c0c3ce1e91c0b682a86b5f9
1626        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        // Extracted from: https://testnet.starkscan.co/class/0x071b7f73b5e2b4f81f7cf01d4d1569ccba2921b3fa3170cf11cff3720dfe918e
1636        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}