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, Signature};
9use miden_protocol::account::{AccountId, StorageMapKey, StorageSlotName};
10use miden_protocol::assembly::diagnostics::reporting::PrintDiagnostic;
11use miden_protocol::asset::AssetId;
12use miden_protocol::block::BlockNumber;
13use miden_protocol::crypto::merkle::smt::SmtProofError;
14use miden_protocol::errors::{
15 AccountDeltaError,
16 AssetError,
17 NoteError,
18 OutputNoteError,
19 ProvenTransactionError,
20 TransactionInputError,
21 TransactionOutputError,
22};
23use miden_protocol::note::{NoteId, PartialNoteMetadata};
24use miden_protocol::transaction::{TransactionEventId, TransactionSummary};
25use miden_protocol::{Felt, Word};
26use miden_prover::ProverError;
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: {error}")]
52 EpilogueExecution {
53 error: TransactionExecutorError,
54 successful_notes_cycle_counts: Vec<usize>,
56 },
57 #[error("transaction note execution failed on note index {failed_note_index}: {error}")]
58 NoteExecution {
59 failed_note_index: usize,
60 error: TransactionExecutorError,
61 successful_notes_cycle_counts: Vec<usize>,
63 failed_note_cycle_count: Option<usize>,
68 },
69}
70
71impl From<TransactionCheckerError> for TransactionExecutorError {
72 fn from(error: TransactionCheckerError) -> Self {
73 match error {
74 TransactionCheckerError::TransactionPreparation(error) => error,
75 TransactionCheckerError::PrologueExecution(error) => error,
76 TransactionCheckerError::EpilogueExecution { error, .. } => error,
77 TransactionCheckerError::NoteExecution { error, .. } => error,
78 }
79 }
80}
81
82#[derive(Debug, Error)]
86pub enum TransactionExecutorError {
87 #[error("failed to fetch transaction inputs from the data store")]
88 FetchTransactionInputsFailed(#[source] DataStoreError),
89 #[error("failed to fetch asset witnesses from the data store")]
90 FetchAssetWitnessFailed(#[source] DataStoreError),
91 #[error("foreign account inputs for ID {0} are not anchored on reference block")]
92 ForeignAccountNotAnchoredInReference(AccountId),
93 #[error(
94 "execution options' cycles must be between {min_cycles} and {max_cycles}, but found {actual}"
95 )]
96 InvalidExecutionOptionsCycles {
97 min_cycles: u32,
98 max_cycles: u32,
99 actual: u32,
100 },
101 #[error("failed to create transaction inputs")]
102 InvalidTransactionInputs(#[source] TransactionInputError),
103 #[error("failed to create transaction host")]
106 TransactionHostCreationFailed(#[source] Box<TransactionKernelError>),
107 #[error("failed to process account update commitment: {0}")]
108 AccountUpdateCommitment(&'static str),
109 #[error(
110 "account patch commitment computed in transaction kernel ({in_kernel_commitment}) does not match account patch computed via the host ({host_commitment})"
111 )]
112 InconsistentAccountPatchCommitment {
113 in_kernel_commitment: Word,
114 host_commitment: Word,
115 },
116 #[error("input account ID {input_id} does not match output account ID {output_id}")]
117 InconsistentAccountId {
118 input_id: AccountId,
119 output_id: AccountId,
120 },
121 #[error("account witness provided for account ID {0} is invalid")]
122 InvalidAccountWitness(AccountId, #[source] SmtProofError),
123 #[error(
124 "input note {0} was created in a block past the transaction reference block number ({1})"
125 )]
126 NoteBlockPastReferenceBlock(NoteId, BlockNumber),
127 #[error("failed to construct transaction outputs")]
128 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
129 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
132 TransactionProgramExecutionFailed(ExecutionError),
133 #[error("transaction is unauthorized with summary {0:?}")]
136 Unauthorized(Box<TransactionSummary>),
137 #[error(
138 "failed to respond to signature requested since no authenticator is assigned to the host"
139 )]
140 MissingAuthenticator,
141 #[error("received an auth request event emitted outside the authentication procedure")]
142 AuthRequestOutsideAuthProcedure,
143 #[error("received privileged event {0} emitted outside the tx kernel context")]
144 PrivilegedEventFromOutsideTransactionKernelContext(TransactionEventId),
145}
146
147#[cfg(any(test, feature = "testing"))]
148impl TransactionExecutorError {
149 pub fn unwrap_unauthorized_err(self) -> Box<TransactionSummary> {
150 match self {
151 TransactionExecutorError::Unauthorized(transaction_summary) => transaction_summary,
152 other => panic!("expected TransactionExecutorError::Unauthorized, got {other}"),
153 }
154 }
155}
156
157#[derive(Debug, Error)]
161pub enum TransactionProverError {
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 create transaction host")]
171 TransactionHostCreationFailed(#[source] Box<TransactionKernelError>),
172 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
175 TransactionProgramExecutionFailed(ExecutionError),
176 #[error("failed to generate transaction proof")]
177 TransactionProofGenerationFailed(#[source] ProverError),
178 #[error("{error_msg}")]
180 Other {
181 error_msg: Box<str>,
182 source: Option<Box<dyn Error + Send + Sync + 'static>>,
184 },
185}
186
187impl TransactionProverError {
188 pub fn other(message: impl Into<String>) -> Self {
191 let message: String = message.into();
192 Self::Other { error_msg: message.into(), source: None }
193 }
194
195 pub fn other_with_source(
198 message: impl Into<String>,
199 source: impl Error + Send + Sync + 'static,
200 ) -> Self {
201 let message: String = message.into();
202 Self::Other {
203 error_msg: message.into(),
204 source: Some(Box::new(source)),
205 }
206 }
207}
208
209#[derive(Debug, Error)]
213pub enum TransactionKernelError {
214 #[error("failed to add asset to account delta")]
215 AccountDeltaAddAssetFailed(#[source] AccountDeltaError),
216 #[error("failed to remove asset from account delta")]
217 AccountDeltaRemoveAssetFailed(#[source] AccountDeltaError),
218 #[error("failed to add asset to note")]
219 FailedToAddAssetToNote(#[source] NoteError),
220 #[error(
221 "transaction initialized an upgrade to account code {0} but the advice map did not provide the new code"
222 )]
223 AccountCodeUpgradeMissing(Word),
224 #[error(
225 "transaction initialized an upgrade to account code {new_code_commitment} but the advice map provides invalid code"
226 )]
227 AccountCodeUpgradeInvalid {
228 new_code_commitment: Word,
229 source: DeserializationError,
230 },
231 #[error(
232 "transaction initialized an upgrade to account code {expected} but the advice map provides code {actual}"
233 )]
234 AccountCodeUpgradeCommitmentMismatch { expected: Word, actual: Word },
235 #[error("account code upgrade is not allowed for new accounts")]
236 AccountCodeUpgradeNotAllowedForNewAccount,
237 #[error("note storage has commitment {actual} but expected commitment {expected}")]
238 InvalidNoteStorage { expected: Word, actual: Word },
239 #[error(
240 "failed to respond to signature requested since no authenticator is assigned to the host"
241 )]
242 MissingAuthenticator,
243 #[error("received an auth request event emitted outside the authentication procedure")]
244 AuthRequestOutsideAuthProcedure,
245 #[error("received privileged event {0} emitted outside the tx kernel context")]
246 PrivilegedEventFromOutsideTransactionKernelContext(TransactionEventId),
247 #[error("failed to generate signature")]
248 SignatureGenerationFailed(#[source] AuthenticationError),
249 #[error("transaction returned unauthorized event but a commitment did not match: {0}")]
250 TransactionSummaryCommitmentMismatch(#[source] Box<dyn Error + Send + Sync + 'static>),
251 #[error(
252 "transaction summary binds expiration delta {actual} but the transaction's expiration delta is {expected}"
253 )]
254 TransactionSummaryExpirationDeltaMismatch { expected: u16, actual: u16 },
255 #[error("transaction summary binds block {0}, which the transaction does not authenticate")]
256 TransactionSummaryUnknownBlockNumber(BlockNumber),
257 #[error("failed to construct transaction summary")]
258 TransactionSummaryConstructionFailed(#[source] Box<dyn Error + Send + Sync + 'static>),
259 #[error("asset data extracted from the stack by event handler `{handler}` is not well formed")]
260 MalformedAssetInEventHandler {
261 handler: &'static str,
262 source: AssetError,
263 },
264 #[error(
265 "note storage data extracted from the advice map by the event handler is not well formed"
266 )]
267 MalformedNoteStorage(#[source] NoteError),
268 #[error(
269 "note script elements `{script_elements:?}` extracted from the advice map by the event handler are not well formed"
270 )]
271 MalformedNoteScript {
272 script_elements: Vec<Felt>,
273 source: DeserializationError,
274 },
275 #[error(
276 "encoded signature under advice map key {signature_key} has {actual} elements, but a valid encoded signature has between 1 and {max} elements",
277 max = Signature::MAX_NUM_ENCODED_SIGNATURE_FELTS
278 )]
279 InvalidEncodedSignatureLength { signature_key: Word, actual: usize },
280 #[error("recipient data `{0:?}` in the advice provider is not well formed")]
281 MalformedRecipientData(Vec<Felt>),
282 #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")]
283 MissingNote(usize),
284 #[error(
285 "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider"
286 )]
287 PublicNoteMissingDetails(PartialNoteMetadata, Word),
288 #[error(
289 "commitment of note attachment advice data is {actual} which does not match commitment {provided} provided to add_attachment"
290 )]
291 NoteAttachmentCommitmentMismatch { actual: Word, provided: Word },
292 #[error(
293 "note storage in advice provider contains fewer items ({actual}) than specified ({specified}) by its number of storage items"
294 )]
295 TooFewElementsForNoteStorage { specified: u64, actual: u64 },
296 #[error("account procedure with procedure root {0} is not in the account procedure index map")]
297 UnknownAccountProcedure(Word),
298 #[error("code commitment {0} is not in the account procedure index map")]
299 UnknownCodeCommitment(Word),
300 #[error("account storage slots number is missing in memory at address {0}")]
301 AccountStorageSlotsNumMissing(u32),
302 #[error("account nonce can only be incremented once")]
303 NonceCanOnlyIncrementOnce,
304 #[error("partial storage of a new account is missing the storage map of slot {0}")]
305 NewAccountMissingStorageMap(StorageSlotName),
306 #[error(
307 "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}"
308 )]
309 GetForeignAccountInputs {
310 foreign_account_id: AccountId,
311 ref_block: BlockNumber,
312 source: DataStoreError,
314 },
315 #[error(
316 "failed to get vault asset witness from data store for vault root {vault_root} and asset_id {asset_id}"
317 )]
318 GetVaultAssetWitness {
319 vault_root: Word,
320 asset_id: AssetId,
321 source: DataStoreError,
323 },
324 #[error(
325 "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}"
326 )]
327 GetStorageMapWitness {
328 map_root: Word,
329 map_key: StorageMapKey,
330 source: DataStoreError,
332 },
333 #[error("transaction requires a signature")]
336 Unauthorized(Box<TransactionSummary>),
337 #[error("{message}")]
339 Other {
340 message: Box<str>,
341 source: Option<Box<dyn Error + Send + Sync + 'static>>,
343 },
344}
345
346impl TransactionKernelError {
347 pub fn other(message: impl Into<String>) -> Self {
350 let message: String = message.into();
351 Self::Other { message: message.into(), source: None }
352 }
353
354 pub fn other_with_source(
357 message: impl Into<String>,
358 source: impl Error + Send + Sync + 'static,
359 ) -> Self {
360 let message: String = message.into();
361 Self::Other {
362 message: message.into(),
363 source: Some(Box::new(source)),
364 }
365 }
366}
367
368#[derive(Debug, Error)]
372pub enum DataStoreError {
373 #[error("account with id {0} not found in data store")]
374 AccountNotFound(AccountId),
375 #[error("block with number {0} not found in data store")]
376 BlockNotFound(BlockNumber),
377 #[error("{error_msg}")]
380 Other {
381 error_msg: Box<str>,
382 source: Option<Box<dyn Error + Send + Sync + 'static>>,
384 },
385}
386
387impl DataStoreError {
388 pub fn other(message: impl Into<String>) -> Self {
390 let message: String = message.into();
391 Self::Other { error_msg: message.into(), source: None }
392 }
393
394 pub fn other_with_source(
397 message: impl Into<String>,
398 source: impl Error + Send + Sync + 'static,
399 ) -> Self {
400 let message: String = message.into();
401 Self::Other {
402 error_msg: message.into(),
403 source: Some(Box::new(source)),
404 }
405 }
406}
407
408#[derive(Debug, Error)]
412pub enum AuthenticationError {
413 #[error("signature rejected: {0}")]
414 RejectedSignature(String),
415 #[error("public key `{0}` is not contained in the authenticator's keys")]
416 UnknownPublicKey(PublicKeyCommitment),
417 #[error("{error_msg}")]
420 Other {
421 error_msg: Box<str>,
422 source: Option<Box<dyn Error + Send + Sync + 'static>>,
424 },
425}
426
427impl AuthenticationError {
428 pub fn other(message: impl Into<String>) -> Self {
431 let message: String = message.into();
432 Self::Other { error_msg: message.into(), source: None }
433 }
434
435 pub fn other_with_source(
438 message: impl Into<String>,
439 source: impl Error + Send + Sync + 'static,
440 ) -> Self {
441 let message: String = message.into();
442 Self::Other {
443 error_msg: message.into(),
444 source: Some(Box::new(source)),
445 }
446 }
447}
448
449#[cfg(test)]
450mod error_assertions {
451 use super::*;
452
453 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
455
456 fn _assert_data_store_error_bounds(err: DataStoreError) {
457 _assert_error_is_send_sync_static(err);
458 }
459
460 fn _assert_authentication_error_bounds(err: AuthenticationError) {
461 _assert_error_is_send_sync_static(err);
462 }
463
464 fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) {
465 _assert_error_is_send_sync_static(err);
466 }
467}