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};
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(
224 "transaction summary binds expiration delta {actual} but the transaction's expiration delta is {expected}"
225 )]
226 TransactionSummaryExpirationDeltaMismatch { expected: u16, actual: u16 },
227 #[error("failed to construct transaction summary")]
228 TransactionSummaryConstructionFailed(#[source] Box<dyn Error + Send + Sync + 'static>),
229 #[error("asset data extracted from the stack by event handler `{handler}` is not well formed")]
230 MalformedAssetInEventHandler {
231 handler: &'static str,
232 source: AssetError,
233 },
234 #[error(
235 "note storage data extracted from the advice map by the event handler is not well formed"
236 )]
237 MalformedNoteStorage(#[source] NoteError),
238 #[error(
239 "note script data `{data:?}` extracted from the advice map by the event handler is not well formed"
240 )]
241 MalformedNoteScript {
242 data: Vec<Felt>,
243 source: DeserializationError,
244 },
245 #[error(
246 "encoded signature under advice map key {signature_key} has {actual} elements, but a valid encoded signature has between 1 and {max} elements",
247 max = Signature::MAX_NUM_ENCODED_SIGNATURE_FELTS
248 )]
249 InvalidEncodedSignatureLength { signature_key: Word, actual: usize },
250 #[error("recipient data `{0:?}` in the advice provider is not well formed")]
251 MalformedRecipientData(Vec<Felt>),
252 #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")]
253 MissingNote(usize),
254 #[error(
255 "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider"
256 )]
257 PublicNoteMissingDetails(PartialNoteMetadata, Word),
258 #[error(
259 "commitment of note attachment advice data is {actual} which does not match commitment {provided} provided to add_attachment"
260 )]
261 NoteAttachmentCommitmentMismatch { actual: Word, provided: Word },
262 #[error(
263 "note storage in advice provider contains fewer items ({actual}) than specified ({specified}) by its number of storage items"
264 )]
265 TooFewElementsForNoteStorage { specified: u64, actual: u64 },
266 #[error("account procedure with procedure root {0} is not in the account procedure index map")]
267 UnknownAccountProcedure(Word),
268 #[error("code commitment {0} is not in the account procedure index map")]
269 UnknownCodeCommitment(Word),
270 #[error("account storage slots number is missing in memory at address {0}")]
271 AccountStorageSlotsNumMissing(u32),
272 #[error("account nonce can only be incremented once")]
273 NonceCanOnlyIncrementOnce,
274 #[error(
275 "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}"
276 )]
277 GetForeignAccountInputs {
278 foreign_account_id: AccountId,
279 ref_block: BlockNumber,
280 source: DataStoreError,
282 },
283 #[error(
284 "failed to get vault asset witness from data store for vault root {vault_root} and asset_id {asset_id}"
285 )]
286 GetVaultAssetWitness {
287 vault_root: Word,
288 asset_id: AssetId,
289 source: DataStoreError,
291 },
292 #[error(
293 "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}"
294 )]
295 GetStorageMapWitness {
296 map_root: Word,
297 map_key: StorageMapKey,
298 source: DataStoreError,
300 },
301 #[error("transaction requires a signature")]
304 Unauthorized(Box<TransactionSummary>),
305 #[error("{message}")]
307 Other {
308 message: Box<str>,
309 source: Option<Box<dyn Error + Send + Sync + 'static>>,
311 },
312}
313
314impl TransactionKernelError {
315 pub fn other(message: impl Into<String>) -> Self {
318 let message: String = message.into();
319 Self::Other { message: message.into(), source: None }
320 }
321
322 pub fn other_with_source(
325 message: impl Into<String>,
326 source: impl Error + Send + Sync + 'static,
327 ) -> Self {
328 let message: String = message.into();
329 Self::Other {
330 message: message.into(),
331 source: Some(Box::new(source)),
332 }
333 }
334}
335
336#[derive(Debug, Error)]
340pub enum DataStoreError {
341 #[error("account with id {0} not found in data store")]
342 AccountNotFound(AccountId),
343 #[error("block with number {0} not found in data store")]
344 BlockNotFound(BlockNumber),
345 #[error("{error_msg}")]
348 Other {
349 error_msg: Box<str>,
350 source: Option<Box<dyn Error + Send + Sync + 'static>>,
352 },
353}
354
355impl DataStoreError {
356 pub fn other(message: impl Into<String>) -> Self {
358 let message: String = message.into();
359 Self::Other { error_msg: message.into(), source: None }
360 }
361
362 pub fn other_with_source(
365 message: impl Into<String>,
366 source: impl Error + Send + Sync + 'static,
367 ) -> Self {
368 let message: String = message.into();
369 Self::Other {
370 error_msg: message.into(),
371 source: Some(Box::new(source)),
372 }
373 }
374}
375
376#[derive(Debug, Error)]
380pub enum AuthenticationError {
381 #[error("signature rejected: {0}")]
382 RejectedSignature(String),
383 #[error("public key `{0}` is not contained in the authenticator's keys")]
384 UnknownPublicKey(PublicKeyCommitment),
385 #[error("{error_msg}")]
388 Other {
389 error_msg: Box<str>,
390 source: Option<Box<dyn Error + Send + Sync + 'static>>,
392 },
393}
394
395impl AuthenticationError {
396 pub fn other(message: impl Into<String>) -> Self {
399 let message: String = message.into();
400 Self::Other { error_msg: message.into(), source: None }
401 }
402
403 pub fn other_with_source(
406 message: impl Into<String>,
407 source: impl Error + Send + Sync + 'static,
408 ) -> Self {
409 let message: String = message.into();
410 Self::Other {
411 error_msg: message.into(),
412 source: Some(Box::new(source)),
413 }
414 }
415}
416
417#[cfg(test)]
418mod error_assertions {
419 use super::*;
420
421 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
423
424 fn _assert_data_store_error_bounds(err: DataStoreError) {
425 _assert_error_is_send_sync_static(err);
426 }
427
428 fn _assert_authentication_error_bounds(err: AuthenticationError) {
429 _assert_error_is_send_sync_static(err);
430 }
431
432 fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) {
433 _assert_error_is_send_sync_static(err);
434 }
435}