use crate::{EvalError, ExecError, FinalizeError};
use anyhow::Error;
use snarkvm_circuit_environment::ConstraintUnsatisfied;
use snarkvm_console_network::Network;
use snarkvm_console_program::{Identifier, ProgramID};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ProcessAuthError {
#[error("Stack authorization failed: {0}")]
StackAuth(#[from] StackAuthError),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum ProcessEvalError {
#[error("Stack evaluation failed: {0}")]
StackEval(#[from] StackEvalError),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum ProcessExecError {
#[error("Stack execution failed: {0}")]
StackExec(#[from] StackExecError),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum ProcessDeployError {
#[error("Stack synthesis failed: {0}")]
StackExec(#[from] StackExecError),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum CallEvalError {
#[error("Substack evaluation failed: {0}")]
StackEval(#[from] StackEvalError),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum CallExecError {
#[error("Substack execution failed: {0}")]
StackExec(#[from] StackExecError),
#[error("Substack evaluation failed: {0}")]
StackEval(#[from] StackEvalError),
#[error(transparent)]
Constraint(#[from] ConstraintUnsatisfied),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum StackAuthError {
#[error("Stack execution failed: {0}")]
Exec(#[from] StackExecError),
#[error("Stack evaluation failed: {0}")]
Eval(#[from] StackEvalError),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum StackExecError {
#[error(transparent)]
Instruction(#[from] IndexedInstructionError<InstructionError>),
#[error(transparent)]
Constraint(#[from] ConstraintUnsatisfied),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum StackEvalError {
#[error(transparent)]
Instruction(#[from] IndexedInstructionError<InstructionEvalError>),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
#[error("Instruction ({instruction}) at index {index} failed: {error}")]
pub struct IndexedInstructionError<E> {
pub index: usize,
pub instruction: String,
pub error: E,
}
#[derive(Debug, Error)]
pub enum InstructionError {
#[error("Failed to evaluate: {0}")]
Eval(#[from] InstructionEvalError),
#[error("Failed to execute: {0}")]
Exec(#[from] InstructionExecError),
}
#[derive(Debug, Error)]
pub enum InstructionEvalError {
#[error(transparent)]
Eval(#[from] EvalError),
#[error("Call failed: {0}")]
Call(#[from] Box<CallEvalError>),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum InstructionExecError {
#[error("Call failed: {0}")]
Call(#[from] Box<CallExecError>),
#[error(transparent)]
Exec(#[from] ExecError),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
impl<E> IndexedInstructionError<E> {
pub fn new(index: usize, instruction: String, error: E) -> Self {
Self { index, instruction, error }
}
}
pub struct IndexedFinalizeError<N: Network, C: ToString> {
pub program_id: Option<(ProgramID<N>, u16)>,
pub resource: Option<Identifier<N>>,
pub command: Option<Box<(usize, C)>>,
pub error: FinalizeError,
}
impl<N: Network, C: ToString> std::fmt::Debug for IndexedFinalizeError<N, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl<N: Network, C: ToString> std::fmt::Display for IndexedFinalizeError<N, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let locator = match (&self.program_id, &self.resource) {
(Some((program_id, _)), Some(resource)) => format!("{program_id}/{resource}"),
(Some((program_id, _)), None) => format!("{program_id}"),
(None, Some(resource)) => format!("{resource}"),
(None, None) => "None".to_string(),
};
match &self.command {
Some(cmd) => {
let (index, command) = cmd.as_ref();
write!(
f,
"Failed to finalize '{locator}' command ({}) at index {index}: {}",
command.to_string(),
self.error
)
}
None => write!(f, "Failed to finalize '{locator}': {}", self.error),
}
}
}
impl<N: Network, C: ToString> std::error::Error for IndexedFinalizeError<N, C> {}
impl<N: Network, C: ToString> From<Error> for IndexedFinalizeError<N, C> {
fn from(error: Error) -> Self {
Self::new(None, None, None, FinalizeError::Anyhow(error))
}
}
impl<N: Network, C: ToString> IndexedFinalizeError<N, C> {
pub fn new(
program_id: Option<(ProgramID<N>, u16)>,
resource: Option<Identifier<N>>,
command: Option<(usize, C)>,
error: FinalizeError,
) -> Self {
Self { program_id, resource, command: command.map(Box::new), error }
}
}
#[macro_export]
macro_rules! indexed_finalize_bail {
($program_id:expr, $resource:expr, $index:expr, $command:expr, $($arg:tt)+) => {{
return Err(IndexedFinalizeError::new(
$program_id,
$resource,
Some(($index, $command)),
FinalizeError::Anyhow(anyhow!($($arg)+)),
));
}};
($program_id:expr, $resource:expr, $($arg:tt)+) => {{
return Err(IndexedFinalizeError::new(
$program_id,
$resource,
None,
FinalizeError::Anyhow(anyhow!($($arg)+)),
));
}};
}
pub trait IntoIndexedFinalize<N: Network, C: ToString, T> {
fn into_indexed(
self,
program_id: Option<(ProgramID<N>, u16)>,
resource: Option<Identifier<N>>,
command: Option<(usize, C)>,
) -> anyhow::Result<T, IndexedFinalizeError<N, C>>;
}
impl<N: Network, C: ToString, T> IntoIndexedFinalize<N, C, T> for anyhow::Result<T, Error> {
fn into_indexed(
self,
program_id: Option<(ProgramID<N>, u16)>,
resource: Option<Identifier<N>>,
command: Option<(usize, C)>,
) -> anyhow::Result<T, IndexedFinalizeError<N, C>> {
self.map_err(|e| IndexedFinalizeError::new(program_id, resource, command, FinalizeError::Anyhow(e)))
}
}