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#[cfg(any(test, feature = "testing"))]
154impl TransactionExecutorError {
155 pub fn unwrap_unauthorized_err(self) -> Box<TransactionSummary> {
156 match self {
157 TransactionExecutorError::Unauthorized(transaction_summary) => transaction_summary,
158 other => panic!("expected TransactionExecutorError::Unauthorized, got {other}"),
159 }
160 }
161}
162
163#[derive(Debug, Error)]
167pub enum TransactionProverError {
168 #[error("failed to apply account delta")]
169 AccountDeltaApplyFailed(#[source] AccountError),
170 #[error("failed to remove the fee asset from the pre-fee account delta")]
171 RemoveFeeAssetFromDelta(#[source] AccountDeltaError),
172 #[error("failed to construct transaction outputs")]
173 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
174 #[error("failed to shrink output note")]
175 OutputNoteShrinkFailed(#[source] OutputNoteError),
176 #[error("failed to build proven transaction")]
177 ProvenTransactionBuildFailed(#[source] ProvenTransactionError),
178 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
181 TransactionProgramExecutionFailed(ExecutionError),
182 #[error("{error_msg}")]
184 Other {
185 error_msg: Box<str>,
186 source: Option<Box<dyn Error + Send + Sync + 'static>>,
188 },
189}
190
191impl TransactionProverError {
192 pub fn other(message: impl Into<String>) -> Self {
195 let message: String = message.into();
196 Self::Other { error_msg: message.into(), source: None }
197 }
198
199 pub fn other_with_source(
202 message: impl Into<String>,
203 source: impl Error + Send + Sync + 'static,
204 ) -> Self {
205 let message: String = message.into();
206 Self::Other {
207 error_msg: message.into(),
208 source: Some(Box::new(source)),
209 }
210 }
211}
212
213#[derive(Debug, Error)]
217pub enum TransactionVerifierError {
218 #[error("failed to verify transaction")]
219 TransactionVerificationFailed(#[source] VerificationError),
220 #[error("transaction proof security level is {actual} but must be at least {expected_minimum}")]
221 InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 },
222}
223
224#[derive(Debug, Error)]
228pub enum TransactionKernelError {
229 #[error("failed to add asset to account delta")]
230 AccountDeltaAddAssetFailed(#[source] AccountDeltaError),
231 #[error("failed to remove asset from account delta")]
232 AccountDeltaRemoveAssetFailed(#[source] AccountDeltaError),
233 #[error("failed to add asset to note")]
234 FailedToAddAssetToNote(#[source] NoteError),
235 #[error("note storage has commitment {actual} but expected commitment {expected}")]
236 InvalidNoteStorage { expected: Word, actual: Word },
237 #[error(
238 "failed to respond to signature requested since no authenticator is assigned to the host"
239 )]
240 MissingAuthenticator,
241 #[error("failed to generate signature")]
242 SignatureGenerationFailed(#[source] AuthenticationError),
243 #[error("transaction returned unauthorized event but a commitment did not match: {0}")]
244 TransactionSummaryCommitmentMismatch(#[source] Box<dyn Error + Send + Sync + 'static>),
245 #[error("failed to construct transaction summary")]
246 TransactionSummaryConstructionFailed(#[source] Box<dyn Error + Send + Sync + 'static>),
247 #[error("asset data extracted from the stack by event handler `{handler}` is not well formed")]
248 MalformedAssetInEventHandler {
249 handler: &'static str,
250 source: AssetError,
251 },
252 #[error(
253 "note storage data extracted from the advice map by the event handler is not well formed"
254 )]
255 MalformedNoteStorage(#[source] NoteError),
256 #[error(
257 "note script data `{data:?}` extracted from the advice map by the event handler is not well formed"
258 )]
259 MalformedNoteScript {
260 data: Vec<Felt>,
261 source: DeserializationError,
262 },
263 #[error("recipient data `{0:?}` in the advice provider is not well formed")]
264 MalformedRecipientData(Vec<Felt>),
265 #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")]
266 MissingNote(usize),
267 #[error(
268 "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider"
269 )]
270 PublicNoteMissingDetails(PartialNoteMetadata, Word),
271 #[error(
272 "commitment of note attachment advice data is {actual} which does not match commitment {provided} provided to add_attachment"
273 )]
274 NoteAttachmentCommitmentMismatch { actual: Word, provided: Word },
275 #[error(
276 "note storage in advice provider contains fewer items ({actual}) than specified ({specified}) by its number of storage items"
277 )]
278 TooFewElementsForNoteStorage { specified: u64, actual: u64 },
279 #[error("account procedure with procedure root {0} is not in the account procedure index map")]
280 UnknownAccountProcedure(Word),
281 #[error("code commitment {0} is not in the account procedure index map")]
282 UnknownCodeCommitment(Word),
283 #[error("account storage slots number is missing in memory at address {0}")]
284 AccountStorageSlotsNumMissing(u32),
285 #[error("account nonce can only be incremented once")]
286 NonceCanOnlyIncrementOnce,
287 #[error("failed to convert fee asset into fungible asset")]
288 FailedToConvertFeeAsset(#[source] AssetError),
289 #[error(
290 "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}"
291 )]
292 GetForeignAccountInputs {
293 foreign_account_id: AccountId,
294 ref_block: BlockNumber,
295 source: DataStoreError,
297 },
298 #[error(
299 "failed to get vault asset witness from data store for vault root {vault_root} and vault_key {asset_key}"
300 )]
301 GetVaultAssetWitness {
302 vault_root: Word,
303 asset_key: AssetVaultKey,
304 source: DataStoreError,
306 },
307 #[error(
308 "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}"
309 )]
310 GetStorageMapWitness {
311 map_root: Word,
312 map_key: StorageMapKey,
313 source: DataStoreError,
315 },
316 #[error(
317 "fee asset amount {account_balance} in the account vault is not sufficient to cover the transaction fee of {tx_fee}"
318 )]
319 InsufficientFee { account_balance: u64, tx_fee: u64 },
320 #[error("transaction requires a signature")]
323 Unauthorized(Box<TransactionSummary>),
324 #[error("{message}")]
326 Other {
327 message: Box<str>,
328 source: Option<Box<dyn Error + Send + Sync + 'static>>,
330 },
331}
332
333impl TransactionKernelError {
334 pub fn other(message: impl Into<String>) -> Self {
337 let message: String = message.into();
338 Self::Other { message: message.into(), source: None }
339 }
340
341 pub fn other_with_source(
344 message: impl Into<String>,
345 source: impl Error + Send + Sync + 'static,
346 ) -> Self {
347 let message: String = message.into();
348 Self::Other {
349 message: message.into(),
350 source: Some(Box::new(source)),
351 }
352 }
353}
354
355#[derive(Debug, Error)]
359pub enum DataStoreError {
360 #[error("account with id {0} not found in data store")]
361 AccountNotFound(AccountId),
362 #[error("block with number {0} not found in data store")]
363 BlockNotFound(BlockNumber),
364 #[error("{error_msg}")]
367 Other {
368 error_msg: Box<str>,
369 source: Option<Box<dyn Error + Send + Sync + 'static>>,
371 },
372}
373
374impl DataStoreError {
375 pub fn other(message: impl Into<String>) -> Self {
377 let message: String = message.into();
378 Self::Other { error_msg: message.into(), source: None }
379 }
380
381 pub fn other_with_source(
384 message: impl Into<String>,
385 source: impl Error + Send + Sync + 'static,
386 ) -> Self {
387 let message: String = message.into();
388 Self::Other {
389 error_msg: message.into(),
390 source: Some(Box::new(source)),
391 }
392 }
393}
394
395#[derive(Debug, Error)]
399pub enum AuthenticationError {
400 #[error("signature rejected: {0}")]
401 RejectedSignature(String),
402 #[error("public key `{0}` is not contained in the authenticator's keys")]
403 UnknownPublicKey(PublicKeyCommitment),
404 #[error("{error_msg}")]
407 Other {
408 error_msg: Box<str>,
409 source: Option<Box<dyn Error + Send + Sync + 'static>>,
411 },
412}
413
414impl AuthenticationError {
415 pub fn other(message: impl Into<String>) -> Self {
418 let message: String = message.into();
419 Self::Other { error_msg: message.into(), source: None }
420 }
421
422 pub fn other_with_source(
425 message: impl Into<String>,
426 source: impl Error + Send + Sync + 'static,
427 ) -> Self {
428 let message: String = message.into();
429 Self::Other {
430 error_msg: message.into(),
431 source: Some(Box::new(source)),
432 }
433 }
434}
435
436#[cfg(test)]
437mod error_assertions {
438 use super::*;
439
440 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
442
443 fn _assert_data_store_error_bounds(err: DataStoreError) {
444 _assert_error_is_send_sync_static(err);
445 }
446
447 fn _assert_authentication_error_bounds(err: AuthenticationError) {
448 _assert_error_is_send_sync_static(err);
449 }
450
451 fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) {
452 _assert_error_is_send_sync_static(err);
453 }
454}