1use crate::{
10 stdlib::{
11 collections::{BTreeMap, HashMap},
12 fmt,
13 prelude::*,
14 sync::Arc,
15 },
16 types::builtin_name::BuiltinName,
17 utils::CAIRO_PRIME,
18};
19
20use crate::utils::PRIME_STR;
21use crate::Felt252;
22use crate::{
23 serde::deserialize_utils,
24 types::{
25 errors::program_errors::ProgramError,
26 instruction::Register,
27 program::{HintsCollection, Program, SharedProgramData},
28 relocatable::MaybeRelocatable,
29 },
30};
31use num_bigint::BigInt;
32use num_traits::{float::FloatCore, Num};
33use serde::{de, de::MapAccess, de::SeqAccess, Deserialize, Deserializer, Serialize};
34use serde_json::Number;
35
36#[cfg(feature = "test_utils")]
37use arbitrary::{self, Arbitrary, Unstructured};
38
39#[cfg_attr(feature = "test_utils", derive(Arbitrary, Clone))]
40#[derive(Deserialize, Debug)]
41pub struct ProgramJson {
42 pub prime: String,
43 pub builtins: Vec<BuiltinName>,
44 #[serde(deserialize_with = "deserialize_array_of_bigint_hex")]
45 pub data: Vec<MaybeRelocatable>,
46 pub identifiers: HashMap<String, Identifier>,
47 pub hints: BTreeMap<usize, Vec<HintParams>>,
48 pub reference_manager: ReferenceManager,
49 #[serde(default)]
50 pub attributes: Vec<Attribute>,
51 pub debug_info: Option<DebugInfo>,
52}
53
54#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
55#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
56pub struct HintParams {
57 pub code: String,
58 pub accessible_scopes: Vec<String>,
59 pub flow_tracking_data: FlowTrackingData,
60}
61
62#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
63#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
64pub struct FlowTrackingData {
65 pub ap_tracking: ApTracking,
66 #[serde(deserialize_with = "deserialize_map_to_string_and_usize_hashmap")]
67 pub reference_ids: HashMap<String, usize>,
68}
69
70#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
71#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
72pub struct ApTracking {
73 pub group: usize,
74 pub offset: usize,
75}
76
77impl ApTracking {
78 pub fn new() -> ApTracking {
79 ApTracking {
80 group: 0,
81 offset: 0,
82 }
83 }
84}
85
86impl Default for ApTracking {
87 fn default() -> Self {
88 Self::new()
89 }
90}
91
92#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
93#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
94pub struct Identifier {
95 pub pc: Option<usize>,
96 #[serde(rename(deserialize = "type"))]
97 pub type_: Option<String>,
98 #[serde(default)]
99 #[serde(deserialize_with = "felt_from_number")]
100 pub value: Option<Felt252>,
101
102 pub full_name: Option<String>,
103 pub members: Option<HashMap<String, Member>>,
104 pub cairo_type: Option<String>,
105 pub size: Option<usize>,
106}
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 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 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 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, pub offset2: OffsetValue, pub outer_dereference: bool, pub inner_dereference: bool, pub value_type: String, }
290
291impl ValueAddress {
292 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 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 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 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 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 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 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 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 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 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 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 #[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 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 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}