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(
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("recipient data `{0:?}` in the advice provider is not well formed")]
246 MalformedRecipientData(Vec<Felt>),
247 #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")]
248 MissingNote(usize),
249 #[error(
250 "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider"
251 )]
252 PublicNoteMissingDetails(PartialNoteMetadata, Word),
253 #[error(
254 "commitment of note attachment advice data is {actual} which does not match commitment {provided} provided to add_attachment"
255 )]
256 NoteAttachmentCommitmentMismatch { actual: Word, provided: Word },
257 #[error(
258 "note storage in advice provider contains fewer items ({actual}) than specified ({specified}) by its number of storage items"
259 )]
260 TooFewElementsForNoteStorage { specified: u64, actual: u64 },
261 #[error("account procedure with procedure root {0} is not in the account procedure index map")]
262 UnknownAccountProcedure(Word),
263 #[error("code commitment {0} is not in the account procedure index map")]
264 UnknownCodeCommitment(Word),
265 #[error("account storage slots number is missing in memory at address {0}")]
266 AccountStorageSlotsNumMissing(u32),
267 #[error("account nonce can only be incremented once")]
268 NonceCanOnlyIncrementOnce,
269 #[error(
270 "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}"
271 )]
272 GetForeignAccountInputs {
273 foreign_account_id: AccountId,
274 ref_block: BlockNumber,
275 source: DataStoreError,
277 },
278 #[error(
279 "failed to get vault asset witness from data store for vault root {vault_root} and asset_id {asset_id}"
280 )]
281 GetVaultAssetWitness {
282 vault_root: Word,
283 asset_id: AssetId,
284 source: DataStoreError,
286 },
287 #[error(
288 "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}"
289 )]
290 GetStorageMapWitness {
291 map_root: Word,
292 map_key: StorageMapKey,
293 source: DataStoreError,
295 },
296 #[error("transaction requires a signature")]
299 Unauthorized(Box<TransactionSummary>),
300 #[error("{message}")]
302 Other {
303 message: Box<str>,
304 source: Option<Box<dyn Error + Send + Sync + 'static>>,
306 },
307}
308
309impl TransactionKernelError {
310 pub fn other(message: impl Into<String>) -> Self {
313 let message: String = message.into();
314 Self::Other { message: message.into(), source: None }
315 }
316
317 pub fn other_with_source(
320 message: impl Into<String>,
321 source: impl Error + Send + Sync + 'static,
322 ) -> Self {
323 let message: String = message.into();
324 Self::Other {
325 message: message.into(),
326 source: Some(Box::new(source)),
327 }
328 }
329}
330
331#[derive(Debug, Error)]
335pub enum DataStoreError {
336 #[error("account with id {0} not found in data store")]
337 AccountNotFound(AccountId),
338 #[error("block with number {0} not found in data store")]
339 BlockNotFound(BlockNumber),
340 #[error("{error_msg}")]
343 Other {
344 error_msg: Box<str>,
345 source: Option<Box<dyn Error + Send + Sync + 'static>>,
347 },
348}
349
350impl DataStoreError {
351 pub fn other(message: impl Into<String>) -> Self {
353 let message: String = message.into();
354 Self::Other { error_msg: message.into(), source: None }
355 }
356
357 pub fn other_with_source(
360 message: impl Into<String>,
361 source: impl Error + Send + Sync + 'static,
362 ) -> Self {
363 let message: String = message.into();
364 Self::Other {
365 error_msg: message.into(),
366 source: Some(Box::new(source)),
367 }
368 }
369}
370
371#[derive(Debug, Error)]
375pub enum AuthenticationError {
376 #[error("signature rejected: {0}")]
377 RejectedSignature(String),
378 #[error("public key `{0}` is not contained in the authenticator's keys")]
379 UnknownPublicKey(PublicKeyCommitment),
380 #[error("{error_msg}")]
383 Other {
384 error_msg: Box<str>,
385 source: Option<Box<dyn Error + Send + Sync + 'static>>,
387 },
388}
389
390impl AuthenticationError {
391 pub fn other(message: impl Into<String>) -> Self {
394 let message: String = message.into();
395 Self::Other { error_msg: message.into(), source: None }
396 }
397
398 pub fn other_with_source(
401 message: impl Into<String>,
402 source: impl Error + Send + Sync + 'static,
403 ) -> Self {
404 let message: String = message.into();
405 Self::Other {
406 error_msg: message.into(),
407 source: Some(Box::new(source)),
408 }
409 }
410}
411
412#[cfg(test)]
413mod error_assertions {
414 use super::*;
415
416 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
418
419 fn _assert_data_store_error_bounds(err: DataStoreError) {
420 _assert_error_is_send_sync_static(err);
421 }
422
423 fn _assert_authentication_error_bounds(err: AuthenticationError) {
424 _assert_error_is_send_sync_static(err);
425 }
426
427 fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) {
428 _assert_error_is_send_sync_static(err);
429 }
430}