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