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