1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::error::Error;
5
6use miden_assembly::Report;
7use miden_assembly::diagnostics::reporting::PrintDiagnostic;
8use miden_core::Felt;
9use miden_core::mast::MastForestError;
10use miden_crypto::merkle::MmrError;
11use miden_crypto::utils::HexParseError;
12use miden_processor::DeserializationError;
13use thiserror::Error;
14
15use super::account::AccountId;
16use super::asset::{FungibleAsset, NonFungibleAsset, TokenSymbol};
17use super::crypto::merkle::MerkleError;
18use super::note::NoteId;
19use super::{MAX_BATCHES_PER_BLOCK, MAX_OUTPUT_NOTES_PER_BATCH, Word};
20use crate::account::{
21 AccountCode,
22 AccountIdPrefix,
23 AccountStorage,
24 AccountType,
25 StorageValueName,
26 StorageValueNameError,
27 TemplateTypeError,
28};
29use crate::address::AddressType;
30use crate::batch::BatchId;
31use crate::block::BlockNumber;
32use crate::note::{NoteAssets, NoteExecutionHint, NoteTag, NoteType, Nullifier};
33use crate::transaction::TransactionId;
34use crate::{
35 ACCOUNT_UPDATE_MAX_SIZE,
36 MAX_ACCOUNTS_PER_BATCH,
37 MAX_INPUT_NOTES_PER_BATCH,
38 MAX_INPUT_NOTES_PER_TX,
39 MAX_INPUTS_PER_NOTE,
40 MAX_OUTPUT_NOTES_PER_TX,
41};
42
43#[derive(Debug, Error)]
47pub enum AccountComponentTemplateError {
48 #[error("storage slot name `{0}` is duplicate")]
49 DuplicateEntryNames(StorageValueName),
50 #[error("storage placeholder name `{0}` is duplicate")]
51 DuplicatePlaceholderName(StorageValueName),
52 #[error("slot {0} is defined multiple times")]
53 DuplicateSlot(u8),
54 #[error("storage value name is incorrect: {0}")]
55 IncorrectStorageValueName(#[source] StorageValueNameError),
56 #[error("type `{0}` is not valid for `{1}` slots")]
57 InvalidType(String, String),
58 #[error("error deserializing component metadata: {0}")]
59 MetadataDeserializationError(String),
60 #[error("multi-slot entry should contain as many values as storage slot indices")]
61 MultiSlotArityMismatch,
62 #[error("multi-slot entry slot range should occupy more than one storage slot")]
63 MultiSlotSpansOneSlot,
64 #[error("component storage slots are not contiguous ({0} is followed by {1})")]
65 NonContiguousSlots(u8, u8),
66 #[error("storage value for placeholder `{0}` was not provided in the init storage data")]
67 PlaceholderValueNotProvided(StorageValueName),
68 #[error("error converting value into expected type: ")]
69 StorageValueParsingError(#[source] TemplateTypeError),
70 #[error("storage map contains duplicate keys")]
71 StorageMapHasDuplicateKeys(#[source] Box<dyn Error + Send + Sync + 'static>),
72 #[error("component storage slots have to start at 0, but they start at {0}")]
73 StorageSlotsDoNotStartAtZero(u8),
74 #[cfg(feature = "std")]
75 #[error("error trying to deserialize from toml")]
76 TomlDeserializationError(#[source] toml::de::Error),
77 #[cfg(feature = "std")]
78 #[error("error trying to deserialize from toml")]
79 TomlSerializationError(#[source] toml::ser::Error),
80}
81
82#[derive(Debug, Error)]
86pub enum AccountError {
87 #[error("failed to deserialize account code")]
88 AccountCodeDeserializationError(#[source] DeserializationError),
89 #[error("account code does not contain an auth component")]
90 AccountCodeNoAuthComponent,
91 #[error("account code contains multiple auth components")]
92 AccountCodeMultipleAuthComponents,
93 #[error("account code must contain at least one non-auth procedure")]
94 AccountCodeNoProcedures,
95 #[error("account code contains {0} procedures but it may contain at most {max} procedures", max = AccountCode::MAX_NUM_PROCEDURES)]
96 AccountCodeTooManyProcedures(usize),
97 #[error("account procedure {0}'s storage offset {1} does not fit into u8")]
98 AccountCodeProcedureStorageOffsetTooLarge(Word, Felt),
99 #[error("account procedure {0}'s storage size {1} does not fit into u8")]
100 AccountCodeProcedureStorageSizeTooLarge(Word, Felt),
101 #[error("account procedure {0}'s final two elements must be Felt::ZERO")]
102 AccountCodeProcedureInvalidPadding(Word),
103 #[error("failed to assemble account component:\n{}", PrintDiagnostic::new(.0))]
104 AccountComponentAssemblyError(Report),
105 #[error("failed to merge components into one account code mast forest")]
106 AccountComponentMastForestMergeError(#[source] MastForestError),
107 #[error("procedure with MAST root {0} is present in multiple account components")]
108 AccountComponentDuplicateProcedureRoot(Word),
109 #[error("failed to create account component")]
110 AccountComponentTemplateInstantiationError(#[source] AccountComponentTemplateError),
111 #[error("account component contains multiple authentication procedures")]
112 AccountComponentMultipleAuthProcedures,
113 #[error("failed to update asset vault")]
114 AssetVaultUpdateError(#[source] AssetVaultError),
115 #[error("account build error: {0}")]
116 BuildError(String, #[source] Option<Box<AccountError>>),
117 #[error("failed to parse account ID from final account header")]
118 FinalAccountHeaderIdParsingFailed(#[source] AccountIdError),
119 #[error("account header data has length {actual} but it must be of length {expected}")]
120 HeaderDataIncorrectLength { actual: usize, expected: usize },
121 #[error("current account nonce {current} plus increment {increment} overflows a felt to {new}")]
122 NonceOverflow {
123 current: Felt,
124 increment: Felt,
125 new: Felt,
126 },
127 #[error(
128 "digest of the seed has {actual} trailing zeroes but must have at least {expected} trailing zeroes"
129 )]
130 SeedDigestTooFewTrailingZeros { expected: u32, actual: u32 },
131 #[error("storage map root {0} not found in the account storage")]
132 StorageMapRootNotFound(Word),
133 #[error("storage slot at index {0} is not of type map")]
134 StorageSlotNotMap(u8),
135 #[error("storage slot at index {0} is not of type value")]
136 StorageSlotNotValue(u8),
137 #[error("storage slot index is {index} but the slots length is {slots_len}")]
138 StorageIndexOutOfBounds { slots_len: u8, index: u8 },
139 #[error("number of storage slots is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)]
140 StorageTooManySlots(u64),
141 #[error("procedure storage offset + size is {0} which exceeds the maximum value of {max}",
142 max = AccountStorage::MAX_NUM_STORAGE_SLOTS
143 )]
144 StorageOffsetPlusSizeOutOfBounds(u16),
145 #[error(
146 "procedure which does not access storage (storage size = 0) has non-zero storage offset"
147 )]
148 PureProcedureWithStorageOffset,
149 #[error(
150 "account component at index {component_index} is incompatible with account of type {account_type}"
151 )]
152 UnsupportedComponentForAccountType {
153 account_type: AccountType,
154 component_index: usize,
155 },
156 #[error("{error_msg}")]
159 Other {
160 error_msg: Box<str>,
161 source: Option<Box<dyn Error + Send + Sync + 'static>>,
163 },
164}
165
166impl AccountError {
167 pub fn other(message: impl Into<String>) -> Self {
169 let message: String = message.into();
170 Self::Other { error_msg: message.into(), source: None }
171 }
172
173 pub fn other_with_source(
176 message: impl Into<String>,
177 source: impl Error + Send + Sync + 'static,
178 ) -> Self {
179 let message: String = message.into();
180 Self::Other {
181 error_msg: message.into(),
182 source: Some(Box::new(source)),
183 }
184 }
185}
186
187#[derive(Debug, Error)]
191pub enum AccountIdError {
192 #[error("failed to convert bytes into account ID prefix field element")]
193 AccountIdInvalidPrefixFieldElement(#[source] DeserializationError),
194 #[error("failed to convert bytes into account ID suffix field element")]
195 AccountIdInvalidSuffixFieldElement(#[source] DeserializationError),
196 #[error("`{0}` is not a known account storage mode")]
197 UnknownAccountStorageMode(Box<str>),
198 #[error(r#"`{0}` is not a known account type, expected one of "FungibleFaucet", "NonFungibleFaucet", "RegularAccountImmutableCode" or "RegularAccountUpdatableCode""#)]
199 UnknownAccountType(Box<str>),
200 #[error("failed to parse hex string into account ID")]
201 AccountIdHexParseError(#[source] HexParseError),
202 #[error("`{0}` is not a known account ID version")]
203 UnknownAccountIdVersion(u8),
204 #[error("most significant bit of account ID suffix must be zero")]
205 AccountIdSuffixMostSignificantBitMustBeZero,
206 #[error("least significant byte of account ID suffix must be zero")]
207 AccountIdSuffixLeastSignificantByteMustBeZero,
208}
209
210#[derive(Debug, Error)]
214pub enum AccountTreeError {
215 #[error(
216 "account tree contains multiple account IDs that share the same prefix {duplicate_prefix}"
217 )]
218 DuplicateIdPrefix { duplicate_prefix: AccountIdPrefix },
219 #[error(
220 "entries passed to account tree contain multiple state commitments for the same account ID prefix {prefix}"
221 )]
222 DuplicateStateCommitments { prefix: AccountIdPrefix },
223 #[error("untracked account ID {id} used in partial account tree")]
224 UntrackedAccountId { id: AccountId, source: MerkleError },
225 #[error("new tree root after account witness insertion does not match previous tree root")]
226 TreeRootConflict(#[source] MerkleError),
227 #[error("failed to apply mutations to account tree")]
228 ApplyMutations(#[source] MerkleError),
229 #[error("smt leaf's index is not a valid account ID prefix")]
230 InvalidAccountIdPrefix(#[source] AccountIdError),
231 #[error("account witness merkle path depth {0} does not match AccountTree::DEPTH")]
232 WitnessMerklePathDepthDoesNotMatchAccountTreeDepth(usize),
233}
234
235#[derive(Debug, Error)]
239pub enum AddressError {
240 #[error("tag length {0} should be {expected} bits for network accounts", expected = crate::note::NoteTag::DEFAULT_NETWORK_TAG_LENGTH)]
241 CustomTagLengthNotAllowedForNetworkAccounts(u8),
242 #[error("tag length {0} is too large, must be less than or equal to {max}", max = crate::note::NoteTag::MAX_LOCAL_TAG_LENGTH)]
243 TagLengthTooLarge(u8),
244 #[error("unknown address interface `{0}`")]
245 UnknownAddressInterface(u16),
246 #[error("failed to decode account ID")]
247 AccountIdDecodeError(#[source] AccountIdError),
248 #[error("failed to decode bech32 string into an address")]
249 Bech32DecodeError(#[source] Bech32Error),
250}
251
252#[derive(Debug, Error)]
256pub enum Bech32Error {
257 #[error(transparent)]
258 DecodeError(Box<dyn Error + Send + Sync + 'static>),
259 #[error("found unknown address type {0} which is not the expected {account_addr} account ID address type",
260 account_addr = AddressType::AccountId as u8
261 )]
262 UnknownAddressType(u8),
263 #[error("expected bech32 data to be of length {expected} but it was of length {actual}")]
264 InvalidDataLength { expected: usize, actual: usize },
265}
266
267#[derive(Debug, Error)]
271pub enum NetworkIdError {
272 #[error("failed to parse string into a network ID")]
273 NetworkIdParseError(#[source] Box<dyn Error + Send + Sync + 'static>),
274}
275
276#[derive(Debug, Error)]
280pub enum AccountDeltaError {
281 #[error(
282 "storage slot index {slot_index} is greater than or equal to the number of slots {num_slots}"
283 )]
284 StorageSlotIndexOutOfBounds { slot_index: u8, num_slots: u8 },
285 #[error("storage slot {0} was updated as a value and as a map")]
286 StorageSlotUsedAsDifferentTypes(u8),
287 #[error("non fungible vault can neither be added nor removed twice")]
288 DuplicateNonFungibleVaultUpdate(NonFungibleAsset),
289 #[error(
290 "fungible asset issued by faucet {faucet_id} has delta {delta} which overflows when added to current value {current}"
291 )]
292 FungibleAssetDeltaOverflow {
293 faucet_id: AccountId,
294 current: i64,
295 delta: i64,
296 },
297 #[error(
298 "account update of type `{left_update_type}` cannot be merged with account update of type `{right_update_type}`"
299 )]
300 IncompatibleAccountUpdates {
301 left_update_type: &'static str,
302 right_update_type: &'static str,
303 },
304 #[error("account delta could not be applied to account {account_id}")]
305 AccountDeltaApplicationFailed {
306 account_id: AccountId,
307 source: AccountError,
308 },
309 #[error("non-empty account storage or vault delta with zero nonce delta is not allowed")]
310 NonEmptyStorageOrVaultDeltaWithZeroNonceDelta,
311 #[error(
312 "account nonce increment {current} plus the other nonce increment {increment} overflows a felt to {new}"
313 )]
314 NonceIncrementOverflow {
315 current: Felt,
316 increment: Felt,
317 new: Felt,
318 },
319 #[error("account ID {0} in fungible asset delta is not of type fungible faucet")]
320 NotAFungibleFaucetId(AccountId),
321}
322
323#[derive(Debug, Error)]
327pub enum StorageMapError {
328 #[error("map entries contain key {key} twice with values {value0} and {value1}")]
329 DuplicateKey { key: Word, value0: Word, value1: Word },
330}
331
332#[derive(Debug, Error)]
336pub enum BatchAccountUpdateError {
337 #[error(
338 "account update for account {expected_account_id} cannot be merged with update from transaction {transaction} which was executed against account {actual_account_id}"
339 )]
340 AccountUpdateIdMismatch {
341 transaction: TransactionId,
342 expected_account_id: AccountId,
343 actual_account_id: AccountId,
344 },
345 #[error(
346 "final state commitment in account update from transaction {0} does not match initial state of current update"
347 )]
348 AccountUpdateInitialStateMismatch(TransactionId),
349 #[error("failed to merge account delta from transaction {0}")]
350 TransactionUpdateMergeError(TransactionId, #[source] Box<AccountDeltaError>),
351}
352
353#[derive(Debug, Error)]
357pub enum AssetError {
358 #[error(
359 "fungible asset amount {0} exceeds the max allowed amount of {max_amount}",
360 max_amount = FungibleAsset::MAX_AMOUNT
361 )]
362 FungibleAssetAmountTooBig(u64),
363 #[error("subtracting {subtrahend} from fungible asset amount {minuend} would underflow")]
364 FungibleAssetAmountNotSufficient { minuend: u64, subtrahend: u64 },
365 #[error("fungible asset word {0} does not contain expected ZERO at word index 1")]
366 FungibleAssetExpectedZero(Word),
367 #[error(
368 "cannot add fungible asset with issuer {other_issuer} to fungible asset with issuer {original_issuer}"
369 )]
370 FungibleAssetInconsistentFaucetIds {
371 original_issuer: AccountId,
372 other_issuer: AccountId,
373 },
374 #[error("faucet account ID in asset is invalid")]
375 InvalidFaucetAccountId(#[source] Box<dyn Error + Send + Sync + 'static>),
376 #[error(
377 "faucet id {0} of type {id_type} must be of type {expected_ty} for fungible assets",
378 id_type = .0.account_type(),
379 expected_ty = AccountType::FungibleFaucet
380 )]
381 FungibleFaucetIdTypeMismatch(AccountId),
382 #[error(
383 "faucet id {0} of type {id_type} must be of type {expected_ty} for non fungible assets",
384 id_type = .0.account_type(),
385 expected_ty = AccountType::NonFungibleFaucet
386 )]
387 NonFungibleFaucetIdTypeMismatch(AccountIdPrefix),
388}
389
390#[derive(Debug, Error)]
394pub enum TokenSymbolError {
395 #[error("token symbol value {0} cannot exceed {max}", max = TokenSymbol::MAX_ENCODED_VALUE)]
396 ValueTooLarge(u64),
397 #[error("token symbol should have length between 1 and 6 characters, but {0} was provided")]
398 InvalidLength(usize),
399 #[error("token symbol `{0}` contains characters that are not uppercase ASCII")]
400 InvalidCharacter(String),
401 #[error("token symbol data left after decoding the specified number of characters")]
402 DataNotFullyDecoded,
403}
404
405#[derive(Debug, Error)]
409pub enum AssetVaultError {
410 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
411 AddFungibleAssetBalanceError(#[source] AssetError),
412 #[error("provided assets contain duplicates")]
413 DuplicateAsset(#[source] MerkleError),
414 #[error("non fungible asset {0} already exists in the vault")]
415 DuplicateNonFungibleAsset(NonFungibleAsset),
416 #[error("fungible asset {0} does not exist in the vault")]
417 FungibleAssetNotFound(FungibleAsset),
418 #[error("faucet id {0} is not a fungible faucet id")]
419 NotAFungibleFaucetId(AccountId),
420 #[error("non fungible asset {0} does not exist in the vault")]
421 NonFungibleAssetNotFound(NonFungibleAsset),
422 #[error("subtracting fungible asset amounts would underflow")]
423 SubtractFungibleAssetBalanceError(#[source] AssetError),
424}
425
426#[derive(Debug, Error)]
430pub enum PartialAssetVaultError {
431 #[error("provided SMT entry {entry} is not a valid asset")]
432 InvalidAssetInSmt { entry: Word, source: AssetError },
433 #[error("expected asset vault key to be {expected} but it was {actual}")]
434 VaultKeyMismatch { expected: Word, actual: Word },
435 #[error("failed to add asset proof")]
436 FailedToAddProof(#[source] MerkleError),
437}
438
439#[derive(Debug, Error)]
443pub enum NoteError {
444 #[error("note tag length {0} exceeds the maximum of {max}", max = NoteTag::MAX_LOCAL_TAG_LENGTH)]
445 NoteTagLengthTooLarge(u8),
446 #[error("duplicate fungible asset from issuer {0} in note")]
447 DuplicateFungibleAsset(AccountId),
448 #[error("duplicate non fungible asset {0} in note")]
449 DuplicateNonFungibleAsset(NonFungibleAsset),
450 #[error("note type {0} is inconsistent with note tag {1}")]
451 InconsistentNoteTag(NoteType, u64),
452 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
453 AddFungibleAssetBalanceError(#[source] AssetError),
454 #[error("note sender is not a valid account ID")]
455 NoteSenderInvalidAccountId(#[source] AccountIdError),
456 #[error("note tag use case {0} must be less than 2^{exp}", exp = NoteTag::MAX_USE_CASE_ID_EXPONENT)]
457 NoteTagUseCaseTooLarge(u16),
458 #[error(
459 "note execution hint tag {0} must be in range {from}..={to}",
460 from = NoteExecutionHint::NONE_TAG,
461 to = NoteExecutionHint::ON_BLOCK_SLOT_TAG,
462 )]
463 NoteExecutionHintTagOutOfRange(u8),
464 #[error("note execution hint after block variant cannot contain u32::MAX")]
465 NoteExecutionHintAfterBlockCannotBeU32Max,
466 #[error("invalid note execution hint payload {1} for tag {0}")]
467 InvalidNoteExecutionHintPayload(u8, u32),
468 #[error("note type {0} does not match any of the valid note types {public}, {private} or {encrypted}",
469 public = NoteType::Public,
470 private = NoteType::Private,
471 encrypted = NoteType::Encrypted,
472 )]
473 UnknownNoteType(Box<str>),
474 #[error("note location index {node_index_in_block} is out of bounds 0..={highest_index}")]
475 NoteLocationIndexOutOfBounds {
476 node_index_in_block: u16,
477 highest_index: usize,
478 },
479 #[error("note network execution requires a public note but note is of type {0}")]
480 NetworkExecutionRequiresPublicNote(NoteType),
481 #[error("failed to assemble note script:\n{}", PrintDiagnostic::new(.0))]
482 NoteScriptAssemblyError(Report),
483 #[error("failed to deserialize note script")]
484 NoteScriptDeserializationError(#[source] DeserializationError),
485 #[error("note contains {0} assets which exceeds the maximum of {max}", max = NoteAssets::MAX_NUM_ASSETS)]
486 TooManyAssets(usize),
487 #[error("note contains {0} inputs which exceeds the maximum of {max}", max = MAX_INPUTS_PER_NOTE)]
488 TooManyInputs(usize),
489 #[error("note tag requires a public note but the note is of type {0}")]
490 PublicNoteRequired(NoteType),
491 #[error("{error_msg}")]
492 Other {
493 error_msg: Box<str>,
494 source: Option<Box<dyn Error + Send + Sync + 'static>>,
496 },
497}
498
499impl NoteError {
500 pub fn other(message: impl Into<String>) -> Self {
502 let message: String = message.into();
503 Self::Other { error_msg: message.into(), source: None }
504 }
505
506 pub fn other_with_source(
509 message: impl Into<String>,
510 source: impl Error + Send + Sync + 'static,
511 ) -> Self {
512 let message: String = message.into();
513 Self::Other {
514 error_msg: message.into(),
515 source: Some(Box::new(source)),
516 }
517 }
518}
519
520#[derive(Debug, Error)]
524pub enum PartialBlockchainError {
525 #[error(
526 "block num {block_num} exceeds chain length {chain_length} implied by the partial blockchain"
527 )]
528 BlockNumTooBig {
529 chain_length: usize,
530 block_num: BlockNumber,
531 },
532
533 #[error("duplicate block {block_num} in partial blockchain")]
534 DuplicateBlock { block_num: BlockNumber },
535
536 #[error("partial blockchain does not track authentication paths for block {block_num}")]
537 UntrackedBlock { block_num: BlockNumber },
538
539 #[error(
540 "provided block header with number {block_num} and commitment {block_commitment} is not tracked by partial MMR"
541 )]
542 BlockHeaderCommitmentMismatch {
543 block_num: BlockNumber,
544 block_commitment: Word,
545 source: MmrError,
546 },
547}
548
549impl PartialBlockchainError {
550 pub fn block_num_too_big(chain_length: usize, block_num: BlockNumber) -> Self {
551 Self::BlockNumTooBig { chain_length, block_num }
552 }
553
554 pub fn duplicate_block(block_num: BlockNumber) -> Self {
555 Self::DuplicateBlock { block_num }
556 }
557
558 pub fn untracked_block(block_num: BlockNumber) -> Self {
559 Self::UntrackedBlock { block_num }
560 }
561}
562
563#[derive(Debug, Error)]
567pub enum TransactionScriptError {
568 #[error("failed to assemble transaction script:\n{}", PrintDiagnostic::new(.0))]
569 AssemblyError(Report),
570}
571
572#[derive(Debug, Error)]
576pub enum TransactionInputError {
577 #[error("account seed must be provided for new accounts")]
578 AccountSeedNotProvidedForNewAccount,
579 #[error("account seed must not be provided for existing accounts")]
580 AccountSeedProvidedForExistingAccount,
581 #[error("transaction input note with nullifier {0} is a duplicate")]
582 DuplicateInputNote(Nullifier),
583 #[error(
584 "ID {expected} of the new account does not match the ID {actual} computed from the provided seed"
585 )]
586 InconsistentAccountSeed { expected: AccountId, actual: AccountId },
587 #[error("partial blockchain has length {actual} which does not match block number {expected}")]
588 InconsistentChainLength {
589 expected: BlockNumber,
590 actual: BlockNumber,
591 },
592 #[error(
593 "partial blockchain has commitment {actual} which does not match the block header's chain commitment {expected}"
594 )]
595 InconsistentChainCommitment { expected: Word, actual: Word },
596 #[error("block in which input note with id {0} was created is not in partial blockchain")]
597 InputNoteBlockNotInPartialBlockchain(NoteId),
598 #[error("input note with id {0} was not created in block {1}")]
599 InputNoteNotInBlock(NoteId, BlockNumber),
600 #[error("account ID computed from seed is invalid")]
601 InvalidAccountIdSeed(#[source] AccountIdError),
602 #[error(
603 "total number of input notes is {0} which exceeds the maximum of {MAX_INPUT_NOTES_PER_TX}"
604 )]
605 TooManyInputNotes(usize),
606}
607
608#[derive(Debug, Error)]
612pub enum TransactionOutputError {
613 #[error("transaction output note with id {0} is a duplicate")]
614 DuplicateOutputNote(NoteId),
615 #[error("final account commitment is not in the advice map")]
616 FinalAccountCommitmentMissingInAdviceMap,
617 #[error("fee asset is not a fungible asset")]
618 FeeAssetNotFungibleAsset(#[source] AssetError),
619 #[error("failed to parse final account header")]
620 FinalAccountHeaderParseFailure(#[source] AccountError),
621 #[error(
622 "output notes commitment {expected} from kernel does not match computed commitment {actual}"
623 )]
624 OutputNotesCommitmentInconsistent { expected: Word, actual: Word },
625 #[error("transaction kernel output stack is invalid: {0}")]
626 OutputStackInvalid(String),
627 #[error(
628 "total number of output notes is {0} which exceeds the maximum of {MAX_OUTPUT_NOTES_PER_TX}"
629 )]
630 TooManyOutputNotes(usize),
631 #[error("failed to process account update commitment: {0}")]
632 AccountUpdateCommitment(Box<str>),
633}
634
635#[derive(Debug, Error)]
639pub enum ProvenTransactionError {
640 #[error(
641 "proven transaction's final account commitment {tx_final_commitment} and account details commitment {details_commitment} must match"
642 )]
643 AccountFinalCommitmentMismatch {
644 tx_final_commitment: Word,
645 details_commitment: Word,
646 },
647 #[error(
648 "proven transaction's final account ID {tx_account_id} and account details id {details_account_id} must match"
649 )]
650 AccountIdMismatch {
651 tx_account_id: AccountId,
652 details_account_id: AccountId,
653 },
654 #[error("failed to construct input notes for proven transaction")]
655 InputNotesError(TransactionInputError),
656 #[error("private account {0} should not have account details")]
657 PrivateAccountWithDetails(AccountId),
658 #[error("on-chain account {0} is missing its account details")]
659 OnChainAccountMissingDetails(AccountId),
660 #[error("new on-chain account {0} is missing its account details")]
661 NewOnChainAccountRequiresFullDetails(AccountId),
662 #[error(
663 "existing on-chain account {0} should only provide delta updates instead of full details"
664 )]
665 ExistingOnChainAccountRequiresDeltaDetails(AccountId),
666 #[error("failed to construct output notes for proven transaction")]
667 OutputNotesError(TransactionOutputError),
668 #[error(
669 "account update of size {update_size} for account {account_id} exceeds maximum update size of {ACCOUNT_UPDATE_MAX_SIZE}"
670 )]
671 AccountUpdateSizeLimitExceeded {
672 account_id: AccountId,
673 update_size: usize,
674 },
675 #[error("proven transaction neither changed the account state, nor consumed any notes")]
676 EmptyTransaction,
677}
678
679#[derive(Debug, Error)]
683pub enum ProposedBatchError {
684 #[error(
685 "transaction batch has {0} input notes but at most {MAX_INPUT_NOTES_PER_BATCH} are allowed"
686 )]
687 TooManyInputNotes(usize),
688
689 #[error(
690 "transaction batch has {0} output notes but at most {MAX_OUTPUT_NOTES_PER_BATCH} are allowed"
691 )]
692 TooManyOutputNotes(usize),
693
694 #[error(
695 "transaction batch has {0} account updates but at most {MAX_ACCOUNTS_PER_BATCH} are allowed"
696 )]
697 TooManyAccountUpdates(usize),
698
699 #[error(
700 "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}"
701 )]
702 ExpiredTransaction {
703 transaction_id: TransactionId,
704 transaction_expiration_num: BlockNumber,
705 reference_block_num: BlockNumber,
706 },
707
708 #[error("transaction batch must contain at least one transaction")]
709 EmptyTransactionBatch,
710
711 #[error("transaction {transaction_id} appears twice in the proposed batch input")]
712 DuplicateTransaction { transaction_id: TransactionId },
713
714 #[error(
715 "transaction {second_transaction_id} consumes the note with nullifier {note_nullifier} that is also consumed by another transaction {first_transaction_id} in the batch"
716 )]
717 DuplicateInputNote {
718 note_nullifier: Nullifier,
719 first_transaction_id: TransactionId,
720 second_transaction_id: TransactionId,
721 },
722
723 #[error(
724 "transaction {second_transaction_id} creates the note with id {note_id} that is also created by another transaction {first_transaction_id} in the batch"
725 )]
726 DuplicateOutputNote {
727 note_id: NoteId,
728 first_transaction_id: TransactionId,
729 second_transaction_id: TransactionId,
730 },
731
732 #[error(
733 "note commitment mismatch for note {id}: (input: {input_commitment}, output: {output_commitment})"
734 )]
735 NoteCommitmentMismatch {
736 id: NoteId,
737 input_commitment: Word,
738 output_commitment: Word,
739 },
740
741 #[error("failed to merge transaction delta into account {account_id}")]
742 AccountUpdateError {
743 account_id: AccountId,
744 source: BatchAccountUpdateError,
745 },
746
747 #[error(
748 "unable to prove unauthenticated note inclusion because block {block_number} in which note with id {note_id} was created is not in partial blockchain"
749 )]
750 UnauthenticatedInputNoteBlockNotInPartialBlockchain {
751 block_number: BlockNumber,
752 note_id: NoteId,
753 },
754
755 #[error(
756 "unable to prove unauthenticated note inclusion of note {note_id} in block {block_num}"
757 )]
758 UnauthenticatedNoteAuthenticationFailed {
759 note_id: NoteId,
760 block_num: BlockNumber,
761 source: MerkleError,
762 },
763
764 #[error("partial blockchain has length {actual} which does not match block number {expected}")]
765 InconsistentChainLength {
766 expected: BlockNumber,
767 actual: BlockNumber,
768 },
769
770 #[error(
771 "partial blockchain has root {actual} which does not match block header's root {expected}"
772 )]
773 InconsistentChainRoot { expected: Word, actual: Word },
774
775 #[error(
776 "block {block_reference} referenced by transaction {transaction_id} is not in the partial blockchain"
777 )]
778 MissingTransactionBlockReference {
779 block_reference: Word,
780 transaction_id: TransactionId,
781 },
782}
783
784#[derive(Debug, Error)]
788pub enum ProvenBatchError {
789 #[error("failed to verify transaction {transaction_id} in transaction batch")]
790 TransactionVerificationFailed {
791 transaction_id: TransactionId,
792 source: Box<dyn Error + Send + Sync + 'static>,
793 },
794 #[error(
795 "batch expiration block number {batch_expiration_block_num} is not greater than the reference block number {reference_block_num}"
796 )]
797 InvalidBatchExpirationBlockNum {
798 batch_expiration_block_num: BlockNumber,
799 reference_block_num: BlockNumber,
800 },
801}
802
803#[derive(Debug, Error)]
807pub enum ProposedBlockError {
808 #[error("block must contain at least one transaction batch")]
809 EmptyBlock,
810
811 #[error("block must contain at most {MAX_BATCHES_PER_BLOCK} transaction batches")]
812 TooManyBatches,
813
814 #[error(
815 "batch {batch_id} expired at block {batch_expiration_block_num} but the current block number is {current_block_num}"
816 )]
817 ExpiredBatch {
818 batch_id: BatchId,
819 batch_expiration_block_num: BlockNumber,
820 current_block_num: BlockNumber,
821 },
822
823 #[error("batch {batch_id} appears twice in the block inputs")]
824 DuplicateBatch { batch_id: BatchId },
825
826 #[error(
827 "batch {second_batch_id} consumes the note with nullifier {note_nullifier} that is also consumed by another batch {first_batch_id} in the block"
828 )]
829 DuplicateInputNote {
830 note_nullifier: Nullifier,
831 first_batch_id: BatchId,
832 second_batch_id: BatchId,
833 },
834
835 #[error(
836 "batch {second_batch_id} creates the note with ID {note_id} that is also created by another batch {first_batch_id} in the block"
837 )]
838 DuplicateOutputNote {
839 note_id: NoteId,
840 first_batch_id: BatchId,
841 second_batch_id: BatchId,
842 },
843
844 #[error(
845 "timestamp {provided_timestamp} does not increase monotonically compared to timestamp {previous_timestamp} from the previous block header"
846 )]
847 TimestampDoesNotIncreaseMonotonically {
848 provided_timestamp: u32,
849 previous_timestamp: u32,
850 },
851
852 #[error(
853 "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}"
854 )]
855 ConflictingBatchesUpdateSameAccount {
856 account_id: AccountId,
857 initial_state_commitment: Word,
858 first_batch_id: BatchId,
859 second_batch_id: BatchId,
860 },
861
862 #[error(
863 "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"
864 )]
865 ChainLengthNotEqualToPreviousBlockNumber {
866 chain_length: BlockNumber,
867 prev_block_num: BlockNumber,
868 },
869
870 #[error(
871 "partial blockchain has commitment {chain_commitment} which does not match the chain commitment {prev_block_chain_commitment} of the previous block {prev_block_num}"
872 )]
873 ChainRootNotEqualToPreviousBlockChainCommitment {
874 chain_commitment: Word,
875 prev_block_chain_commitment: Word,
876 prev_block_num: BlockNumber,
877 },
878
879 #[error(
880 "partial blockchain is missing block {reference_block_num} referenced by batch {batch_id} in the block"
881 )]
882 BatchReferenceBlockMissingFromChain {
883 reference_block_num: BlockNumber,
884 batch_id: BatchId,
885 },
886
887 #[error(
888 "note commitment mismatch for note {id}: (input: {input_commitment}, output: {output_commitment})"
889 )]
890 NoteCommitmentMismatch {
891 id: NoteId,
892 input_commitment: Word,
893 output_commitment: Word,
894 },
895
896 #[error(
897 "failed 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 "failed 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(
914 "unauthenticated note with nullifier {nullifier} was not created in the same block and no inclusion proof to authenticate it was provided"
915 )]
916 UnauthenticatedNoteConsumed { nullifier: Nullifier },
917
918 #[error("block inputs do not contain a proof of inclusion for account {0}")]
919 MissingAccountWitness(AccountId),
920
921 #[error(
922 "account {account_id} with state {state_commitment} cannot transition to any of the remaining states {}",
923 remaining_state_commitments.iter().map(Word::to_hex).collect::<Vec<_>>().join(", ")
924 )]
925 InconsistentAccountStateTransition {
926 account_id: AccountId,
927 state_commitment: Word,
928 remaining_state_commitments: Vec<Word>,
929 },
930
931 #[error("no proof for nullifier {0} was provided")]
932 NullifierProofMissing(Nullifier),
933
934 #[error("note with nullifier {0} is already spent")]
935 NullifierSpent(Nullifier),
936
937 #[error("failed to merge transaction delta into account {account_id}")]
938 AccountUpdateError {
939 account_id: AccountId,
940 source: Box<AccountDeltaError>,
941 },
942}
943
944#[derive(Debug, Error)]
948pub enum FeeError {
949 #[error("native asset of the chain must be a fungible faucet but was of type {account_type}")]
950 NativeAssetIdNotFungible { account_type: AccountType },
951}
952
953#[derive(Debug, Error)]
957pub enum NullifierTreeError {
958 #[error(
959 "entries passed to nullifier tree contain multiple block numbers for the same nullifier"
960 )]
961 DuplicateNullifierBlockNumbers(#[source] MerkleError),
962
963 #[error("attempt to mark nullifier {0} as spent but it is already spent")]
964 NullifierAlreadySpent(Nullifier),
965
966 #[error("nullifier {nullifier} is not tracked by the partial nullifier tree")]
967 UntrackedNullifier {
968 nullifier: Nullifier,
969 source: MerkleError,
970 },
971
972 #[error("new tree root after nullifier witness insertion does not match previous tree root")]
973 TreeRootConflict(#[source] MerkleError),
974}