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, PartialNoteMetadata};
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: {error}")]
54 EpilogueExecution {
55 error: TransactionExecutorError,
56 successful_notes_cycle_counts: Vec<usize>,
58 },
59 #[error("transaction note execution failed on note index {failed_note_index}: {error}")]
60 NoteExecution {
61 failed_note_index: usize,
62 error: TransactionExecutorError,
63 successful_notes_cycle_counts: Vec<usize>,
65 failed_note_cycle_count: Option<usize>,
70 },
71}
72
73impl From<TransactionCheckerError> for TransactionExecutorError {
74 fn from(error: TransactionCheckerError) -> Self {
75 match error {
76 TransactionCheckerError::TransactionPreparation(error) => error,
77 TransactionCheckerError::PrologueExecution(error) => error,
78 TransactionCheckerError::EpilogueExecution { error, .. } => error,
79 TransactionCheckerError::NoteExecution { error, .. } => error,
80 }
81 }
82}
83
84#[derive(Debug, Error)]
88pub enum TransactionExecutorError {
89 #[error("failed to read fee asset from transaction inputs")]
90 FeeAssetRetrievalFailed(#[source] TransactionInputsExtractionError),
91 #[error("failed to fetch transaction inputs from the data store")]
92 FetchTransactionInputsFailed(#[source] DataStoreError),
93 #[error("failed to fetch asset witnesses from the data store")]
94 FetchAssetWitnessFailed(#[source] DataStoreError),
95 #[error("fee asset must be fungible but was non-fungible")]
96 FeeAssetMustBeFungible,
97 #[error("foreign account inputs for ID {0} are not anchored on reference block")]
98 ForeignAccountNotAnchoredInReference(AccountId),
99 #[error(
100 "execution options' cycles must be between {min_cycles} and {max_cycles}, but found {actual}"
101 )]
102 InvalidExecutionOptionsCycles {
103 min_cycles: u32,
104 max_cycles: u32,
105 actual: u32,
106 },
107 #[error("failed to create transaction inputs")]
108 InvalidTransactionInputs(#[source] TransactionInputError),
109 #[error("failed to process account update commitment: {0}")]
110 AccountUpdateCommitment(&'static str),
111 #[error(
112 "account delta commitment computed in transaction kernel ({in_kernel_commitment}) does not match account delta computed via the host ({host_commitment})"
113 )]
114 InconsistentAccountDeltaCommitment {
115 in_kernel_commitment: Word,
116 host_commitment: Word,
117 },
118 #[error("failed to remove the fee asset from the pre-fee account delta")]
119 RemoveFeeAssetFromDelta(#[source] AccountDeltaError),
120 #[error("input account ID {input_id} does not match output account ID {output_id}")]
121 InconsistentAccountId {
122 input_id: AccountId,
123 output_id: AccountId,
124 },
125 #[error("expected account nonce delta to be {expected}, found {actual}")]
126 InconsistentAccountNonceDelta { expected: Felt, actual: Felt },
127 #[error(
128 "fee asset amount {account_balance} in the account vault is not sufficient to cover the transaction fee of {tx_fee}"
129 )]
130 InsufficientFee { account_balance: u64, tx_fee: u64 },
131 #[error("account witness provided for account ID {0} is invalid")]
132 InvalidAccountWitness(AccountId, #[source] SmtProofError),
133 #[error(
134 "input note {0} was created in a block past the transaction reference block number ({1})"
135 )]
136 NoteBlockPastReferenceBlock(NoteId, BlockNumber),
137 #[error("failed to construct transaction outputs")]
138 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
139 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
142 TransactionProgramExecutionFailed(ExecutionError),
143 #[error("transaction is unauthorized with summary {0:?}")]
146 Unauthorized(Box<TransactionSummary>),
147 #[error(
148 "failed to respond to signature requested since no authenticator is assigned to the host"
149 )]
150 MissingAuthenticator,
151}
152
153#[derive(Debug, Error)]
157pub enum TransactionProverError {
158 #[error("failed to apply account delta")]
159 AccountDeltaApplyFailed(#[source] AccountError),
160 #[error("failed to remove the fee asset from the pre-fee account delta")]
161 RemoveFeeAssetFromDelta(#[source] AccountDeltaError),
162 #[error("failed to construct transaction outputs")]
163 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
164 #[error("failed to shrink output note")]
165 OutputNoteShrinkFailed(#[source] OutputNoteError),
166 #[error("failed to build proven transaction")]
167 ProvenTransactionBuildFailed(#[source] ProvenTransactionError),
168 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
171 TransactionProgramExecutionFailed(ExecutionError),
172 #[error("{error_msg}")]
174 Other {
175 error_msg: Box<str>,
176 source: Option<Box<dyn Error + Send + Sync + 'static>>,
178 },
179}
180
181impl TransactionProverError {
182 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 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#[derive(Debug, Error)]
207pub enum TransactionVerifierError {
208 #[error("failed to verify transaction")]
209 TransactionVerificationFailed(#[source] VerificationError),
210 #[error("transaction proof security level is {actual} but must be at least {expected_minimum}")]
211 InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 },
212}
213
214#[derive(Debug, Error)]
218pub enum TransactionKernelError {
219 #[error("failed to add asset to account delta")]
220 AccountDeltaAddAssetFailed(#[source] AccountDeltaError),
221 #[error("failed to remove asset from account delta")]
222 AccountDeltaRemoveAssetFailed(#[source] AccountDeltaError),
223 #[error("failed to add asset to note")]
224 FailedToAddAssetToNote(#[source] NoteError),
225 #[error("note storage has commitment {actual} but expected commitment {expected}")]
226 InvalidNoteStorage { expected: Word, actual: Word },
227 #[error(
228 "failed to respond to signature requested since no authenticator is assigned to the host"
229 )]
230 MissingAuthenticator,
231 #[error("failed to generate signature")]
232 SignatureGenerationFailed(#[source] AuthenticationError),
233 #[error("transaction returned unauthorized event but a commitment did not match: {0}")]
234 TransactionSummaryCommitmentMismatch(#[source] Box<dyn Error + Send + Sync + 'static>),
235 #[error("failed to construct transaction summary")]
236 TransactionSummaryConstructionFailed(#[source] Box<dyn Error + Send + Sync + 'static>),
237 #[error("asset data extracted from the stack by event handler `{handler}` is not well formed")]
238 MalformedAssetInEventHandler {
239 handler: &'static str,
240 source: AssetError,
241 },
242 #[error(
243 "note storage data extracted from the advice map by the event handler is not well formed"
244 )]
245 MalformedNoteStorage(#[source] NoteError),
246 #[error(
247 "note script data `{data:?}` extracted from the advice map by the event handler is not well formed"
248 )]
249 MalformedNoteScript {
250 data: Vec<Felt>,
251 source: DeserializationError,
252 },
253 #[error("recipient data `{0:?}` in the advice provider is not well formed")]
254 MalformedRecipientData(Vec<Felt>),
255 #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")]
256 MissingNote(usize),
257 #[error(
258 "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider"
259 )]
260 PublicNoteMissingDetails(PartialNoteMetadata, Word),
261 #[error(
262 "commitment of note attachment advice data is {actual} which does not match commitment {provided} provided to add_attachment"
263 )]
264 NoteAttachmentCommitmentMismatch { actual: Word, provided: Word },
265 #[error(
266 "note storage in advice provider contains fewer items ({actual}) than specified ({specified}) by its number of storage items"
267 )]
268 TooFewElementsForNoteStorage { specified: u64, actual: u64 },
269 #[error("account procedure with procedure root {0} is not in the account procedure index map")]
270 UnknownAccountProcedure(Word),
271 #[error("code commitment {0} is not in the account procedure index map")]
272 UnknownCodeCommitment(Word),
273 #[error("account storage slots number is missing in memory at address {0}")]
274 AccountStorageSlotsNumMissing(u32),
275 #[error("account nonce can only be incremented once")]
276 NonceCanOnlyIncrementOnce,
277 #[error("failed to convert fee asset into fungible asset")]
278 FailedToConvertFeeAsset(#[source] AssetError),
279 #[error(
280 "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}"
281 )]
282 GetForeignAccountInputs {
283 foreign_account_id: AccountId,
284 ref_block: BlockNumber,
285 source: DataStoreError,
287 },
288 #[error(
289 "failed to get vault asset witness from data store for vault root {vault_root} and vault_key {asset_key}"
290 )]
291 GetVaultAssetWitness {
292 vault_root: Word,
293 asset_key: AssetVaultKey,
294 source: DataStoreError,
296 },
297 #[error(
298 "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}"
299 )]
300 GetStorageMapWitness {
301 map_root: Word,
302 map_key: StorageMapKey,
303 source: DataStoreError,
305 },
306 #[error(
307 "fee asset amount {account_balance} in the account vault is not sufficient to cover the transaction fee of {tx_fee}"
308 )]
309 InsufficientFee { account_balance: u64, tx_fee: u64 },
310 #[error("transaction requires a signature")]
313 Unauthorized(Box<TransactionSummary>),
314 #[error("{message}")]
316 Other {
317 message: Box<str>,
318 source: Option<Box<dyn Error + Send + Sync + 'static>>,
320 },
321}
322
323impl TransactionKernelError {
324 pub fn other(message: impl Into<String>) -> Self {
327 let message: String = message.into();
328 Self::Other { message: message.into(), source: None }
329 }
330
331 pub fn other_with_source(
334 message: impl Into<String>,
335 source: impl Error + Send + Sync + 'static,
336 ) -> Self {
337 let message: String = message.into();
338 Self::Other {
339 message: message.into(),
340 source: Some(Box::new(source)),
341 }
342 }
343}
344
345#[derive(Debug, Error)]
349pub enum DataStoreError {
350 #[error("account with id {0} not found in data store")]
351 AccountNotFound(AccountId),
352 #[error("block with number {0} not found in data store")]
353 BlockNotFound(BlockNumber),
354 #[error("{error_msg}")]
357 Other {
358 error_msg: Box<str>,
359 source: Option<Box<dyn Error + Send + Sync + 'static>>,
361 },
362}
363
364impl DataStoreError {
365 pub fn other(message: impl Into<String>) -> Self {
367 let message: String = message.into();
368 Self::Other { error_msg: message.into(), source: None }
369 }
370
371 pub fn other_with_source(
374 message: impl Into<String>,
375 source: impl Error + Send + Sync + 'static,
376 ) -> Self {
377 let message: String = message.into();
378 Self::Other {
379 error_msg: message.into(),
380 source: Some(Box::new(source)),
381 }
382 }
383}
384
385#[derive(Debug, Error)]
389pub enum AuthenticationError {
390 #[error("signature rejected: {0}")]
391 RejectedSignature(String),
392 #[error("public key `{0}` is not contained in the authenticator's keys")]
393 UnknownPublicKey(PublicKeyCommitment),
394 #[error("{error_msg}")]
397 Other {
398 error_msg: Box<str>,
399 source: Option<Box<dyn Error + Send + Sync + 'static>>,
401 },
402}
403
404impl AuthenticationError {
405 pub fn other(message: impl Into<String>) -> Self {
408 let message: String = message.into();
409 Self::Other { error_msg: message.into(), source: None }
410 }
411
412 pub fn other_with_source(
415 message: impl Into<String>,
416 source: impl Error + Send + Sync + 'static,
417 ) -> Self {
418 let message: String = message.into();
419 Self::Other {
420 error_msg: message.into(),
421 source: Some(Box::new(source)),
422 }
423 }
424}
425
426#[cfg(test)]
427mod error_assertions {
428 use super::*;
429
430 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
432
433 fn _assert_data_store_error_bounds(err: DataStoreError) {
434 _assert_error_is_send_sync_static(err);
435 }
436
437 fn _assert_authentication_error_bounds(err: AuthenticationError) {
438 _assert_error_is_send_sync_static(err);
439 }
440
441 fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) {
442 _assert_error_is_send_sync_static(err);
443 }
444}