1#![allow(unused_assignments)]
3
4use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
5
6use miden_core::program::MIN_STACK_DEPTH;
7use miden_debug_types::{SourceFile, SourceSpan};
8use miden_utils_diagnostics::{Diagnostic, miette};
9
10use crate::{
11 BaseHost, ContextId, DebugError, Felt, TraceError, Word,
12 advice::AdviceError,
13 event::{EventError, EventId, EventName},
14 fast::SystemEventError,
15 mast::{MastForest, MastNodeId},
16 utils::to_hex,
17};
18
19#[derive(Debug, thiserror::Error, Diagnostic)]
23pub enum ExecutionError {
24 #[error("failed to execute arithmetic circuit evaluation operation: {error}")]
25 #[diagnostic()]
26 AceChipError {
27 #[label("this call failed")]
28 label: SourceSpan,
29 #[source_code]
30 source_file: Option<Arc<SourceFile>>,
31 error: AceError,
32 },
33 #[error("{err}")]
34 #[diagnostic(forward(err))]
35 AdviceError {
36 #[label]
37 label: SourceSpan,
38 #[source_code]
39 source_file: Option<Arc<SourceFile>>,
40 err: AdviceError,
41 },
42 #[error("exceeded the allowed number of max cycles {0}")]
43 CycleLimitExceeded(u32),
44 #[error("error during processing of event {}", match event_name {
45 Some(name) => format!("'{}' (ID: {})", name, event_id),
46 None => format!("with ID: {}", event_id),
47 })]
48 #[diagnostic()]
49 EventError {
50 #[label]
51 label: SourceSpan,
52 #[source_code]
53 source_file: Option<Arc<SourceFile>>,
54 event_id: EventId,
55 event_name: Option<EventName>,
56 #[source]
57 error: EventError,
58 },
59 #[error("failed to execute the program for internal reason: {0}")]
60 Internal(&'static str),
61 #[error("trace length exceeded the maximum of {0} rows")]
65 TraceLenExceeded(usize),
66 #[error("{err}")]
70 #[diagnostic(forward(err))]
71 MemoryError {
72 #[label]
73 label: SourceSpan,
74 #[source_code]
75 source_file: Option<Arc<SourceFile>>,
76 err: MemoryError,
77 },
78 #[error(transparent)]
83 #[diagnostic(transparent)]
84 MemoryErrorNoCtx(MemoryError),
85 #[error("{err}")]
86 #[diagnostic(forward(err))]
87 OperationError {
88 #[label]
89 label: SourceSpan,
90 #[source_code]
91 source_file: Option<Arc<SourceFile>>,
92 err: OperationError,
93 },
94 #[error("stack should have at most {MIN_STACK_DEPTH} elements at the end of program execution, but had {} elements", MIN_STACK_DEPTH + .0)]
95 OutputStackOverflow(usize),
96 #[error("procedure with root digest {root_digest} could not be found")]
97 #[diagnostic()]
98 ProcedureNotFound {
99 #[label]
100 label: SourceSpan,
101 #[source_code]
102 source_file: Option<Arc<SourceFile>>,
103 root_digest: Word,
104 },
105 #[error("failed to generate STARK proof: {0}")]
106 ProvingError(String),
107 #[error(transparent)]
108 HostError(#[from] HostError),
109}
110
111impl AsRef<dyn Diagnostic> for ExecutionError {
112 fn as_ref(&self) -> &(dyn Diagnostic + 'static) {
113 self
114 }
115}
116
117#[derive(Debug, thiserror::Error)]
121#[error("ace circuit evaluation failed: {0}")]
122pub struct AceError(pub String);
123
124#[derive(Debug, thiserror::Error)]
133pub enum AceEvalError {
134 #[error(transparent)]
135 Ace(#[from] AceError),
136 #[error(transparent)]
137 Memory(#[from] MemoryError),
138}
139
140#[derive(Debug, thiserror::Error)]
145pub enum HostError {
146 #[error("attempted to add event handler for '{event}' (already registered)")]
147 DuplicateEventHandler { event: EventName },
148 #[error("attempted to add event handler for '{event}' (reserved system event)")]
149 ReservedEventNamespace { event: EventName },
150 #[error("debug handler error: {err}")]
151 DebugHandlerError {
152 #[source]
153 err: DebugError,
154 },
155 #[error("trace handler error for trace ID {trace_id}: {err}")]
156 TraceHandlerError {
157 trace_id: u32,
158 #[source]
159 err: TraceError,
160 },
161}
162
163#[derive(Debug, thiserror::Error, Diagnostic)]
172pub enum IoError {
173 #[error(transparent)]
174 Advice(#[from] AdviceError),
175 #[error(transparent)]
176 Memory(#[from] MemoryError),
177 #[error(transparent)]
178 #[diagnostic(transparent)]
179 Operation(#[from] OperationError),
180 #[error(transparent)]
185 #[diagnostic(transparent)]
186 Execution(Box<ExecutionError>),
187}
188
189impl From<ExecutionError> for IoError {
190 fn from(err: ExecutionError) -> Self {
191 IoError::Execution(Box::new(err))
192 }
193}
194
195#[derive(Debug, thiserror::Error, Diagnostic)]
204pub enum MemoryError {
205 #[error("memory address cannot exceed 2^32 but was {addr}")]
206 AddressOutOfBounds { addr: u64 },
207 #[error(
208 "memory address {addr} in context {ctx} was read and written, or written twice, in the same clock cycle {clk}"
209 )]
210 IllegalMemoryAccess { ctx: ContextId, addr: u32, clk: Felt },
211 #[error(
212 "memory range start address cannot exceed end address, but was ({start_addr}, {end_addr})"
213 )]
214 InvalidMemoryRange { start_addr: u64, end_addr: u64 },
215 #[error("word access at memory address {addr} in context {ctx} is unaligned")]
216 #[diagnostic(help(
217 "ensure that the memory address accessed is aligned to a word boundary (it is a multiple of 4)"
218 ))]
219 UnalignedWordAccess { addr: u32, ctx: ContextId },
220 #[error("failed to read from memory: {0}")]
221 MemoryReadFailed(String),
222}
223
224#[derive(Debug, thiserror::Error, Diagnostic)]
233pub enum CryptoError {
234 #[error(transparent)]
235 Advice(#[from] AdviceError),
236 #[error(transparent)]
237 #[diagnostic(transparent)]
238 Operation(#[from] OperationError),
239}
240
241#[derive(Debug, Clone, thiserror::Error, Diagnostic)]
275pub enum OperationError {
276 #[error("external node with mast root {0} resolved to an external node")]
277 CircularExternalNode(Word),
278 #[error("division by zero")]
279 #[diagnostic(help(
280 "ensure the divisor (second stack element) is non-zero before division or modulo operations"
281 ))]
282 DivideByZero,
283 #[error(
284 "assertion failed with error {}",
285 match err_msg {
286 Some(msg) => format!("message: {msg}"),
287 None => format!("code: {err_code}"),
288 }
289 )]
290 #[diagnostic(help(
291 "assertions validate program invariants. Review the assertion condition and ensure all prerequisites are met"
292 ))]
293 FailedAssertion {
294 err_code: Felt,
295 err_msg: Option<Arc<str>>,
296 },
297 #[error(
298 "u32 assertion failed with error {}: invalid values: {invalid_values:?}",
299 match err_msg {
300 Some(msg) => format!("message: {msg}"),
301 None => format!("code: {err_code}"),
302 }
303 )]
304 #[diagnostic(help(
305 "u32assert2 requires both stack values to be valid 32-bit unsigned integers"
306 ))]
307 U32AssertionFailed {
308 err_code: Felt,
309 err_msg: Option<Arc<str>>,
310 invalid_values: Vec<Felt>,
311 },
312 #[error("FRI operation failed: {0}")]
313 FriError(String),
314 #[error(
315 "invalid crypto operation: Merkle path length {path_len} does not match expected depth {depth}"
316 )]
317 InvalidMerklePathLength { path_len: usize, depth: Felt },
318 #[error("when returning from a call, stack depth must be {MIN_STACK_DEPTH}, but was {depth}")]
319 InvalidStackDepthOnReturn { depth: usize },
320 #[error("attempted to calculate integer logarithm with zero argument")]
321 #[diagnostic(help("ilog2 requires a non-zero argument"))]
322 LogArgumentZero,
323 #[error(
324 "MAST forest in host indexed by procedure root {root_digest} doesn't contain that root"
325 )]
326 MalformedMastForestInHost { root_digest: Word },
327 #[error("merkle path verification failed for value {value} at index {index} in the Merkle tree with root {root} (error {err})",
328 value = to_hex(inner.value.as_bytes()),
329 root = to_hex(inner.root.as_bytes()),
330 index = inner.index,
331 err = match &inner.err_msg {
332 Some(msg) => format!("message: {msg}"),
333 None => format!("code: {}", inner.err_code),
334 }
335 )]
336 MerklePathVerificationFailed {
337 inner: Box<MerklePathVerificationFailedInner>,
338 },
339 #[error("operation expected a binary value, but got {value}")]
340 NotBinaryValue { value: Felt },
341 #[error("if statement expected a binary value on top of the stack, but got {value}")]
342 NotBinaryValueIf { value: Felt },
343 #[error("loop condition must be a binary value, but got {value}")]
344 #[diagnostic(help(
345 "this could happen either when first entering the loop, or any subsequent iteration"
346 ))]
347 NotBinaryValueLoop { value: Felt },
348 #[error("operation expected u32 values, but got values: {values:?}")]
349 NotU32Values { values: Vec<Felt> },
350 #[error("syscall failed: procedure with root {proc_root} was not found in the kernel")]
351 SyscallTargetNotInKernel { proc_root: Word },
352 #[error("failed to execute the operation for internal reason: {0}")]
353 Internal(&'static str),
354}
355
356impl OperationError {
357 pub fn with_context(
362 self,
363 mast_forest: &MastForest,
364 node_id: MastNodeId,
365 host: &impl BaseHost,
366 ) -> ExecutionError {
367 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
368 ExecutionError::OperationError { label, source_file, err: self }
369 }
370}
371
372#[derive(Debug, Clone)]
376pub struct MerklePathVerificationFailedInner {
377 pub value: Word,
378 pub index: Felt,
379 pub root: Word,
380 pub err_code: Felt,
381 pub err_msg: Option<Arc<str>>,
382}
383
384fn get_label_and_source_file(
393 op_idx: Option<usize>,
394 mast_forest: &MastForest,
395 node_id: MastNodeId,
396 host: &impl BaseHost,
397) -> (SourceSpan, Option<Arc<SourceFile>>) {
398 mast_forest
399 .get_assembly_op(node_id, op_idx)
400 .and_then(|assembly_op| assembly_op.location())
401 .map_or_else(
402 || (SourceSpan::UNKNOWN, None),
403 |location| host.get_label_and_source_file(location),
404 )
405}
406
407pub fn advice_error_with_context(
412 err: AdviceError,
413 mast_forest: &MastForest,
414 node_id: MastNodeId,
415 host: &impl BaseHost,
416) -> ExecutionError {
417 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
418 ExecutionError::AdviceError { label, source_file, err }
419}
420
421pub fn event_error_with_context(
426 error: EventError,
427 mast_forest: &MastForest,
428 node_id: MastNodeId,
429 host: &impl BaseHost,
430 event_id: EventId,
431 event_name: Option<EventName>,
432) -> ExecutionError {
433 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
434 ExecutionError::EventError {
435 label,
436 source_file,
437 event_id,
438 event_name,
439 error,
440 }
441}
442
443pub fn procedure_not_found_with_context(
445 root_digest: Word,
446 mast_forest: &MastForest,
447 node_id: MastNodeId,
448 host: &impl BaseHost,
449) -> ExecutionError {
450 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
451 ExecutionError::ProcedureNotFound { label, source_file, root_digest }
452}
453
454pub trait MapExecErr<T> {
467 fn map_exec_err(
468 self,
469 mast_forest: &MastForest,
470 node_id: MastNodeId,
471 host: &impl BaseHost,
472 ) -> Result<T, ExecutionError>;
473}
474
475pub trait MapExecErrWithOpIdx<T> {
480 fn map_exec_err_with_op_idx(
481 self,
482 mast_forest: &MastForest,
483 node_id: MastNodeId,
484 host: &impl BaseHost,
485 op_idx: usize,
486 ) -> Result<T, ExecutionError>;
487}
488
489pub trait MapExecErrNoCtx<T> {
494 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError>;
495}
496
497impl<T> MapExecErr<T> for Result<T, OperationError> {
499 #[inline(always)]
500 fn map_exec_err(
501 self,
502 mast_forest: &MastForest,
503 node_id: MastNodeId,
504 host: &impl BaseHost,
505 ) -> Result<T, ExecutionError> {
506 match self {
507 Ok(v) => Ok(v),
508 Err(err) => {
509 let (label, source_file) =
510 get_label_and_source_file(None, mast_forest, node_id, host);
511 Err(ExecutionError::OperationError { label, source_file, err })
512 },
513 }
514 }
515}
516
517impl<T> MapExecErrWithOpIdx<T> for Result<T, OperationError> {
518 #[inline(always)]
519 fn map_exec_err_with_op_idx(
520 self,
521 mast_forest: &MastForest,
522 node_id: MastNodeId,
523 host: &impl BaseHost,
524 op_idx: usize,
525 ) -> Result<T, ExecutionError> {
526 match self {
527 Ok(v) => Ok(v),
528 Err(err) => {
529 let (label, source_file) =
530 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
531 Err(ExecutionError::OperationError { label, source_file, err })
532 },
533 }
534 }
535}
536
537impl<T> MapExecErrNoCtx<T> for Result<T, OperationError> {
538 #[inline(always)]
539 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
540 match self {
541 Ok(v) => Ok(v),
542 Err(err) => Err(ExecutionError::OperationError {
543 label: SourceSpan::UNKNOWN,
544 source_file: None,
545 err,
546 }),
547 }
548 }
549}
550
551impl<T> MapExecErr<T> for Result<T, AdviceError> {
553 #[inline(always)]
554 fn map_exec_err(
555 self,
556 mast_forest: &MastForest,
557 node_id: MastNodeId,
558 host: &impl BaseHost,
559 ) -> Result<T, ExecutionError> {
560 match self {
561 Ok(v) => Ok(v),
562 Err(err) => Err(advice_error_with_context(err, mast_forest, node_id, host)),
563 }
564 }
565}
566
567impl<T> MapExecErrNoCtx<T> for Result<T, AdviceError> {
568 #[inline(always)]
569 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
570 match self {
571 Ok(v) => Ok(v),
572 Err(err) => Err(ExecutionError::AdviceError {
573 label: SourceSpan::UNKNOWN,
574 source_file: None,
575 err,
576 }),
577 }
578 }
579}
580
581impl<T> MapExecErr<T> for Result<T, MemoryError> {
583 #[inline(always)]
584 fn map_exec_err(
585 self,
586 mast_forest: &MastForest,
587 node_id: MastNodeId,
588 host: &impl BaseHost,
589 ) -> Result<T, ExecutionError> {
590 match self {
591 Ok(v) => Ok(v),
592 Err(err) => {
593 let (label, source_file) =
594 get_label_and_source_file(None, mast_forest, node_id, host);
595 Err(ExecutionError::MemoryError { label, source_file, err })
596 },
597 }
598 }
599}
600
601impl<T> MapExecErrWithOpIdx<T> for Result<T, MemoryError> {
602 #[inline(always)]
603 fn map_exec_err_with_op_idx(
604 self,
605 mast_forest: &MastForest,
606 node_id: MastNodeId,
607 host: &impl BaseHost,
608 op_idx: usize,
609 ) -> Result<T, ExecutionError> {
610 match self {
611 Ok(v) => Ok(v),
612 Err(err) => {
613 let (label, source_file) =
614 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
615 Err(ExecutionError::MemoryError { label, source_file, err })
616 },
617 }
618 }
619}
620
621impl<T> MapExecErr<T> for Result<T, SystemEventError> {
623 #[inline(always)]
624 fn map_exec_err(
625 self,
626 mast_forest: &MastForest,
627 node_id: MastNodeId,
628 host: &impl BaseHost,
629 ) -> Result<T, ExecutionError> {
630 match self {
631 Ok(v) => Ok(v),
632 Err(err) => {
633 let (label, source_file) =
634 get_label_and_source_file(None, mast_forest, node_id, host);
635 Err(match err {
636 SystemEventError::Advice(err) => {
637 ExecutionError::AdviceError { label, source_file, err }
638 },
639 SystemEventError::Operation(err) => {
640 ExecutionError::OperationError { label, source_file, err }
641 },
642 SystemEventError::Memory(err) => {
643 ExecutionError::MemoryError { label, source_file, err }
644 },
645 })
646 },
647 }
648 }
649}
650
651impl<T> MapExecErrWithOpIdx<T> for Result<T, SystemEventError> {
652 #[inline(always)]
653 fn map_exec_err_with_op_idx(
654 self,
655 mast_forest: &MastForest,
656 node_id: MastNodeId,
657 host: &impl BaseHost,
658 op_idx: usize,
659 ) -> Result<T, ExecutionError> {
660 match self {
661 Ok(v) => Ok(v),
662 Err(err) => {
663 let (label, source_file) =
664 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
665 Err(match err {
666 SystemEventError::Advice(err) => {
667 ExecutionError::AdviceError { label, source_file, err }
668 },
669 SystemEventError::Operation(err) => {
670 ExecutionError::OperationError { label, source_file, err }
671 },
672 SystemEventError::Memory(err) => {
673 ExecutionError::MemoryError { label, source_file, err }
674 },
675 })
676 },
677 }
678 }
679}
680
681impl<T> MapExecErrWithOpIdx<T> for Result<T, IoError> {
683 #[inline(always)]
684 fn map_exec_err_with_op_idx(
685 self,
686 mast_forest: &MastForest,
687 node_id: MastNodeId,
688 host: &impl BaseHost,
689 op_idx: usize,
690 ) -> Result<T, ExecutionError> {
691 match self {
692 Ok(v) => Ok(v),
693 Err(err) => {
694 let (label, source_file) =
695 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
696 Err(match err {
697 IoError::Advice(err) => ExecutionError::AdviceError { label, source_file, err },
698 IoError::Memory(err) => ExecutionError::MemoryError { label, source_file, err },
699 IoError::Operation(err) => {
700 ExecutionError::OperationError { label, source_file, err }
701 },
702 IoError::Execution(boxed_err) => *boxed_err,
704 })
705 },
706 }
707 }
708}
709
710impl<T> MapExecErrWithOpIdx<T> for Result<T, CryptoError> {
712 #[inline(always)]
713 fn map_exec_err_with_op_idx(
714 self,
715 mast_forest: &MastForest,
716 node_id: MastNodeId,
717 host: &impl BaseHost,
718 op_idx: usize,
719 ) -> Result<T, ExecutionError> {
720 match self {
721 Ok(v) => Ok(v),
722 Err(err) => {
723 let (label, source_file) =
724 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
725 Err(match err {
726 CryptoError::Advice(err) => {
727 ExecutionError::AdviceError { label, source_file, err }
728 },
729 CryptoError::Operation(err) => {
730 ExecutionError::OperationError { label, source_file, err }
731 },
732 })
733 },
734 }
735 }
736}
737
738impl<T> MapExecErrWithOpIdx<T> for Result<T, AceEvalError> {
740 #[inline(always)]
741 fn map_exec_err_with_op_idx(
742 self,
743 mast_forest: &MastForest,
744 node_id: MastNodeId,
745 host: &impl BaseHost,
746 op_idx: usize,
747 ) -> Result<T, ExecutionError> {
748 match self {
749 Ok(v) => Ok(v),
750 Err(err) => {
751 let (label, source_file) =
752 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
753 Err(match err {
754 AceEvalError::Ace(error) => {
755 ExecutionError::AceChipError { label, source_file, error }
756 },
757 AceEvalError::Memory(err) => {
758 ExecutionError::MemoryError { label, source_file, err }
759 },
760 })
761 },
762 }
763 }
764}
765
766#[cfg(test)]
770mod error_assertions {
771 use super::*;
772
773 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
775
776 fn _assert_execution_error_bounds(err: ExecutionError) {
777 _assert_error_is_send_sync_static(err);
778 }
779}