1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::error::Error;
5
6use miden_lib::transaction::TransactionAdviceMapMismatch;
7use miden_objects::account::AccountId;
8use miden_objects::account::auth::PublicKeyCommitment;
9use miden_objects::assembly::diagnostics::reporting::PrintDiagnostic;
10use miden_objects::asset::AssetVaultKey;
11use miden_objects::block::BlockNumber;
12use miden_objects::crypto::merkle::SmtProofError;
13use miden_objects::note::{NoteId, NoteMetadata};
14use miden_objects::transaction::TransactionSummary;
15use miden_objects::{
16 AccountDeltaError,
17 AccountError,
18 AssetError,
19 Felt,
20 NoteError,
21 ProvenTransactionError,
22 TransactionInputError,
23 TransactionOutputError,
24 Word,
25};
26use miden_processor::{DeserializationError, ExecutionError};
27use miden_verifier::VerificationError;
28use thiserror::Error;
29
30#[derive(Debug, Error)]
34pub enum NoteCheckerError {
35 #[error("invalid input note count {0} is out of range)")]
36 InputNoteCountOutOfRange(usize),
37 #[error("transaction preparation failed: {0}")]
38 TransactionPreparation(#[source] TransactionExecutorError),
39 #[error("transaction execution prologue failed: {0}")]
40 PrologueExecution(#[source] TransactionExecutorError),
41}
42
43#[derive(Debug, Error)]
47pub(crate) enum TransactionCheckerError {
48 #[error("transaction preparation failed: {0}")]
49 TransactionPreparation(#[source] TransactionExecutorError),
50 #[error("transaction execution prologue failed: {0}")]
51 PrologueExecution(#[source] TransactionExecutorError),
52 #[error("transaction execution epilogue failed: {0}")]
53 EpilogueExecution(#[source] TransactionExecutorError),
54 #[error("transaction note execution failed on note index {failed_note_index}: {error}")]
55 NoteExecution {
56 failed_note_index: usize,
57 error: TransactionExecutorError,
58 },
59}
60
61impl From<TransactionCheckerError> for TransactionExecutorError {
62 fn from(error: TransactionCheckerError) -> Self {
63 match error {
64 TransactionCheckerError::TransactionPreparation(error) => error,
65 TransactionCheckerError::PrologueExecution(error) => error,
66 TransactionCheckerError::EpilogueExecution(error) => error,
67 TransactionCheckerError::NoteExecution { error, .. } => error,
68 }
69 }
70}
71
72#[derive(Debug, Error)]
76pub enum TransactionExecutorError {
77 #[error("the advice map contains conflicting map entries")]
78 ConflictingAdviceMapEntry(#[source] TransactionAdviceMapMismatch),
79 #[error("failed to fetch transaction inputs from the data store")]
80 FetchTransactionInputsFailed(#[source] DataStoreError),
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 create transaction host")]
122 TransactionHostCreationFailed(#[source] TransactionHostError),
123 #[error("failed to construct transaction outputs")]
124 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
125 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
128 TransactionProgramExecutionFailed(ExecutionError),
129 #[error("transaction is unauthorized with summary {0:?}")]
132 Unauthorized(Box<TransactionSummary>),
133 #[error(
134 "failed to respond to signature requested since no authenticator is assigned to the host"
135 )]
136 MissingAuthenticator,
137}
138
139#[derive(Debug, Error)]
143pub enum TransactionProverError {
144 #[error("failed to apply account delta")]
145 AccountDeltaApplyFailed(#[source] AccountError),
146 #[error("failed to remove the fee asset from the pre-fee account delta")]
147 RemoveFeeAssetFromDelta(#[source] AccountDeltaError),
148 #[error("failed to construct transaction outputs")]
149 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
150 #[error("failed to build proven transaction")]
151 ProvenTransactionBuildFailed(#[source] ProvenTransactionError),
152 #[error("the advice map contains conflicting map entries")]
153 ConflictingAdviceMapEntry(#[source] TransactionAdviceMapMismatch),
154 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
157 TransactionProgramExecutionFailed(ExecutionError),
158 #[error("failed to create account procedure index map")]
159 CreateAccountProcedureIndexMap(#[source] TransactionHostError),
160 #[error("failed to create transaction host")]
161 TransactionHostCreationFailed(#[source] TransactionHostError),
162 #[error("{error_msg}")]
164 Other {
165 error_msg: Box<str>,
166 source: Option<Box<dyn Error + Send + Sync + 'static>>,
168 },
169}
170
171impl TransactionProverError {
172 pub fn other(message: impl Into<String>) -> Self {
175 let message: String = message.into();
176 Self::Other { error_msg: message.into(), source: None }
177 }
178
179 pub fn other_with_source(
182 message: impl Into<String>,
183 source: impl Error + Send + Sync + 'static,
184 ) -> Self {
185 let message: String = message.into();
186 Self::Other {
187 error_msg: message.into(),
188 source: Some(Box::new(source)),
189 }
190 }
191}
192
193#[derive(Debug, Error)]
197pub enum TransactionVerifierError {
198 #[error("failed to verify transaction")]
199 TransactionVerificationFailed(#[source] VerificationError),
200 #[error("transaction proof security level is {actual} but must be at least {expected_minimum}")]
201 InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 },
202}
203
204#[derive(Debug, Error)]
208pub enum TransactionHostError {
209 #[error("{0}")]
210 AccountProcedureIndexMapError(String),
211 #[error("failed to create account procedure info")]
212 AccountProcedureInfoCreationFailed(#[source] AccountError),
213}
214
215#[derive(Debug, Error)]
219pub enum TransactionKernelError {
220 #[error("failed to add asset to account delta")]
221 AccountDeltaAddAssetFailed(#[source] AccountDeltaError),
222 #[error("failed to remove asset from account delta")]
223 AccountDeltaRemoveAssetFailed(#[source] AccountDeltaError),
224 #[error("failed to add asset to note")]
225 FailedToAddAssetToNote(#[source] NoteError),
226 #[error("note input data has hash {actual} but expected hash {expected}")]
227 InvalidNoteInputs { expected: Word, actual: Word },
228 #[error(
229 "storage slot index {actual} is invalid, must be smaller than the number of account storage slots {max}"
230 )]
231 InvalidStorageSlotIndex { max: u64, actual: u64 },
232 #[error(
233 "failed to respond to signature requested since no authenticator is assigned to the host"
234 )]
235 MissingAuthenticator,
236 #[error("failed to generate signature")]
237 SignatureGenerationFailed(#[source] AuthenticationError),
238 #[error("transaction returned unauthorized event but a commitment did not match: {0}")]
239 TransactionSummaryCommitmentMismatch(#[source] Box<dyn Error + Send + Sync + 'static>),
240 #[error("failed to construct transaction summary")]
241 TransactionSummaryConstructionFailed(#[source] Box<dyn Error + Send + Sync + 'static>),
242 #[error("asset data extracted from the stack by event handler `{handler}` is not well formed")]
243 MalformedAssetInEventHandler {
244 handler: &'static str,
245 source: AssetError,
246 },
247 #[error(
248 "note inputs data extracted from the advice map by the event handler is not well formed"
249 )]
250 MalformedNoteInputs(#[source] NoteError),
251 #[error("note metadata created by the event handler is not well formed")]
252 MalformedNoteMetadata(#[source] NoteError),
253 #[error(
254 "note script data `{data:?}` extracted from the advice map by the event handler is not well formed"
255 )]
256 MalformedNoteScript {
257 data: Vec<Felt>,
258 source: DeserializationError,
259 },
260 #[error("recipient data `{0:?}` in the advice provider is not well formed")]
261 MalformedRecipientData(Vec<Felt>),
262 #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")]
263 MissingNote(u64),
264 #[error(
265 "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider"
266 )]
267 PublicNoteMissingDetails(NoteMetadata, Word),
268 #[error(
269 "note input data in advice provider contains fewer elements ({actual}) than specified ({specified}) by its inputs length"
270 )]
271 TooFewElementsForNoteInputs { specified: u64, actual: u64 },
272 #[error("account procedure with procedure root {0} is not in the account procedure index map")]
273 UnknownAccountProcedure(Word),
274 #[error("code commitment {0} is not in the account procedure index map")]
275 UnknownCodeCommitment(Word),
276 #[error("account storage slots number is missing in memory at address {0}")]
277 AccountStorageSlotsNumMissing(u32),
278 #[error("account nonce can only be incremented once")]
279 NonceCanOnlyIncrementOnce,
280 #[error("failed to convert fee asset into fungible asset")]
281 FailedToConvertFeeAsset(#[source] AssetError),
282 #[error(
283 "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}"
284 )]
285 GetForeignAccountInputs {
286 foreign_account_id: AccountId,
287 ref_block: BlockNumber,
288 source: DataStoreError,
290 },
291 #[error(
292 "failed to get vault asset witness from data store for vault root {vault_root} and vault_key {asset_key}"
293 )]
294 GetVaultAssetWitness {
295 vault_root: Word,
296 asset_key: AssetVaultKey,
297 source: DataStoreError,
299 },
300 #[error(
301 "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}"
302 )]
303 GetStorageMapWitness {
304 map_root: Word,
305 map_key: Word,
306 source: DataStoreError,
308 },
309 #[error(
310 "native asset amount {account_balance} in the account vault is not sufficient to cover the transaction fee of {tx_fee}"
311 )]
312 InsufficientFee { account_balance: u64, tx_fee: u64 },
313 #[error("transaction requires a signature")]
316 Unauthorized(Box<TransactionSummary>),
317 #[error("{message}")]
319 Other {
320 message: Box<str>,
321 source: Option<Box<dyn Error + Send + Sync + 'static>>,
323 },
324}
325
326impl TransactionKernelError {
327 pub fn other(message: impl Into<String>) -> Self {
330 let message: String = message.into();
331 Self::Other { message: message.into(), source: None }
332 }
333
334 pub fn other_with_source(
337 message: impl Into<String>,
338 source: impl Error + Send + Sync + 'static,
339 ) -> Self {
340 let message: String = message.into();
341 Self::Other {
342 message: message.into(),
343 source: Some(Box::new(source)),
344 }
345 }
346}
347
348#[derive(Debug, Error)]
352pub enum DataStoreError {
353 #[error("account with id {0} not found in data store")]
354 AccountNotFound(AccountId),
355 #[error("block with number {0} not found in data store")]
356 BlockNotFound(BlockNumber),
357 #[error("note script with root {0} not found in data store")]
358 NoteScriptNotFound(Word),
359 #[error("{error_msg}")]
362 Other {
363 error_msg: Box<str>,
364 source: Option<Box<dyn Error + Send + Sync + 'static>>,
366 },
367}
368
369impl DataStoreError {
370 pub fn other(message: impl Into<String>) -> Self {
372 let message: String = message.into();
373 Self::Other { error_msg: message.into(), source: None }
374 }
375
376 pub fn other_with_source(
379 message: impl Into<String>,
380 source: impl Error + Send + Sync + 'static,
381 ) -> Self {
382 let message: String = message.into();
383 Self::Other {
384 error_msg: message.into(),
385 source: Some(Box::new(source)),
386 }
387 }
388}
389
390#[derive(Debug, Error)]
394pub enum AuthenticationError {
395 #[error("signature rejected: {0}")]
396 RejectedSignature(String),
397 #[error("public key `{0}` is not contained in the authenticator's keys")]
398 UnknownPublicKey(PublicKeyCommitment),
399 #[error("{error_msg}")]
402 Other {
403 error_msg: Box<str>,
404 source: Option<Box<dyn Error + Send + Sync + 'static>>,
406 },
407}
408
409impl AuthenticationError {
410 pub fn other(message: impl Into<String>) -> Self {
413 let message: String = message.into();
414 Self::Other { error_msg: message.into(), source: None }
415 }
416
417 pub fn other_with_source(
420 message: impl Into<String>,
421 source: impl Error + Send + Sync + 'static,
422 ) -> Self {
423 let message: String = message.into();
424 Self::Other {
425 error_msg: message.into(),
426 source: Some(Box::new(source)),
427 }
428 }
429}
430
431#[cfg(test)]
432mod error_assertions {
433 use super::*;
434
435 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
437
438 fn _assert_data_store_error_bounds(err: DataStoreError) {
439 _assert_error_is_send_sync_static(err);
440 }
441
442 fn _assert_authentication_error_bounds(err: AuthenticationError) {
443 _assert_error_is_send_sync_static(err);
444 }
445
446 fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) {
447 _assert_error_is_send_sync_static(err);
448 }
449}