miden_tx/errors/
mod.rs

1use alloc::{boxed::Box, string::String};
2use core::error::Error;
3
4use miden_objects::{
5    AccountError, Felt, ProvenTransactionError, TransactionInputError, TransactionOutputError,
6    account::AccountId, assembly::diagnostics::reporting::PrintDiagnostic, block::BlockNumber,
7    crypto::merkle::SmtProofError, note::NoteId,
8};
9use miden_verifier::VerificationError;
10use thiserror::Error;
11use vm_processor::ExecutionError;
12
13// TRANSACTION EXECUTOR ERROR
14// ================================================================================================
15
16#[derive(Debug, Error)]
17pub enum TransactionExecutorError {
18    #[error("failed to fetch transaction inputs from the data store")]
19    FetchTransactionInputsFailed(#[source] DataStoreError),
20    #[error("foreign account inputs for ID {0} are not anchored on reference block")]
21    ForeignAccountNotAnchoredInReference(AccountId),
22    #[error("failed to create transaction inputs")]
23    InvalidTransactionInputs(#[source] TransactionInputError),
24    #[error("input account ID {input_id} does not match output account ID {output_id}")]
25    InconsistentAccountId {
26        input_id: AccountId,
27        output_id: AccountId,
28    },
29    #[error("expected account nonce {expected:?}, found {actual:?}")]
30    InconsistentAccountNonceDelta {
31        expected: Option<Felt>,
32        actual: Option<Felt>,
33    },
34    #[error("account witness provided for account ID {0} is invalid")]
35    InvalidAccountWitness(AccountId, #[source] SmtProofError),
36    #[error(
37        "input note {0} was created in a block past the transaction reference block number ({1})"
38    )]
39    NoteBlockPastReferenceBlock(NoteId, BlockNumber),
40    #[error("failed to create transaction host")]
41    TransactionHostCreationFailed(#[source] TransactionHostError),
42    #[error("failed to construct transaction outputs")]
43    TransactionOutputConstructionFailed(#[source] TransactionOutputError),
44    // Print the diagnostic directly instead of returning the source error. In the source error
45    // case, the diagnostic is lost if the execution error is not explicitly unwrapped.
46    #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
47    TransactionProgramExecutionFailed(ExecutionError),
48}
49
50// TRANSACTION PROVER ERROR
51// ================================================================================================
52
53#[derive(Debug, Error)]
54pub enum TransactionProverError {
55    #[error("failed to apply account delta")]
56    AccountDeltaApplyFailed(#[source] AccountError),
57    #[error("transaction inputs are not valid")]
58    InvalidTransactionInputs(#[source] TransactionInputError),
59    #[error("failed to construct transaction outputs")]
60    TransactionOutputConstructionFailed(#[source] TransactionOutputError),
61    #[error("failed to build proven transaction")]
62    ProvenTransactionBuildFailed(#[source] ProvenTransactionError),
63    // Print the diagnostic directly instead of returning the source error. In the source error
64    // case, the diagnostic is lost if the execution error is not explicitly unwrapped.
65    #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
66    TransactionProgramExecutionFailed(ExecutionError),
67    #[error("failed to create transaction host")]
68    TransactionHostCreationFailed(#[source] TransactionHostError),
69    /// Custom error variant for errors not covered by the other variants.
70    #[error("{error_msg}")]
71    Other {
72        error_msg: Box<str>,
73        // thiserror will return this when calling Error::source on DataStoreError.
74        source: Option<Box<dyn Error + Send + Sync + 'static>>,
75    },
76}
77
78impl TransactionProverError {
79    /// Creates a custom error using the [`TransactionProverError::Other`] variant from an error
80    /// message.
81    pub fn other(message: impl Into<String>) -> Self {
82        let message: String = message.into();
83        Self::Other { error_msg: message.into(), source: None }
84    }
85
86    /// Creates a custom error using the [`TransactionProverError::Other`] variant from an error
87    /// message and a source error.
88    pub fn other_with_source(
89        message: impl Into<String>,
90        source: impl Error + Send + Sync + 'static,
91    ) -> Self {
92        let message: String = message.into();
93        Self::Other {
94            error_msg: message.into(),
95            source: Some(Box::new(source)),
96        }
97    }
98}
99
100// TRANSACTION VERIFIER ERROR
101// ================================================================================================
102
103#[derive(Debug, Error)]
104pub enum TransactionVerifierError {
105    #[error("failed to verify transaction")]
106    TransactionVerificationFailed(#[source] VerificationError),
107    #[error("transaction proof security level is {actual} but must be at least {expected_minimum}")]
108    InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 },
109}
110
111// TRANSACTION HOST ERROR
112// ================================================================================================
113
114#[derive(Debug, Error)]
115pub enum TransactionHostError {
116    #[error("{0}")]
117    AccountProcedureIndexMapError(String),
118    #[error("failed to create account procedure info")]
119    AccountProcedureInfoCreationFailed(#[source] AccountError),
120}
121
122// DATA STORE ERROR
123// ================================================================================================
124
125#[derive(Debug, Error)]
126pub enum DataStoreError {
127    #[error("account with id {0} not found in data store")]
128    AccountNotFound(AccountId),
129    #[error("block with number {0} not found in data store")]
130    BlockNotFound(BlockNumber),
131    /// Custom error variant for implementors of the [`DataStore`](crate::executor::DataStore)
132    /// trait.
133    #[error("{error_msg}")]
134    Other {
135        error_msg: Box<str>,
136        // thiserror will return this when calling Error::source on DataStoreError.
137        source: Option<Box<dyn Error + Send + Sync + 'static>>,
138    },
139}
140
141impl DataStoreError {
142    /// Creates a custom error using the [`DataStoreError::Other`] variant from an error message.
143    pub fn other(message: impl Into<String>) -> Self {
144        let message: String = message.into();
145        Self::Other { error_msg: message.into(), source: None }
146    }
147
148    /// Creates a custom error using the [`DataStoreError::Other`] variant from an error message and
149    /// a source error.
150    pub fn other_with_source(
151        message: impl Into<String>,
152        source: impl Error + Send + Sync + 'static,
153    ) -> Self {
154        let message: String = message.into();
155        Self::Other {
156            error_msg: message.into(),
157            source: Some(Box::new(source)),
158        }
159    }
160}
161
162// AUTHENTICATION ERROR
163// ================================================================================================
164
165#[derive(Debug, Error)]
166pub enum AuthenticationError {
167    #[error("signature rejected: {0}")]
168    RejectedSignature(String),
169    #[error("unknown public key: {0}")]
170    UnknownPublicKey(String),
171    /// Custom error variant for implementors of the
172    /// [`TransactionAuthenticatior`](crate::auth::TransactionAuthenticator) trait.
173    #[error("{error_msg}")]
174    Other {
175        error_msg: Box<str>,
176        // thiserror will return this when calling Error::source on DataStoreError.
177        source: Option<Box<dyn Error + Send + Sync + 'static>>,
178    },
179}
180
181impl AuthenticationError {
182    /// Creates a custom error using the [`AuthenticationError::Other`] variant from an error
183    /// message.
184    pub fn other(message: impl Into<String>) -> Self {
185        let message: String = message.into();
186        Self::Other { error_msg: message.into(), source: None }
187    }
188
189    /// Creates a custom error using the [`AuthenticationError::Other`] variant from an error
190    /// message and a source error.
191    pub fn other_with_source(
192        message: impl Into<String>,
193        source: impl Error + Send + Sync + 'static,
194    ) -> Self {
195        let message: String = message.into();
196        Self::Other {
197            error_msg: message.into(),
198            source: Some(Box::new(source)),
199        }
200    }
201}
202
203#[cfg(test)]
204mod error_assertions {
205    use super::*;
206
207    /// Asserts at compile time that the passed error has Send + Sync + 'static bounds.
208    fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
209
210    fn _assert_data_store_error_bounds(err: DataStoreError) {
211        _assert_error_is_send_sync_static(err);
212    }
213
214    fn _assert_authentication_error_bounds(err: AuthenticationError) {
215        _assert_error_is_send_sync_static(err);
216    }
217}