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