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