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::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 thiserror::Error;
27
28#[derive(Debug, Error)]
32pub enum NoteCheckerError {
33 #[error("invalid input note count {0} is out of range)")]
34 InputNoteCountOutOfRange(usize),
35 #[error("transaction preparation failed: {0}")]
36 TransactionPreparation(#[source] TransactionExecutorError),
37 #[error("transaction execution prologue failed: {0}")]
38 PrologueExecution(#[source] TransactionExecutorError),
39}
40
41#[derive(Debug, Error)]
45pub(crate) enum TransactionCheckerError {
46 #[error("transaction preparation failed: {0}")]
47 TransactionPreparation(#[source] TransactionExecutorError),
48 #[error("transaction execution prologue failed: {0}")]
49 PrologueExecution(#[source] TransactionExecutorError),
50 #[error("transaction execution epilogue failed: {error}")]
51 EpilogueExecution {
52 error: TransactionExecutorError,
53 successful_notes_cycle_counts: Vec<usize>,
55 },
56 #[error("transaction note execution failed on note index {failed_note_index}: {error}")]
57 NoteExecution {
58 failed_note_index: usize,
59 error: TransactionExecutorError,
60 successful_notes_cycle_counts: Vec<usize>,
62 failed_note_cycle_count: Option<usize>,
67 },
68}
69
70impl From<TransactionCheckerError> for TransactionExecutorError {
71 fn from(error: TransactionCheckerError) -> Self {
72 match error {
73 TransactionCheckerError::TransactionPreparation(error) => error,
74 TransactionCheckerError::PrologueExecution(error) => error,
75 TransactionCheckerError::EpilogueExecution { error, .. } => error,
76 TransactionCheckerError::NoteExecution { error, .. } => error,
77 }
78 }
79}
80
81#[derive(Debug, Error)]
85pub enum TransactionExecutorError {
86 #[error("failed to fetch transaction inputs from the data store")]
87 FetchTransactionInputsFailed(#[source] DataStoreError),
88 #[error("failed to fetch asset witnesses from the data store")]
89 FetchAssetWitnessFailed(#[source] DataStoreError),
90 #[error("foreign account inputs for ID {0} are not anchored on reference block")]
91 ForeignAccountNotAnchoredInReference(AccountId),
92 #[error(
93 "execution options' cycles must be between {min_cycles} and {max_cycles}, but found {actual}"
94 )]
95 InvalidExecutionOptionsCycles {
96 min_cycles: u32,
97 max_cycles: u32,
98 actual: u32,
99 },
100 #[error("failed to create transaction inputs")]
101 InvalidTransactionInputs(#[source] TransactionInputError),
102 #[error("failed to process account update commitment: {0}")]
103 AccountUpdateCommitment(&'static str),
104 #[error(
105 "account patch commitment computed in transaction kernel ({in_kernel_commitment}) does not match account patch computed via the host ({host_commitment})"
106 )]
107 InconsistentAccountPatchCommitment {
108 in_kernel_commitment: Word,
109 host_commitment: Word,
110 },
111 #[error("input account ID {input_id} does not match output account ID {output_id}")]
112 InconsistentAccountId {
113 input_id: AccountId,
114 output_id: AccountId,
115 },
116 #[error("account witness provided for account ID {0} is invalid")]
117 InvalidAccountWitness(AccountId, #[source] SmtProofError),
118 #[error(
119 "input note {0} was created in a block past the transaction reference block number ({1})"
120 )]
121 NoteBlockPastReferenceBlock(NoteId, BlockNumber),
122 #[error("failed to construct transaction outputs")]
123 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
124 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
127 TransactionProgramExecutionFailed(ExecutionError),
128 #[error("transaction is unauthorized with summary {0:?}")]
131 Unauthorized(Box<TransactionSummary>),
132 #[error(
133 "failed to respond to signature requested since no authenticator is assigned to the host"
134 )]
135 MissingAuthenticator,
136 #[error("received an auth request event emitted outside the authentication procedure")]
137 AuthRequestOutsideAuthProcedure,
138 #[error("received privileged event {0} emitted outside the tx kernel context")]
139 PrivilegedEventFromOutsideTransactionKernelContext(TransactionEventId),
140}
141
142#[cfg(any(test, feature = "testing"))]
143impl TransactionExecutorError {
144 pub fn unwrap_unauthorized_err(self) -> Box<TransactionSummary> {
145 match self {
146 TransactionExecutorError::Unauthorized(transaction_summary) => transaction_summary,
147 other => panic!("expected TransactionExecutorError::Unauthorized, got {other}"),
148 }
149 }
150}
151
152#[derive(Debug, Error)]
156pub enum TransactionProverError {
157 #[error("failed to construct transaction outputs")]
158 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
159 #[error("failed to shrink output note")]
160 OutputNoteShrinkFailed(#[source] OutputNoteError),
161 #[error("failed to build proven transaction")]
162 ProvenTransactionBuildFailed(#[source] ProvenTransactionError),
163 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
166 TransactionProgramExecutionFailed(ExecutionError),
167 #[error("{error_msg}")]
169 Other {
170 error_msg: Box<str>,
171 source: Option<Box<dyn Error + Send + Sync + 'static>>,
173 },
174}
175
176impl TransactionProverError {
177 pub fn other(message: impl Into<String>) -> Self {
180 let message: String = message.into();
181 Self::Other { error_msg: message.into(), source: None }
182 }
183
184 pub fn other_with_source(
187 message: impl Into<String>,
188 source: impl Error + Send + Sync + 'static,
189 ) -> Self {
190 let message: String = message.into();
191 Self::Other {
192 error_msg: message.into(),
193 source: Some(Box::new(source)),
194 }
195 }
196}
197
198#[derive(Debug, Error)]
202pub enum TransactionKernelError {
203 #[error("failed to add asset to account delta")]
204 AccountDeltaAddAssetFailed(#[source] AccountDeltaError),
205 #[error("failed to remove asset from account delta")]
206 AccountDeltaRemoveAssetFailed(#[source] AccountDeltaError),
207 #[error("failed to add asset to note")]
208 FailedToAddAssetToNote(#[source] NoteError),
209 #[error("note storage has commitment {actual} but expected commitment {expected}")]
210 InvalidNoteStorage { expected: Word, actual: Word },
211 #[error(
212 "failed to respond to signature requested since no authenticator is assigned to the host"
213 )]
214 MissingAuthenticator,
215 #[error("received an auth request event emitted outside the authentication procedure")]
216 AuthRequestOutsideAuthProcedure,
217 #[error("received privileged event {0} emitted outside the tx kernel context")]
218 PrivilegedEventFromOutsideTransactionKernelContext(TransactionEventId),
219 #[error("failed to generate signature")]
220 SignatureGenerationFailed(#[source] AuthenticationError),
221 #[error("transaction returned unauthorized event but a commitment did not match: {0}")]
222 TransactionSummaryCommitmentMismatch(#[source] Box<dyn Error + Send + Sync + 'static>),
223 #[error("failed to construct transaction summary")]
224 TransactionSummaryConstructionFailed(#[source] Box<dyn Error + Send + Sync + 'static>),
225 #[error("asset data extracted from the stack by event handler `{handler}` is not well formed")]
226 MalformedAssetInEventHandler {
227 handler: &'static str,
228 source: AssetError,
229 },
230 #[error(
231 "note storage data extracted from the advice map by the event handler is not well formed"
232 )]
233 MalformedNoteStorage(#[source] NoteError),
234 #[error(
235 "note script data `{data:?}` extracted from the advice map by the event handler is not well formed"
236 )]
237 MalformedNoteScript {
238 data: Vec<Felt>,
239 source: DeserializationError,
240 },
241 #[error("recipient data `{0:?}` in the advice provider is not well formed")]
242 MalformedRecipientData(Vec<Felt>),
243 #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")]
244 MissingNote(usize),
245 #[error(
246 "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider"
247 )]
248 PublicNoteMissingDetails(PartialNoteMetadata, Word),
249 #[error(
250 "commitment of note attachment advice data is {actual} which does not match commitment {provided} provided to add_attachment"
251 )]
252 NoteAttachmentCommitmentMismatch { actual: Word, provided: Word },
253 #[error(
254 "note storage in advice provider contains fewer items ({actual}) than specified ({specified}) by its number of storage items"
255 )]
256 TooFewElementsForNoteStorage { specified: u64, actual: u64 },
257 #[error("account procedure with procedure root {0} is not in the account procedure index map")]
258 UnknownAccountProcedure(Word),
259 #[error("code commitment {0} is not in the account procedure index map")]
260 UnknownCodeCommitment(Word),
261 #[error("account storage slots number is missing in memory at address {0}")]
262 AccountStorageSlotsNumMissing(u32),
263 #[error("account nonce can only be incremented once")]
264 NonceCanOnlyIncrementOnce,
265 #[error(
266 "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}"
267 )]
268 GetForeignAccountInputs {
269 foreign_account_id: AccountId,
270 ref_block: BlockNumber,
271 source: DataStoreError,
273 },
274 #[error(
275 "failed to get vault asset witness from data store for vault root {vault_root} and asset_id {asset_id}"
276 )]
277 GetVaultAssetWitness {
278 vault_root: Word,
279 asset_id: AssetId,
280 source: DataStoreError,
282 },
283 #[error(
284 "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}"
285 )]
286 GetStorageMapWitness {
287 map_root: Word,
288 map_key: StorageMapKey,
289 source: DataStoreError,
291 },
292 #[error("transaction requires a signature")]
295 Unauthorized(Box<TransactionSummary>),
296 #[error("{message}")]
298 Other {
299 message: Box<str>,
300 source: Option<Box<dyn Error + Send + Sync + 'static>>,
302 },
303}
304
305impl TransactionKernelError {
306 pub fn other(message: impl Into<String>) -> Self {
309 let message: String = message.into();
310 Self::Other { message: message.into(), source: None }
311 }
312
313 pub fn other_with_source(
316 message: impl Into<String>,
317 source: impl Error + Send + Sync + 'static,
318 ) -> Self {
319 let message: String = message.into();
320 Self::Other {
321 message: message.into(),
322 source: Some(Box::new(source)),
323 }
324 }
325}
326
327#[derive(Debug, Error)]
331pub enum DataStoreError {
332 #[error("account with id {0} not found in data store")]
333 AccountNotFound(AccountId),
334 #[error("block with number {0} not found in data store")]
335 BlockNotFound(BlockNumber),
336 #[error("{error_msg}")]
339 Other {
340 error_msg: Box<str>,
341 source: Option<Box<dyn Error + Send + Sync + 'static>>,
343 },
344}
345
346impl DataStoreError {
347 pub fn other(message: impl Into<String>) -> Self {
349 let message: String = message.into();
350 Self::Other { error_msg: message.into(), source: None }
351 }
352
353 pub fn other_with_source(
356 message: impl Into<String>,
357 source: impl Error + Send + Sync + 'static,
358 ) -> Self {
359 let message: String = message.into();
360 Self::Other {
361 error_msg: message.into(),
362 source: Some(Box::new(source)),
363 }
364 }
365}
366
367#[derive(Debug, Error)]
371pub enum AuthenticationError {
372 #[error("signature rejected: {0}")]
373 RejectedSignature(String),
374 #[error("public key `{0}` is not contained in the authenticator's keys")]
375 UnknownPublicKey(PublicKeyCommitment),
376 #[error("{error_msg}")]
379 Other {
380 error_msg: Box<str>,
381 source: Option<Box<dyn Error + Send + Sync + 'static>>,
383 },
384}
385
386impl AuthenticationError {
387 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#[cfg(test)]
409mod error_assertions {
410 use super::*;
411
412 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
414
415 fn _assert_data_store_error_bounds(err: DataStoreError) {
416 _assert_error_is_send_sync_static(err);
417 }
418
419 fn _assert_authentication_error_bounds(err: AuthenticationError) {
420 _assert_error_is_send_sync_static(err);
421 }
422
423 fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) {
424 _assert_error_is_send_sync_static(err);
425 }
426}