1use std::fmt::{self, Display};
2
3use crate::types::relocatable::Relocatable;
4
5use thiserror::Error;
6
7use crate::{
8 hint_processor::hint_processor_utils::get_maybe_relocatable_from_reference,
9 serde::deserialize_program::{ApTracking, Attribute, Location, OffsetValue},
10 types::{instruction::Register, relocatable::MaybeRelocatable},
11 vm::runners::cairo_runner::CairoRunner,
12};
13
14use super::vm_errors::VirtualMachineError;
15#[derive(Debug, Error)]
16pub struct VmException {
17 pub pc: Relocatable,
18 pub inst_location: Option<Location>,
19 pub inner_exc: VirtualMachineError,
20 pub error_attr_value: Option<String>,
21 pub traceback: Option<String>,
22}
23
24impl VmException {
25 pub fn from_vm_error(runner: &CairoRunner, error: VirtualMachineError) -> Self {
26 let pc = runner.vm.run_context.pc;
27 let error_attr_value = if pc.segment_index == 0 {
28 get_error_attr_value(pc.offset, runner)
29 } else {
30 None
31 };
32 let hint_index = if let VirtualMachineError::Hint(ref bx) = error {
33 Some(bx.0)
34 } else {
35 None
36 };
37 VmException {
38 pc,
39 inst_location: if pc.segment_index == 0 {
40 get_location(pc.offset, runner, hint_index)
41 } else {
42 None
43 },
44 inner_exc: error,
45 error_attr_value,
46 traceback: get_traceback(runner),
47 }
48 }
49}
50
51pub fn get_error_attr_value(pc: usize, runner: &CairoRunner) -> Option<String> {
52 let mut errors = String::new();
53 for attribute in &runner.program.shared_program_data.error_message_attributes {
54 if attribute.start_pc <= pc && attribute.end_pc > pc {
55 errors.push_str(&format!(
56 "Error message: {}\n",
57 substitute_error_message_references(attribute, runner)
58 ));
59 }
60 }
61 if errors.is_empty() {
62 None
63 } else {
64 Some(errors)
65 }
66}
67
68pub fn get_location(
69 pc: usize,
70 runner: &CairoRunner,
71 hint_index: Option<usize>,
72) -> Option<Location> {
73 let instruction_location = runner
74 .program
75 .shared_program_data
76 .instruction_locations
77 .as_ref()?
78 .get(&pc)?;
79 if let Some(index) = hint_index {
80 instruction_location
81 .hints
82 .get(index)
83 .map(|hint_location| hint_location.location.clone())
84 } else {
85 Some(instruction_location.inst.clone())
86 }
87}
88
89pub fn get_traceback(runner: &CairoRunner) -> Option<String> {
91 let mut traceback = String::new();
92 for (_fp, traceback_pc) in runner.vm.get_traceback_entries() {
93 if let (0, Some(ref attr)) = (
94 traceback_pc.segment_index,
95 get_error_attr_value(traceback_pc.offset, runner),
96 ) {
97 traceback.push_str(attr)
98 }
99 match (
100 traceback_pc.segment_index,
101 get_location(traceback_pc.offset, runner, None),
102 ) {
103 (0, Some(location)) => traceback.push_str(&format!(
104 "{}\n",
105 location.to_string_with_content(&format!("(pc={})", traceback_pc))
106 )),
107 _ => traceback.push_str(&format!("Unknown location (pc={})\n", traceback_pc)),
108 }
109 }
110 (!traceback.is_empty())
111 .then(|| format!("Cairo traceback (most recent call last):\n{traceback}"))
112}
113
114fn substitute_error_message_references(
117 error_message_attr: &Attribute,
118 runner: &CairoRunner,
119) -> String {
120 let mut error_msg = error_message_attr.value.clone();
121 if let Some(tracking_data) = &error_message_attr.flow_tracking_data {
122 let mut invalid_references = Vec::<String>::new();
123 for (cairo_variable_path, ref_id) in &tracking_data.reference_ids {
125 let cairo_variable_name = match cairo_variable_path.rsplit('.').next() {
127 Some(string) => string,
128 None => continue,
129 };
130 let formated_variable_name = format!("{{{cairo_variable_name}}}");
133 if error_msg.contains(&formated_variable_name) {
135 match get_value_from_simple_reference(*ref_id, &tracking_data.ap_tracking, runner) {
137 Some(cairo_variable) => {
138 error_msg =
140 error_msg.replace(&formated_variable_name, &format!("{cairo_variable}"))
141 }
142 None => {
143 invalid_references.push(cairo_variable_name.to_string());
146 }
147 }
148 }
149 }
150 if !invalid_references.is_empty() {
151 error_msg.push_str(&format!(
153 " (Cannot evaluate ap-based or complex references: [{}])",
154 invalid_references
155 .iter()
156 .fold(String::new(), |acc, arg| acc + &format!("'{arg}'"))
157 ));
158 }
159 }
160 error_msg
161}
162
163fn get_value_from_simple_reference(
164 ref_id: usize,
165 ap_tracking: &ApTracking,
166 runner: &CairoRunner,
167) -> Option<MaybeRelocatable> {
168 let reference = runner
169 .program
170 .shared_program_data
171 .reference_manager
172 .get(ref_id)?;
173 match reference.offset1 {
175 OffsetValue::Reference(Register::AP, _, _, _) => None,
176 _ => {
177 match reference.cairo_type {
179 Some(ref cairo_type) if cairo_type.contains("felt") => Some(
180 get_maybe_relocatable_from_reference(&runner.vm, reference, ap_tracking)?,
181 ),
182 _ => None,
183 }
184 }
185 }
186}
187
188impl Display for VmException {
189 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
190 let message = format!("Error at pc={}:\n{}", self.pc, self.inner_exc);
192 let mut error_msg = String::new();
193 if let Some(ref string) = self.error_attr_value {
195 error_msg.push_str(string)
196 }
197 if let Some(ref location) = self.inst_location {
199 let mut location_msg = String::new();
200 let (mut location, mut message) = (location, &message);
201 loop {
202 location_msg = format!(
203 "{}\n{}",
204 location.to_string_with_content(message),
205 location_msg
206 );
207 if let Some(parent) = &location.parent_location {
209 (location, message) = (&parent.0, &parent.1)
210 } else {
211 break;
212 }
213 }
214 error_msg.push_str(&location_msg);
215 } else {
216 error_msg.push_str(&format!("{message}\n"));
217 }
218 if let Some(ref string) = self.traceback {
219 error_msg.push_str(string);
220 }
221 write!(f, "{error_msg}")
223 }
224}
225
226impl Location {
227 pub fn to_string(&self, message: &str) -> String {
229 let msg_prefix = if message.is_empty() { "" } else { ": " };
230 format!(
231 "{}:{}:{}{}{}",
232 self.input_file.filename, self.start_line, self.start_col, msg_prefix, message
233 )
234 }
235
236 pub fn to_string_with_content(&self, message: &str) -> String {
237 let mut string = self.to_string(message);
238 let input_file_path = std::path::Path::new(&self.input_file.filename);
239 #[cfg(test)]
240 let input_file_path = {
241 use std::path::PathBuf;
242 match std::env::current_dir() {
243 Ok(current_dir) => {
244 let mut parent_dir: PathBuf = current_dir
245 .parent()
246 .expect("should have a parent directory")
247 .into();
248 parent_dir.push(input_file_path);
249 parent_dir
250 }
251 Err(_) => return string,
253 }
254 };
255 if let Ok(file_content) = std::fs::read(input_file_path) {
256 string.push_str(&format!("\n{}", self.get_location_marks(&file_content)));
257 }
258 string
259 }
260
261 pub fn get_location_marks(&self, file_contents: &[u8]) -> String {
262 let mut contents = String::new();
263 if let Ok(content) = str::from_utf8(file_contents) {
264 contents.push_str(content);
265 }
266 let split_lines: Vec<&str> = contents.split('\n').collect();
267 if !(0 < self.start_line && ((self.start_line - 1) as usize) < split_lines.len()) {
268 return String::new();
269 }
270 let start_line = split_lines[(self.start_line - 1) as usize];
271 let start_col = self.start_col as usize;
272 let mut result = format!("{start_line}\n");
273 let end_col = if self.start_line == self.end_line {
274 self.end_col as usize
275 } else {
276 start_line.len() + 1
277 };
278 let left_margin: String = vec![' '; start_col - 1].into_iter().collect();
279 if end_col > start_col + 1 {
280 let highlight: String = vec!['*'; end_col - start_col - 2].into_iter().collect();
281 result.push_str(&format!("{left_margin}^{highlight}^"));
282 } else {
283 result.push_str(&format!("{left_margin}^"))
284 }
285 result
286 }
287}
288#[cfg(test)]
289mod test {
290 use crate::types::layout_name::LayoutName;
291 use assert_matches::assert_matches;
292 use std::collections::HashMap;
293 use std::path::Path;
294
295 use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
296 use crate::serde::deserialize_program::{
297 Attribute, HintLocation, InputFile, InstructionLocation,
298 };
299 use crate::types::program::Program;
300 use crate::types::relocatable::Relocatable;
301 use crate::utils::test_utils::*;
302
303 use super::*;
304 #[test]
305 fn get_vm_exception_from_vm_error() {
306 let pc: Relocatable = (0, 0).into();
307 let location = Location {
308 end_line: 2,
309 end_col: 2,
310 input_file: InputFile {
311 filename: String::from("Folder/file.cairo"),
312 },
313 parent_location: None,
314 start_line: 1,
315 start_col: 1,
316 };
317 let instruction_location = InstructionLocation {
318 inst: location.clone(),
319 hints: vec![],
320 };
321 let program = program!(
322 instruction_locations = Some(HashMap::from([(pc.offset, instruction_location)])),
323 );
324 let runner = cairo_runner!(program);
325 assert_matches!(
326 VmException::from_vm_error(&runner, VirtualMachineError::NoImm,),
327 VmException {
328 pc: x,
329 inst_location: Some(y),
330 inner_exc: VirtualMachineError::NoImm,
331 error_attr_value: None,
332 traceback: None,
333 } if x == pc && y == location
334 )
335 }
336
337 #[test]
338 fn location_to_string_no_message() {
339 let location = Location {
340 end_line: 2,
341 end_col: 2,
342 input_file: InputFile {
343 filename: String::from("Folder/file.cairo"),
344 },
345 parent_location: None,
346 start_line: 1,
347 start_col: 1,
348 };
349 let message = String::new();
350 assert_eq!(
351 location.to_string(&message),
352 String::from("Folder/file.cairo:1:1")
353 )
354 }
355
356 #[test]
357 fn location_to_string_with_message() {
358 let location = Location {
359 end_line: 2,
360 end_col: 2,
361 input_file: InputFile {
362 filename: String::from("Folder/file.cairo"),
363 },
364 parent_location: None,
365 start_line: 1,
366 start_col: 1,
367 };
368 let message = String::from("While expanding the reference");
369 assert_eq!(
370 location.to_string(&message),
371 String::from("Folder/file.cairo:1:1: While expanding the reference")
372 )
373 }
374
375 #[test]
376 fn vm_exception_display_instruction_no_location_no_attributes() {
377 let vm_excep = VmException {
378 pc: (0, 2).into(),
379 inst_location: None,
380 inner_exc: VirtualMachineError::FailedToComputeOperands(Box::new((
381 "op0".to_string(),
382 Relocatable::from((0, 4)),
383 ))),
384 error_attr_value: None,
385 traceback: None,
386 };
387 assert_eq!(
388 vm_excep.to_string(),
389 format!(
390 "Error at pc=0:2:\n{}\n",
391 VirtualMachineError::FailedToComputeOperands(Box::new((
392 "op0".to_string(),
393 Relocatable::from((0, 4))
394 )))
395 )
396 )
397 }
398
399 #[test]
400 fn vm_exception_display_instruction_no_location_with_attributes() {
401 let vm_excep = VmException {
402 pc: (0, 2).into(),
403 inst_location: None,
404 inner_exc: VirtualMachineError::FailedToComputeOperands(Box::new((
405 "op0".to_string(),
406 Relocatable::from((0, 4)),
407 ))),
408 error_attr_value: Some(String::from("Error message: Block may fail\n")),
409 traceback: None,
410 };
411 assert_eq!(
412 vm_excep.to_string(),
413 format!(
414 "Error message: Block may fail\nError at pc=0:2:\n{}\n",
415 VirtualMachineError::FailedToComputeOperands(Box::new((
416 "op0".to_string(),
417 Relocatable::from((0, 4))
418 )))
419 )
420 )
421 }
422
423 #[test]
424 fn vm_exception_display_instruction_no_attributes_no_parent() {
425 let location = Location {
426 end_line: 2,
427 end_col: 2,
428 input_file: InputFile {
429 filename: String::from("Folder/file.cairo"),
430 },
431 parent_location: None,
432 start_line: 1,
433 start_col: 1,
434 };
435 let vm_excep = VmException {
436 pc: (0, 2).into(),
437 inst_location: Some(location),
438 inner_exc: VirtualMachineError::FailedToComputeOperands(Box::new((
439 "op0".to_string(),
440 Relocatable::from((0, 4)),
441 ))),
442 error_attr_value: None,
443 traceback: None,
444 };
445 assert_eq!(
446 vm_excep.to_string(),
447 format!(
448 "Folder/file.cairo:1:1: Error at pc=0:2:\n{}\n",
449 VirtualMachineError::FailedToComputeOperands(Box::new((
450 "op0".to_string(),
451 Relocatable::from((0, 4))
452 )))
453 )
454 )
455 }
456
457 #[test]
458 fn vm_exception_display_instruction_no_attributes_with_parent() {
459 let location = Location {
460 end_line: 2,
461 end_col: 2,
462 input_file: InputFile {
463 filename: String::from("Folder/file.cairo"),
464 },
465 parent_location: Some((
466 Box::new(Location {
467 end_line: 3,
468 end_col: 3,
469 input_file: InputFile {
470 filename: String::from("Folder/file_b.cairo"),
471 },
472 parent_location: None,
473 start_line: 2,
474 start_col: 2,
475 }),
476 String::from("While expanding the reference:"),
477 )),
478 start_line: 1,
479 start_col: 1,
480 };
481 let vm_excep = VmException {
482 pc: (0, 2).into(),
483 inst_location: Some(location),
484 inner_exc: VirtualMachineError::FailedToComputeOperands(Box::new((
485 "op0".to_string(),
486 Relocatable::from((0, 4)),
487 ))),
488 error_attr_value: None,
489 traceback: None,
490 };
491 assert_eq!(
492 vm_excep.to_string(),
493 format!(
494 "Folder/file_b.cairo:2:2: While expanding the reference:\nFolder/file.cairo:1:1: Error at pc=0:2:\n{}\n",
495 VirtualMachineError::FailedToComputeOperands(Box::new(("op0".to_string(), Relocatable::from((0, 4)))))
496 )
497 )
498 }
499
500 #[test]
501 fn get_error_attr_value_some() {
502 let attributes = vec![Attribute {
503 name: String::from("Error message"),
504 start_pc: 1,
505 end_pc: 5,
506 value: String::from("Invalid hash"),
507 flow_tracking_data: None,
508 }];
509 let program = program!(error_message_attributes = attributes,);
510 let runner = cairo_runner!(program);
511 assert_eq!(
512 get_error_attr_value(2, &runner),
513 Some(String::from("Error message: Invalid hash\n"))
514 );
515 }
516
517 #[test]
518 fn get_error_attr_value_none() {
519 let attributes = vec![Attribute {
520 name: String::from("Error message"),
521 start_pc: 1,
522 end_pc: 5,
523 value: String::from("Invalid hash"),
524 flow_tracking_data: None,
525 }];
526 let program = program!(error_message_attributes = attributes,);
527 let runner = cairo_runner!(program);
528 assert_eq!(get_error_attr_value(5, &runner), None);
529 }
530
531 #[test]
532 fn get_location_some() {
533 let location = Location {
534 end_line: 2,
535 end_col: 2,
536 input_file: InputFile {
537 filename: String::from("Folder/file.cairo"),
538 },
539 parent_location: None,
540 start_line: 1,
541 start_col: 1,
542 };
543 let instruction_location = InstructionLocation {
544 inst: location.clone(),
545 hints: vec![],
546 };
547 let program =
548 program!(instruction_locations = Some(HashMap::from([(2, instruction_location)])),);
549 let runner = cairo_runner!(program);
550 assert_eq!(get_location(2, &runner, None), Some(location));
551 }
552
553 #[test]
554 fn get_location_none() {
555 let location = Location {
556 end_line: 2,
557 end_col: 2,
558 input_file: InputFile {
559 filename: String::from("Folder/file.cairo"),
560 },
561 parent_location: None,
562 start_line: 1,
563 start_col: 1,
564 };
565 let instruction_location = InstructionLocation {
566 inst: location,
567 hints: vec![],
568 };
569 let program =
570 program!(instruction_locations = Some(HashMap::from([(2, instruction_location)])),);
571 let runner = cairo_runner!(program);
572 assert_eq!(get_location(3, &runner, None), None);
573 }
574
575 #[test]
576 fn get_location_some_hint_index() {
577 let location_a = Location {
578 end_line: 2,
579 end_col: 2,
580 input_file: InputFile {
581 filename: String::from("Folder/file_a.cairo"),
582 },
583 parent_location: None,
584 start_line: 1,
585 start_col: 1,
586 };
587 let location_b = Location {
588 end_line: 3,
589 end_col: 2,
590 input_file: InputFile {
591 filename: String::from("Folder/file_b.cairo"),
592 },
593 parent_location: None,
594 start_line: 1,
595 start_col: 5,
596 };
597 let hint_location = HintLocation {
598 location: location_b.clone(),
599 n_prefix_newlines: 2,
600 };
601 let instruction_location = InstructionLocation {
602 inst: location_a,
603 hints: vec![hint_location],
604 };
605 let program =
606 program!(instruction_locations = Some(HashMap::from([(2, instruction_location)])),);
607 let runner = cairo_runner!(program);
608 assert_eq!(get_location(2, &runner, Some(0)), Some(location_b));
609 }
610
611 #[test]
612 fn get_traceback_bad_dict_update() {
613 let program = Program::from_bytes(
614 include_bytes!("../../../../cairo_programs/bad_programs/bad_dict_update.json"),
615 Some("main"),
616 )
617 .expect("Call to `Program::from_file()` failed.");
618
619 let mut hint_processor = BuiltinHintProcessor::new_empty();
620 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false);
621
622 let end = cairo_runner.initialize(false).unwrap();
623 assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_err());
624
625 let expected_traceback = String::from("Cairo traceback (most recent call last):\ncairo_programs/bad_programs/bad_dict_update.cairo:10:5: (pc=0:34)\n dict_update{dict_ptr=my_dict}(key=2, prev_value=3, new_value=4);\n ^*************************************************************^\n");
626
627 let mut hint_processor = BuiltinHintProcessor::new_empty();
628 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false);
629
630 let end = cairo_runner.initialize(false).unwrap();
631 assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_err());
632 assert_eq!(get_traceback(&cairo_runner), Some(expected_traceback));
633 }
634
635 #[test]
636 fn get_traceback_bad_usort() {
637 let program = Program::from_bytes(
638 include_bytes!("../../../../cairo_programs/bad_programs/bad_usort.json"),
639 Some("main"),
640 )
641 .unwrap();
642 let expected_traceback = r"Cairo traceback (most recent call last):
643cairo_programs/bad_programs/bad_usort.cairo:91:48: (pc=0:97)
644 let (output_len, output, multiplicities) = usort(input_len=3, input=input_array);
645 ^***********************************^
646cairo_programs/bad_programs/bad_usort.cairo:36:5: (pc=0:30)
647 verify_usort{output=output}(
648 ^**************************^
649cairo_programs/bad_programs/bad_usort.cairo:64:5: (pc=0:60)
650 verify_multiplicity(multiplicity=multiplicity, input_len=input_len, input=input, value=value);
651 ^*******************************************************************************************^
652";
653
654 let mut hint_processor = BuiltinHintProcessor::new_empty();
655 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false);
656
657 let end = cairo_runner.initialize(false).unwrap();
658 assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_err());
659 assert_eq!(
660 get_traceback(&cairo_runner),
661 Some(expected_traceback.to_string())
662 );
663 }
664
665 #[test]
666 fn location_to_string_with_contents_no_contents() {
667 let location = Location {
668 end_line: 2,
669 end_col: 2,
670 input_file: InputFile {
671 filename: String::from("Folder/file.cairo"),
672 },
673 parent_location: None,
674 start_line: 1,
675 start_col: 1,
676 };
677 let message = String::from("While expanding the reference");
678 assert_eq!(
679 location.to_string_with_content(&message),
680 String::from("Folder/file.cairo:1:1: While expanding the reference")
681 )
682 }
683
684 #[test]
685 fn location_to_string_with_contents() {
686 let location = Location {
687 end_line: 5,
688 end_col: 2,
689 input_file: InputFile {
690 filename: String::from("cairo_programs/bad_programs/bad_usort.cairo"),
691 },
692 parent_location: None,
693 start_line: 5,
694 start_col: 1,
695 };
696 let message = String::from("Error at pc=0:75:");
697
698 let expected_message = "cairo_programs/bad_programs/bad_usort.cairo:5:1: Error at pc=0:75:\nfunc usort{range_check_ptr}(input_len: felt, input: felt*) -> (\n^";
699
700 assert_eq!(
701 location.to_string_with_content(&message),
702 expected_message.to_string()
703 )
704 }
705
706 #[test]
707 fn location_to_string_with_contents_no_file() {
708 let location = Location {
709 end_line: 5,
710 end_col: 2,
711 input_file: InputFile {
712 filename: String::from("cairo_programs/bad_prtypoograms/bad_usort.cairo"),
713 },
714 parent_location: None,
715 start_line: 5,
716 start_col: 1,
717 };
718 let message = String::from("Error at pc=0:75:\n");
719 assert_eq!(
720 location.to_string_with_content(&message),
721 String::from(
722 "cairo_programs/bad_prtypoograms/bad_usort.cairo:5:1: Error at pc=0:75:\n"
723 )
724 )
725 }
726
727 #[test]
728 fn location_get_location_marks() {
729 let location = Location {
730 end_line: 5,
731 end_col: 2,
732 input_file: InputFile {
733 filename: String::from("../cairo_programs/bad_programs/bad_usort.cairo"),
734 },
735 parent_location: None,
736 start_line: 5,
737 start_col: 1,
738 };
739 let input_file_path = Path::new(&location.input_file.filename);
740 let file_content = std::fs::read(input_file_path).expect("Failed to open file");
741 assert_eq!(
742 location.get_location_marks(&file_content),
743 String::from("func usort{range_check_ptr}(input_len: felt, input: felt*) -> (\n^")
744 )
745 }
746
747 #[test]
748 fn location_get_location_marks_empty_file() {
749 let location = Location {
750 end_line: 5,
751 end_col: 2,
752 input_file: InputFile {
753 filename: String::from("cairo_programs/bad_programs/bad_usort.cairo"),
754 },
755 parent_location: None,
756 start_line: 5,
757 start_col: 1,
758 };
759 let reader: &[u8] = &[];
760 assert_eq!(location.get_location_marks(reader), String::from(""))
761 }
762
763 #[test]
764 fn run_bad_range_check_and_check_error_displayed() {
765 let expected_error_string = r#"Error message: Failed range-check
766cairo_programs/bad_programs/bad_range_check.cairo:5:9: Error at pc=0:0:
767An ASSERT_EQ instruction failed: 4 != 5.
768 [range_check_ptr] = num;
769 ^*********************^
770Cairo traceback (most recent call last):
771cairo_programs/bad_programs/bad_range_check.cairo:23:5: (pc=0:29)
772 sub_by_1_check_range(6, 7);
773 ^************************^
774cairo_programs/bad_programs/bad_range_check.cairo:19:12: (pc=0:21)
775 return sub_by_1_check_range(sub_1_check_range(num), sub_amount -1);
776 ^*********************************************************^
777cairo_programs/bad_programs/bad_range_check.cairo:19:33: (pc=0:17)
778 return sub_by_1_check_range(sub_1_check_range(num), sub_amount -1);
779 ^********************^
780cairo_programs/bad_programs/bad_range_check.cairo:11:5: (pc=0:6)
781 check_range(num - 1);
782 ^******************^
783"#;
784 let program = Program::from_bytes(
785 include_bytes!("../../../../cairo_programs/bad_programs/bad_range_check.json"),
786 Some("main"),
787 )
788 .unwrap();
789
790 let mut hint_processor = BuiltinHintProcessor::new_empty();
791 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false);
792
793 let end = cairo_runner.initialize(false).unwrap();
794 let error = cairo_runner
795 .run_until_pc(end, &mut hint_processor)
796 .unwrap_err();
797 let vm_excepction = VmException::from_vm_error(&cairo_runner, error);
798 assert_eq!(vm_excepction.to_string(), expected_error_string);
799 }
800
801 #[test]
802 fn run_bad_usort_and_check_error_displayed() {
803 let expected_error_string = r#"cairo_programs/bad_programs/bad_usort.cairo:79:5: Error at pc=0:75:
804Got an exception while executing a hint: unexpected verify multiplicity fail: positions length != 0
805 %{ assert len(positions) == 0 %}
806 ^******************************^
807Cairo traceback (most recent call last):
808cairo_programs/bad_programs/bad_usort.cairo:91:48: (pc=0:97)
809 let (output_len, output, multiplicities) = usort(input_len=3, input=input_array);
810 ^***********************************^
811cairo_programs/bad_programs/bad_usort.cairo:36:5: (pc=0:30)
812 verify_usort{output=output}(
813 ^**************************^
814cairo_programs/bad_programs/bad_usort.cairo:64:5: (pc=0:60)
815 verify_multiplicity(multiplicity=multiplicity, input_len=input_len, input=input, value=value);
816 ^*******************************************************************************************^
817"#;
818 let program = Program::from_bytes(
819 include_bytes!("../../../../cairo_programs/bad_programs/bad_usort.json"),
820 Some("main"),
821 )
822 .unwrap();
823
824 let mut hint_processor = BuiltinHintProcessor::new_empty();
825 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false);
826
827 let end = cairo_runner.initialize(false).unwrap();
828 let error = cairo_runner
829 .run_until_pc(end, &mut hint_processor)
830 .unwrap_err();
831 let vm_excepction = VmException::from_vm_error(&cairo_runner, error);
832 assert_eq!(vm_excepction.to_string(), expected_error_string);
833 }
834
835 #[test]
836 fn run_bad_ec_recover_product_mod() {
837 let expected_error_string = r#"cairo_programs/bad_programs/ec_recover_product_mod_m_zero.cairo:16:5: Error at pc=0:21:
838Got an exception while executing a hint: Attempted to divide by zero
839 %{
840 ^^
841Cairo traceback (most recent call last):
842cairo_programs/bad_programs/ec_recover_product_mod_m_zero.cairo:11:5: (pc=0:18)
843 ec_recover_product(a, b, m);
844 ^*************************^
845"#;
846 let program = Program::from_bytes(
847 include_bytes!(
848 "../../../../cairo_programs/bad_programs/ec_recover_product_mod_m_zero.json"
849 ),
850 Some("main"),
851 )
852 .unwrap();
853
854 let mut hint_processor = BuiltinHintProcessor::new_empty();
855 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false);
856
857 let end = cairo_runner.initialize(false).unwrap();
858 let error = cairo_runner
859 .run_until_pc(end, &mut hint_processor)
860 .unwrap_err();
861 let vm_excepction = VmException::from_vm_error(&cairo_runner, error);
862 assert_eq!(vm_excepction.to_string(), expected_error_string);
863 }
864
865 #[test]
866 fn run_bad_ec_recover_div_mod_n_packed_n_zero() {
867 let expected_error_string = r#"cairo_programs/bad_programs/ec_recover_div_mod_n_packed_n_zero.cairo:16:5: Error at pc=0:21:
868Got an exception while executing a hint: Attempted to divide by zero
869 %{
870 ^^
871Cairo traceback (most recent call last):
872cairo_programs/bad_programs/ec_recover_div_mod_n_packed_n_zero.cairo:11:5: (pc=0:18)
873 ec_recover_product(x, s, n);
874 ^*************************^
875"#;
876 let program = Program::from_bytes(
877 include_bytes!(
878 "../../../../cairo_programs/bad_programs/ec_recover_div_mod_n_packed_n_zero.json"
879 ),
880 Some("main"),
881 )
882 .unwrap();
883
884 let mut hint_processor = BuiltinHintProcessor::new_empty();
885 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false);
886
887 let end = cairo_runner.initialize(false).unwrap();
888 let error = cairo_runner
889 .run_until_pc(end, &mut hint_processor)
890 .unwrap_err();
891 let vm_excepction = VmException::from_vm_error(&cairo_runner, error);
892 assert_eq!(vm_excepction.to_string(), expected_error_string);
893 }
894
895 #[test]
896 fn run_bad_uint512_unsigned_div_rem() {
897 let expected_error_string = r#"cairo_programs/bad_programs/uint512_unsigned_div_rem_div_is_zero.cairo:24:1: Error at pc=0:17:
898Got an exception while executing a hint: Attempted to divide by zero
899%{
900^^
901Cairo traceback (most recent call last):
902cairo_programs/bad_programs/uint512_unsigned_div_rem_div_is_zero.cairo:15:2: (pc=0:12)
903 hint_func(x, div);
904 ^***************^
905"#;
906 let program = Program::from_bytes(
907 include_bytes!(
908 "../../../../cairo_programs/bad_programs/uint512_unsigned_div_rem_div_is_zero.json"
909 ),
910 Some("main"),
911 )
912 .unwrap();
913
914 let mut hint_processor = BuiltinHintProcessor::new_empty();
915 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false);
916
917 let end = cairo_runner.initialize(false).unwrap();
918 let error = cairo_runner
919 .run_until_pc(end, &mut hint_processor)
920 .unwrap_err();
921 let vm_excepction = VmException::from_vm_error(&cairo_runner, error);
922 assert_eq!(vm_excepction.to_string(), expected_error_string);
923 }
924
925 #[test]
926 fn run_bad_uint256_sub_check_error_displayed() {
927 let expected_error_string = r#"cairo_programs/bad_programs/uint256_sub_b_gt_256.cairo:17:1: Error at pc=0:17:
928Got an exception while executing a hint: Inconsistent memory assignment at address Relocatable { segment_index: 1, offset: 6 }. Int(1) != Int(41367660292349381832802403122744918015)
929%{
930^^
931Cairo traceback (most recent call last):
932cairo_programs/bad_programs/uint256_sub_b_gt_256.cairo:10:2: (pc=0:12)
933 hint_func(a, b, res);
934 ^******************^
935"#;
936 let program = Program::from_bytes(
937 include_bytes!("../../../../cairo_programs/bad_programs/uint256_sub_b_gt_256.json"),
938 Some("main"),
939 )
940 .unwrap();
941
942 let mut hint_processor = BuiltinHintProcessor::new_empty();
943 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false);
944
945 let end = cairo_runner.initialize(false).unwrap();
946 let error = cairo_runner
947 .run_until_pc(end, &mut hint_processor)
948 .unwrap_err();
949 let vm_excepction = VmException::from_vm_error(&cairo_runner, error);
950 assert_eq!(vm_excepction.to_string(), expected_error_string);
951 }
952
953 #[test]
954 fn get_value_from_simple_reference_ap_based() {
955 let program = Program::from_bytes(
956 include_bytes!("../../../../cairo_programs/bad_programs/error_msg_attr_tempvar.json"),
957 Some("main"),
958 )
959 .unwrap();
960 let runner = cairo_runner!(program);
963 assert_eq!(
965 get_value_from_simple_reference(0, &ApTracking::default(), &runner),
966 None
967 )
968 }
969
970 #[test]
971 fn substitute_error_message_references_ap_based() {
972 let program = Program::from_bytes(
973 include_bytes!("../../../../cairo_programs/bad_programs/error_msg_attr_tempvar.json"),
974 Some("main"),
975 )
976 .unwrap();
977 let runner = cairo_runner!(program);
980 let attribute = &program.shared_program_data.error_message_attributes[0];
981 assert_eq!(
982 substitute_error_message_references(attribute, &runner),
983 format!(
984 "{} (Cannot evaluate ap-based or complex references: ['x'])",
985 attribute.value
986 )
987 );
988 }
989
990 #[test]
991 fn get_value_from_simple_reference_complex() {
992 let program = Program::from_bytes(
993 include_bytes!("../../../../cairo_programs/bad_programs/error_msg_attr_struct.json"),
994 Some("main"),
995 )
996 .unwrap();
997 let runner = cairo_runner!(program);
1000 assert_eq!(
1002 get_value_from_simple_reference(0, &ApTracking::default(), &runner),
1003 None
1004 )
1005 }
1006
1007 #[test]
1008 fn substitute_error_message_references_complex() {
1009 let program = Program::from_bytes(
1010 include_bytes!("../../../../cairo_programs/bad_programs/error_msg_attr_struct.json"),
1011 Some("main"),
1012 )
1013 .unwrap();
1014 let runner = cairo_runner!(program);
1017 let attribute = &program.shared_program_data.error_message_attributes[0];
1018 assert_eq!(
1019 substitute_error_message_references(attribute, &runner),
1020 format!(
1021 "{} (Cannot evaluate ap-based or complex references: ['cat'])",
1022 attribute.value
1023 )
1024 );
1025 }
1026
1027 #[test]
1028 fn get_vm_exception_from_vm_error_pc_not_program_segment() {
1029 let pc = (9, 5).into();
1030 let location = Location {
1031 end_line: 2,
1032 end_col: 2,
1033 input_file: InputFile {
1034 filename: String::from("Folder/file.cairo"),
1035 },
1036 parent_location: None,
1037 start_line: 1,
1038 start_col: 1,
1039 };
1040 let instruction_location = InstructionLocation {
1041 inst: location,
1042 hints: vec![],
1043 };
1044 let program =
1045 program!(instruction_locations = Some(HashMap::from([(5, instruction_location)])),);
1046 let mut runner = cairo_runner!(program);
1047 runner.vm.set_pc(pc);
1048 assert_matches!(
1049 VmException::from_vm_error(&runner, VirtualMachineError::NoImm,),
1050 VmException {
1051 pc: x,
1052 inst_location: None,
1053 inner_exc: VirtualMachineError::NoImm,
1054 error_attr_value: None,
1055 traceback: None,
1056 } if x == pc
1057 )
1058 }
1059}