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