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