miden_processor/
errors.rs

1// Allow unused assignments - required by miette::Diagnostic derive macro
2#![allow(unused_assignments)]
3
4use alloc::{string::String, sync::Arc, vec::Vec};
5
6use miden_air::RowIndex;
7use miden_core::{
8    EventId, EventName, Felt, QuadFelt, Word,
9    mast::{DecoratorId, MastForest, MastNodeErrorContext, MastNodeId},
10    stack::MIN_STACK_DEPTH,
11    utils::to_hex,
12};
13use miden_debug_types::{SourceFile, SourceSpan};
14use miden_utils_diagnostics::{Diagnostic, miette};
15use winter_prover::ProverError;
16
17use crate::{
18    AssertError, BaseHost, DebugError, EventError, MemoryError, TraceError,
19    host::advice::AdviceError,
20};
21
22// EXECUTION ERROR
23// ================================================================================================
24
25#[derive(Debug, thiserror::Error, Diagnostic)]
26pub enum ExecutionError {
27    #[error("advice provider error at clock cycle {clk}")]
28    #[diagnostic()]
29    AdviceError {
30        #[label]
31        label: SourceSpan,
32        #[source_code]
33        source_file: Option<Arc<SourceFile>>,
34        clk: RowIndex,
35        #[source]
36        #[diagnostic_source]
37        err: AdviceError,
38    },
39    #[error("external node with mast root {0} resolved to an external node")]
40    CircularExternalNode(Word),
41    #[error("exceeded the allowed number of max cycles {0}")]
42    CycleLimitExceeded(u32),
43    #[error("decorator id {decorator_id} does not exist in MAST forest")]
44    DecoratorNotFoundInForest { decorator_id: DecoratorId },
45    #[error("debug handler error at clock cycle {clk}: {err}")]
46    DebugHandlerError {
47        clk: RowIndex,
48        #[source]
49        err: DebugError,
50    },
51    #[error("trace handler error at clock cycle {clk} for trace ID {trace_id}: {err}")]
52    TraceHandlerError {
53        clk: RowIndex,
54        trace_id: u32,
55        #[source]
56        err: TraceError,
57    },
58    #[error("division by zero at clock cycle {clk}")]
59    #[diagnostic()]
60    DivideByZero {
61        #[label]
62        label: SourceSpan,
63        #[source_code]
64        source_file: Option<Arc<SourceFile>>,
65        clk: RowIndex,
66    },
67    #[error("failed to execute the dynamic code block provided by the stack with root {hex}; the block could not be found",
68      hex = .digest.to_hex()
69    )]
70    #[diagnostic()]
71    DynamicNodeNotFound {
72        #[label]
73        label: SourceSpan,
74        #[source_code]
75        source_file: Option<Arc<SourceFile>>,
76        digest: Word,
77    },
78    #[error("error during processing of event {}", match event_name {
79        Some(name) => format!("'{}' (ID: {})", name, event_id),
80        None => format!("with ID: {}", event_id),
81    })]
82    #[diagnostic()]
83    EventError {
84        #[label]
85        label: SourceSpan,
86        #[source_code]
87        source_file: Option<Arc<SourceFile>>,
88        event_id: EventId,
89        event_name: Option<EventName>,
90        #[source]
91        error: EventError,
92    },
93    #[error("attempted to add event handler for '{event}' (already registered)")]
94    DuplicateEventHandler { event: EventName },
95    #[error("attempted to add event handler for '{event}' (reserved system event)")]
96    ReservedEventNamespace { event: EventName },
97    #[error("assertion failed at clock cycle {clk} with error {}{}",
98      match err_msg {
99        Some(msg) => format!("message: {msg}"),
100        None => format!("code: {err_code}"),
101      },
102      match err {
103        Some(err) => format!(" (host error: {err})"),
104        None => String::new(),
105      }
106    )]
107    #[diagnostic()]
108    FailedAssertion {
109        #[label]
110        label: SourceSpan,
111        #[source_code]
112        source_file: Option<Arc<SourceFile>>,
113        clk: RowIndex,
114        err_code: Felt,
115        err_msg: Option<Arc<str>>,
116        #[source]
117        err: Option<AssertError>,
118    },
119    #[error("failed to execute the program for internal reason: {0}")]
120    FailedToExecuteProgram(&'static str),
121    #[error("FRI domain segment value cannot exceed 3, but was {0}")]
122    InvalidFriDomainSegment(u64),
123    #[error("degree-respecting projection is inconsistent: expected {0} but was {1}")]
124    InvalidFriLayerFolding(QuadFelt, QuadFelt),
125    #[error(
126        "when returning from a call or dyncall, stack depth must be {MIN_STACK_DEPTH}, but was {depth}"
127    )]
128    #[diagnostic()]
129    InvalidStackDepthOnReturn {
130        #[label("when returning from this call site")]
131        label: SourceSpan,
132        #[source_code]
133        source_file: Option<Arc<SourceFile>>,
134        depth: usize,
135    },
136    #[error("attempted to calculate integer logarithm with zero argument at clock cycle {clk}")]
137    #[diagnostic()]
138    LogArgumentZero {
139        #[label]
140        label: SourceSpan,
141        #[source_code]
142        source_file: Option<Arc<SourceFile>>,
143        clk: RowIndex,
144    },
145    #[error("malformed signature key: {key_type}")]
146    #[diagnostic(help("the secret key associated with the provided public key is malformed"))]
147    MalformedSignatureKey {
148        #[label]
149        label: SourceSpan,
150        #[source_code]
151        source_file: Option<Arc<SourceFile>>,
152        key_type: &'static str,
153    },
154    #[error(
155        "MAST forest in host indexed by procedure root {root_digest} doesn't contain that root"
156    )]
157    MalformedMastForestInHost {
158        #[label]
159        label: SourceSpan,
160        #[source_code]
161        source_file: Option<Arc<SourceFile>>,
162        root_digest: Word,
163    },
164    #[error("node id {node_id} does not exist in MAST forest")]
165    MastNodeNotFoundInForest { node_id: MastNodeId },
166    #[error(transparent)]
167    #[diagnostic(transparent)]
168    MemoryError(MemoryError),
169    #[error("no MAST forest contains the procedure with root digest {root_digest}")]
170    NoMastForestWithProcedure {
171        #[label]
172        label: SourceSpan,
173        #[source_code]
174        source_file: Option<Arc<SourceFile>>,
175        root_digest: Word,
176    },
177    #[error("merkle path verification failed for value {value} at index {index} in the Merkle tree with root {root} (error {err})",
178      value = to_hex(value.as_bytes()),
179      root = to_hex(root.as_bytes()),
180      err = match err_msg {
181        Some(msg) => format!("message: {msg}"),
182        None => format!("code: {err_code}"),
183      }
184    )]
185    MerklePathVerificationFailed {
186        #[label]
187        label: SourceSpan,
188        #[source_code]
189        source_file: Option<Arc<SourceFile>>,
190        value: Word,
191        index: Felt,
192        root: Word,
193        err_code: Felt,
194        err_msg: Option<Arc<str>>,
195    },
196    #[error("if statement expected a binary value on top of the stack, but got {value}")]
197    #[diagnostic()]
198    NotBinaryValueIf {
199        #[label]
200        label: SourceSpan,
201        #[source_code]
202        source_file: Option<Arc<SourceFile>>,
203        value: Felt,
204    },
205    #[error("operation expected a binary value, but got {value}")]
206    #[diagnostic()]
207    NotBinaryValueOp {
208        #[label]
209        label: SourceSpan,
210        #[source_code]
211        source_file: Option<Arc<SourceFile>>,
212        value: Felt,
213    },
214    #[error("loop condition must be a binary value, but got {value}")]
215    #[diagnostic(help(
216        "this could happen either when first entering the loop, or any subsequent iteration"
217    ))]
218    NotBinaryValueLoop {
219        #[label]
220        label: SourceSpan,
221        #[source_code]
222        source_file: Option<Arc<SourceFile>>,
223        value: Felt,
224    },
225    #[error("operation expected u32 values, but got values: {values:?} (error code: {err_code})")]
226    NotU32Values {
227        #[label]
228        label: SourceSpan,
229        #[source_code]
230        source_file: Option<Arc<SourceFile>>,
231        values: Vec<Felt>,
232        err_code: Felt,
233    },
234    #[error(
235        "Operand stack input is {input} but it is expected to fit in a u32 at clock cycle {clk}"
236    )]
237    #[diagnostic()]
238    NotU32StackValue {
239        #[label]
240        label: SourceSpan,
241        #[source_code]
242        source_file: Option<Arc<SourceFile>>,
243        clk: RowIndex,
244        input: u64,
245    },
246    #[error("stack should have at most {MIN_STACK_DEPTH} elements at the end of program execution, but had {} elements", MIN_STACK_DEPTH + .0)]
247    OutputStackOverflow(usize),
248    #[error("a program has already been executed in this process")]
249    ProgramAlreadyExecuted,
250    #[error("proof generation failed")]
251    ProverError(#[source] ProverError),
252    #[error("smt node {node_hex} not found", node_hex = to_hex(node.as_bytes()))]
253    SmtNodeNotFound {
254        #[label]
255        label: SourceSpan,
256        #[source_code]
257        source_file: Option<Arc<SourceFile>>,
258        node: Word,
259    },
260    #[error("expected pre-image length of node {node_hex} to be a multiple of 8 but was {preimage_len}",
261      node_hex = to_hex(node.as_bytes()),
262    )]
263    SmtNodePreImageNotValid {
264        #[label]
265        label: SourceSpan,
266        #[source_code]
267        source_file: Option<Arc<SourceFile>>,
268        node: Word,
269        preimage_len: usize,
270    },
271    #[error("syscall failed: procedure with root {hex} was not found in the kernel",
272      hex = to_hex(proc_root.as_bytes())
273    )]
274    SyscallTargetNotInKernel {
275        #[label]
276        label: SourceSpan,
277        #[source_code]
278        source_file: Option<Arc<SourceFile>>,
279        proc_root: Word,
280    },
281    #[error("failed to execute arithmetic circuit evaluation operation: {error}")]
282    #[diagnostic()]
283    AceChipError {
284        #[label("this call failed")]
285        label: SourceSpan,
286        #[source_code]
287        source_file: Option<Arc<SourceFile>>,
288        error: AceError,
289    },
290    #[error("execution yielded unexpected precompiles")]
291    UnexpectedPrecompiles,
292    #[error(
293        "invalid crypto operation: Merkle path length {path_len} does not match expected depth {depth} at clock cycle {clk}"
294    )]
295    #[diagnostic()]
296    InvalidCryptoInput {
297        #[label]
298        label: SourceSpan,
299        #[source_code]
300        source_file: Option<Arc<SourceFile>>,
301        clk: RowIndex,
302        path_len: usize,
303        depth: Felt,
304    },
305}
306
307impl ExecutionError {
308    pub fn advice_error(
309        err: AdviceError,
310        clk: RowIndex,
311        err_ctx: &impl ErrorContext,
312    ) -> ExecutionError {
313        let (label, source_file) = err_ctx.label_and_source_file();
314        ExecutionError::AdviceError { label, source_file, err, clk }
315    }
316
317    pub fn divide_by_zero(clk: RowIndex, err_ctx: &impl ErrorContext) -> Self {
318        let (label, source_file) = err_ctx.label_and_source_file();
319        Self::DivideByZero { clk, label, source_file }
320    }
321
322    pub fn input_not_u32(clk: RowIndex, input: u64, err_ctx: &impl ErrorContext) -> Self {
323        let (label, source_file) = err_ctx.label_and_source_file();
324        Self::NotU32StackValue { clk, input, label, source_file }
325    }
326
327    pub fn dynamic_node_not_found(digest: Word, err_ctx: &impl ErrorContext) -> Self {
328        let (label, source_file) = err_ctx.label_and_source_file();
329
330        Self::DynamicNodeNotFound { label, source_file, digest }
331    }
332
333    pub fn event_error(
334        error: EventError,
335        event_id: EventId,
336        event_name: Option<EventName>,
337        err_ctx: &impl ErrorContext,
338    ) -> Self {
339        let (label, source_file) = err_ctx.label_and_source_file();
340
341        Self::EventError {
342            label,
343            source_file,
344            event_id,
345            event_name,
346            error,
347        }
348    }
349
350    pub fn failed_assertion(
351        clk: RowIndex,
352        err_code: Felt,
353        err_msg: Option<Arc<str>>,
354        err: Option<AssertError>,
355        err_ctx: &impl ErrorContext,
356    ) -> Self {
357        let (label, source_file) = err_ctx.label_and_source_file();
358
359        Self::FailedAssertion {
360            label,
361            source_file,
362            clk,
363            err_code,
364            err_msg,
365            err,
366        }
367    }
368
369    pub fn invalid_stack_depth_on_return(depth: usize, err_ctx: &impl ErrorContext) -> Self {
370        let (label, source_file) = err_ctx.label_and_source_file();
371        Self::InvalidStackDepthOnReturn { label, source_file, depth }
372    }
373
374    pub fn log_argument_zero(clk: RowIndex, err_ctx: &impl ErrorContext) -> Self {
375        let (label, source_file) = err_ctx.label_and_source_file();
376        Self::LogArgumentZero { label, source_file, clk }
377    }
378
379    pub fn malfored_mast_forest_in_host(root_digest: Word, err_ctx: &impl ErrorContext) -> Self {
380        let (label, source_file) = err_ctx.label_and_source_file();
381        Self::MalformedMastForestInHost { label, source_file, root_digest }
382    }
383
384    pub fn malformed_signature_key(key_type: &'static str, err_ctx: &impl ErrorContext) -> Self {
385        let (label, source_file) = err_ctx.label_and_source_file();
386        Self::MalformedSignatureKey { label, source_file, key_type }
387    }
388
389    pub fn merkle_path_verification_failed(
390        value: Word,
391        index: Felt,
392        root: Word,
393        err_code: Felt,
394        err_msg: Option<Arc<str>>,
395        err_ctx: &impl ErrorContext,
396    ) -> Self {
397        let (label, source_file) = err_ctx.label_and_source_file();
398
399        Self::MerklePathVerificationFailed {
400            label,
401            source_file,
402            value,
403            index,
404            root,
405            err_code,
406            err_msg,
407        }
408    }
409
410    pub fn no_mast_forest_with_procedure(root_digest: Word, err_ctx: &impl ErrorContext) -> Self {
411        let (label, source_file) = err_ctx.label_and_source_file();
412        Self::NoMastForestWithProcedure { label, source_file, root_digest }
413    }
414
415    pub fn not_binary_value_if(value: Felt, err_ctx: &impl ErrorContext) -> Self {
416        let (label, source_file) = err_ctx.label_and_source_file();
417        Self::NotBinaryValueIf { label, source_file, value }
418    }
419
420    pub fn not_binary_value_op(value: Felt, err_ctx: &impl ErrorContext) -> Self {
421        let (label, source_file) = err_ctx.label_and_source_file();
422        Self::NotBinaryValueOp { label, source_file, value }
423    }
424
425    pub fn not_binary_value_loop(value: Felt, err_ctx: &impl ErrorContext) -> Self {
426        let (label, source_file) = err_ctx.label_and_source_file();
427        Self::NotBinaryValueLoop { label, source_file, value }
428    }
429
430    pub fn not_u32_value(value: Felt, err_code: Felt, err_ctx: &impl ErrorContext) -> Self {
431        let (label, source_file) = err_ctx.label_and_source_file();
432        Self::NotU32Values {
433            label,
434            source_file,
435            values: vec![value],
436            err_code,
437        }
438    }
439
440    pub fn not_u32_values(values: Vec<Felt>, err_code: Felt, err_ctx: &impl ErrorContext) -> Self {
441        let (label, source_file) = err_ctx.label_and_source_file();
442        Self::NotU32Values { label, source_file, values, err_code }
443    }
444
445    pub fn smt_node_not_found(node: Word, err_ctx: &impl ErrorContext) -> Self {
446        let (label, source_file) = err_ctx.label_and_source_file();
447        Self::SmtNodeNotFound { label, source_file, node }
448    }
449
450    pub fn smt_node_preimage_not_valid(
451        node: Word,
452        preimage_len: usize,
453        err_ctx: &impl ErrorContext,
454    ) -> Self {
455        let (label, source_file) = err_ctx.label_and_source_file();
456        Self::SmtNodePreImageNotValid { label, source_file, node, preimage_len }
457    }
458
459    pub fn syscall_target_not_in_kernel(proc_root: Word, err_ctx: &impl ErrorContext) -> Self {
460        let (label, source_file) = err_ctx.label_and_source_file();
461        Self::SyscallTargetNotInKernel { label, source_file, proc_root }
462    }
463
464    pub fn failed_arithmetic_evaluation(err_ctx: &impl ErrorContext, error: AceError) -> Self {
465        let (label, source_file) = err_ctx.label_and_source_file();
466        Self::AceChipError { label, source_file, error }
467    }
468
469    pub fn invalid_crypto_input(
470        clk: RowIndex,
471        path_len: usize,
472        depth: Felt,
473        err_ctx: &impl ErrorContext,
474    ) -> Self {
475        let (label, source_file) = err_ctx.label_and_source_file();
476        Self::InvalidCryptoInput { label, source_file, clk, path_len, depth }
477    }
478}
479
480impl AsRef<dyn Diagnostic> for ExecutionError {
481    fn as_ref(&self) -> &(dyn Diagnostic + 'static) {
482        self
483    }
484}
485
486// ACE ERROR
487// ================================================================================================
488
489#[derive(Debug, thiserror::Error)]
490pub enum AceError {
491    #[error("num of variables should be word aligned and non-zero but was {0}")]
492    NumVarIsNotWordAlignedOrIsEmpty(u64),
493    #[error("num of evaluation gates should be word aligned and non-zero but was {0}")]
494    NumEvalIsNotWordAlignedOrIsEmpty(u64),
495    #[error("circuit does not evaluate to zero")]
496    CircuitNotEvaluateZero,
497    #[error("failed to read from memory")]
498    FailedMemoryRead,
499    #[error("failed to decode instruction")]
500    FailedDecodeInstruction,
501    #[error("failed to read from the wiring bus")]
502    FailedWireBusRead,
503    #[error("num of wires must be less than 2^30 but was {0}")]
504    TooManyWires(u64),
505}
506
507// ERROR CONTEXT
508// ===============================================================================================
509
510/// Constructs an error context for the given node in the MAST forest.
511///
512/// When the `no_err_ctx` feature is disabled, this macro returns a proper error context; otherwise,
513/// it returns `()`. That is, this macro is designed to be zero-cost when the `no_err_ctx` feature
514/// is enabled.
515///
516/// Usage:
517/// - `err_ctx!(mast_forest, node, source_manager)` - creates basic error context
518/// - `err_ctx!(mast_forest, node, source_manager, op_idx)` - creates error context with operation
519///   index
520#[cfg(not(feature = "no_err_ctx"))]
521#[macro_export]
522macro_rules! err_ctx {
523    ($mast_forest:expr, $node:expr, $host:expr) => {
524        $crate::errors::ErrorContextImpl::new($mast_forest, $node, $host)
525    };
526    ($mast_forest:expr, $node:expr, $host:expr, $op_idx:expr) => {
527        $crate::errors::ErrorContextImpl::new_with_op_idx($mast_forest, $node, $host, $op_idx)
528    };
529}
530
531/// Constructs an error context for the given node in the MAST forest.
532///
533/// When the `no_err_ctx` feature is disabled, this macro returns a proper error context; otherwise,
534/// it returns `()`. That is, this macro is designed to be zero-cost when the `no_err_ctx` feature
535/// is enabled.
536///
537/// Usage:
538/// - `err_ctx!(mast_forest, node, source_manager)` - creates basic error context
539/// - `err_ctx!(mast_forest, node, source_manager, op_idx)` - creates error context with operation
540///   index
541#[cfg(feature = "no_err_ctx")]
542#[macro_export]
543macro_rules! err_ctx {
544    ($mast_forest:expr, $node:expr, $host:expr) => {{ () }};
545    ($mast_forest:expr, $node:expr, $host:expr, $op_idx:expr) => {{ () }};
546}
547
548/// Trait defining the interface for error context providers.
549///
550/// This trait contains the same methods as `ErrorContext` to provide a common
551/// interface for error context functionality.
552pub trait ErrorContext {
553    /// Returns the label and source file associated with the error context, if any.
554    ///
555    /// Note that `SourceSpan::UNKNOWN` will be returned to indicate an empty span.
556    fn label_and_source_file(&self) -> (SourceSpan, Option<Arc<SourceFile>>);
557}
558
559/// Context information to be used when reporting errors.
560pub struct ErrorContextImpl {
561    label: SourceSpan,
562    source_file: Option<Arc<SourceFile>>,
563}
564
565impl ErrorContextImpl {
566    pub fn new(
567        mast_forest: &MastForest,
568        node: &impl MastNodeErrorContext,
569        host: &impl BaseHost,
570    ) -> Self {
571        let (label, source_file) =
572            Self::precalc_label_and_source_file(None, mast_forest, node, host);
573        Self { label, source_file }
574    }
575
576    pub fn new_with_op_idx(
577        mast_forest: &MastForest,
578        node: &impl MastNodeErrorContext,
579        host: &impl BaseHost,
580        op_idx: usize,
581    ) -> Self {
582        let op_idx = op_idx.into();
583        let (label, source_file) =
584            Self::precalc_label_and_source_file(op_idx, mast_forest, node, host);
585        Self { label, source_file }
586    }
587
588    fn precalc_label_and_source_file(
589        op_idx: Option<usize>,
590        mast_forest: &MastForest,
591        node: &impl MastNodeErrorContext,
592        host: &impl BaseHost,
593    ) -> (SourceSpan, Option<Arc<SourceFile>>) {
594        node.get_assembly_op(mast_forest, op_idx)
595            .and_then(|assembly_op| assembly_op.location())
596            .map_or_else(
597                || (SourceSpan::UNKNOWN, None),
598                |location| host.get_label_and_source_file(location),
599            )
600    }
601}
602
603impl ErrorContext for ErrorContextImpl {
604    fn label_and_source_file(&self) -> (SourceSpan, Option<Arc<SourceFile>>) {
605        (self.label, self.source_file.clone())
606    }
607}
608
609impl ErrorContext for () {
610    fn label_and_source_file(&self) -> (SourceSpan, Option<Arc<SourceFile>>) {
611        (SourceSpan::UNKNOWN, None)
612    }
613}
614
615// TESTS
616// ================================================================================================
617
618#[cfg(test)]
619mod error_assertions {
620    use super::*;
621
622    /// Asserts at compile time that the passed error has Send + Sync + 'static bounds.
623    fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
624
625    fn _assert_execution_error_bounds(err: ExecutionError) {
626        _assert_error_is_send_sync_static(err);
627    }
628}