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