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