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