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 field::QuadFelt,
16 mast::{MastForest, MastNodeId},
17 utils::to_hex,
18};
19
20#[derive(Debug, thiserror::Error, Diagnostic)]
24pub enum ExecutionError {
25 #[error("failed to execute arithmetic circuit evaluation operation: {error}")]
26 #[diagnostic()]
27 AceChipError {
28 #[label("this call failed")]
29 label: SourceSpan,
30 #[source_code]
31 source_file: Option<Arc<SourceFile>>,
32 error: AceError,
33 },
34 #[error("{err}")]
35 #[diagnostic(forward(err))]
36 AdviceError {
37 #[label]
38 label: SourceSpan,
39 #[source_code]
40 source_file: Option<Arc<SourceFile>>,
41 err: AdviceError,
42 },
43 #[error("exceeded the allowed number of max cycles {0}")]
44 CycleLimitExceeded(u32),
45 #[error("debug handler error: {err}")]
46 DebugHandlerError {
47 #[source]
48 err: DebugError,
49 },
50 #[error("attempted to add event handler for '{event}' (already registered)")]
51 DuplicateEventHandler { event: EventName },
52 #[error("error during processing of event {}", match event_name {
53 Some(name) => format!("'{}' (ID: {})", name, event_id),
54 None => format!("with ID: {}", event_id),
55 })]
56 #[diagnostic()]
57 EventError {
58 #[label]
59 label: SourceSpan,
60 #[source_code]
61 source_file: Option<Arc<SourceFile>>,
62 event_id: EventId,
63 event_name: Option<EventName>,
64 #[source]
65 error: EventError,
66 },
67 #[error("failed to execute the program for internal reason: {0}")]
68 Internal(&'static str),
69 #[error("{err}")]
73 #[diagnostic(forward(err))]
74 MemoryError {
75 #[label]
76 label: SourceSpan,
77 #[source_code]
78 source_file: Option<Arc<SourceFile>>,
79 err: MemoryError,
80 },
81 #[error(transparent)]
86 #[diagnostic(transparent)]
87 MemoryErrorNoCtx(MemoryError),
88 #[error("{err}")]
89 #[diagnostic(forward(err))]
90 OperationError {
91 #[label]
92 label: SourceSpan,
93 #[source_code]
94 source_file: Option<Arc<SourceFile>>,
95 err: OperationError,
96 },
97 #[error("stack should have at most {MIN_STACK_DEPTH} elements at the end of program execution, but had {} elements", MIN_STACK_DEPTH + .0)]
98 OutputStackOverflow(usize),
99 #[error("failed to serialize proof: {0}")]
100 ProofSerializationError(String),
101 #[error("attempted to add event handler for '{event}' (reserved system event)")]
102 ReservedEventNamespace { event: EventName },
103 #[error("trace handler error for trace ID {trace_id}: {err}")]
104 TraceHandlerError {
105 trace_id: u32,
106 #[source]
107 err: TraceError,
108 },
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)]
121pub enum AceError {
122 #[error("num of variables should be word aligned and non-zero but was {0}")]
123 NumVarIsNotWordAlignedOrIsEmpty(u64),
124 #[error("num of evaluation gates should be word aligned and non-zero but was {0}")]
125 NumEvalIsNotWordAlignedOrIsEmpty(u64),
126 #[error("circuit does not evaluate to zero")]
127 CircuitNotEvaluateZero,
128 #[error("failed to read from memory")]
129 FailedMemoryRead,
130 #[error("failed to decode instruction")]
131 FailedDecodeInstruction,
132 #[error("failed to read from the wiring bus")]
133 FailedWireBusRead,
134 #[error("num of wires must be less than 2^30 but was {0}")]
135 TooManyWires(u64),
136}
137
138#[derive(Debug, thiserror::Error)]
147pub enum AceEvalError {
148 #[error(transparent)]
149 Ace(#[from] AceError),
150 #[error(transparent)]
151 Memory(#[from] MemoryError),
152}
153
154#[derive(Debug, thiserror::Error, Diagnostic)]
163pub enum IoError {
164 #[error(transparent)]
165 Advice(#[from] AdviceError),
166 #[error(transparent)]
167 Memory(#[from] MemoryError),
168 #[error(transparent)]
173 #[diagnostic(transparent)]
174 Execution(Box<ExecutionError>),
175}
176
177impl From<ExecutionError> for IoError {
178 fn from(err: ExecutionError) -> Self {
179 IoError::Execution(Box::new(err))
180 }
181}
182
183#[derive(Debug, thiserror::Error, Diagnostic)]
192pub enum MemoryError {
193 #[error("memory address cannot exceed 2^32 but was {addr}")]
194 AddressOutOfBounds { addr: u64 },
195 #[error(
196 "memory address {addr} in context {ctx} was read and written, or written twice, in the same clock cycle {clk}"
197 )]
198 IllegalMemoryAccess { ctx: ContextId, addr: u32, clk: Felt },
199 #[error(
200 "memory range start address cannot exceed end address, but was ({start_addr}, {end_addr})"
201 )]
202 InvalidMemoryRange { start_addr: u64, end_addr: u64 },
203 #[error("word access at memory address {addr} in context {ctx} is unaligned")]
204 #[diagnostic(help(
205 "ensure that the memory address accessed is aligned to a word boundary (it is a multiple of 4)"
206 ))]
207 UnalignedWordAccess { addr: u32, ctx: ContextId },
208}
209
210#[derive(Debug, thiserror::Error, Diagnostic)]
219pub enum CryptoError {
220 #[error(transparent)]
221 Advice(#[from] AdviceError),
222 #[error(transparent)]
223 #[diagnostic(transparent)]
224 Operation(#[from] OperationError),
225}
226
227#[derive(Debug, Clone, thiserror::Error, Diagnostic)]
261pub enum OperationError {
262 #[error("external node with mast root {0} resolved to an external node")]
263 CircularExternalNode(Word),
264 #[error("division by zero")]
265 #[diagnostic(help(
266 "ensure the divisor (second stack element) is non-zero before division or modulo operations"
267 ))]
268 DivideByZero,
269 #[error("failed to execute dynamic code block; block with root {digest} could not be found")]
270 DynamicNodeNotFound { digest: Word },
271 #[error(
272 "assertion failed with error {}",
273 match err_msg {
274 Some(msg) => format!("message: {msg}"),
275 None => format!("code: {err_code}"),
276 }
277 )]
278 #[diagnostic(help(
279 "assertions validate program invariants. Review the assertion condition and ensure all prerequisites are met"
280 ))]
281 FailedAssertion {
282 err_code: Felt,
283 err_msg: Option<Arc<str>>,
284 },
285 #[error("FRI domain size was 0")]
286 InvalidFriDomainGenerator,
287 #[error("FRI domain segment value cannot exceed 3, but was {0}")]
288 InvalidFriDomainSegment(u64),
289 #[error("degree-respecting projection is inconsistent: expected {0} but was {1}")]
290 InvalidFriLayerFolding(QuadFelt, QuadFelt),
291 #[error(
292 "invalid crypto operation: Merkle path length {path_len} does not match expected depth {depth}"
293 )]
294 InvalidMerklePathLength { path_len: usize, depth: Felt },
295 #[error("when returning from a call, stack depth must be {MIN_STACK_DEPTH}, but was {depth}")]
296 InvalidStackDepthOnReturn { depth: usize },
297 #[error("attempted to calculate integer logarithm with zero argument")]
298 #[diagnostic(help("ilog2 requires a non-zero argument"))]
299 LogArgumentZero,
300 #[error(
301 "MAST forest in host indexed by procedure root {root_digest} doesn't contain that root"
302 )]
303 MalformedMastForestInHost { root_digest: Word },
304 #[error("merkle path verification failed for value {value} at index {index} in the Merkle tree with root {root} (error {err})",
305 value = to_hex(inner.value.as_bytes()),
306 root = to_hex(inner.root.as_bytes()),
307 index = inner.index,
308 err = match &inner.err_msg {
309 Some(msg) => format!("message: {msg}"),
310 None => format!("code: {}", inner.err_code),
311 }
312 )]
313 MerklePathVerificationFailed {
314 inner: Box<MerklePathVerificationFailedInner>,
315 },
316 #[error("no MAST forest contains the procedure with root digest {root_digest}")]
317 NoMastForestWithProcedure { root_digest: Word },
318 #[error("operation expected a binary value, but got {value}")]
319 NotBinaryValue { value: Felt },
320 #[error("if statement expected a binary value on top of the stack, but got {value}")]
321 NotBinaryValueIf { value: Felt },
322 #[error("loop condition must be a binary value, but got {value}")]
323 #[diagnostic(help(
324 "this could happen either when first entering the loop, or any subsequent iteration"
325 ))]
326 NotBinaryValueLoop { value: Felt },
327 #[error("operation expected u32 values, but got values: {values:?}")]
328 NotU32Values { values: Vec<Felt> },
329 #[error("syscall failed: procedure with root {proc_root} was not found in the kernel")]
330 SyscallTargetNotInKernel { proc_root: Word },
331}
332
333impl OperationError {
334 pub fn with_context(
339 self,
340 mast_forest: &MastForest,
341 node_id: MastNodeId,
342 host: &impl Host,
343 ) -> ExecutionError {
344 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
345 ExecutionError::OperationError { label, source_file, err: self }
346 }
347}
348
349#[derive(Debug, Clone)]
353pub struct MerklePathVerificationFailedInner {
354 pub value: Word,
355 pub index: Felt,
356 pub root: Word,
357 pub err_code: Felt,
358 pub err_msg: Option<Arc<str>>,
359}
360
361fn get_label_and_source_file(
370 op_idx: Option<usize>,
371 mast_forest: &MastForest,
372 node_id: MastNodeId,
373 host: &impl Host,
374) -> (SourceSpan, Option<Arc<SourceFile>>) {
375 mast_forest
376 .get_assembly_op(node_id, op_idx)
377 .and_then(|assembly_op| assembly_op.location())
378 .map_or_else(
379 || (SourceSpan::UNKNOWN, None),
380 |location| host.get_label_and_source_file(location),
381 )
382}
383
384pub fn advice_error_with_context(
389 err: AdviceError,
390 mast_forest: &MastForest,
391 node_id: MastNodeId,
392 host: &impl Host,
393) -> ExecutionError {
394 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
395 ExecutionError::AdviceError { label, source_file, err }
396}
397
398pub fn event_error_with_context(
403 error: EventError,
404 mast_forest: &MastForest,
405 node_id: MastNodeId,
406 host: &impl Host,
407 event_id: EventId,
408 event_name: Option<EventName>,
409) -> ExecutionError {
410 let (label, source_file) = get_label_and_source_file(None, mast_forest, node_id, host);
411 ExecutionError::EventError {
412 label,
413 source_file,
414 event_id,
415 event_name,
416 error,
417 }
418}
419
420pub trait MapExecErr<T> {
433 fn map_exec_err(
434 self,
435 mast_forest: &MastForest,
436 node_id: MastNodeId,
437 host: &impl Host,
438 ) -> Result<T, ExecutionError>;
439}
440
441pub trait MapExecErrWithOpIdx<T> {
446 fn map_exec_err_with_op_idx(
447 self,
448 mast_forest: &MastForest,
449 node_id: MastNodeId,
450 host: &impl Host,
451 op_idx: usize,
452 ) -> Result<T, ExecutionError>;
453}
454
455pub trait MapExecErrNoCtx<T> {
460 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError>;
461}
462
463impl<T> MapExecErr<T> for Result<T, OperationError> {
465 #[inline(always)]
466 fn map_exec_err(
467 self,
468 mast_forest: &MastForest,
469 node_id: MastNodeId,
470 host: &impl Host,
471 ) -> Result<T, ExecutionError> {
472 match self {
473 Ok(v) => Ok(v),
474 Err(err) => {
475 let (label, source_file) =
476 get_label_and_source_file(None, mast_forest, node_id, host);
477 Err(ExecutionError::OperationError { label, source_file, err })
478 },
479 }
480 }
481}
482
483impl<T> MapExecErrWithOpIdx<T> for Result<T, OperationError> {
484 #[inline(always)]
485 fn map_exec_err_with_op_idx(
486 self,
487 mast_forest: &MastForest,
488 node_id: MastNodeId,
489 host: &impl Host,
490 op_idx: usize,
491 ) -> Result<T, ExecutionError> {
492 match self {
493 Ok(v) => Ok(v),
494 Err(err) => {
495 let (label, source_file) =
496 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
497 Err(ExecutionError::OperationError { label, source_file, err })
498 },
499 }
500 }
501}
502
503impl<T> MapExecErr<T> for Result<T, AdviceError> {
505 #[inline(always)]
506 fn map_exec_err(
507 self,
508 mast_forest: &MastForest,
509 node_id: MastNodeId,
510 host: &impl Host,
511 ) -> Result<T, ExecutionError> {
512 match self {
513 Ok(v) => Ok(v),
514 Err(err) => Err(advice_error_with_context(err, mast_forest, node_id, host)),
515 }
516 }
517}
518
519impl<T> MapExecErrNoCtx<T> for Result<T, AdviceError> {
520 #[inline(always)]
521 fn map_exec_err_no_ctx(self) -> Result<T, ExecutionError> {
522 match self {
523 Ok(v) => Ok(v),
524 Err(err) => Err(ExecutionError::AdviceError {
525 label: SourceSpan::UNKNOWN,
526 source_file: None,
527 err,
528 }),
529 }
530 }
531}
532
533impl<T> MapExecErr<T> for Result<T, MemoryError> {
535 #[inline(always)]
536 fn map_exec_err(
537 self,
538 mast_forest: &MastForest,
539 node_id: MastNodeId,
540 host: &impl Host,
541 ) -> Result<T, ExecutionError> {
542 match self {
543 Ok(v) => Ok(v),
544 Err(err) => {
545 let (label, source_file) =
546 get_label_and_source_file(None, mast_forest, node_id, host);
547 Err(ExecutionError::MemoryError { label, source_file, err })
548 },
549 }
550 }
551}
552
553impl<T> MapExecErrWithOpIdx<T> for Result<T, MemoryError> {
554 #[inline(always)]
555 fn map_exec_err_with_op_idx(
556 self,
557 mast_forest: &MastForest,
558 node_id: MastNodeId,
559 host: &impl Host,
560 op_idx: usize,
561 ) -> Result<T, ExecutionError> {
562 match self {
563 Ok(v) => Ok(v),
564 Err(err) => {
565 let (label, source_file) =
566 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
567 Err(ExecutionError::MemoryError { label, source_file, err })
568 },
569 }
570 }
571}
572
573impl<T> MapExecErr<T> for Result<T, SystemEventError> {
575 #[inline(always)]
576 fn map_exec_err(
577 self,
578 mast_forest: &MastForest,
579 node_id: MastNodeId,
580 host: &impl Host,
581 ) -> Result<T, ExecutionError> {
582 match self {
583 Ok(v) => Ok(v),
584 Err(err) => {
585 let (label, source_file) =
586 get_label_and_source_file(None, mast_forest, node_id, host);
587 Err(match err {
588 SystemEventError::Advice(err) => {
589 ExecutionError::AdviceError { label, source_file, err }
590 },
591 SystemEventError::Operation(err) => {
592 ExecutionError::OperationError { label, source_file, err }
593 },
594 SystemEventError::Memory(err) => {
595 ExecutionError::MemoryError { label, source_file, err }
596 },
597 })
598 },
599 }
600 }
601}
602
603impl<T> MapExecErrWithOpIdx<T> for Result<T, SystemEventError> {
604 #[inline(always)]
605 fn map_exec_err_with_op_idx(
606 self,
607 mast_forest: &MastForest,
608 node_id: MastNodeId,
609 host: &impl Host,
610 op_idx: usize,
611 ) -> Result<T, ExecutionError> {
612 match self {
613 Ok(v) => Ok(v),
614 Err(err) => {
615 let (label, source_file) =
616 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
617 Err(match err {
618 SystemEventError::Advice(err) => {
619 ExecutionError::AdviceError { label, source_file, err }
620 },
621 SystemEventError::Operation(err) => {
622 ExecutionError::OperationError { label, source_file, err }
623 },
624 SystemEventError::Memory(err) => {
625 ExecutionError::MemoryError { label, source_file, err }
626 },
627 })
628 },
629 }
630 }
631}
632
633impl<T> MapExecErrWithOpIdx<T> for Result<T, IoError> {
635 #[inline(always)]
636 fn map_exec_err_with_op_idx(
637 self,
638 mast_forest: &MastForest,
639 node_id: MastNodeId,
640 host: &impl Host,
641 op_idx: usize,
642 ) -> Result<T, ExecutionError> {
643 match self {
644 Ok(v) => Ok(v),
645 Err(err) => {
646 let (label, source_file) =
647 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
648 Err(match err {
649 IoError::Advice(err) => ExecutionError::AdviceError { label, source_file, err },
650 IoError::Memory(err) => ExecutionError::MemoryError { label, source_file, err },
651 IoError::Execution(boxed_err) => *boxed_err,
653 })
654 },
655 }
656 }
657}
658
659impl<T> MapExecErrWithOpIdx<T> for Result<T, CryptoError> {
661 #[inline(always)]
662 fn map_exec_err_with_op_idx(
663 self,
664 mast_forest: &MastForest,
665 node_id: MastNodeId,
666 host: &impl Host,
667 op_idx: usize,
668 ) -> Result<T, ExecutionError> {
669 match self {
670 Ok(v) => Ok(v),
671 Err(err) => {
672 let (label, source_file) =
673 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
674 Err(match err {
675 CryptoError::Advice(err) => {
676 ExecutionError::AdviceError { label, source_file, err }
677 },
678 CryptoError::Operation(err) => {
679 ExecutionError::OperationError { label, source_file, err }
680 },
681 })
682 },
683 }
684 }
685}
686
687impl<T> MapExecErrWithOpIdx<T> for Result<T, AceEvalError> {
689 #[inline(always)]
690 fn map_exec_err_with_op_idx(
691 self,
692 mast_forest: &MastForest,
693 node_id: MastNodeId,
694 host: &impl Host,
695 op_idx: usize,
696 ) -> Result<T, ExecutionError> {
697 match self {
698 Ok(v) => Ok(v),
699 Err(err) => {
700 let (label, source_file) =
701 get_label_and_source_file(Some(op_idx), mast_forest, node_id, host);
702 Err(match err {
703 AceEvalError::Ace(error) => {
704 ExecutionError::AceChipError { label, source_file, error }
705 },
706 AceEvalError::Memory(err) => {
707 ExecutionError::MemoryError { label, source_file, err }
708 },
709 })
710 },
711 }
712 }
713}
714
715#[cfg(test)]
719mod error_assertions {
720 use super::*;
721
722 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
724
725 fn _assert_execution_error_bounds(err: ExecutionError) {
726 _assert_error_is_send_sync_static(err);
727 }
728}