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!("'{name}' (ID: {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("operand stack depth {depth} exceeds the maximum of {max}")]
62 StackDepthLimitExceeded { depth: usize, max: usize },
63 #[error("trace length exceeded the maximum of {0} rows")]
67 TraceLenExceeded(usize),
68 #[error("{err}")]
72 #[diagnostic(forward(err))]
73 MemoryError {
74 #[label]
75 label: SourceSpan,
76 #[source_code]
77 source_file: Option<Arc<SourceFile>>,
78 err: MemoryError,
79 },
80 #[error(transparent)]
85 #[diagnostic(transparent)]
86 MemoryErrorNoCtx(MemoryError),
87 #[error("{err}")]
88 #[diagnostic(forward(err))]
89 OperationError {
90 #[label]
91 label: SourceSpan,
92 #[source_code]
93 source_file: Option<Arc<SourceFile>>,
94 err: OperationError,
95 },
96 #[error("stack should have at most {MIN_STACK_DEPTH} elements at the end of program execution, but had {} elements", MIN_STACK_DEPTH + .0)]
97 OutputStackOverflow(usize),
98 #[error("procedure with root digest {root_digest} could not be found")]
99 #[diagnostic()]
100 ProcedureNotFound {
101 #[label]
102 label: SourceSpan,
103 #[source_code]
104 source_file: Option<Arc<SourceFile>>,
105 root_digest: Word,
106 },
107 #[error("failed to generate STARK proof: {0}")]
108 ProvingError(String),
109 #[error(transparent)]
110 HostError(#[from] HostError),
111}
112
113impl ExecutionError {
114 pub fn advice_error_no_context(err: AdviceError) -> Self {
116 Self::AdviceError {
117 label: SourceSpan::UNKNOWN,
118 source_file: None,
119 err,
120 }
121 }
122}
123
124impl AsRef<dyn Diagnostic> for ExecutionError {
125 fn as_ref(&self) -> &(dyn Diagnostic + 'static) {
126 self
127 }
128}
129
130#[derive(Debug, thiserror::Error)]
134#[error("ace circuit evaluation failed: {0}")]
135pub struct AceError(pub String);
136
137#[derive(Debug, thiserror::Error)]
146pub enum AceEvalError {
147 #[error(transparent)]
148 Ace(#[from] AceError),
149 #[error(transparent)]
150 Memory(#[from] MemoryError),
151}
152
153#[derive(Debug, thiserror::Error)]
158pub enum HostError {
159 #[error("attempted to add event handler for '{event}' (already registered)")]
160 DuplicateEventHandler { event: EventName },
161 #[error("attempted to add event handler for '{event}' (reserved system event)")]
162 ReservedEventNamespace { event: EventName },
163 #[error("debug handler error: {err}")]
164 DebugHandlerError {
165 #[source]
166 err: DebugError,
167 },
168 #[error("trace handler error for trace ID {trace_id}: {err}")]
169 TraceHandlerError {
170 trace_id: u32,
171 #[source]
172 err: TraceError,
173 },
174}
175
176#[derive(Debug, thiserror::Error, Diagnostic)]
185pub enum IoError {
186 #[error(transparent)]
187 Advice(#[from] AdviceError),
188 #[error(transparent)]
189 Memory(#[from] MemoryError),
190 #[error(transparent)]
191 #[diagnostic(transparent)]
192 Operation(#[from] OperationError),
193 #[error(transparent)]
198 #[diagnostic(transparent)]
199 Execution(Box<ExecutionError>),
200}
201
202impl From<ExecutionError> for IoError {
203 fn from(err: ExecutionError) -> Self {
204 IoError::Execution(Box::new(err))
205 }
206}
207
208#[derive(Debug, thiserror::Error, Diagnostic)]
217pub enum MemoryError {
218 #[error("memory address cannot exceed 2^32 but was {addr}")]
219 AddressOutOfBounds { addr: u64 },
220 #[error(
221 "memory address {addr} in context {ctx} was read and written, or written twice, in the same clock cycle {clk}"
222 )]
223 IllegalMemoryAccess { ctx: ContextId, addr: u32, clk: Felt },
224 #[error(
225 "memory range start address cannot exceed end address, but was ({start_addr}, {end_addr})"
226 )]
227 InvalidMemoryRange { start_addr: u64, end_addr: u64 },
228 #[error("word access at memory address {addr} in context {ctx} is unaligned")]
229 #[diagnostic(help(
230 "ensure that the memory address accessed is aligned to a word boundary (it is a multiple of 4)"
231 ))]
232 UnalignedWordAccess { addr: u32, ctx: ContextId },
233 #[error("failed to read from memory: {0}")]
234 MemoryReadFailed(String),
235}
236
237#[derive(Debug, thiserror::Error, Diagnostic)]
246pub enum CryptoError {
247 #[error(transparent)]
248 Advice(#[from] AdviceError),
249 #[error(transparent)]
250 #[diagnostic(transparent)]
251 Operation(#[from] OperationError),
252}
253
254#[derive(Debug, Clone, thiserror::Error, Diagnostic)]
288pub enum OperationError {
289 #[error("external node with mast root {0} resolved to an external node")]
290 CircularExternalNode(Word),
291 #[error("division by zero")]
292 #[diagnostic(help(
293 "ensure the divisor (second stack element) is non-zero before division or modulo operations"
294 ))]
295 DivideByZero,
296 #[error(
297 "assertion failed with error {}",
298 match err_msg {
299 Some(msg) => format!("message: {msg}"),
300 None => format!("code: {err_code}"),
301 }
302 )]
303 #[diagnostic(help(
304 "assertions validate program invariants. Review the assertion condition and ensure all prerequisites are met"
305 ))]
306 FailedAssertion {
307 err_code: Felt,
308 err_msg: Option<Arc<str>>,
309 },
310 #[error(
311 "u32 assertion failed with error {}: invalid values: {invalid_values:?}",
312 match err_msg {
313 Some(msg) => format!("message: {msg}"),
314 None => format!("code: {err_code}"),
315 }
316 )]
317 #[diagnostic(help(
318 "u32assert2 requires both stack values to be valid 32-bit unsigned integers"
319 ))]
320 U32AssertionFailed {
321 err_code: Felt,
322 err_msg: Option<Arc<str>>,
323 invalid_values: Vec<Felt>,
324 },
325 #[error("FRI operation failed: {0}")]
326 FriError(String),
327 #[error(
328 "invalid crypto operation: Merkle path length {path_len} does not match expected depth {depth}"
329 )]
330 InvalidMerklePathLength { path_len: usize, depth: Felt },
331 #[error("when returning from a call, stack depth must be {MIN_STACK_DEPTH}, but was {depth}")]
332 InvalidStackDepthOnReturn { depth: usize },
333 #[error("attempted to calculate integer logarithm with zero argument")]
334 #[diagnostic(help("ilog2 requires a non-zero argument"))]
335 LogArgumentZero,
336 #[error(
337 "MAST forest in host indexed by procedure root {root_digest} doesn't contain that root"
338 )]
339 MalformedMastForestInHost { root_digest: Word },
340 #[error("merkle path verification failed for value {value} at index {index} in the Merkle tree with root {root} (error {err})",
341 value = to_hex(inner.value.as_bytes()),
342 root = to_hex(inner.root.as_bytes()),
343 index = inner.index,
344 err = match &inner.err_msg {
345 Some(msg) => format!("message: {msg}"),
346 None => format!("code: {}", inner.err_code),
347 }
348 )]
349 MerklePathVerificationFailed {
350 inner: Box<MerklePathVerificationFailedInner>,
351 },
352 #[error("operation expected a binary value, but got {value}")]
353 NotBinaryValue { value: Felt },
354 #[error("if statement expected a binary value on top of the stack, but got {value}")]
355 NotBinaryValueIf { value: Felt },
356 #[error("loop condition must be a binary value, but got {value}")]
357 #[diagnostic(help(
358 "this could happen either when first entering the loop, or any subsequent iteration"
359 ))]
360 NotBinaryValueLoop { value: Felt },
361 #[error("operation expected u32 values, but got values: {values:?}")]
362 NotU32Values { values: Vec<Felt> },
363 #[error("syscall failed: procedure with root {proc_root} was not found in the kernel")]
364 SyscallTargetNotInKernel { proc_root: Word },
365 #[error("failed to execute the operation for internal reason: {0}")]
366 Internal(&'static str),
367}
368
369impl OperationError {
370 pub fn with_context(
375 self,
376 mast_forest: &MastForest,
377 node_id: MastNodeId,
378 host: &impl BaseHost,
379 ) -> ExecutionError {
380 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
381 ExecutionError::OperationError { label, source_file, err: self }
382 }
383}
384
385#[derive(Debug, Clone)]
389pub struct MerklePathVerificationFailedInner {
390 pub value: Word,
391 pub index: Felt,
392 pub root: Word,
393 pub err_code: Felt,
394 pub err_msg: Option<Arc<str>>,
395}
396
397fn get_label_and_source_file(
406 op_idx: Option<usize>,
407 mast_forest: &MastForest,
408 node_id: MastNodeId,
409 host: &impl BaseHost,
410) -> (SourceSpan, Option<Arc<SourceFile>>) {
411 mast_forest
412 .get_assembly_op(node_id, op_idx)
413 .and_then(|assembly_op| assembly_op.location())
414 .map_or_else(
415 || (SourceSpan::UNKNOWN, None),
416 |location| host.get_label_and_source_file(location),
417 )
418}
419
420pub fn advice_error_with_context(
425 err: AdviceError,
426 mast_forest: &MastForest,
427 node_id: MastNodeId,
428 host: &impl BaseHost,
429 op_idx: Option<usize>,
430) -> ExecutionError {
431 let (label, source_file) = get_label_and_source_file(op_idx, mast_forest, node_id, host);
432 ExecutionError::AdviceError { label, source_file, err }
433}
434
435pub fn event_error_with_context(
440 error: EventError,
441 mast_forest: &MastForest,
442 node_id: MastNodeId,
443 host: &impl BaseHost,
444 op_idx: Option<usize>,
445 event_id: EventId,
446 event_name: Option<EventName>,
447) -> ExecutionError {
448 let (label, source_file) = get_label_and_source_file(op_idx, mast_forest, node_id, host);
449 ExecutionError::EventError {
450 label,
451 source_file,
452 event_id,
453 event_name,
454 error,
455 }
456}
457
458pub fn procedure_not_found_with_context(
460 root_digest: Word,
461 mast_forest: &MastForest,
462 node_id: MastNodeId,
463 host: &impl BaseHost,
464) -> ExecutionError {
465 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
466 ExecutionError::ProcedureNotFound { label, source_file, root_digest }
467}
468
469pub trait MapExecErr<T> {
482 fn map_exec_err(
483 self,
484 mast_forest: &MastForest,
485 node_id: MastNodeId,
486 host: &impl BaseHost,
487 ) -> Result<T, ExecutionError>;
488}
489
490pub trait MapExecErrWithOpIdx<T> {
495 fn map_exec_err_with_op_idx(
496 self,
497 mast_forest: &MastForest,
498 node_id: MastNodeId,
499 host: &impl BaseHost,
500 op_idx: usize,
501 ) -> Result<T, ExecutionError>;
502}
503
504pub trait MapExecErrNoCtx<T> {
509 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError>;
510}
511
512impl<T> MapExecErr<T> for Result<T, OperationError> {
514 #[inline(always)]
515 fn map_exec_err(
516 self,
517 mast_forest: &MastForest,
518 node_id: MastNodeId,
519 host: &impl BaseHost,
520 ) -> Result<T, ExecutionError> {
521 match self {
522 Ok(v) => Ok(v),
523 Err(err) => {
524 let (label, source_file) =
525 get_label_and_source_file(None, mast_forest, node_id, host);
526 Err(ExecutionError::OperationError { label, source_file, err })
527 },
528 }
529 }
530}
531
532impl<T> MapExecErrWithOpIdx<T> for Result<T, OperationError> {
533 #[inline(always)]
534 fn map_exec_err_with_op_idx(
535 self,
536 mast_forest: &MastForest,
537 node_id: MastNodeId,
538 host: &impl BaseHost,
539 op_idx: usize,
540 ) -> Result<T, ExecutionError> {
541 match self {
542 Ok(v) => Ok(v),
543 Err(err) => {
544 let (label, source_file) =
545 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
546 Err(ExecutionError::OperationError { label, source_file, err })
547 },
548 }
549 }
550}
551
552impl<T> MapExecErrNoCtx<T> for Result<T, OperationError> {
553 #[inline(always)]
554 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
555 match self {
556 Ok(v) => Ok(v),
557 Err(err) => Err(ExecutionError::OperationError {
558 label: SourceSpan::UNKNOWN,
559 source_file: None,
560 err,
561 }),
562 }
563 }
564}
565
566impl<T> MapExecErr<T> for Result<T, AdviceError> {
568 #[inline(always)]
569 fn map_exec_err(
570 self,
571 mast_forest: &MastForest,
572 node_id: MastNodeId,
573 host: &impl BaseHost,
574 ) -> Result<T, ExecutionError> {
575 match self {
576 Ok(v) => Ok(v),
577 Err(err) => Err(advice_error_with_context(err, mast_forest, node_id, host, None)),
578 }
579 }
580}
581
582impl<T> MapExecErrNoCtx<T> for Result<T, AdviceError> {
583 #[inline(always)]
584 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
585 match self {
586 Ok(v) => Ok(v),
587 Err(err) => Err(ExecutionError::AdviceError {
588 label: SourceSpan::UNKNOWN,
589 source_file: None,
590 err,
591 }),
592 }
593 }
594}
595
596impl<T> MapExecErr<T> for Result<T, MemoryError> {
598 #[inline(always)]
599 fn map_exec_err(
600 self,
601 mast_forest: &MastForest,
602 node_id: MastNodeId,
603 host: &impl BaseHost,
604 ) -> Result<T, ExecutionError> {
605 match self {
606 Ok(v) => Ok(v),
607 Err(err) => {
608 let (label, source_file) =
609 get_label_and_source_file(None, mast_forest, node_id, host);
610 Err(ExecutionError::MemoryError { label, source_file, err })
611 },
612 }
613 }
614}
615
616impl<T> MapExecErrWithOpIdx<T> for Result<T, MemoryError> {
617 #[inline(always)]
618 fn map_exec_err_with_op_idx(
619 self,
620 mast_forest: &MastForest,
621 node_id: MastNodeId,
622 host: &impl BaseHost,
623 op_idx: usize,
624 ) -> Result<T, ExecutionError> {
625 match self {
626 Ok(v) => Ok(v),
627 Err(err) => {
628 let (label, source_file) =
629 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
630 Err(ExecutionError::MemoryError { label, source_file, err })
631 },
632 }
633 }
634}
635
636impl<T> MapExecErr<T> for Result<T, SystemEventError> {
638 #[inline(always)]
639 fn map_exec_err(
640 self,
641 mast_forest: &MastForest,
642 node_id: MastNodeId,
643 host: &impl BaseHost,
644 ) -> Result<T, ExecutionError> {
645 match self {
646 Ok(v) => Ok(v),
647 Err(err) => {
648 let (label, source_file) =
649 get_label_and_source_file(None, mast_forest, node_id, host);
650 Err(match err {
651 SystemEventError::Advice(err) => {
652 ExecutionError::AdviceError { label, source_file, err }
653 },
654 SystemEventError::Operation(err) => {
655 ExecutionError::OperationError { label, source_file, err }
656 },
657 SystemEventError::Memory(err) => {
658 ExecutionError::MemoryError { label, source_file, err }
659 },
660 })
661 },
662 }
663 }
664}
665
666impl<T> MapExecErrWithOpIdx<T> for Result<T, SystemEventError> {
667 #[inline(always)]
668 fn map_exec_err_with_op_idx(
669 self,
670 mast_forest: &MastForest,
671 node_id: MastNodeId,
672 host: &impl BaseHost,
673 op_idx: usize,
674 ) -> Result<T, ExecutionError> {
675 match self {
676 Ok(v) => Ok(v),
677 Err(err) => {
678 let (label, source_file) =
679 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
680 Err(match err {
681 SystemEventError::Advice(err) => {
682 ExecutionError::AdviceError { label, source_file, err }
683 },
684 SystemEventError::Operation(err) => {
685 ExecutionError::OperationError { label, source_file, err }
686 },
687 SystemEventError::Memory(err) => {
688 ExecutionError::MemoryError { label, source_file, err }
689 },
690 })
691 },
692 }
693 }
694}
695
696impl<T> MapExecErrWithOpIdx<T> for Result<T, IoError> {
698 #[inline(always)]
699 fn map_exec_err_with_op_idx(
700 self,
701 mast_forest: &MastForest,
702 node_id: MastNodeId,
703 host: &impl BaseHost,
704 op_idx: usize,
705 ) -> Result<T, ExecutionError> {
706 match self {
707 Ok(v) => Ok(v),
708 Err(err) => {
709 let (label, source_file) =
710 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
711 Err(match err {
712 IoError::Advice(err) => ExecutionError::AdviceError { label, source_file, err },
713 IoError::Memory(err) => ExecutionError::MemoryError { label, source_file, err },
714 IoError::Operation(err) => {
715 ExecutionError::OperationError { label, source_file, err }
716 },
717 IoError::Execution(boxed_err) => *boxed_err,
719 })
720 },
721 }
722 }
723}
724
725impl<T> MapExecErrWithOpIdx<T> for Result<T, CryptoError> {
727 #[inline(always)]
728 fn map_exec_err_with_op_idx(
729 self,
730 mast_forest: &MastForest,
731 node_id: MastNodeId,
732 host: &impl BaseHost,
733 op_idx: usize,
734 ) -> Result<T, ExecutionError> {
735 match self {
736 Ok(v) => Ok(v),
737 Err(err) => {
738 let (label, source_file) =
739 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
740 Err(match err {
741 CryptoError::Advice(err) => {
742 ExecutionError::AdviceError { label, source_file, err }
743 },
744 CryptoError::Operation(err) => {
745 ExecutionError::OperationError { label, source_file, err }
746 },
747 })
748 },
749 }
750 }
751}
752
753impl<T> MapExecErrWithOpIdx<T> for Result<T, AceEvalError> {
755 #[inline(always)]
756 fn map_exec_err_with_op_idx(
757 self,
758 mast_forest: &MastForest,
759 node_id: MastNodeId,
760 host: &impl BaseHost,
761 op_idx: usize,
762 ) -> Result<T, ExecutionError> {
763 match self {
764 Ok(v) => Ok(v),
765 Err(err) => {
766 let (label, source_file) =
767 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
768 Err(match err {
769 AceEvalError::Ace(error) => {
770 ExecutionError::AceChipError { label, source_file, error }
771 },
772 AceEvalError::Memory(err) => {
773 ExecutionError::MemoryError { label, source_file, err }
774 },
775 })
776 },
777 }
778 }
779}
780
781#[cfg(test)]
785mod error_assertions {
786 use super::*;
787
788 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
790
791 fn _assert_execution_error_bounds(err: ExecutionError) {
792 _assert_error_is_send_sync_static(err);
793 }
794}