1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::error::Error;
5
6use miden_processor::{DeserializationError, ExecutionError};
7use miden_protocol::account::AccountId;
8use miden_protocol::account::auth::PublicKeyCommitment;
9use miden_protocol::assembly::diagnostics::reporting::PrintDiagnostic;
10use miden_protocol::asset::AssetVaultKey;
11use miden_protocol::block::BlockNumber;
12use miden_protocol::crypto::merkle::smt::SmtProofError;
13use miden_protocol::errors::{
14 AccountDeltaError,
15 AccountError,
16 AssetError,
17 NoteError,
18 ProvenTransactionError,
19 TransactionInputError,
20 TransactionOutputError,
21};
22use miden_protocol::note::{NoteId, NoteMetadata};
23use miden_protocol::transaction::TransactionSummary;
24use miden_protocol::{Felt, Word};
25use miden_verifier::VerificationError;
26use thiserror::Error;
27
28#[derive(Debug, Error)]
32pub enum NoteCheckerError {
33 #[error("invalid input note count {0} is out of range)")]
34 InputNoteCountOutOfRange(usize),
35 #[error("transaction preparation failed: {0}")]
36 TransactionPreparation(#[source] TransactionExecutorError),
37 #[error("transaction execution prologue failed: {0}")]
38 PrologueExecution(#[source] TransactionExecutorError),
39}
40
41#[derive(Debug, Error)]
45pub(crate) enum TransactionCheckerError {
46 #[error("transaction preparation failed: {0}")]
47 TransactionPreparation(#[source] TransactionExecutorError),
48 #[error("transaction execution prologue failed: {0}")]
49 PrologueExecution(#[source] TransactionExecutorError),
50 #[error("transaction execution epilogue failed: {0}")]
51 EpilogueExecution(#[source] TransactionExecutorError),
52 #[error("transaction note execution failed on note index {failed_note_index}: {error}")]
53 NoteExecution {
54 failed_note_index: usize,
55 error: TransactionExecutorError,
56 },
57}
58
59impl From<TransactionCheckerError> for TransactionExecutorError {
60 fn from(error: TransactionCheckerError) -> Self {
61 match error {
62 TransactionCheckerError::TransactionPreparation(error) => error,
63 TransactionCheckerError::PrologueExecution(error) => error,
64 TransactionCheckerError::EpilogueExecution(error) => error,
65 TransactionCheckerError::NoteExecution { error, .. } => error,
66 }
67 }
68}
69
70#[derive(Debug, Error)]
74pub enum TransactionExecutorError {
75 #[error("failed to fetch transaction inputs from the data store")]
76 FetchTransactionInputsFailed(#[source] DataStoreError),
77 #[error("failed to fetch asset witnesses from the data store")]
78 FetchAssetWitnessFailed(#[source] DataStoreError),
79 #[error("fee asset must be fungible but was non-fungible")]
80 FeeAssetMustBeFungible,
81 #[error("foreign account inputs for ID {0} are not anchored on reference block")]
82 ForeignAccountNotAnchoredInReference(AccountId),
83 #[error(
84 "execution options' cycles must be between {min_cycles} and {max_cycles}, but found {actual}"
85 )]
86 InvalidExecutionOptionsCycles {
87 min_cycles: u32,
88 max_cycles: u32,
89 actual: u32,
90 },
91 #[error("failed to create transaction inputs")]
92 InvalidTransactionInputs(#[source] TransactionInputError),
93 #[error("failed to process account update commitment: {0}")]
94 AccountUpdateCommitment(&'static str),
95 #[error(
96 "account delta commitment computed in transaction kernel ({in_kernel_commitment}) does not match account delta computed via the host ({host_commitment})"
97 )]
98 InconsistentAccountDeltaCommitment {
99 in_kernel_commitment: Word,
100 host_commitment: Word,
101 },
102 #[error("failed to remove the fee asset from the pre-fee account delta")]
103 RemoveFeeAssetFromDelta(#[source] AccountDeltaError),
104 #[error("input account ID {input_id} does not match output account ID {output_id}")]
105 InconsistentAccountId {
106 input_id: AccountId,
107 output_id: AccountId,
108 },
109 #[error("expected account nonce delta to be {expected}, found {actual}")]
110 InconsistentAccountNonceDelta { expected: Felt, actual: Felt },
111 #[error(
112 "native asset amount {account_balance} in the account vault is not sufficient to cover the transaction fee of {tx_fee}"
113 )]
114 InsufficientFee { account_balance: u64, tx_fee: u64 },
115 #[error("account witness provided for account ID {0} is invalid")]
116 InvalidAccountWitness(AccountId, #[source] SmtProofError),
117 #[error(
118 "input note {0} was created in a block past the transaction reference block number ({1})"
119 )]
120 NoteBlockPastReferenceBlock(NoteId, BlockNumber),
121 #[error("failed to construct transaction outputs")]
122 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
123 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
126 TransactionProgramExecutionFailed(ExecutionError),
127 #[error("transaction is unauthorized with summary {0:?}")]
130 Unauthorized(Box<TransactionSummary>),
131 #[error(
132 "failed to respond to signature requested since no authenticator is assigned to the host"
133 )]
134 MissingAuthenticator,
135}
136
137#[derive(Debug, Error)]
141pub enum TransactionProverError {
142 #[error("failed to apply account delta")]
143 AccountDeltaApplyFailed(#[source] AccountError),
144 #[error("failed to remove the fee asset from the pre-fee account delta")]
145 RemoveFeeAssetFromDelta(#[source] AccountDeltaError),
146 #[error("failed to construct transaction outputs")]
147 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
148 #[error("failed to build proven transaction")]
149 ProvenTransactionBuildFailed(#[source] ProvenTransactionError),
150 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
153 TransactionProgramExecutionFailed(ExecutionError),
154 #[error("{error_msg}")]
156 Other {
157 error_msg: Box<str>,
158 source: Option<Box<dyn Error + Send + Sync + 'static>>,
160 },
161}
162
163impl TransactionProverError {
164 pub fn other(message: impl Into<String>) -> Self {
167 let message: String = message.into();
168 Self::Other { error_msg: message.into(), source: None }
169 }
170
171 pub fn other_with_source(
174 message: impl Into<String>,
175 source: impl Error + Send + Sync + 'static,
176 ) -> Self {
177 let message: String = message.into();
178 Self::Other {
179 error_msg: message.into(),
180 source: Some(Box::new(source)),
181 }
182 }
183}
184
185#[derive(Debug, Error)]
189pub enum TransactionVerifierError {
190 #[error("failed to verify transaction")]
191 TransactionVerificationFailed(#[source] VerificationError),
192 #[error("transaction proof security level is {actual} but must be at least {expected_minimum}")]
193 InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 },
194}
195
196#[derive(Debug, Error)]
200pub enum TransactionKernelError {
201 #[error("failed to add asset to account delta")]
202 AccountDeltaAddAssetFailed(#[source] AccountDeltaError),
203 #[error("failed to remove asset from account delta")]
204 AccountDeltaRemoveAssetFailed(#[source] AccountDeltaError),
205 #[error("failed to add asset to note")]
206 FailedToAddAssetToNote(#[source] NoteError),
207 #[error("note input data has hash {actual} but expected hash {expected}")]
208 InvalidNoteInputs { expected: Word, actual: Word },
209 #[error(
210 "failed to respond to signature requested since no authenticator is assigned to the host"
211 )]
212 MissingAuthenticator,
213 #[error("failed to generate signature")]
214 SignatureGenerationFailed(#[source] AuthenticationError),
215 #[error("transaction returned unauthorized event but a commitment did not match: {0}")]
216 TransactionSummaryCommitmentMismatch(#[source] Box<dyn Error + Send + Sync + 'static>),
217 #[error("failed to construct transaction summary")]
218 TransactionSummaryConstructionFailed(#[source] Box<dyn Error + Send + Sync + 'static>),
219 #[error("asset data extracted from the stack by event handler `{handler}` is not well formed")]
220 MalformedAssetInEventHandler {
221 handler: &'static str,
222 source: AssetError,
223 },
224 #[error(
225 "note inputs data extracted from the advice map by the event handler is not well formed"
226 )]
227 MalformedNoteInputs(#[source] NoteError),
228 #[error(
229 "note script data `{data:?}` extracted from the advice map by the event handler is not well formed"
230 )]
231 MalformedNoteScript {
232 data: Vec<Felt>,
233 source: DeserializationError,
234 },
235 #[error("recipient data `{0:?}` in the advice provider is not well formed")]
236 MalformedRecipientData(Vec<Felt>),
237 #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")]
238 MissingNote(usize),
239 #[error(
240 "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider"
241 )]
242 PublicNoteMissingDetails(NoteMetadata, Word),
243 #[error("attachment provided to set_attachment must be empty when attachment kind is None")]
244 NoteAttachmentNoneIsNotEmpty,
245 #[error(
246 "commitment of note attachment {actual} does not match attachment {provided} provided to set_attachment"
247 )]
248 NoteAttachmentArrayMismatch { actual: Word, provided: Word },
249 #[error(
250 "note input data in advice provider contains fewer elements ({actual}) than specified ({specified}) by its inputs length"
251 )]
252 TooFewElementsForNoteInputs { specified: u64, actual: u64 },
253 #[error("account procedure with procedure root {0} is not in the account procedure index map")]
254 UnknownAccountProcedure(Word),
255 #[error("code commitment {0} is not in the account procedure index map")]
256 UnknownCodeCommitment(Word),
257 #[error("account storage slots number is missing in memory at address {0}")]
258 AccountStorageSlotsNumMissing(u32),
259 #[error("account nonce can only be incremented once")]
260 NonceCanOnlyIncrementOnce,
261 #[error("failed to convert fee asset into fungible asset")]
262 FailedToConvertFeeAsset(#[source] AssetError),
263 #[error(
264 "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}"
265 )]
266 GetForeignAccountInputs {
267 foreign_account_id: AccountId,
268 ref_block: BlockNumber,
269 source: DataStoreError,
271 },
272 #[error(
273 "failed to get vault asset witness from data store for vault root {vault_root} and vault_key {asset_key}"
274 )]
275 GetVaultAssetWitness {
276 vault_root: Word,
277 asset_key: AssetVaultKey,
278 source: DataStoreError,
280 },
281 #[error(
282 "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}"
283 )]
284 GetStorageMapWitness {
285 map_root: Word,
286 map_key: Word,
287 source: DataStoreError,
289 },
290 #[error(
291 "native asset amount {account_balance} in the account vault is not sufficient to cover the transaction fee of {tx_fee}"
292 )]
293 InsufficientFee { account_balance: u64, tx_fee: u64 },
294 #[error("transaction requires a signature")]
297 Unauthorized(Box<TransactionSummary>),
298 #[error("{message}")]
300 Other {
301 message: Box<str>,
302 source: Option<Box<dyn Error + Send + Sync + 'static>>,
304 },
305}
306
307impl TransactionKernelError {
308 pub fn other(message: impl Into<String>) -> Self {
311 let message: String = message.into();
312 Self::Other { message: message.into(), source: None }
313 }
314
315 pub fn other_with_source(
318 message: impl Into<String>,
319 source: impl Error + Send + Sync + 'static,
320 ) -> Self {
321 let message: String = message.into();
322 Self::Other {
323 message: message.into(),
324 source: Some(Box::new(source)),
325 }
326 }
327}
328
329#[derive(Debug, Error)]
333pub enum DataStoreError {
334 #[error("account with id {0} not found in data store")]
335 AccountNotFound(AccountId),
336 #[error("block with number {0} not found in data store")]
337 BlockNotFound(BlockNumber),
338 #[error("{error_msg}")]
341 Other {
342 error_msg: Box<str>,
343 source: Option<Box<dyn Error + Send + Sync + 'static>>,
345 },
346}
347
348impl DataStoreError {
349 pub fn other(message: impl Into<String>) -> Self {
351 let message: String = message.into();
352 Self::Other { error_msg: message.into(), source: None }
353 }
354
355 pub fn other_with_source(
358 message: impl Into<String>,
359 source: impl Error + Send + Sync + 'static,
360 ) -> Self {
361 let message: String = message.into();
362 Self::Other {
363 error_msg: message.into(),
364 source: Some(Box::new(source)),
365 }
366 }
367}
368
369#[derive(Debug, Error)]
373pub enum AuthenticationError {
374 #[error("signature rejected: {0}")]
375 RejectedSignature(String),
376 #[error("public key `{0}` is not contained in the authenticator's keys")]
377 UnknownPublicKey(PublicKeyCommitment),
378 #[error("{error_msg}")]
381 Other {
382 error_msg: Box<str>,
383 source: Option<Box<dyn Error + Send + Sync + 'static>>,
385 },
386}
387
388impl AuthenticationError {
389 pub fn other(message: impl Into<String>) -> Self {
392 let message: String = message.into();
393 Self::Other { error_msg: message.into(), source: None }
394 }
395
396 pub fn other_with_source(
399 message: impl Into<String>,
400 source: impl Error + Send + Sync + 'static,
401 ) -> Self {
402 let message: String = message.into();
403 Self::Other {
404 error_msg: message.into(),
405 source: Some(Box::new(source)),
406 }
407 }
408}
409
410#[cfg(test)]
411mod error_assertions {
412 use super::*;
413
414 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
416
417 fn _assert_data_store_error_bounds(err: DataStoreError) {
418 _assert_error_is_send_sync_static(err);
419 }
420
421 fn _assert_authentication_error_bounds(err: AuthenticationError) {
422 _assert_error_is_send_sync_static(err);
423 }
424
425 fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) {
426 _assert_error_is_send_sync_static(err);
427 }
428}