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 ContextId, DebugError, Felt, Host, 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("FRI operation failed: {0}")]
298 FriError(String),
299 #[error(
300 "invalid crypto operation: Merkle path length {path_len} does not match expected depth {depth}"
301 )]
302 InvalidMerklePathLength { path_len: usize, depth: Felt },
303 #[error("when returning from a call, stack depth must be {MIN_STACK_DEPTH}, but was {depth}")]
304 InvalidStackDepthOnReturn { depth: usize },
305 #[error("attempted to calculate integer logarithm with zero argument")]
306 #[diagnostic(help("ilog2 requires a non-zero argument"))]
307 LogArgumentZero,
308 #[error(
309 "MAST forest in host indexed by procedure root {root_digest} doesn't contain that root"
310 )]
311 MalformedMastForestInHost { root_digest: Word },
312 #[error("merkle path verification failed for value {value} at index {index} in the Merkle tree with root {root} (error {err})",
313 value = to_hex(inner.value.as_bytes()),
314 root = to_hex(inner.root.as_bytes()),
315 index = inner.index,
316 err = match &inner.err_msg {
317 Some(msg) => format!("message: {msg}"),
318 None => format!("code: {}", inner.err_code),
319 }
320 )]
321 MerklePathVerificationFailed {
322 inner: Box<MerklePathVerificationFailedInner>,
323 },
324 #[error("operation expected a binary value, but got {value}")]
325 NotBinaryValue { value: Felt },
326 #[error("if statement expected a binary value on top of the stack, but got {value}")]
327 NotBinaryValueIf { value: Felt },
328 #[error("loop condition must be a binary value, but got {value}")]
329 #[diagnostic(help(
330 "this could happen either when first entering the loop, or any subsequent iteration"
331 ))]
332 NotBinaryValueLoop { value: Felt },
333 #[error("operation expected u32 values, but got values: {values:?}")]
334 NotU32Values { values: Vec<Felt> },
335 #[error("syscall failed: procedure with root {proc_root} was not found in the kernel")]
336 SyscallTargetNotInKernel { proc_root: Word },
337 #[error("failed to execute the operation for internal reason: {0}")]
338 Internal(&'static str),
339}
340
341impl OperationError {
342 pub fn with_context(
347 self,
348 mast_forest: &MastForest,
349 node_id: MastNodeId,
350 host: &impl Host,
351 ) -> ExecutionError {
352 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
353 ExecutionError::OperationError { label, source_file, err: self }
354 }
355}
356
357#[derive(Debug, Clone)]
361pub struct MerklePathVerificationFailedInner {
362 pub value: Word,
363 pub index: Felt,
364 pub root: Word,
365 pub err_code: Felt,
366 pub err_msg: Option<Arc<str>>,
367}
368
369fn get_label_and_source_file(
378 op_idx: Option<usize>,
379 mast_forest: &MastForest,
380 node_id: MastNodeId,
381 host: &impl Host,
382) -> (SourceSpan, Option<Arc<SourceFile>>) {
383 mast_forest
384 .get_assembly_op(node_id, op_idx)
385 .and_then(|assembly_op| assembly_op.location())
386 .map_or_else(
387 || (SourceSpan::UNKNOWN, None),
388 |location| host.get_label_and_source_file(location),
389 )
390}
391
392pub fn advice_error_with_context(
397 err: AdviceError,
398 mast_forest: &MastForest,
399 node_id: MastNodeId,
400 host: &impl Host,
401) -> ExecutionError {
402 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
403 ExecutionError::AdviceError { label, source_file, err }
404}
405
406pub fn event_error_with_context(
411 error: EventError,
412 mast_forest: &MastForest,
413 node_id: MastNodeId,
414 host: &impl Host,
415 event_id: EventId,
416 event_name: Option<EventName>,
417) -> ExecutionError {
418 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
419 ExecutionError::EventError {
420 label,
421 source_file,
422 event_id,
423 event_name,
424 error,
425 }
426}
427
428pub fn procedure_not_found_with_context(
430 root_digest: Word,
431 mast_forest: &MastForest,
432 node_id: MastNodeId,
433 host: &impl Host,
434) -> ExecutionError {
435 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
436 ExecutionError::ProcedureNotFound { label, source_file, root_digest }
437}
438
439pub trait MapExecErr<T> {
452 fn map_exec_err(
453 self,
454 mast_forest: &MastForest,
455 node_id: MastNodeId,
456 host: &impl Host,
457 ) -> Result<T, ExecutionError>;
458}
459
460pub trait MapExecErrWithOpIdx<T> {
465 fn map_exec_err_with_op_idx(
466 self,
467 mast_forest: &MastForest,
468 node_id: MastNodeId,
469 host: &impl Host,
470 op_idx: usize,
471 ) -> Result<T, ExecutionError>;
472}
473
474pub trait MapExecErrNoCtx<T> {
479 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError>;
480}
481
482impl<T> MapExecErr<T> for Result<T, OperationError> {
484 #[inline(always)]
485 fn map_exec_err(
486 self,
487 mast_forest: &MastForest,
488 node_id: MastNodeId,
489 host: &impl Host,
490 ) -> Result<T, ExecutionError> {
491 match self {
492 Ok(v) => Ok(v),
493 Err(err) => {
494 let (label, source_file) =
495 get_label_and_source_file(None, mast_forest, node_id, host);
496 Err(ExecutionError::OperationError { label, source_file, err })
497 },
498 }
499 }
500}
501
502impl<T> MapExecErrWithOpIdx<T> for Result<T, OperationError> {
503 #[inline(always)]
504 fn map_exec_err_with_op_idx(
505 self,
506 mast_forest: &MastForest,
507 node_id: MastNodeId,
508 host: &impl Host,
509 op_idx: usize,
510 ) -> Result<T, ExecutionError> {
511 match self {
512 Ok(v) => Ok(v),
513 Err(err) => {
514 let (label, source_file) =
515 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
516 Err(ExecutionError::OperationError { label, source_file, err })
517 },
518 }
519 }
520}
521
522impl<T> MapExecErrNoCtx<T> for Result<T, OperationError> {
523 #[inline(always)]
524 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
525 match self {
526 Ok(v) => Ok(v),
527 Err(err) => Err(ExecutionError::OperationError {
528 label: SourceSpan::UNKNOWN,
529 source_file: None,
530 err,
531 }),
532 }
533 }
534}
535
536impl<T> MapExecErr<T> for Result<T, AdviceError> {
538 #[inline(always)]
539 fn map_exec_err(
540 self,
541 mast_forest: &MastForest,
542 node_id: MastNodeId,
543 host: &impl Host,
544 ) -> Result<T, ExecutionError> {
545 match self {
546 Ok(v) => Ok(v),
547 Err(err) => Err(advice_error_with_context(err, mast_forest, node_id, host)),
548 }
549 }
550}
551
552impl<T> MapExecErrNoCtx<T> for Result<T, AdviceError> {
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::AdviceError {
558 label: SourceSpan::UNKNOWN,
559 source_file: None,
560 err,
561 }),
562 }
563 }
564}
565
566impl<T> MapExecErr<T> for Result<T, MemoryError> {
568 #[inline(always)]
569 fn map_exec_err(
570 self,
571 mast_forest: &MastForest,
572 node_id: MastNodeId,
573 host: &impl Host,
574 ) -> Result<T, ExecutionError> {
575 match self {
576 Ok(v) => Ok(v),
577 Err(err) => {
578 let (label, source_file) =
579 get_label_and_source_file(None, mast_forest, node_id, host);
580 Err(ExecutionError::MemoryError { label, source_file, err })
581 },
582 }
583 }
584}
585
586impl<T> MapExecErrWithOpIdx<T> for Result<T, MemoryError> {
587 #[inline(always)]
588 fn map_exec_err_with_op_idx(
589 self,
590 mast_forest: &MastForest,
591 node_id: MastNodeId,
592 host: &impl Host,
593 op_idx: usize,
594 ) -> Result<T, ExecutionError> {
595 match self {
596 Ok(v) => Ok(v),
597 Err(err) => {
598 let (label, source_file) =
599 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
600 Err(ExecutionError::MemoryError { label, source_file, err })
601 },
602 }
603 }
604}
605
606impl<T> MapExecErr<T> for Result<T, SystemEventError> {
608 #[inline(always)]
609 fn map_exec_err(
610 self,
611 mast_forest: &MastForest,
612 node_id: MastNodeId,
613 host: &impl Host,
614 ) -> Result<T, ExecutionError> {
615 match self {
616 Ok(v) => Ok(v),
617 Err(err) => {
618 let (label, source_file) =
619 get_label_and_source_file(None, mast_forest, node_id, host);
620 Err(match err {
621 SystemEventError::Advice(err) => {
622 ExecutionError::AdviceError { label, source_file, err }
623 },
624 SystemEventError::Operation(err) => {
625 ExecutionError::OperationError { label, source_file, err }
626 },
627 SystemEventError::Memory(err) => {
628 ExecutionError::MemoryError { label, source_file, err }
629 },
630 })
631 },
632 }
633 }
634}
635
636impl<T> MapExecErrWithOpIdx<T> for Result<T, SystemEventError> {
637 #[inline(always)]
638 fn map_exec_err_with_op_idx(
639 self,
640 mast_forest: &MastForest,
641 node_id: MastNodeId,
642 host: &impl Host,
643 op_idx: usize,
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(Some(op_idx), 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, IoError> {
668 #[inline(always)]
669 fn map_exec_err_with_op_idx(
670 self,
671 mast_forest: &MastForest,
672 node_id: MastNodeId,
673 host: &impl Host,
674 op_idx: usize,
675 ) -> Result<T, ExecutionError> {
676 match self {
677 Ok(v) => Ok(v),
678 Err(err) => {
679 let (label, source_file) =
680 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
681 Err(match err {
682 IoError::Advice(err) => ExecutionError::AdviceError { label, source_file, err },
683 IoError::Memory(err) => ExecutionError::MemoryError { label, source_file, err },
684 IoError::Operation(err) => {
685 ExecutionError::OperationError { label, source_file, err }
686 },
687 IoError::Execution(boxed_err) => *boxed_err,
689 })
690 },
691 }
692 }
693}
694
695impl<T> MapExecErrWithOpIdx<T> for Result<T, CryptoError> {
697 #[inline(always)]
698 fn map_exec_err_with_op_idx(
699 self,
700 mast_forest: &MastForest,
701 node_id: MastNodeId,
702 host: &impl Host,
703 op_idx: usize,
704 ) -> Result<T, ExecutionError> {
705 match self {
706 Ok(v) => Ok(v),
707 Err(err) => {
708 let (label, source_file) =
709 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
710 Err(match err {
711 CryptoError::Advice(err) => {
712 ExecutionError::AdviceError { label, source_file, err }
713 },
714 CryptoError::Operation(err) => {
715 ExecutionError::OperationError { label, source_file, err }
716 },
717 })
718 },
719 }
720 }
721}
722
723impl<T> MapExecErrWithOpIdx<T> for Result<T, AceEvalError> {
725 #[inline(always)]
726 fn map_exec_err_with_op_idx(
727 self,
728 mast_forest: &MastForest,
729 node_id: MastNodeId,
730 host: &impl Host,
731 op_idx: usize,
732 ) -> Result<T, ExecutionError> {
733 match self {
734 Ok(v) => Ok(v),
735 Err(err) => {
736 let (label, source_file) =
737 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
738 Err(match err {
739 AceEvalError::Ace(error) => {
740 ExecutionError::AceChipError { label, source_file, error }
741 },
742 AceEvalError::Memory(err) => {
743 ExecutionError::MemoryError { label, source_file, err }
744 },
745 })
746 },
747 }
748 }
749}
750
751#[cfg(test)]
755mod error_assertions {
756 use super::*;
757
758 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
760
761 fn _assert_execution_error_bounds(err: ExecutionError) {
762 _assert_error_is_send_sync_static(err);
763 }
764}