Skip to main content

snarkvm_synthesizer_error/
process.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::{EvalError, ExecError, FinalizeError};
17use anyhow::Error;
18use snarkvm_circuit_environment::ConstraintUnsatisfied;
19use snarkvm_console_network::Network;
20use snarkvm_console_program::{Identifier, ProgramID};
21use thiserror::Error;
22
23// NOTE: Many errors in this module temporarily contain `Anyhow` variants.
24// Remove these variants as we migrate errors to thiserror.
25
26/// Errors that may occur during process authorization.
27#[derive(Debug, Error)]
28pub enum ProcessAuthError {
29    /// Stack authorization failed.
30    #[error("Stack authorization failed: {0}")]
31    StackAuth(#[from] StackAuthError),
32    /// A temporary variant for type-erased anyhow errors.
33    #[error(transparent)]
34    Anyhow(#[from] anyhow::Error),
35}
36
37/// Errors that may occur during process evaluation.
38#[derive(Debug, Error)]
39pub enum ProcessEvalError {
40    /// Stack evaluation failed.
41    #[error("Stack evaluation failed: {0}")]
42    StackEval(#[from] StackEvalError),
43    /// A temporary variant for type-erased anyhow errors.
44    #[error(transparent)]
45    Anyhow(#[from] anyhow::Error),
46}
47
48/// Errors that may occur during process execution.
49#[derive(Debug, Error)]
50pub enum ProcessExecError {
51    /// Stack execution failed.
52    #[error("Stack execution failed: {0}")]
53    StackExec(#[from] StackExecError),
54    /// A temporary variant for type-erased anyhow errors.
55    #[error(transparent)]
56    Anyhow(#[from] anyhow::Error),
57}
58
59/// Errors that may occur during process deployment.
60#[derive(Debug, Error)]
61pub enum ProcessDeployError {
62    /// Stack execution failed during synthesis.
63    #[error("Stack synthesis failed: {0}")]
64    StackExec(#[from] StackExecError),
65    /// A temporary variant for type-erased anyhow errors.
66    #[error(transparent)]
67    Anyhow(#[from] anyhow::Error),
68}
69
70/// Errors that may occur during call evaluation.
71#[derive(Debug, Error)]
72pub enum CallEvalError {
73    /// An error occurred during substack evaluation.
74    #[error("Substack evaluation failed: {0}")]
75    StackEval(#[from] StackEvalError),
76    /// A temporary variant for type-erased anyhow errors.
77    #[error(transparent)]
78    Anyhow(#[from] anyhow::Error),
79}
80
81/// Errors that may occur during call execution.
82#[derive(Debug, Error)]
83pub enum CallExecError {
84    /// An error occurred during substack execution.
85    #[error("Substack execution failed: {0}")]
86    StackExec(#[from] StackExecError),
87    /// An error occurred during substack evaluation.
88    #[error("Substack evaluation failed: {0}")]
89    StackEval(#[from] StackEvalError),
90    /// A circuit constraint was not satisfied.
91    #[error(transparent)]
92    Constraint(#[from] ConstraintUnsatisfied),
93    /// A temporary variant for type-erased anyhow errors.
94    #[error(transparent)]
95    Anyhow(#[from] anyhow::Error),
96}
97
98/// Errors that may occur during stack authorization.
99#[derive(Debug, Error)]
100pub enum StackAuthError {
101    /// Stack execution failed.
102    #[error("Stack execution failed: {0}")]
103    Exec(#[from] StackExecError),
104    /// Stack evaluation failed.
105    #[error("Stack evaluation failed: {0}")]
106    Eval(#[from] StackEvalError),
107    /// A temporary variant for type-erased anyhow errors.
108    #[error(transparent)]
109    Anyhow(#[from] anyhow::Error),
110}
111
112/// Errors that may occur during stack execution.
113#[derive(Debug, Error)]
114pub enum StackExecError {
115    /// Instruction at the given index failed.
116    #[error(transparent)]
117    Instruction(#[from] IndexedInstructionError<InstructionError>),
118    /// A circuit constraint was not satisfied.
119    #[error(transparent)]
120    Constraint(#[from] ConstraintUnsatisfied),
121    /// A temporary variant for type-erased anyhow errors.
122    #[error(transparent)]
123    Anyhow(#[from] anyhow::Error),
124}
125
126/// Errors that may occur during stack evaluation.
127#[derive(Debug, Error)]
128pub enum StackEvalError {
129    /// Instruction at the given index failed.
130    #[error(transparent)]
131    Instruction(#[from] IndexedInstructionError<InstructionEvalError>),
132    /// A temporary variant for type-erased anyhow errors.
133    #[error(transparent)]
134    Anyhow(#[from] anyhow::Error),
135}
136
137/// An instruction error occurred at a particular index.
138#[derive(Debug, Error)]
139#[error("Instruction ({instruction}) at index {index} failed: {error}")]
140pub struct IndexedInstructionError<E> {
141    /// The index of the failing instruction.
142    pub index: usize,
143    /// The failing instruction formatted.
144    pub instruction: String,
145    /// The instruction error.
146    pub error: E,
147}
148
149/// An error occurred during the execution/evaluation/synthesis of an
150/// instruction.
151#[derive(Debug, Error)]
152pub enum InstructionError {
153    /// Failed to evaluate an instruction.
154    #[error("Failed to evaluate: {0}")]
155    Eval(#[from] InstructionEvalError),
156    /// Failed to execute an instruction.
157    #[error("Failed to execute: {0}")]
158    Exec(#[from] InstructionExecError),
159}
160
161/// An error occurred during the evaluation of an instruction.
162#[derive(Debug, Error)]
163pub enum InstructionEvalError {
164    /// An instruction evaluation failed.
165    #[error(transparent)]
166    Eval(#[from] EvalError),
167    /// An error occurred during a `Call` instruction.
168    #[error("Call failed: {0}")]
169    Call(#[from] Box<CallEvalError>),
170    /// A temporary variant for type-erased anyhow errors.
171    #[error(transparent)]
172    Anyhow(#[from] anyhow::Error),
173}
174
175/// An error occurred during the execution of an instruction.
176#[derive(Debug, Error)]
177pub enum InstructionExecError {
178    /// An error occurred during a `Call` instruction.
179    #[error("Call failed: {0}")]
180    Call(#[from] Box<CallExecError>),
181    /// An instruction execution error.
182    #[error(transparent)]
183    Exec(#[from] ExecError),
184    /// A temporary variant for type-erased anyhow errors.
185    #[error(transparent)]
186    Anyhow(#[from] anyhow::Error),
187}
188
189impl<E> IndexedInstructionError<E> {
190    /// Short-hand constructor for the `IndexedInstructionError` type.
191    pub fn new(index: usize, instruction: String, error: E) -> Self {
192        Self { index, instruction, error }
193    }
194}
195
196/// `C` is the command type stored in the error. Callers that have a concrete command type (e.g.
197/// `Command<N>` from synthesizer-program) may use it directly; callers without access to that
198/// type may use `String` (via `Display`). Using a generic avoids a circular crate dependency
199/// between `snarkvm-synthesizer-error` and `snarkvm-synthesizer-program`.
200pub struct IndexedFinalizeError<N: Network, C: ToString> {
201    /// The program ID of the failing command, if available.
202    pub program_id: Option<(ProgramID<N>, u16)>,
203    /// The resource (function or constructor name) of the failing command, if available.
204    pub resource: Option<Identifier<N>>,
205    /// The index and the failing command, if available. Boxed to keep the struct small.
206    pub command: Option<Box<(usize, C)>>,
207    /// The finalize error.
208    pub error: FinalizeError,
209}
210
211impl<N: Network, C: ToString> std::fmt::Debug for IndexedFinalizeError<N, C> {
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        std::fmt::Display::fmt(self, f)
214    }
215}
216
217impl<N: Network, C: ToString> std::fmt::Display for IndexedFinalizeError<N, C> {
218    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219        // Build a display string from the optional program ID and resource.
220        let locator = match (&self.program_id, &self.resource) {
221            (Some((program_id, _)), Some(resource)) => format!("{program_id}/{resource}"),
222            (Some((program_id, _)), None) => format!("{program_id}"),
223            (None, Some(resource)) => format!("{resource}"),
224            (None, None) => "None".to_string(),
225        };
226        match &self.command {
227            Some(cmd) => {
228                let (index, command) = cmd.as_ref();
229                write!(
230                    f,
231                    "Failed to finalize '{locator}' command ({}) at index {index}: {}",
232                    command.to_string(),
233                    self.error
234                )
235            }
236            None => write!(f, "Failed to finalize '{locator}': {}", self.error),
237        }
238    }
239}
240
241impl<N: Network, C: ToString> std::error::Error for IndexedFinalizeError<N, C> {}
242
243impl<N: Network, C: ToString> From<Error> for IndexedFinalizeError<N, C> {
244    /// Converts an anyhow error into an `IndexedFinalizeError` with no location or command context.
245    fn from(error: Error) -> Self {
246        Self::new(None, None, None, FinalizeError::Anyhow(error))
247    }
248}
249
250impl<N: Network, C: ToString> IndexedFinalizeError<N, C> {
251    /// Constructs an `IndexedFinalizeError` from its components.
252    pub fn new(
253        program_id: Option<(ProgramID<N>, u16)>,
254        resource: Option<Identifier<N>>,
255        command: Option<(usize, C)>,
256        error: FinalizeError,
257    ) -> Self {
258        Self { program_id, resource, command: command.map(Box::new), error }
259    }
260}
261
262/// A helper macro to bail with an `IndexedFinalizeError`.
263///
264/// Two forms:
265///   - `indexed_finalize_bail!(program_id, resource, index, command, error message)` — with command context.
266///   - `indexed_finalize_bail!(program_id, resource, error message)` — without command context.
267///
268/// `program_id` must be `Option<(ProgramID<N>, u16)>` and `resource` must be `Option<Identifier<N>>`.
269#[macro_export]
270macro_rules! indexed_finalize_bail {
271    // With program_id + resource + index + command + message.
272    ($program_id:expr, $resource:expr, $index:expr, $command:expr, $($arg:tt)+) => {{
273        return Err(IndexedFinalizeError::new(
274            $program_id,
275            $resource,
276            Some(($index, $command)),
277            FinalizeError::Anyhow(anyhow!($($arg)+)),
278        ));
279    }};
280    // With program_id + resource + message only (no command context).
281    ($program_id:expr, $resource:expr, $($arg:tt)+) => {{
282        return Err(IndexedFinalizeError::new(
283            $program_id,
284            $resource,
285            None,
286            FinalizeError::Anyhow(anyhow!($($arg)+)),
287        ));
288    }};
289}
290
291pub trait IntoIndexedFinalize<N: Network, C: ToString, T> {
292    fn into_indexed(
293        self,
294        program_id: Option<(ProgramID<N>, u16)>,
295        resource: Option<Identifier<N>>,
296        command: Option<(usize, C)>,
297    ) -> anyhow::Result<T, IndexedFinalizeError<N, C>>;
298}
299
300impl<N: Network, C: ToString, T> IntoIndexedFinalize<N, C, T> for anyhow::Result<T, Error> {
301    fn into_indexed(
302        self,
303        program_id: Option<(ProgramID<N>, u16)>,
304        resource: Option<Identifier<N>>,
305        command: Option<(usize, C)>,
306    ) -> anyhow::Result<T, IndexedFinalizeError<N, C>> {
307        self.map_err(|e| IndexedFinalizeError::new(program_id, resource, command, FinalizeError::Anyhow(e)))
308    }
309}