miden_tx/errors/
mod.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use core::error::Error;
4
5use miden_lib::transaction::TransactionAdviceMapMismatch;
6use miden_objects::account::AccountId;
7use miden_objects::assembly::diagnostics::reporting::PrintDiagnostic;
8use miden_objects::block::BlockNumber;
9use miden_objects::crypto::merkle::SmtProofError;
10use miden_objects::note::NoteId;
11use miden_objects::transaction::TransactionSummary;
12use miden_objects::{
13    AccountDeltaError,
14    AccountError,
15    Felt,
16    ProvenTransactionError,
17    TransactionInputError,
18    TransactionOutputError,
19    Word,
20};
21use miden_processor::ExecutionError;
22use miden_verifier::VerificationError;
23use thiserror::Error;
24
25// NOTE EXECUTION ERROR
26// ================================================================================================
27
28#[derive(Debug, Error)]
29pub enum NoteCheckerError {
30    #[error("transaction preparation failed: {0}")]
31    TransactionPreparationFailed(#[source] TransactionExecutorError),
32    #[error("transaction execution prologue failed: {0}")]
33    PrologueExecutionFailed(#[source] TransactionExecutorError),
34    #[error("transaction execution epilogue failed: {0}")]
35    EpilogueExecutionFailed(#[source] TransactionExecutorError),
36    #[error("transaction note execution failed on note index {failed_note_index}: {error}")]
37    NoteExecutionFailed {
38        failed_note_index: usize,
39        error: TransactionExecutorError,
40    },
41}
42
43// TRANSACTION EXECUTOR ERROR
44// ================================================================================================
45
46#[derive(Debug, Error)]
47pub enum TransactionExecutorError {
48    #[error("the advice map contains conflicting map entries")]
49    ConflictingAdviceMapEntry(#[source] TransactionAdviceMapMismatch),
50    #[error("failed to fetch transaction inputs from the data store")]
51    FetchTransactionInputsFailed(#[source] DataStoreError),
52    #[error("foreign account inputs for ID {0} are not anchored on reference block")]
53    ForeignAccountNotAnchoredInReference(AccountId),
54    #[error(
55        "execution options' cycles must be between {min_cycles} and {max_cycles}, but found {actual}"
56    )]
57    InvalidExecutionOptionsCycles {
58        min_cycles: u32,
59        max_cycles: u32,
60        actual: u32,
61    },
62    #[error("failed to create transaction inputs")]
63    InvalidTransactionInputs(#[source] TransactionInputError),
64    #[error("failed to process account update commitment: {0}")]
65    AccountUpdateCommitment(&'static str),
66    #[error(
67        "account delta commitment computed in transaction kernel ({in_kernel_commitment}) does not match account delta computed via the host ({host_commitment})"
68    )]
69    InconsistentAccountDeltaCommitment {
70        in_kernel_commitment: Word,
71        host_commitment: Word,
72    },
73    #[error("failed to remove the fee asset from the pre-fee account delta")]
74    RemoveFeeAssetFromDelta(#[source] AccountDeltaError),
75    #[error("input account ID {input_id} does not match output account ID {output_id}")]
76    InconsistentAccountId {
77        input_id: AccountId,
78        output_id: AccountId,
79    },
80    #[error("expected account nonce delta to be {expected}, found {actual}")]
81    InconsistentAccountNonceDelta { expected: Felt, actual: Felt },
82    #[error(
83        "native asset amount {account_balance} in the account vault is not sufficient to cover the transaction fee of {tx_fee}"
84    )]
85    InsufficientFee { account_balance: u64, tx_fee: u64 },
86    #[error("account witness provided for account ID {0} is invalid")]
87    InvalidAccountWitness(AccountId, #[source] SmtProofError),
88    #[error(
89        "input note {0} was created in a block past the transaction reference block number ({1})"
90    )]
91    NoteBlockPastReferenceBlock(NoteId, BlockNumber),
92    #[error("failed to create transaction host")]
93    TransactionHostCreationFailed(#[source] TransactionHostError),
94    #[error("failed to construct transaction outputs")]
95    TransactionOutputConstructionFailed(#[source] TransactionOutputError),
96    // Print the diagnostic directly instead of returning the source error. In the source error
97    // case, the diagnostic is lost if the execution error is not explicitly unwrapped.
98    #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
99    TransactionProgramExecutionFailed(ExecutionError),
100    /// This variant can be matched on to get the summary of a transaction for signing purposes.
101    // It is boxed to avoid triggering clippy::result_large_err for functions that return this type.
102    #[error("transaction is unauthorized with summary {0:?}")]
103    Unauthorized(Box<TransactionSummary>),
104}
105
106// TRANSACTION PROVER ERROR
107// ================================================================================================
108
109#[derive(Debug, Error)]
110pub enum TransactionProverError {
111    #[error("failed to apply account delta")]
112    AccountDeltaApplyFailed(#[source] AccountError),
113    #[error("failed to remove the fee asset from the pre-fee account delta")]
114    RemoveFeeAssetFromDelta(#[source] AccountDeltaError),
115    #[error("failed to construct transaction outputs")]
116    TransactionOutputConstructionFailed(#[source] TransactionOutputError),
117    #[error("failed to build proven transaction")]
118    ProvenTransactionBuildFailed(#[source] ProvenTransactionError),
119    #[error("the advice map contains conflicting map entries")]
120    ConflictingAdviceMapEntry(#[source] TransactionAdviceMapMismatch),
121    // Print the diagnostic directly instead of returning the source error. In the source error
122    // case, the diagnostic is lost if the execution error is not explicitly unwrapped.
123    #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
124    TransactionProgramExecutionFailed(ExecutionError),
125    #[error("failed to create transaction host")]
126    TransactionHostCreationFailed(#[source] TransactionHostError),
127    /// Custom error variant for errors not covered by the other variants.
128    #[error("{error_msg}")]
129    Other {
130        error_msg: Box<str>,
131        // thiserror will return this when calling Error::source on DataStoreError.
132        source: Option<Box<dyn Error + Send + Sync + 'static>>,
133    },
134}
135
136impl TransactionProverError {
137    /// Creates a custom error using the [`TransactionProverError::Other`] variant from an error
138    /// message.
139    pub fn other(message: impl Into<String>) -> Self {
140        let message: String = message.into();
141        Self::Other { error_msg: message.into(), source: None }
142    }
143
144    /// Creates a custom error using the [`TransactionProverError::Other`] variant from an error
145    /// message and a source error.
146    pub fn other_with_source(
147        message: impl Into<String>,
148        source: impl Error + Send + Sync + 'static,
149    ) -> Self {
150        let message: String = message.into();
151        Self::Other {
152            error_msg: message.into(),
153            source: Some(Box::new(source)),
154        }
155    }
156}
157
158// TRANSACTION VERIFIER ERROR
159// ================================================================================================
160
161#[derive(Debug, Error)]
162pub enum TransactionVerifierError {
163    #[error("failed to verify transaction")]
164    TransactionVerificationFailed(#[source] VerificationError),
165    #[error("transaction proof security level is {actual} but must be at least {expected_minimum}")]
166    InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 },
167}
168
169// TRANSACTION HOST ERROR
170// ================================================================================================
171
172#[derive(Debug, Error)]
173pub enum TransactionHostError {
174    #[error("{0}")]
175    AccountProcedureIndexMapError(String),
176    #[error("failed to create account procedure info")]
177    AccountProcedureInfoCreationFailed(#[source] AccountError),
178}
179
180// DATA STORE ERROR
181// ================================================================================================
182
183#[derive(Debug, Error)]
184pub enum DataStoreError {
185    #[error("account with id {0} not found in data store")]
186    AccountNotFound(AccountId),
187    #[error("block with number {0} not found in data store")]
188    BlockNotFound(BlockNumber),
189    /// Custom error variant for implementors of the [`DataStore`](crate::executor::DataStore)
190    /// trait.
191    #[error("{error_msg}")]
192    Other {
193        error_msg: Box<str>,
194        // thiserror will return this when calling Error::source on DataStoreError.
195        source: Option<Box<dyn Error + Send + Sync + 'static>>,
196    },
197}
198
199impl DataStoreError {
200    /// Creates a custom error using the [`DataStoreError::Other`] variant from an error message.
201    pub fn other(message: impl Into<String>) -> Self {
202        let message: String = message.into();
203        Self::Other { error_msg: message.into(), source: None }
204    }
205
206    /// Creates a custom error using the [`DataStoreError::Other`] variant from an error message and
207    /// a source error.
208    pub fn other_with_source(
209        message: impl Into<String>,
210        source: impl Error + Send + Sync + 'static,
211    ) -> Self {
212        let message: String = message.into();
213        Self::Other {
214            error_msg: message.into(),
215            source: Some(Box::new(source)),
216        }
217    }
218}
219
220// AUTHENTICATION ERROR
221// ================================================================================================
222
223#[derive(Debug, Error)]
224pub enum AuthenticationError {
225    #[error("signature rejected: {0}")]
226    RejectedSignature(String),
227    #[error("unknown public key: {0}")]
228    UnknownPublicKey(String),
229    /// Custom error variant for implementors of the
230    /// [`TransactionAuthenticatior`](crate::auth::TransactionAuthenticator) trait.
231    #[error("{error_msg}")]
232    Other {
233        error_msg: Box<str>,
234        // thiserror will return this when calling Error::source on DataStoreError.
235        source: Option<Box<dyn Error + Send + Sync + 'static>>,
236    },
237}
238
239impl AuthenticationError {
240    /// Creates a custom error using the [`AuthenticationError::Other`] variant from an error
241    /// message.
242    pub fn other(message: impl Into<String>) -> Self {
243        let message: String = message.into();
244        Self::Other { error_msg: message.into(), source: None }
245    }
246
247    /// Creates a custom error using the [`AuthenticationError::Other`] variant from an error
248    /// message and a source error.
249    pub fn other_with_source(
250        message: impl Into<String>,
251        source: impl Error + Send + Sync + 'static,
252    ) -> Self {
253        let message: String = message.into();
254        Self::Other {
255            error_msg: message.into(),
256            source: Some(Box::new(source)),
257        }
258    }
259}
260
261#[cfg(test)]
262mod error_assertions {
263    use super::*;
264
265    /// Asserts at compile time that the passed error has Send + Sync + 'static bounds.
266    fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
267
268    fn _assert_data_store_error_bounds(err: DataStoreError) {
269        _assert_error_is_send_sync_static(err);
270    }
271
272    fn _assert_authentication_error_bounds(err: AuthenticationError) {
273        _assert_error_is_send_sync_static(err);
274    }
275}