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