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 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 process account update commitment: {0}")]
104 AccountUpdateCommitment(&'static str),
105 #[error(
106 "account patch commitment computed in transaction kernel ({in_kernel_commitment}) does not match account patch computed via the host ({host_commitment})"
107 )]
108 InconsistentAccountPatchCommitment {
109 in_kernel_commitment: Word,
110 host_commitment: Word,
111 },
112 #[error("input account ID {input_id} does not match output account ID {output_id}")]
113 InconsistentAccountId {
114 input_id: AccountId,
115 output_id: AccountId,
116 },
117 #[error("account witness provided for account ID {0} is invalid")]
118 InvalidAccountWitness(AccountId, #[source] SmtProofError),
119 #[error(
120 "input note {0} was created in a block past the transaction reference block number ({1})"
121 )]
122 NoteBlockPastReferenceBlock(NoteId, BlockNumber),
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 #[error("received an auth request event emitted outside the authentication procedure")]
138 AuthRequestOutsideAuthProcedure,
139 #[error("received privileged event {0} emitted outside the tx kernel context")]
140 PrivilegedEventFromOutsideTransactionKernelContext(TransactionEventId),
141}
142
143#[cfg(any(test, feature = "testing"))]
144impl TransactionExecutorError {
145 pub fn unwrap_unauthorized_err(self) -> Box<TransactionSummary> {
146 match self {
147 TransactionExecutorError::Unauthorized(transaction_summary) => transaction_summary,
148 other => panic!("expected TransactionExecutorError::Unauthorized, got {other}"),
149 }
150 }
151}
152
153#[derive(Debug, Error)]
157pub enum TransactionProverError {
158 #[error("failed to construct transaction outputs")]
159 TransactionOutputConstructionFailed(#[source] TransactionOutputError),
160 #[error("failed to shrink output note")]
161 OutputNoteShrinkFailed(#[source] OutputNoteError),
162 #[error("failed to build proven transaction")]
163 ProvenTransactionBuildFailed(#[source] ProvenTransactionError),
164 #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))]
167 TransactionProgramExecutionFailed(ExecutionError),
168 #[error("failed to generate transaction proof")]
169 TransactionProofGenerationFailed(#[source] ProverError),
170 #[error("{error_msg}")]
172 Other {
173 error_msg: Box<str>,
174 source: Option<Box<dyn Error + Send + Sync + 'static>>,
176 },
177}
178
179impl TransactionProverError {
180 pub fn other(message: impl Into<String>) -> Self {
183 let message: String = message.into();
184 Self::Other { error_msg: message.into(), source: None }
185 }
186
187 pub fn other_with_source(
190 message: impl Into<String>,
191 source: impl Error + Send + Sync + 'static,
192 ) -> Self {
193 let message: String = message.into();
194 Self::Other {
195 error_msg: message.into(),
196 source: Some(Box::new(source)),
197 }
198 }
199}
200
201#[derive(Debug, Error)]
205pub enum TransactionKernelError {
206 #[error("failed to add asset to account delta")]
207 AccountDeltaAddAssetFailed(#[source] AccountDeltaError),
208 #[error("failed to remove asset from account delta")]
209 AccountDeltaRemoveAssetFailed(#[source] AccountDeltaError),
210 #[error("failed to add asset to note")]
211 FailedToAddAssetToNote(#[source] NoteError),
212 #[error("note storage has commitment {actual} but expected commitment {expected}")]
213 InvalidNoteStorage { expected: Word, actual: Word },
214 #[error(
215 "failed to respond to signature requested since no authenticator is assigned to the host"
216 )]
217 MissingAuthenticator,
218 #[error("received an auth request event emitted outside the authentication procedure")]
219 AuthRequestOutsideAuthProcedure,
220 #[error("received privileged event {0} emitted outside the tx kernel context")]
221 PrivilegedEventFromOutsideTransactionKernelContext(TransactionEventId),
222 #[error("failed to generate signature")]
223 SignatureGenerationFailed(#[source] AuthenticationError),
224 #[error("transaction returned unauthorized event but a commitment did not match: {0}")]
225 TransactionSummaryCommitmentMismatch(#[source] Box<dyn Error + Send + Sync + 'static>),
226 #[error(
227 "transaction summary binds expiration delta {actual} but the transaction's expiration delta is {expected}"
228 )]
229 TransactionSummaryExpirationDeltaMismatch { expected: u16, actual: u16 },
230 #[error("transaction summary binds block {0}, which the transaction does not authenticate")]
231 TransactionSummaryUnknownBlockNumber(BlockNumber),
232 #[error("failed to construct transaction summary")]
233 TransactionSummaryConstructionFailed(#[source] Box<dyn Error + Send + Sync + 'static>),
234 #[error("asset data extracted from the stack by event handler `{handler}` is not well formed")]
235 MalformedAssetInEventHandler {
236 handler: &'static str,
237 source: AssetError,
238 },
239 #[error(
240 "note storage data extracted from the advice map by the event handler is not well formed"
241 )]
242 MalformedNoteStorage(#[source] NoteError),
243 #[error(
244 "note script data `{data:?}` extracted from the advice map by the event handler is not well formed"
245 )]
246 MalformedNoteScript {
247 data: Vec<Felt>,
248 source: DeserializationError,
249 },
250 #[error(
251 "encoded signature under advice map key {signature_key} has {actual} elements, but a valid encoded signature has between 1 and {max} elements",
252 max = Signature::MAX_NUM_ENCODED_SIGNATURE_FELTS
253 )]
254 InvalidEncodedSignatureLength { signature_key: Word, actual: usize },
255 #[error("recipient data `{0:?}` in the advice provider is not well formed")]
256 MalformedRecipientData(Vec<Felt>),
257 #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")]
258 MissingNote(usize),
259 #[error(
260 "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider"
261 )]
262 PublicNoteMissingDetails(PartialNoteMetadata, Word),
263 #[error(
264 "commitment of note attachment advice data is {actual} which does not match commitment {provided} provided to add_attachment"
265 )]
266 NoteAttachmentCommitmentMismatch { actual: Word, provided: Word },
267 #[error(
268 "note storage in advice provider contains fewer items ({actual}) than specified ({specified}) by its number of storage items"
269 )]
270 TooFewElementsForNoteStorage { specified: u64, actual: u64 },
271 #[error("account procedure with procedure root {0} is not in the account procedure index map")]
272 UnknownAccountProcedure(Word),
273 #[error("code commitment {0} is not in the account procedure index map")]
274 UnknownCodeCommitment(Word),
275 #[error("account storage slots number is missing in memory at address {0}")]
276 AccountStorageSlotsNumMissing(u32),
277 #[error("account nonce can only be incremented once")]
278 NonceCanOnlyIncrementOnce,
279 #[error(
280 "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}"
281 )]
282 GetForeignAccountInputs {
283 foreign_account_id: AccountId,
284 ref_block: BlockNumber,
285 source: DataStoreError,
287 },
288 #[error(
289 "failed to get vault asset witness from data store for vault root {vault_root} and asset_id {asset_id}"
290 )]
291 GetVaultAssetWitness {
292 vault_root: Word,
293 asset_id: AssetId,
294 source: DataStoreError,
296 },
297 #[error(
298 "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}"
299 )]
300 GetStorageMapWitness {
301 map_root: Word,
302 map_key: StorageMapKey,
303 source: DataStoreError,
305 },
306 #[error("transaction requires a signature")]
309 Unauthorized(Box<TransactionSummary>),
310 #[error("{message}")]
312 Other {
313 message: Box<str>,
314 source: Option<Box<dyn Error + Send + Sync + 'static>>,
316 },
317}
318
319impl TransactionKernelError {
320 pub fn other(message: impl Into<String>) -> Self {
323 let message: String = message.into();
324 Self::Other { message: message.into(), source: None }
325 }
326
327 pub fn other_with_source(
330 message: impl Into<String>,
331 source: impl Error + Send + Sync + 'static,
332 ) -> Self {
333 let message: String = message.into();
334 Self::Other {
335 message: message.into(),
336 source: Some(Box::new(source)),
337 }
338 }
339}
340
341#[derive(Debug, Error)]
345pub enum DataStoreError {
346 #[error("account with id {0} not found in data store")]
347 AccountNotFound(AccountId),
348 #[error("block with number {0} not found in data store")]
349 BlockNotFound(BlockNumber),
350 #[error("{error_msg}")]
353 Other {
354 error_msg: Box<str>,
355 source: Option<Box<dyn Error + Send + Sync + 'static>>,
357 },
358}
359
360impl DataStoreError {
361 pub fn other(message: impl Into<String>) -> Self {
363 let message: String = message.into();
364 Self::Other { error_msg: message.into(), source: None }
365 }
366
367 pub fn other_with_source(
370 message: impl Into<String>,
371 source: impl Error + Send + Sync + 'static,
372 ) -> Self {
373 let message: String = message.into();
374 Self::Other {
375 error_msg: message.into(),
376 source: Some(Box::new(source)),
377 }
378 }
379}
380
381#[derive(Debug, Error)]
385pub enum AuthenticationError {
386 #[error("signature rejected: {0}")]
387 RejectedSignature(String),
388 #[error("public key `{0}` is not contained in the authenticator's keys")]
389 UnknownPublicKey(PublicKeyCommitment),
390 #[error("{error_msg}")]
393 Other {
394 error_msg: Box<str>,
395 source: Option<Box<dyn Error + Send + Sync + 'static>>,
397 },
398}
399
400impl AuthenticationError {
401 pub fn other(message: impl Into<String>) -> Self {
404 let message: String = message.into();
405 Self::Other { error_msg: message.into(), source: None }
406 }
407
408 pub fn other_with_source(
411 message: impl Into<String>,
412 source: impl Error + Send + Sync + 'static,
413 ) -> Self {
414 let message: String = message.into();
415 Self::Other {
416 error_msg: message.into(),
417 source: Some(Box::new(source)),
418 }
419 }
420}
421
422#[cfg(test)]
423mod error_assertions {
424 use super::*;
425
426 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
428
429 fn _assert_data_store_error_bounds(err: DataStoreError) {
430 _assert_error_is_send_sync_static(err);
431 }
432
433 fn _assert_authentication_error_bounds(err: AuthenticationError) {
434 _assert_error_is_send_sync_static(err);
435 }
436
437 fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) {
438 _assert_error_is_send_sync_static(err);
439 }
440}