miden_tx/errors/
mod.rs

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