1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::error::Error;
5
6use miden_assembly::Report;
7use miden_assembly::diagnostics::reporting::PrintDiagnostic;
8use miden_core::Felt;
9use miden_core::mast::MastForestError;
10use miden_crypto::merkle::mmr::MmrError;
11use miden_crypto::merkle::smt::{SmtLeafError, SmtProofError};
12use miden_crypto::utils::HexParseError;
13use thiserror::Error;
14
15use super::account::AccountId;
16use super::asset::{AssetVaultKey, FungibleAsset, NonFungibleAsset, TokenSymbol};
17use super::crypto::merkle::MerkleError;
18use super::note::NoteId;
19use super::{MAX_BATCHES_PER_BLOCK, MAX_OUTPUT_NOTES_PER_BATCH, Word};
20use crate::account::component::{SchemaTypeError, StorageValueName, StorageValueNameError};
21use crate::account::{
22 AccountCode,
23 AccountIdPrefix,
24 AccountStorage,
25 AccountType,
26 StorageMapKey,
27 StorageSlotId,
28 StorageSlotName,
29};
30use crate::address::AddressType;
31use crate::asset::AssetId;
32use crate::batch::BatchId;
33use crate::block::BlockNumber;
34use crate::note::{
35 NoteAssets,
36 NoteAttachmentArray,
37 NoteAttachmentKind,
38 NoteAttachmentScheme,
39 NoteTag,
40 NoteType,
41 Nullifier,
42};
43use crate::transaction::TransactionId;
44use crate::utils::serde::DeserializationError;
45use crate::vm::EventId;
46use crate::{
47 ACCOUNT_UPDATE_MAX_SIZE,
48 MAX_ACCOUNTS_PER_BATCH,
49 MAX_INPUT_NOTES_PER_BATCH,
50 MAX_INPUT_NOTES_PER_TX,
51 MAX_NOTE_STORAGE_ITEMS,
52 MAX_OUTPUT_NOTES_PER_TX,
53 NOTE_MAX_SIZE,
54};
55
56#[cfg(any(feature = "testing", test))]
57mod masm_error;
58#[cfg(any(feature = "testing", test))]
59pub use masm_error::MasmError;
60
61#[cfg(any(feature = "testing", test))]
63pub mod tx_kernel {
64 include!(concat!(env!("OUT_DIR"), "/tx_kernel_errors.rs"));
65}
66
67#[cfg(any(feature = "testing", test))]
69pub mod protocol {
70 include!(concat!(env!("OUT_DIR"), "/protocol_errors.rs"));
71}
72
73#[derive(Debug, Error)]
77pub enum ComponentMetadataError {
78 #[error("storage slot name `{0}` is duplicate")]
79 DuplicateSlotName(StorageSlotName),
80 #[error("storage init value name `{0}` is duplicate")]
81 DuplicateInitValueName(StorageValueName),
82 #[error("storage value name is incorrect: {0}")]
83 IncorrectStorageValueName(#[source] StorageValueNameError),
84 #[error("invalid storage schema: {0}")]
85 InvalidSchema(String),
86 #[error("type `{0}` is not valid for `{1}` slots")]
87 InvalidType(String, String),
88 #[error("error deserializing component metadata: {0}")]
89 MetadataDeserializationError(String),
90 #[error("init storage value `{0}` was not provided")]
91 InitValueNotProvided(StorageValueName),
92 #[error("invalid init storage value for `{0}`: {1}")]
93 InvalidInitStorageValue(StorageValueName, String),
94 #[error("error converting value into expected type: {0}")]
95 StorageValueParsingError(#[source] SchemaTypeError),
96 #[error("storage map contains duplicate keys")]
97 StorageMapHasDuplicateKeys(#[source] Box<dyn Error + Send + Sync + 'static>),
98 #[cfg(feature = "std")]
99 #[error("error trying to deserialize from toml")]
100 TomlDeserializationError(#[source] toml::de::Error),
101 #[cfg(feature = "std")]
102 #[error("error trying to deserialize from toml")]
103 TomlSerializationError(#[source] toml::ser::Error),
104}
105
106#[derive(Debug, Error)]
110pub enum AccountError {
111 #[error("failed to deserialize account code")]
112 AccountCodeDeserializationError(#[source] DeserializationError),
113 #[error("account code does not contain an auth component")]
114 AccountCodeNoAuthComponent,
115 #[error("account code contains multiple auth components")]
116 AccountCodeMultipleAuthComponents,
117 #[error("account code must contain at least one non-auth procedure")]
118 AccountCodeNoProcedures,
119 #[error("account code contains {0} procedures but it may contain at most {max} procedures", max = AccountCode::MAX_NUM_PROCEDURES)]
120 AccountCodeTooManyProcedures(usize),
121 #[error("failed to assemble account component:\n{}", PrintDiagnostic::new(.0))]
122 AccountComponentAssemblyError(Report),
123 #[error("failed to merge components into one account code mast forest")]
124 AccountComponentMastForestMergeError(#[source] MastForestError),
125 #[error("account component contains multiple authentication procedures")]
126 AccountComponentMultipleAuthProcedures,
127 #[error("failed to update asset vault")]
128 AssetVaultUpdateError(#[source] AssetVaultError),
129 #[error("account build error: {0}")]
130 BuildError(String, #[source] Option<Box<AccountError>>),
131 #[error("failed to parse account ID from final account header")]
132 FinalAccountHeaderIdParsingFailed(#[source] AccountIdError),
133 #[error("account header data has length {actual} but it must be of length {expected}")]
134 HeaderDataIncorrectLength { actual: usize, expected: usize },
135 #[error("active account nonce {current} plus increment {increment} overflows a felt to {new}")]
136 NonceOverflow {
137 current: Felt,
138 increment: Felt,
139 new: Felt,
140 },
141 #[error(
142 "digest of the seed has {actual} trailing zeroes but must have at least {expected} trailing zeroes"
143 )]
144 SeedDigestTooFewTrailingZeros { expected: u32, actual: u32 },
145 #[error("account ID {actual} computed from seed does not match ID {expected} on account")]
146 AccountIdSeedMismatch { actual: AccountId, expected: AccountId },
147 #[error("account ID seed was provided for an existing account")]
148 ExistingAccountWithSeed,
149 #[error("account ID seed was not provided for a new account")]
150 NewAccountMissingSeed,
151 #[error(
152 "an account with a seed cannot be converted into a delta since it represents an unregistered account"
153 )]
154 DeltaFromAccountWithSeed,
155 #[error("seed converts to an invalid account ID")]
156 SeedConvertsToInvalidAccountId(#[source] AccountIdError),
157 #[error("storage map root {0} not found in the account storage")]
158 StorageMapRootNotFound(Word),
159 #[error("storage slot {0} is not of type map")]
160 StorageSlotNotMap(StorageSlotName),
161 #[error("storage slot {0} is not of type value")]
162 StorageSlotNotValue(StorageSlotName),
163 #[error("storage slot name {0} is assigned to more than one slot")]
164 DuplicateStorageSlotName(StorageSlotName),
165 #[error("storage does not contain a slot with name {slot_name}")]
166 StorageSlotNameNotFound { slot_name: StorageSlotName },
167 #[error("storage does not contain a slot with ID {slot_id}")]
168 StorageSlotIdNotFound { slot_id: StorageSlotId },
169 #[error("storage slots must be sorted by slot ID")]
170 UnsortedStorageSlots,
171 #[error("number of storage slots is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)]
172 StorageTooManySlots(u64),
173 #[error(
174 "account component at index {component_index} is incompatible with account of type {account_type}"
175 )]
176 UnsupportedComponentForAccountType {
177 account_type: AccountType,
178 component_index: usize,
179 },
180 #[error(
181 "failed to apply full state delta to existing account; full state deltas can be converted to accounts directly"
182 )]
183 ApplyFullStateDeltaToAccount,
184 #[error("only account deltas representing a full account can be converted to a full account")]
185 PartialStateDeltaToAccount,
186 #[error("maximum number of storage map leaves exceeded")]
187 MaxNumStorageMapLeavesExceeded(#[source] MerkleError),
188 #[error("{error_msg}")]
191 Other {
192 error_msg: Box<str>,
193 source: Option<Box<dyn Error + Send + Sync + 'static>>,
195 },
196}
197
198impl AccountError {
199 pub fn other(message: impl Into<String>) -> Self {
201 let message: String = message.into();
202 Self::Other { error_msg: message.into(), source: None }
203 }
204
205 pub fn other_with_source(
208 message: impl Into<String>,
209 source: impl Error + Send + Sync + 'static,
210 ) -> Self {
211 let message: String = message.into();
212 Self::Other {
213 error_msg: message.into(),
214 source: Some(Box::new(source)),
215 }
216 }
217}
218
219#[derive(Debug, Error)]
223pub enum AccountIdError {
224 #[error("failed to convert bytes into account ID prefix field element")]
225 AccountIdInvalidPrefixFieldElement(#[source] DeserializationError),
226 #[error("failed to convert bytes into account ID suffix field element")]
227 AccountIdInvalidSuffixFieldElement(#[source] DeserializationError),
228 #[error("`{0}` is not a known account storage mode")]
229 UnknownAccountStorageMode(Box<str>),
230 #[error(r#"`{0}` is not a known account type, expected one of "FungibleFaucet", "NonFungibleFaucet", "RegularAccountImmutableCode" or "RegularAccountUpdatableCode""#)]
231 UnknownAccountType(Box<str>),
232 #[error("failed to parse hex string into account ID")]
233 AccountIdHexParseError(#[source] HexParseError),
234 #[error("`{0}` is not a known account ID version")]
235 UnknownAccountIdVersion(u8),
236 #[error("most significant bit of account ID suffix must be zero")]
237 AccountIdSuffixMostSignificantBitMustBeZero,
238 #[error("least significant byte of account ID suffix must be zero")]
239 AccountIdSuffixLeastSignificantByteMustBeZero,
240 #[error("failed to decode bech32 string into account ID")]
241 Bech32DecodeError(#[source] Bech32Error),
242}
243
244#[derive(Debug, Error)]
248pub enum StorageSlotNameError {
249 #[error("slot name must only contain characters a..z, A..Z, 0..9, double colon or underscore")]
250 InvalidCharacter,
251 #[error("slot names must be separated by double colons")]
252 UnexpectedColon,
253 #[error("slot name components must not start with an underscore")]
254 UnexpectedUnderscore,
255 #[error(
256 "slot names must contain at least {} components separated by double colons",
257 StorageSlotName::MIN_NUM_COMPONENTS
258 )]
259 TooShort,
260 #[error("slot names must contain at most {} characters", StorageSlotName::MAX_LENGTH)]
261 TooLong,
262}
263
264#[derive(Debug, Error)]
268pub enum AccountTreeError {
269 #[error(
270 "account tree contains multiple account IDs that share the same prefix {duplicate_prefix}"
271 )]
272 DuplicateIdPrefix { duplicate_prefix: AccountIdPrefix },
273 #[error(
274 "entries passed to account tree contain multiple state commitments for the same account ID prefix {prefix}"
275 )]
276 DuplicateStateCommitments { prefix: AccountIdPrefix },
277 #[error("untracked account ID {id} used in partial account tree")]
278 UntrackedAccountId { id: AccountId, source: MerkleError },
279 #[error("new tree root after account witness insertion does not match previous tree root")]
280 TreeRootConflict(#[source] MerkleError),
281 #[error("failed to apply mutations to account tree")]
282 ApplyMutations(#[source] MerkleError),
283 #[error("failed to compute account tree mutations")]
284 ComputeMutations(#[source] MerkleError),
285 #[error("provided smt contains an invalid account ID in key {key}")]
286 InvalidAccountIdKey { key: Word, source: AccountIdError },
287 #[error("smt leaf's index is not a valid account ID prefix")]
288 InvalidAccountIdPrefix(#[source] AccountIdError),
289 #[error("account witness merkle path depth {0} does not match AccountTree::DEPTH")]
290 WitnessMerklePathDepthDoesNotMatchAccountTreeDepth(usize),
291}
292
293#[derive(Debug, Error)]
297pub enum AddressError {
298 #[error("tag length {0} is too large, must be less than or equal to {max}",
299 max = NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH
300 )]
301 TagLengthTooLarge(u8),
302 #[error("unknown address interface `{0}`")]
303 UnknownAddressInterface(u16),
304 #[error("failed to decode account ID")]
305 AccountIdDecodeError(#[source] AccountIdError),
306 #[error("address separator must not be included without routing parameters")]
307 TrailingSeparator,
308 #[error("failed to decode bech32 string into an address")]
309 Bech32DecodeError(#[source] Bech32Error),
310 #[error("{error_msg}")]
311 DecodeError {
312 error_msg: Box<str>,
313 source: Option<Box<dyn Error + Send + Sync + 'static>>,
315 },
316 #[error("found unknown routing parameter key {0}")]
317 UnknownRoutingParameterKey(u8),
318}
319
320impl AddressError {
321 pub fn decode_error(message: impl Into<String>) -> Self {
323 let message: String = message.into();
324 Self::DecodeError { error_msg: message.into(), source: None }
325 }
326
327 pub fn decode_error_with_source(
330 message: impl Into<String>,
331 source: impl Error + Send + Sync + 'static,
332 ) -> Self {
333 let message: String = message.into();
334 Self::DecodeError {
335 error_msg: message.into(),
336 source: Some(Box::new(source)),
337 }
338 }
339}
340
341#[derive(Debug, Error)]
345pub enum Bech32Error {
346 #[error(transparent)]
347 DecodeError(Box<dyn Error + Send + Sync + 'static>),
348 #[error("found unknown address type {0} which is not the expected {account_addr} account ID address type",
349 account_addr = AddressType::AccountId as u8
350 )]
351 UnknownAddressType(u8),
352 #[error("expected bech32 data to be of length {expected} but it was of length {actual}")]
353 InvalidDataLength { expected: usize, actual: usize },
354}
355
356#[derive(Debug, Error)]
360pub enum NetworkIdError {
361 #[error("failed to parse string into a network ID")]
362 NetworkIdParseError(#[source] Box<dyn Error + Send + Sync + 'static>),
363}
364
365#[derive(Debug, Error)]
369pub enum AccountDeltaError {
370 #[error("storage slot {0} was used as different slot types")]
371 StorageSlotUsedAsDifferentTypes(StorageSlotName),
372 #[error("non fungible vault can neither be added nor removed twice")]
373 DuplicateNonFungibleVaultUpdate(NonFungibleAsset),
374 #[error(
375 "fungible asset issued by faucet {faucet_id} has delta {delta} which overflows when added to current value {current}"
376 )]
377 FungibleAssetDeltaOverflow {
378 faucet_id: AccountId,
379 current: i64,
380 delta: i64,
381 },
382 #[error(
383 "account update of type `{left_update_type}` cannot be merged with account update of type `{right_update_type}`"
384 )]
385 IncompatibleAccountUpdates {
386 left_update_type: &'static str,
387 right_update_type: &'static str,
388 },
389 #[error("account delta could not be applied to account {account_id}")]
390 AccountDeltaApplicationFailed {
391 account_id: AccountId,
392 source: AccountError,
393 },
394 #[error("non-empty account storage or vault delta with zero nonce delta is not allowed")]
395 NonEmptyStorageOrVaultDeltaWithZeroNonceDelta,
396 #[error(
397 "account nonce increment {current} plus the other nonce increment {increment} overflows a felt to {new}"
398 )]
399 NonceIncrementOverflow {
400 current: Felt,
401 increment: Felt,
402 new: Felt,
403 },
404 #[error("account ID {0} in fungible asset delta is not of type fungible faucet")]
405 NotAFungibleFaucetId(AccountId),
406 #[error("cannot merge two full state deltas")]
407 MergingFullStateDeltas,
408}
409
410#[derive(Debug, Error)]
414pub enum StorageMapError {
415 #[error("map entries contain key {key} twice with values {value0} and {value1}")]
416 DuplicateKey {
417 key: StorageMapKey,
418 value0: Word,
419 value1: Word,
420 },
421 #[error("map key {key} is not present in provided SMT proof")]
422 MissingKey { key: StorageMapKey },
423}
424
425#[derive(Debug, Error)]
429pub enum BatchAccountUpdateError {
430 #[error(
431 "account update for account {expected_account_id} cannot be merged with update from transaction {transaction} which was executed against account {actual_account_id}"
432 )]
433 AccountUpdateIdMismatch {
434 transaction: TransactionId,
435 expected_account_id: AccountId,
436 actual_account_id: AccountId,
437 },
438 #[error(
439 "final state commitment in account update from transaction {0} does not match initial state of current update"
440 )]
441 AccountUpdateInitialStateMismatch(TransactionId),
442 #[error("failed to merge account delta from transaction {0}")]
443 TransactionUpdateMergeError(TransactionId, #[source] Box<AccountDeltaError>),
444}
445
446#[derive(Debug, Error)]
450pub enum AssetError {
451 #[error(
452 "fungible asset amount {0} exceeds the max allowed amount of {max_amount}",
453 max_amount = FungibleAsset::MAX_AMOUNT
454 )]
455 FungibleAssetAmountTooBig(u64),
456 #[error("subtracting {subtrahend} from fungible asset amount {minuend} would underflow")]
457 FungibleAssetAmountNotSufficient { minuend: u64, subtrahend: u64 },
458 #[error(
459 "cannot combine fungible assets with different vault keys: {original_key} and {other_key}"
460 )]
461 FungibleAssetInconsistentVaultKeys {
462 original_key: AssetVaultKey,
463 other_key: AssetVaultKey,
464 },
465 #[error("faucet account ID in asset is invalid")]
466 InvalidFaucetAccountId(#[source] Box<dyn Error + Send + Sync + 'static>),
467 #[error(
468 "faucet id {0} of type {id_type} must be of type {expected_ty} for fungible assets",
469 id_type = .0.account_type(),
470 expected_ty = AccountType::FungibleFaucet
471 )]
472 FungibleFaucetIdTypeMismatch(AccountId),
473 #[error(
474 "asset ID prefix and suffix in a non-fungible asset's vault key must match indices 0 and 1 in the value, but asset ID was {asset_id} and value was {value}"
475 )]
476 NonFungibleAssetIdMustMatchValue { asset_id: AssetId, value: Word },
477 #[error("asset ID prefix and suffix in a fungible asset's vault key must be zero but was {0}")]
478 FungibleAssetIdMustBeZero(AssetId),
479 #[error(
480 "the three most significant elements in a fungible asset's value must be zero but provided value was {0}"
481 )]
482 FungibleAssetValueMostSignificantElementsMustBeZero(Word),
483 #[error(
484 "faucet id {0} of type {id_type} must be of type {expected_ty} for non fungible assets",
485 id_type = .0.account_type(),
486 expected_ty = AccountType::NonFungibleFaucet
487 )]
488 NonFungibleFaucetIdTypeMismatch(AccountId),
489 #[error("smt proof in asset witness contains invalid key or value")]
490 AssetWitnessInvalid(#[source] Box<AssetError>),
491 #[error("invalid native asset callbacks encoding: {0}")]
492 InvalidAssetCallbackFlag(u8),
493}
494
495#[derive(Debug, Error)]
499pub enum TokenSymbolError {
500 #[error("token symbol value {0} cannot exceed {max}", max = TokenSymbol::MAX_ENCODED_VALUE)]
501 ValueTooLarge(u64),
502 #[error(
503 "token symbol value {0} cannot be less than {min}",
504 min = TokenSymbol::MIN_ENCODED_VALUE
505 )]
506 ValueTooSmall(u64),
507 #[error("token symbol should have length between 1 and 12 characters, but {0} was provided")]
508 InvalidLength(usize),
509 #[error("token symbol contains a character that is not uppercase ASCII")]
510 InvalidCharacter,
511 #[error("token symbol data left after decoding the specified number of characters")]
512 DataNotFullyDecoded,
513}
514
515#[derive(Debug, Error)]
519pub enum AssetVaultError {
520 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
521 AddFungibleAssetBalanceError(#[source] AssetError),
522 #[error("provided assets contain duplicates")]
523 DuplicateAsset(#[source] MerkleError),
524 #[error("non fungible asset {0} already exists in the vault")]
525 DuplicateNonFungibleAsset(NonFungibleAsset),
526 #[error("fungible asset {0} does not exist in the vault")]
527 FungibleAssetNotFound(FungibleAsset),
528 #[error("faucet id {0} is not a fungible faucet id")]
529 NotAFungibleFaucetId(AccountId),
530 #[error("non fungible asset {0} does not exist in the vault")]
531 NonFungibleAssetNotFound(NonFungibleAsset),
532 #[error("subtracting fungible asset amounts would underflow")]
533 SubtractFungibleAssetBalanceError(#[source] AssetError),
534 #[error("maximum number of asset vault leaves exceeded")]
535 MaxLeafEntriesExceeded(#[source] MerkleError),
536}
537
538#[derive(Debug, Error)]
542pub enum PartialAssetVaultError {
543 #[error("provided SMT entry {entry} is not a valid asset")]
544 InvalidAssetInSmt { entry: Word, source: AssetError },
545 #[error("failed to add asset proof")]
546 FailedToAddProof(#[source] MerkleError),
547 #[error("asset is not tracked in the partial vault")]
548 UntrackedAsset(#[source] MerkleError),
549}
550
551#[derive(Debug, Error)]
555pub enum NoteError {
556 #[error("library does not contain a procedure with @note_script attribute")]
557 NoteScriptNoProcedureWithAttribute,
558 #[error("library contains multiple procedures with @note_script attribute")]
559 NoteScriptMultipleProceduresWithAttribute,
560 #[error("procedure at path '{0}' not found in library")]
561 NoteScriptProcedureNotFound(Box<str>),
562 #[error("procedure at path '{0}' does not have @note_script attribute")]
563 NoteScriptProcedureMissingAttribute(Box<str>),
564 #[error("note tag length {0} exceeds the maximum of {max}", max = NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)]
565 NoteTagLengthTooLarge(u8),
566 #[error("duplicate fungible asset from issuer {0} in note")]
567 DuplicateFungibleAsset(AccountId),
568 #[error("duplicate non fungible asset {0} in note")]
569 DuplicateNonFungibleAsset(NonFungibleAsset),
570 #[error("note type {0} is inconsistent with note tag {1}")]
571 InconsistentNoteTag(NoteType, u64),
572 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
573 AddFungibleAssetBalanceError(#[source] AssetError),
574 #[error("note sender is not a valid account ID")]
575 NoteSenderInvalidAccountId(#[source] AccountIdError),
576 #[error("note execution hint after block variant cannot contain u32::MAX")]
577 NoteExecutionHintAfterBlockCannotBeU32Max,
578 #[error("invalid note execution hint payload {1} for tag {0}")]
579 InvalidNoteExecutionHintPayload(u8, u32),
580 #[error(
581 "note type {0} does not match any of the valid note types {public} or {private}",
582 public = NoteType::Public,
583 private = NoteType::Private,
584 )]
585 UnknownNoteType(Box<str>),
586 #[error("block note tree index {block_note_tree_index} is out of bounds 0..={highest_index}")]
587 BlockNoteTreeIndexOutOfBounds {
588 block_note_tree_index: u16,
589 highest_index: usize,
590 },
591 #[error("note network execution requires a public note but note is of type {0}")]
592 NetworkExecutionRequiresPublicNote(NoteType),
593 #[error("failed to assemble note script:\n{}", PrintDiagnostic::new(.0))]
594 NoteScriptAssemblyError(Report),
595 #[error("failed to deserialize note script")]
596 NoteScriptDeserializationError(#[source] DeserializationError),
597 #[error("note contains {0} assets which exceeds the maximum of {max}", max = NoteAssets::MAX_NUM_ASSETS)]
598 TooManyAssets(usize),
599 #[error("note contains {0} storage items which exceeds the maximum of {max}", max = MAX_NOTE_STORAGE_ITEMS)]
600 TooManyStorageItems(usize),
601 #[error("invalid note storage length: expected {expected} items, got {actual}")]
602 InvalidNoteStorageLength { expected: usize, actual: usize },
603 #[error("note tag requires a public note but the note is of type {0}")]
604 PublicNoteRequired(NoteType),
605 #[error(
606 "note attachment cannot commit to more than {} elements",
607 NoteAttachmentArray::MAX_NUM_ELEMENTS
608 )]
609 NoteAttachmentArraySizeExceeded(usize),
610 #[error("unknown note attachment kind {0}")]
611 UnknownNoteAttachmentKind(u8),
612 #[error("note attachment of kind None must have attachment scheme None")]
613 AttachmentKindNoneMustHaveAttachmentSchemeNone,
614 #[error(
615 "note attachment kind mismatch: header has {header_kind:?} but attachment has {attachment_kind:?}"
616 )]
617 AttachmentKindMismatch {
618 header_kind: NoteAttachmentKind,
619 attachment_kind: NoteAttachmentKind,
620 },
621 #[error(
622 "note attachment scheme mismatch: header has {header_scheme:?} but attachment has {attachment_scheme:?}"
623 )]
624 AttachmentSchemeMismatch {
625 header_scheme: NoteAttachmentScheme,
626 attachment_scheme: NoteAttachmentScheme,
627 },
628 #[error("{error_msg}")]
629 Other {
630 error_msg: Box<str>,
631 source: Option<Box<dyn Error + Send + Sync + 'static>>,
633 },
634}
635
636impl NoteError {
637 pub fn other(message: impl Into<String>) -> Self {
639 let message: String = message.into();
640 Self::Other { error_msg: message.into(), source: None }
641 }
642
643 pub fn other_with_source(
646 message: impl Into<String>,
647 source: impl Error + Send + Sync + 'static,
648 ) -> Self {
649 let message: String = message.into();
650 Self::Other {
651 error_msg: message.into(),
652 source: Some(Box::new(source)),
653 }
654 }
655}
656
657#[derive(Debug, Error)]
661pub enum PartialBlockchainError {
662 #[error(
663 "block num {block_num} exceeds chain length {chain_length} implied by the partial blockchain"
664 )]
665 BlockNumTooBig {
666 chain_length: usize,
667 block_num: BlockNumber,
668 },
669
670 #[error("duplicate block {block_num} in partial blockchain")]
671 DuplicateBlock { block_num: BlockNumber },
672
673 #[error("partial blockchain does not track authentication paths for block {block_num}")]
674 UntrackedBlock { block_num: BlockNumber },
675
676 #[error(
677 "provided block header with number {block_num} and commitment {block_commitment} is not tracked by partial MMR"
678 )]
679 BlockHeaderCommitmentMismatch {
680 block_num: BlockNumber,
681 block_commitment: Word,
682 source: MmrError,
683 },
684}
685
686impl PartialBlockchainError {
687 pub fn block_num_too_big(chain_length: usize, block_num: BlockNumber) -> Self {
688 Self::BlockNumTooBig { chain_length, block_num }
689 }
690
691 pub fn duplicate_block(block_num: BlockNumber) -> Self {
692 Self::DuplicateBlock { block_num }
693 }
694
695 pub fn untracked_block(block_num: BlockNumber) -> Self {
696 Self::UntrackedBlock { block_num }
697 }
698}
699
700#[derive(Debug, Error)]
704pub enum TransactionScriptError {
705 #[error("failed to assemble transaction script:\n{}", PrintDiagnostic::new(.0))]
706 AssemblyError(Report),
707}
708
709#[derive(Debug, Error)]
713pub enum TransactionInputError {
714 #[error("transaction input note with nullifier {0} is a duplicate")]
715 DuplicateInputNote(Nullifier),
716 #[error("partial blockchain has length {actual} which does not match block number {expected}")]
717 InconsistentChainLength {
718 expected: BlockNumber,
719 actual: BlockNumber,
720 },
721 #[error(
722 "partial blockchain has commitment {actual} which does not match the block header's chain commitment {expected}"
723 )]
724 InconsistentChainCommitment { expected: Word, actual: Word },
725 #[error("block in which input note with id {0} was created is not in partial blockchain")]
726 InputNoteBlockNotInPartialBlockchain(NoteId),
727 #[error("input note with id {0} was not created in block {1}")]
728 InputNoteNotInBlock(NoteId, BlockNumber),
729 #[error(
730 "total number of input notes is {0} which exceeds the maximum of {MAX_INPUT_NOTES_PER_TX}"
731 )]
732 TooManyInputNotes(usize),
733}
734
735#[derive(Debug, Error)]
739pub enum TransactionInputsExtractionError {
740 #[error("specified foreign account id matches the transaction input's account id")]
741 AccountNotForeign,
742 #[error("foreign account data not found in advice map for account {0}")]
743 ForeignAccountNotFound(AccountId),
744 #[error("foreign account code not found for account {0}")]
745 ForeignAccountCodeNotFound(AccountId),
746 #[error("storage header data not found in advice map for account {0}")]
747 StorageHeaderNotFound(AccountId),
748 #[error("failed to handle account data")]
749 AccountError(#[from] AccountError),
750 #[error("failed to handle merkle data")]
751 MerkleError(#[from] MerkleError),
752 #[error("failed to handle account tree data")]
753 AccountTreeError(#[from] AccountTreeError),
754 #[error("missing vault root from Merkle store")]
755 MissingVaultRoot,
756 #[error("missing storage map root from Merkle store")]
757 MissingMapRoot,
758 #[error("failed to construct SMT proof")]
759 SmtProofError(#[from] SmtProofError),
760 #[error("failed to construct an asset")]
761 AssetError(#[from] AssetError),
762 #[error("failed to handle storage map data")]
763 StorageMapError(#[from] StorageMapError),
764 #[error("failed to convert elements to leaf index: {0}")]
765 LeafConversionError(String),
766 #[error("failed to construct SMT leaf")]
767 SmtLeafError(#[from] SmtLeafError),
768}
769
770#[derive(Debug, Error)]
774pub enum TransactionOutputError {
775 #[error("transaction output note with id {0} is a duplicate")]
776 DuplicateOutputNote(NoteId),
777 #[error("final account commitment is not in the advice map")]
778 FinalAccountCommitmentMissingInAdviceMap,
779 #[error("fee asset is not a fungible asset")]
780 FeeAssetNotFungibleAsset(#[source] AssetError),
781 #[error("failed to parse final account header")]
782 FinalAccountHeaderParseFailure(#[source] AccountError),
783 #[error(
784 "output notes commitment {expected} from kernel does not match computed commitment {actual}"
785 )]
786 OutputNotesCommitmentInconsistent { expected: Word, actual: Word },
787 #[error("transaction kernel output stack is invalid: {0}")]
788 OutputStackInvalid(String),
789 #[error(
790 "total number of output notes is {0} which exceeds the maximum of {MAX_OUTPUT_NOTES_PER_TX}"
791 )]
792 TooManyOutputNotes(usize),
793 #[error("failed to process account update commitment: {0}")]
794 AccountUpdateCommitment(Box<str>),
795}
796
797#[derive(Debug, Error)]
804pub enum OutputNoteError {
805 #[error("note with id {0} is private but expected a public note")]
806 NoteIsPrivate(NoteId),
807 #[error("note with id {0} is public but expected a private note")]
808 NoteIsPublic(NoteId),
809 #[error(
810 "public note with id {note_id} has size {note_size} bytes which exceeds maximum note size of {NOTE_MAX_SIZE}"
811 )]
812 NoteSizeLimitExceeded { note_id: NoteId, note_size: usize },
813}
814
815#[derive(Debug, Error)]
819pub enum TransactionEventError {
820 #[error("event id {0} is not a valid transaction event")]
821 InvalidTransactionEvent(EventId),
822}
823
824#[derive(Debug, Error)]
828pub enum TransactionTraceParsingError {
829 #[error("trace id {0} is an unknown transaction kernel trace")]
830 UnknownTransactionTrace(u32),
831}
832
833#[derive(Debug, Error)]
837pub enum ProvenTransactionError {
838 #[error(
839 "proven transaction's final account commitment {tx_final_commitment} and account details commitment {details_commitment} must match"
840 )]
841 AccountFinalCommitmentMismatch {
842 tx_final_commitment: Word,
843 details_commitment: Word,
844 },
845 #[error(
846 "proven transaction's final account ID {tx_account_id} and account details id {details_account_id} must match"
847 )]
848 AccountIdMismatch {
849 tx_account_id: AccountId,
850 details_account_id: AccountId,
851 },
852 #[error("failed to construct input notes for proven transaction")]
853 InputNotesError(TransactionInputError),
854 #[error("private account {0} should not have account details")]
855 PrivateAccountWithDetails(AccountId),
856 #[error("account {0} with public state is missing its account details")]
857 PublicStateAccountMissingDetails(AccountId),
858 #[error("new account {id} with public state must be accompanied by a full state delta")]
859 NewPublicStateAccountRequiresFullStateDelta { id: AccountId, source: AccountError },
860 #[error(
861 "existing account {0} with public state should only provide delta updates instead of full details"
862 )]
863 ExistingPublicStateAccountRequiresDeltaDetails(AccountId),
864 #[error("failed to construct output notes for proven transaction")]
865 OutputNotesError(#[source] TransactionOutputError),
866 #[error(
867 "account update of size {update_size} for account {account_id} exceeds maximum update size of {ACCOUNT_UPDATE_MAX_SIZE}"
868 )]
869 AccountUpdateSizeLimitExceeded {
870 account_id: AccountId,
871 update_size: usize,
872 },
873 #[error("proven transaction neither changed the account state, nor consumed any notes")]
874 EmptyTransaction,
875 #[error("failed to validate account delta in transaction account update")]
876 AccountDeltaCommitmentMismatch(#[source] Box<dyn Error + Send + Sync + 'static>),
877}
878
879#[derive(Debug, Error)]
883pub enum ProposedBatchError {
884 #[error(
885 "transaction batch has {0} input notes but at most {MAX_INPUT_NOTES_PER_BATCH} are allowed"
886 )]
887 TooManyInputNotes(usize),
888
889 #[error(
890 "transaction batch has {0} output notes but at most {MAX_OUTPUT_NOTES_PER_BATCH} are allowed"
891 )]
892 TooManyOutputNotes(usize),
893
894 #[error(
895 "transaction batch has {0} account updates but at most {MAX_ACCOUNTS_PER_BATCH} are allowed"
896 )]
897 TooManyAccountUpdates(usize),
898
899 #[error(
900 "transaction {transaction_id} expires at block number {transaction_expiration_num} which is not greater than the number of the batch's reference block {reference_block_num}"
901 )]
902 ExpiredTransaction {
903 transaction_id: TransactionId,
904 transaction_expiration_num: BlockNumber,
905 reference_block_num: BlockNumber,
906 },
907
908 #[error("transaction batch must contain at least one transaction")]
909 EmptyTransactionBatch,
910
911 #[error("transaction {transaction_id} appears twice in the proposed batch input")]
912 DuplicateTransaction { transaction_id: TransactionId },
913
914 #[error(
915 "transaction {second_transaction_id} consumes the note with nullifier {note_nullifier} that is also consumed by another transaction {first_transaction_id} in the batch"
916 )]
917 DuplicateInputNote {
918 note_nullifier: Nullifier,
919 first_transaction_id: TransactionId,
920 second_transaction_id: TransactionId,
921 },
922
923 #[error(
924 "transaction {second_transaction_id} creates the note with id {note_id} that is also created by another transaction {first_transaction_id} in the batch"
925 )]
926 DuplicateOutputNote {
927 note_id: NoteId,
928 first_transaction_id: TransactionId,
929 second_transaction_id: TransactionId,
930 },
931
932 #[error(
933 "note commitment mismatch for note {id}: (input: {input_commitment}, output: {output_commitment})"
934 )]
935 NoteCommitmentMismatch {
936 id: NoteId,
937 input_commitment: Word,
938 output_commitment: Word,
939 },
940
941 #[error("failed to merge transaction delta into account {account_id}")]
942 AccountUpdateError {
943 account_id: AccountId,
944 source: BatchAccountUpdateError,
945 },
946
947 #[error(
948 "unable to prove unauthenticated note inclusion because block {block_number} in which note with id {note_id} was created is not in partial blockchain"
949 )]
950 UnauthenticatedInputNoteBlockNotInPartialBlockchain {
951 block_number: BlockNumber,
952 note_id: NoteId,
953 },
954
955 #[error(
956 "unable to prove unauthenticated note inclusion of note {note_id} in block {block_num}"
957 )]
958 UnauthenticatedNoteAuthenticationFailed {
959 note_id: NoteId,
960 block_num: BlockNumber,
961 source: MerkleError,
962 },
963
964 #[error("partial blockchain has length {actual} which does not match block number {expected}")]
965 InconsistentChainLength {
966 expected: BlockNumber,
967 actual: BlockNumber,
968 },
969
970 #[error(
971 "partial blockchain has root {actual} which does not match block header's root {expected}"
972 )]
973 InconsistentChainRoot { expected: Word, actual: Word },
974
975 #[error(
976 "block {block_reference} referenced by transaction {transaction_id} is not in the partial blockchain"
977 )]
978 MissingTransactionBlockReference {
979 block_reference: Word,
980 transaction_id: TransactionId,
981 },
982}
983
984#[derive(Debug, Error)]
988pub enum ProvenBatchError {
989 #[error("failed to verify transaction {transaction_id} in transaction batch")]
990 TransactionVerificationFailed {
991 transaction_id: TransactionId,
992 source: Box<dyn Error + Send + Sync + 'static>,
993 },
994 #[error(
995 "batch expiration block number {batch_expiration_block_num} is not greater than the reference block number {reference_block_num}"
996 )]
997 InvalidBatchExpirationBlockNum {
998 batch_expiration_block_num: BlockNumber,
999 reference_block_num: BlockNumber,
1000 },
1001}
1002
1003#[derive(Debug, Error)]
1007pub enum ProposedBlockError {
1008 #[error("block must contain at least one transaction batch")]
1009 EmptyBlock,
1010
1011 #[error("block must contain at most {MAX_BATCHES_PER_BLOCK} transaction batches")]
1012 TooManyBatches,
1013
1014 #[error(
1015 "batch {batch_id} expired at block {batch_expiration_block_num} but the current block number is {current_block_num}"
1016 )]
1017 ExpiredBatch {
1018 batch_id: BatchId,
1019 batch_expiration_block_num: BlockNumber,
1020 current_block_num: BlockNumber,
1021 },
1022
1023 #[error("batch {batch_id} appears twice in the block inputs")]
1024 DuplicateBatch { batch_id: BatchId },
1025
1026 #[error(
1027 "batch {second_batch_id} consumes the note with nullifier {note_nullifier} that is also consumed by another batch {first_batch_id} in the block"
1028 )]
1029 DuplicateInputNote {
1030 note_nullifier: Nullifier,
1031 first_batch_id: BatchId,
1032 second_batch_id: BatchId,
1033 },
1034
1035 #[error(
1036 "batch {second_batch_id} creates the note with ID {note_id} that is also created by another batch {first_batch_id} in the block"
1037 )]
1038 DuplicateOutputNote {
1039 note_id: NoteId,
1040 first_batch_id: BatchId,
1041 second_batch_id: BatchId,
1042 },
1043
1044 #[error(
1045 "timestamp {provided_timestamp} does not increase monotonically compared to timestamp {previous_timestamp} from the previous block header"
1046 )]
1047 TimestampDoesNotIncreaseMonotonically {
1048 provided_timestamp: u32,
1049 previous_timestamp: u32,
1050 },
1051
1052 #[error(
1053 "account {account_id} is updated from the same initial state commitment {initial_state_commitment} by multiple conflicting batches with IDs {first_batch_id} and {second_batch_id}"
1054 )]
1055 ConflictingBatchesUpdateSameAccount {
1056 account_id: AccountId,
1057 initial_state_commitment: Word,
1058 first_batch_id: BatchId,
1059 second_batch_id: BatchId,
1060 },
1061
1062 #[error(
1063 "partial blockchain has length {chain_length} which does not match the block number {prev_block_num} of the previous block referenced by the to-be-built block"
1064 )]
1065 ChainLengthNotEqualToPreviousBlockNumber {
1066 chain_length: BlockNumber,
1067 prev_block_num: BlockNumber,
1068 },
1069
1070 #[error(
1071 "partial blockchain has commitment {chain_commitment} which does not match the chain commitment {prev_block_chain_commitment} of the previous block {prev_block_num}"
1072 )]
1073 ChainRootNotEqualToPreviousBlockChainCommitment {
1074 chain_commitment: Word,
1075 prev_block_chain_commitment: Word,
1076 prev_block_num: BlockNumber,
1077 },
1078
1079 #[error(
1080 "partial blockchain is missing block {reference_block_num} referenced by batch {batch_id} in the block"
1081 )]
1082 BatchReferenceBlockMissingFromChain {
1083 reference_block_num: BlockNumber,
1084 batch_id: BatchId,
1085 },
1086
1087 #[error(
1088 "note commitment mismatch for note {id}: (input: {input_commitment}, output: {output_commitment})"
1089 )]
1090 NoteCommitmentMismatch {
1091 id: NoteId,
1092 input_commitment: Word,
1093 output_commitment: Word,
1094 },
1095
1096 #[error(
1097 "failed to prove unauthenticated note inclusion because block {block_number} in which note with id {note_id} was created is not in partial blockchain"
1098 )]
1099 UnauthenticatedInputNoteBlockNotInPartialBlockchain {
1100 block_number: BlockNumber,
1101 note_id: NoteId,
1102 },
1103
1104 #[error(
1105 "failed to prove unauthenticated note inclusion of note {note_id} in block {block_num}"
1106 )]
1107 UnauthenticatedNoteAuthenticationFailed {
1108 note_id: NoteId,
1109 block_num: BlockNumber,
1110 source: MerkleError,
1111 },
1112
1113 #[error(
1114 "unauthenticated note with nullifier {nullifier} was not created in the same block and no inclusion proof to authenticate it was provided"
1115 )]
1116 UnauthenticatedNoteConsumed { nullifier: Nullifier },
1117
1118 #[error("block inputs do not contain a proof of inclusion for account {0}")]
1119 MissingAccountWitness(AccountId),
1120
1121 #[error(
1122 "account {account_id} with state {state_commitment} cannot transition to any of the remaining states {}",
1123 remaining_state_commitments.iter().map(Word::to_hex).collect::<Vec<_>>().join(", ")
1124 )]
1125 InconsistentAccountStateTransition {
1126 account_id: AccountId,
1127 state_commitment: Word,
1128 remaining_state_commitments: Vec<Word>,
1129 },
1130
1131 #[error("no proof for nullifier {0} was provided")]
1132 NullifierProofMissing(Nullifier),
1133
1134 #[error("note with nullifier {0} is already spent")]
1135 NullifierSpent(Nullifier),
1136
1137 #[error("failed to merge transaction delta into account {account_id}")]
1138 AccountUpdateError {
1139 account_id: AccountId,
1140 source: Box<AccountDeltaError>,
1141 },
1142
1143 #[error("failed to track account witness")]
1144 AccountWitnessTracking { source: AccountTreeError },
1145
1146 #[error(
1147 "account tree root of the previous block header is {prev_block_account_root} but the root of the partial tree computed from account witnesses is {stale_account_root}, indicating that the witnesses are stale"
1148 )]
1149 StaleAccountTreeRoot {
1150 prev_block_account_root: Word,
1151 stale_account_root: Word,
1152 },
1153
1154 #[error("account ID prefix already exists in the tree")]
1155 AccountIdPrefixDuplicate { source: AccountTreeError },
1156
1157 #[error(
1158 "nullifier tree root of the previous block header is {prev_block_nullifier_root} but the root of the partial tree computed from nullifier witnesses is {stale_nullifier_root}, indicating that the witnesses are stale"
1159 )]
1160 StaleNullifierTreeRoot {
1161 prev_block_nullifier_root: Word,
1162 stale_nullifier_root: Word,
1163 },
1164
1165 #[error("nullifier witness has a different root than the current nullifier tree root")]
1166 NullifierWitnessRootMismatch(NullifierTreeError),
1167}
1168
1169#[derive(Debug, Error)]
1173pub enum FeeError {
1174 #[error("native asset of the chain must be a fungible faucet but was of type {account_type}")]
1175 NativeAssetIdNotFungible { account_type: AccountType },
1176}
1177
1178#[derive(Debug, Error)]
1182pub enum NullifierTreeError {
1183 #[error(
1184 "entries passed to nullifier tree contain multiple block numbers for the same nullifier"
1185 )]
1186 DuplicateNullifierBlockNumbers(#[source] MerkleError),
1187
1188 #[error("attempt to mark nullifier {0} as spent but it is already spent")]
1189 NullifierAlreadySpent(Nullifier),
1190
1191 #[error("maximum number of nullifier tree leaves exceeded")]
1192 MaxLeafEntriesExceeded(#[source] MerkleError),
1193
1194 #[error("nullifier {nullifier} is not tracked by the partial nullifier tree")]
1195 UntrackedNullifier {
1196 nullifier: Nullifier,
1197 source: MerkleError,
1198 },
1199
1200 #[error("new tree root after nullifier witness insertion does not match previous tree root")]
1201 TreeRootConflict(#[source] MerkleError),
1202
1203 #[error("failed to compute nullifier tree mutations")]
1204 ComputeMutations(#[source] MerkleError),
1205
1206 #[error("invalid nullifier block number")]
1207 InvalidNullifierBlockNumber(Word),
1208}
1209
1210#[derive(Debug, Error)]
1214pub enum AuthSchemeError {
1215 #[error("auth scheme identifier `{0}` is not valid")]
1216 InvalidAuthSchemeIdentifier(String),
1217}