1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::error::Error;
5
6use miden_assembly::Report;
7use miden_assembly::diagnostics::reporting::PrintDiagnostic;
8use miden_core::mast::MastForestError;
9use miden_crypto::merkle::mmr::MmrError;
10use miden_crypto::merkle::smt::{SmtLeafError, SmtProofError};
11use miden_crypto::utils::HexParseError;
12use miden_processor::ExecutionError;
13use miden_verifier::VerificationError;
14use thiserror::Error;
15
16use super::account::{AccountId, RoleSymbol};
17use super::asset::{Asset, AssetComposition, AssetId, FungibleAsset, TokenSymbol};
18use super::crypto::merkle::MerkleError;
19use super::note::NoteId;
20use super::{
21 MAX_ACCOUNTS_PER_BLOCK,
22 MAX_BATCHES_PER_BLOCK,
23 MAX_INPUT_NOTES_PER_BLOCK,
24 MAX_OUTPUT_NOTES_PER_BATCH,
25 Word,
26};
27use crate::account::component::{SchemaTypeError, StorageValueName, StorageValueNameError};
28use crate::account::delta::AssetDeltaOperation;
29use crate::account::{
30 AccountCode,
31 AccountHeader,
32 AccountIdPrefix,
33 AccountProcedureRoot,
34 AccountStorage,
35 AccountVaultDelta,
36 StorageMapKey,
37 StorageSlotId,
38 StorageSlotName,
39};
40use crate::address::AddressType;
41use crate::asset::AssetClass;
42use crate::batch::BatchId;
43use crate::block::{BlockNumber, ValidatorConfig};
44use crate::note::{
45 NoteAssets,
46 NoteAttachment,
47 NoteAttachmentScheme,
48 NoteAttachments,
49 NoteTag,
50 NoteType,
51 Nullifier,
52};
53use crate::protocol_config::KernelConfig;
54use crate::script::MastForestScriptError;
55use crate::transaction::TransactionId;
56use crate::utils::serde::DeserializationError;
57use crate::vm::EventId;
58use crate::{
59 ACCOUNT_UPDATE_MAX_SIZE,
60 Felt,
61 MAX_ACCOUNTS_PER_BATCH,
62 MAX_INPUT_NOTES_PER_BATCH,
63 MAX_INPUT_NOTES_PER_TX,
64 MAX_NOTE_STORAGE_ITEMS,
65 MAX_OUTPUT_NOTES_PER_TX,
66 NOTE_MAX_SIZE,
67};
68
69#[cfg(any(feature = "testing", test))]
70mod masm_error;
71#[cfg(any(feature = "testing", test))]
72pub use masm_error::MasmError;
73
74#[cfg(any(feature = "testing", test))]
76pub mod tx_kernel {
77 include!(concat!(env!("OUT_DIR"), "/tx_kernel_errors.rs"));
78}
79
80#[cfg(any(feature = "testing", test))]
82pub mod protocol {
83 include!(concat!(env!("OUT_DIR"), "/protocol_errors.rs"));
84}
85
86#[derive(Debug, Error)]
90pub enum ComponentMetadataError {
91 #[error("storage slot name `{0}` is duplicate")]
92 DuplicateSlotName(StorageSlotName),
93 #[error("storage init value name `{0}` is duplicate")]
94 DuplicateInitValueName(StorageValueName),
95 #[error("storage value name is incorrect: {0}")]
96 IncorrectStorageValueName(#[source] StorageValueNameError),
97 #[error("invalid storage schema: {0}")]
98 InvalidSchema(String),
99 #[error("type `{0}` is not valid for `{1}` slots")]
100 InvalidType(String, String),
101 #[error("error deserializing component metadata: {0}")]
102 MetadataDeserializationError(String),
103 #[error("init storage value `{0}` was not provided")]
104 InitValueNotProvided(StorageValueName),
105 #[error("invalid init storage value for `{0}`: {1}")]
106 InvalidInitStorageValue(StorageValueName, String),
107 #[error("error converting value into expected type: {0}")]
108 StorageValueParsingError(#[source] SchemaTypeError),
109 #[error("storage map contains duplicate keys")]
110 StorageMapHasDuplicateKeys(#[source] Box<dyn Error + Send + Sync + 'static>),
111 #[cfg(feature = "std")]
112 #[error("error trying to deserialize from toml")]
113 TomlDeserializationError(#[source] toml::de::Error),
114 #[cfg(feature = "std")]
115 #[error("error trying to deserialize from toml")]
116 TomlSerializationError(#[source] toml::ser::Error),
117}
118
119#[derive(Debug, Error)]
123pub enum AccountError {
124 #[error("account code does not contain an auth component")]
125 AccountCodeNoAuthComponent,
126 #[error("account code contains multiple auth components")]
127 AccountCodeMultipleAuthComponents,
128 #[error("account code must contain at least one non-auth procedure")]
129 AccountCodeNoProcedures,
130 #[error("account procedure {0} is not contained in the provided mast forest")]
131 AccountCodeProcedureNotInMastForest(AccountProcedureRoot),
132 #[error("account code contains {0} procedures but it may contain at most {max} procedures", max = AccountCode::MAX_NUM_PROCEDURES)]
133 AccountCodeTooManyProcedures(usize),
134 #[error("account code contains a duplicate procedure with root {0}")]
135 AccountCodeDuplicateProcedureRoot(AccountProcedureRoot),
136 #[error(
137 "account code procedures following the authentication procedure are not sorted in ascending order"
138 )]
139 AccountCodeProceduresUnsorted,
140 #[error("failed to assemble account component:\n{}", PrintDiagnostic::new(.0))]
141 AccountComponentAssemblyError(Report),
142 #[error("failed to merge components into one account code mast forest")]
143 AccountComponentMastForestMergeError(#[source] MastForestError),
144 #[error("account component contains multiple authentication procedures")]
145 AccountComponentMultipleAuthProcedures,
146 #[error(
147 "storage of account {0} contains an asset callback slot but its asset callback flag is disabled, so the callback would never be invoked"
148 )]
149 AssetCallbackSlotWithDisabledFlag(AccountId),
150 #[error("failed to update asset vault")]
151 AssetVaultUpdateError(#[source] AssetVaultError),
152 #[error("account build error: {0}")]
153 BuildError(String, #[source] Option<Box<AccountError>>),
154 #[error("failed to parse account ID from final account header")]
155 FinalAccountHeaderIdParsingFailed(#[source] AccountIdError),
156 #[error("account header data has length {actual} but it must be of length {expected}",
157 expected = AccountHeader::NUM_ELEMENTS
158 )]
159 UnexpectedHeaderLength { actual: usize },
160 #[error("account has an unsupported version {0}")]
161 UnsupportedAccountVersion(u64),
162 #[error("final nonce {new} is not strictly greater than current account nonce {current}")]
163 NonceMustIncrease { current: Felt, new: Felt },
164 #[error(
165 "digest of the seed has {actual} trailing zeroes but must have at least {expected} trailing zeroes"
166 )]
167 SeedDigestTooFewTrailingZeros { expected: u32, actual: u32 },
168 #[error("account ID {actual} computed from seed does not match ID {expected} on account")]
169 AccountIdSeedMismatch { actual: AccountId, expected: AccountId },
170 #[error("account ID seed was provided for an existing account")]
171 ExistingAccountWithSeed,
172 #[error("account ID seed was not provided for a new account")]
173 NewAccountMissingSeed,
174 #[error(
175 "an account with a seed cannot be converted into a delta since it represents an unregistered account"
176 )]
177 DeltaFromAccountWithSeed,
178 #[error(
179 "an account with a seed cannot be converted into a patch since it represents an unregistered account"
180 )]
181 PatchFromAccountWithSeed,
182 #[error("seed converts to an invalid account ID")]
183 SeedConvertsToInvalidAccountId(#[source] AccountIdError),
184 #[error("storage map root {0} not found in the account storage")]
185 StorageMapRootNotFound(Word),
186 #[error("storage slot {0} is not of type map")]
187 StorageSlotNotMap(StorageSlotName),
188 #[error("storage slot {0} is not of type value")]
189 StorageSlotNotValue(StorageSlotName),
190 #[error("storage slot name {0} is assigned to more than one slot")]
191 DuplicateStorageSlotName(StorageSlotName),
192 #[error("storage does not contain a slot with name {slot_name}")]
193 StorageSlotNameNotFound { slot_name: StorageSlotName },
194 #[error("storage does not contain a slot with ID {slot_id}")]
195 StorageSlotIdNotFound { slot_id: StorageSlotId },
196 #[error("storage slots must be sorted by slot ID")]
197 UnsortedStorageSlots,
198 #[error("reserved element of a storage slot must be zero but was {0}")]
199 StorageSlotReservedElementNotZero(Felt),
200 #[error("number of storage slots is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)]
201 StorageTooManySlots(u64),
202 #[error(
203 "failed to apply full state patch to existing account; full state patches can be converted to accounts directly"
204 )]
205 ApplyFullStatePatchToAccount,
206 #[error("patch is for account ID {patch_id} but is being applied to account {account_id}")]
207 PatchAccountIdMismatch {
208 account_id: AccountId,
209 patch_id: AccountId,
210 },
211 #[error("only account deltas representing a full account can be converted to a full account")]
212 PartialStateDeltaToAccount,
213 #[error("assets cannot be removed from a new account with an empty asset vault")]
214 AssetsRemovedFromNewAccount,
215 #[error("only account patches representing a full account can be converted to a full account")]
216 PartialStatePatchToAccount,
217 #[error("maximum number of storage map leaves exceeded")]
218 MaxNumStorageMapLeavesExceeded(#[source] MerkleError),
219 #[error("unknown storage patch operation tag {0}")]
220 UnknownStoragePatchOperation(u8),
221 #[error("{error_msg}")]
224 Other {
225 error_msg: Box<str>,
226 source: Option<Box<dyn Error + Send + Sync + 'static>>,
228 },
229}
230
231impl AccountError {
232 pub fn other(message: impl Into<String>) -> Self {
234 let message: String = message.into();
235 Self::Other { error_msg: message.into(), source: None }
236 }
237
238 pub fn other_with_source(
241 message: impl Into<String>,
242 source: impl Error + Send + Sync + 'static,
243 ) -> Self {
244 let message: String = message.into();
245 Self::Other {
246 error_msg: message.into(),
247 source: Some(Box::new(source)),
248 }
249 }
250}
251
252#[derive(Debug)]
254pub(crate) enum AccountUpdateDetailsValidationError {
255 PrivateAccountWithDetails(AccountId),
256 PublicStateAccountMissingDetails(AccountId),
257 AccountIdMismatch {
258 account_id: AccountId,
259 patch_account_id: AccountId,
260 },
261}
262
263#[derive(Debug)]
265pub(crate) struct AccountUpdateSizeValidationError {
266 pub(crate) account_id: AccountId,
267 pub(crate) update_size: usize,
268}
269
270#[derive(Debug)]
272pub(crate) enum NewPublicAccountValidationError {
273 RequiresFullStatePatch {
274 id: AccountId,
275 source: AccountError,
276 },
277 FinalCommitmentMismatch {
278 final_state_commitment: Word,
279 account_commitment: Word,
280 },
281}
282
283#[derive(Debug, Error)]
287pub enum AccountIdError {
288 #[error("failed to convert bytes into account ID prefix field element")]
289 AccountIdInvalidPrefixFieldElement(#[source] DeserializationError),
290 #[error("failed to convert bytes into account ID suffix field element")]
291 AccountIdInvalidSuffixFieldElement(#[source] DeserializationError),
292 #[error("`{0}` is not a known account type")]
293 UnknownAccountType(Box<str>),
294 #[error("failed to parse hex string into account ID")]
295 AccountIdHexParseError(#[source] HexParseError),
296 #[error("`{0}` is not a known account ID version")]
297 UnknownAccountIdVersion(u8),
298 #[error("most significant bit of account ID suffix must be zero")]
299 AccountIdSuffixMostSignificantBitMustBeZero,
300 #[error("least significant byte of account ID suffix must be zero")]
301 AccountIdSuffixLeastSignificantByteMustBeZero,
302 #[error("failed to decode bech32 string into account ID")]
303 Bech32DecodeError(#[source] Bech32Error),
304}
305
306#[derive(Debug, Error)]
310pub enum StorageSlotNameError {
311 #[error("slot name must only contain characters a..z, A..Z, 0..9, double colon or underscore")]
312 InvalidCharacter,
313 #[error("slot names must be separated by double colons")]
314 UnexpectedColon,
315 #[error("slot name components must not start with an underscore")]
316 UnexpectedUnderscore,
317 #[error(
318 "slot names must contain at least {} components separated by double colons",
319 StorageSlotName::MIN_NUM_COMPONENTS
320 )]
321 TooShort,
322 #[error("slot names must contain at most {} characters", StorageSlotName::MAX_LENGTH)]
323 TooLong,
324}
325
326#[derive(Debug, Error)]
330pub enum AccountCodeInterfaceError {
331 #[error(
332 "account code interface must contain at least {} procedures, but only {actual} were given",
333 AccountCode::MIN_NUM_PROCEDURES
334 )]
335 TooFewProcedures { actual: usize },
336 #[error(
337 "account code interface contains {actual} procedures but it may contain at most {} procedures",
338 AccountCode::MAX_NUM_PROCEDURES
339 )]
340 TooManyProcedures { actual: usize },
341}
342
343#[derive(Debug, Error)]
347pub enum AccountComponentNameError {
348 #[error(
349 "account component name must only contain characters a..z, A..Z, 0..9, double colon or underscore"
350 )]
351 InvalidCharacter,
352 #[error("account component names must be separated by double colons")]
353 UnexpectedColon,
354 #[error("account component name components must not start with an underscore")]
355 UnexpectedUnderscore,
356 #[error(
357 "account component names must contain at least {} components separated by double colons",
358 StorageSlotName::MIN_NUM_COMPONENTS
359 )]
360 TooShort,
361 #[error(
362 "account component names must contain at most {} characters",
363 StorageSlotName::MAX_LENGTH
364 )]
365 TooLong,
366}
367
368#[derive(Debug, Error)]
372pub enum AccountTreeError {
373 #[error(
374 "account tree contains multiple account IDs that share the same prefix {duplicate_prefix}"
375 )]
376 DuplicateIdPrefix { duplicate_prefix: AccountIdPrefix },
377 #[error(
378 "entries passed to account tree contain multiple state commitments for the same account ID prefix {prefix}"
379 )]
380 DuplicateStateCommitments { prefix: AccountIdPrefix },
381 #[error("untracked account ID {id} used in partial account tree")]
382 UntrackedAccountId { id: AccountId, source: MerkleError },
383 #[error("new tree root after account witness insertion does not match previous tree root")]
384 TreeRootConflict(#[source] MerkleError),
385 #[error("failed to apply mutations to account tree")]
386 ApplyMutations(#[source] MerkleError),
387 #[error("failed to compute account tree mutations")]
388 ComputeMutations(#[source] MerkleError),
389 #[error("provided smt contains an invalid account ID in key {key}")]
390 InvalidAccountIdKey { key: Word, source: AccountIdError },
391 #[error("smt leaf's index is not a valid account ID prefix")]
392 InvalidAccountIdPrefix(#[source] AccountIdError),
393 #[error("account witness merkle path depth {0} does not match AccountTree::DEPTH")]
394 WitnessMerklePathDepthDoesNotMatchAccountTreeDepth(usize),
395}
396
397#[derive(Debug, Error)]
401pub enum AddressError {
402 #[error("tag length {0} is too large, must be less than or equal to {max}",
403 max = NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH
404 )]
405 TagLengthTooLarge(u8),
406 #[error("unknown address interface `{0}`")]
407 UnknownAddressInterface(u16),
408 #[error("failed to decode account ID")]
409 AccountIdDecodeError(#[source] AccountIdError),
410 #[error("address separator must not be included without routing parameters")]
411 TrailingSeparator,
412 #[error("failed to decode bech32 string into an address")]
413 Bech32DecodeError(#[source] Bech32Error),
414 #[error("{error_msg}")]
415 DecodeError {
416 error_msg: Box<str>,
417 source: Option<Box<dyn Error + Send + Sync + 'static>>,
419 },
420 #[error("found unknown routing parameter key {0}")]
421 UnknownRoutingParameterKey(u8),
422}
423
424impl AddressError {
425 pub fn decode_error(message: impl Into<String>) -> Self {
427 let message: String = message.into();
428 Self::DecodeError { error_msg: message.into(), source: None }
429 }
430
431 pub fn decode_error_with_source(
434 message: impl Into<String>,
435 source: impl Error + Send + Sync + 'static,
436 ) -> Self {
437 let message: String = message.into();
438 Self::DecodeError {
439 error_msg: message.into(),
440 source: Some(Box::new(source)),
441 }
442 }
443}
444
445#[derive(Debug, Error)]
449pub enum Bech32Error {
450 #[error(transparent)]
451 DecodeError(Box<dyn Error + Send + Sync + 'static>),
452 #[error("found unknown address type {0} which is not the expected {account_addr} account ID address type",
453 account_addr = AddressType::AccountId as u8
454 )]
455 UnknownAddressType(u8),
456 #[error("expected bech32 data to be of length {expected} but it was of length {actual}")]
457 InvalidDataLength { expected: usize, actual: usize },
458}
459
460#[derive(Debug, Error)]
464pub enum NetworkIdError {
465 #[error("failed to parse string into a network ID")]
466 NetworkIdParseError(#[source] Box<dyn Error + Send + Sync + 'static>),
467}
468
469#[derive(Debug, Error)]
473pub enum AccountDeltaError {
474 #[error("storage slot {0} was used as different slot types")]
475 StorageSlotUsedAsDifferentTypes(StorageSlotName),
476 #[error("asset {0} is changed by more than one asset delta")]
477 DuplicateAssetDelta(AssetId),
478 #[error(
479 "number of {delta_op} operations in account vault delta is {num_ops} but max is {max}",
480 max = AccountVaultDelta::MAX_ASSETS_PER_DELTA_OP
481 )]
482 TooManyVaultAssetDeltas {
483 delta_op: AssetDeltaOperation,
484 num_ops: usize,
485 },
486 #[error(
487 "account update of type `{left_update_type}` cannot be merged with account update of type `{right_update_type}`"
488 )]
489 IncompatibleAccountUpdates {
490 left_update_type: &'static str,
491 right_update_type: &'static str,
492 },
493 #[error("account delta could not be applied to account {account_id}")]
494 AccountDeltaApplicationFailed {
495 account_id: AccountId,
496 source: AccountError,
497 },
498 #[error("non-empty account storage or vault delta with zero nonce delta is not allowed")]
499 NonEmptyStorageOrVaultDeltaWithZeroNonceDelta,
500 #[error("cannot merge two full state deltas")]
501 MergingFullStateDeltas,
502 #[error("a full state delta must only contain storage create operations")]
503 FullStateDeltaContainsNonCreateOp,
504}
505
506#[derive(Debug, Error)]
507pub enum AccountPatchError {
508 #[error("final nonce can never be set to zero")]
509 FinalNonceIsZero,
510
511 #[error(
512 "state change to an account (store, vault or code) require that the final nonce is incremented"
513 )]
514 StateChangeRequiresNonceUpdate,
515
516 #[error("account code must be provided for new accounts (with nonce = 1)")]
517 CodeMustBeProvidedForNewAccounts,
518
519 #[error("a full state patch must only contain storage create operations")]
520 FullStatePatchContainsNonCreateStorageOp,
521
522 #[error("storage slot {0} was used as different slot types")]
523 StorageSlotUsedAsDifferentTypes(StorageSlotName),
524
525 #[error("storage slot name {0} is assigned to more than one slot patch")]
526 DuplicateStorageSlotName(StorageSlotName),
527
528 #[error("number of storage slot patches is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)]
529 TooManyStorageSlotPatches(usize),
530
531 #[error(
532 "a full state patch cannot be merged on top of another patch; it must be the merge base"
533 )]
534 MergeIncomingFullStatePatch,
535
536 #[error("failed to merge storage patch for slot {0}: cannot create a slot twice")]
537 StoragePatchMergeDoubleCreate(StorageSlotName),
538
539 #[error(
540 "failed to merge storage patch for slot {0}: cannot create a slot after it was updated, which indicates it already exists"
541 )]
542 StoragePatchMergeCreateAfterUpdate(StorageSlotName),
543
544 #[error("failed to merge storage patch for slot {0}: cannot update slot after it was removed")]
545 StoragePatchMergeUpdateAfterRemove(StorageSlotName),
546
547 #[error("failed to merge storage patch for slot {0}: cannot remove a slot twice")]
548 StoragePatchMergeDoubleRemove(StorageSlotName),
549
550 #[error(
551 "nonce in the patch being merged is {new} which is not exactly one greater than current patch nonce {current}"
552 )]
553 NonceMustIncrementByOne { current: Felt, new: Felt },
554
555 #[error(
556 "patch is for account ID {actual} but is being merged into patch for account {expected}"
557 )]
558 AccountIdMismatch { expected: AccountId, actual: AccountId },
559
560 #[error(
561 "account update of type `{left_update_type}` cannot be merged with account update of type `{right_update_type}`"
562 )]
563 IncompatibleAccountUpdates {
564 left_update_type: &'static str,
565 right_update_type: &'static str,
566 },
567}
568
569#[derive(Debug, Error)]
573pub enum StorageMapError {
574 #[error("map entries contain key {key} twice with values {value0} and {value1}")]
575 DuplicateKey {
576 key: StorageMapKey,
577 value0: Word,
578 value1: Word,
579 },
580 #[error("map key {key} is not present in provided SMT proof")]
581 MissingKey { key: StorageMapKey },
582 #[error("maximum number of storage map leaf entries exceeded")]
583 MaxLeafEntriesExceeded(#[source] MerkleError),
584}
585
586#[derive(Debug, Error)]
590pub enum BatchAccountUpdateError {
591 #[error(
592 "account update of size {update_size} for account {account_id} exceeds maximum update size of {ACCOUNT_UPDATE_MAX_SIZE}"
593 )]
594 AccountUpdateSizeLimitExceeded {
595 account_id: AccountId,
596 update_size: usize,
597 },
598 #[error("private account {0} should not have account details")]
599 PrivateAccountWithDetails(AccountId),
600 #[error("account {0} with public state is missing its account details")]
601 PublicStateAccountMissingDetails(AccountId),
602 #[error(
603 "batch account update's account ID {account_id} and account patch ID {patch_account_id} must match"
604 )]
605 AccountIdMismatch {
606 account_id: AccountId,
607 patch_account_id: AccountId,
608 },
609 #[error("new account {id} with public state must be accompanied by a full state patch")]
610 NewPublicStateAccountRequiresFullStatePatch { id: AccountId, source: AccountError },
611 #[error(
612 "batch account update's final commitment {final_state_commitment} and reconstructed account commitment {account_commitment} must match"
613 )]
614 AccountFinalCommitmentMismatch {
615 final_state_commitment: Word,
616 account_commitment: Word,
617 },
618 #[error(
619 "account update for account {expected_account_id} cannot be merged with update from transaction {transaction} which was executed against account {actual_account_id}"
620 )]
621 AccountUpdateIdMismatch {
622 transaction: TransactionId,
623 expected_account_id: AccountId,
624 actual_account_id: AccountId,
625 },
626 #[error(
627 "final state commitment in account update from transaction {0} does not match initial state of current update"
628 )]
629 AccountUpdateInitialStateMismatch(TransactionId),
630 #[error("failed to merge account patch from transaction {0}")]
631 TransactionUpdateMergeError(TransactionId, #[source] Box<AccountPatchError>),
632}
633
634#[derive(Debug, Error)]
638pub enum BlockAccountUpdateError {
639 #[error("private account {0} should not have account details")]
640 PrivateAccountWithDetails(AccountId),
641 #[error("account {0} with public state is missing its account details")]
642 PublicStateAccountMissingDetails(AccountId),
643 #[error(
644 "block account update's account ID {account_id} and account patch ID {patch_account_id} must match"
645 )]
646 AccountIdMismatch {
647 account_id: AccountId,
648 patch_account_id: AccountId,
649 },
650 #[error("new account {id} with public state must be accompanied by a full state patch")]
651 NewPublicStateAccountRequiresFullStatePatch { id: AccountId, source: AccountError },
652 #[error(
653 "block account update's final commitment {final_state_commitment} and reconstructed account commitment {account_commitment} must match"
654 )]
655 AccountFinalCommitmentMismatch {
656 final_state_commitment: Word,
657 account_commitment: Word,
658 },
659}
660
661#[derive(Debug, Error)]
665pub enum BlockBodyError {
666 #[error("block has {0} account updates but at most {MAX_ACCOUNTS_PER_BLOCK} are allowed")]
667 TooManyAccountUpdates(usize),
668 #[error("block has {0} nullifiers but at most {MAX_INPUT_NOTES_PER_BLOCK} are allowed")]
669 TooManyNullifiers(usize),
670 #[error("block has {0} output note batches but at most {MAX_BATCHES_PER_BLOCK} are allowed")]
671 TooManyOutputNoteBatches(usize),
672 #[error(
673 "output note batch {batch_index} has {note_count} notes but at most {MAX_OUTPUT_NOTES_PER_BATCH} are allowed"
674 )]
675 TooManyOutputNotes { batch_index: usize, note_count: usize },
676 #[error("output note batch {batch_index} contains invalid note index {note_index}")]
677 InvalidOutputNoteIndex { batch_index: usize, note_index: usize },
678 #[error("output note batch {batch_index} contains note index {note_index} twice")]
679 DuplicateOutputNoteIndex { batch_index: usize, note_index: usize },
680 #[error("output note {0} appears twice in the block body")]
681 DuplicateOutputNote(NoteId),
682 #[error("account update for {0} appears twice in the block body")]
683 DuplicateAccountUpdate(AccountId),
684 #[error("nullifier {0} appears twice in the block body")]
685 DuplicateNullifier(Nullifier),
686 #[error("transaction {0} appears twice in the block body")]
687 DuplicateTransaction(TransactionId),
688}
689
690#[derive(Debug, Error)]
694pub enum AssetError {
695 #[error(
696 "fungible asset amount {0} exceeds the max allowed amount of {max_amount}",
697 max_amount = FungibleAsset::MAX_AMOUNT
698 )]
699 FungibleAssetAmountTooBig(u64),
700 #[error("subtracting {subtrahend} from fungible asset amount {minuend} would underflow")]
701 FungibleAssetAmountNotSufficient { minuend: u64, subtrahend: u64 },
702 #[error(
703 "cannot combine fungible assets with different asset IDs: {original_id} and {other_id}"
704 )]
705 FungibleAssetInconsistentIds { original_id: AssetId, other_id: AssetId },
706 #[error("faucet account ID in asset is invalid")]
707 InvalidFaucetAccountId(#[source] Box<dyn Error + Send + Sync + 'static>),
708 #[error(
709 "asset class prefix and suffix in a non-fungible asset ID must match indices 0 and 1 in the value, but asset class was {asset_class} and value was {value}"
710 )]
711 NonFungibleAssetClassMustMatchValue { asset_class: AssetClass, value: Word },
712 #[error("asset class prefix and suffix in a fungible asset ID must be zero but was {0}")]
713 FungibleAssetClassMustBeZero(AssetClass),
714 #[error(
715 "the three most significant elements in a fungible asset's value must be zero but provided value was {0}"
716 )]
717 FungibleAssetValueMostSignificantElementsMustBeZero(Word),
718 #[error("smt proof in asset witness contains invalid ID or value")]
719 AssetWitnessInvalid(#[source] Box<AssetError>),
720 #[error("asset ID {id} is not present in the provided asset witness SMT proof")]
721 AssetWitnessMissingId { id: AssetId },
722 #[error("unknown asset composition encoding: {0}")]
723 UnknownAssetComposition(u8),
724 #[error("unknown asset delta operation encoding: {0}")]
725 UnknownAssetDeltaOperation(u8),
726 #[error("asset composition {0:?} is not supported at this operational site")]
727 UnsupportedAssetComposition(AssetComposition),
728 #[error(
729 "asset composition mismatch for faucet {faucet_id}: expected {expected:?}, found {actual:?}"
730 )]
731 AssetCompositionMismatch {
732 faucet_id: AccountId,
733 expected: AssetComposition,
734 actual: AssetComposition,
735 },
736 #[error("asset metadata byte 0x{0:02x} has reserved bits set to non-zero values")]
737 ReservedAssetMetadata(u8),
738 #[error("unknown asset ID version: {0}")]
739 UnknownAssetIdVersion(u8),
740}
741
742#[derive(Debug, Error)]
746pub enum TokenSymbolError {
747 #[error("token symbol value {0} cannot exceed {max}", max = TokenSymbol::MAX_ENCODED_VALUE)]
748 ValueTooLarge(u64),
749 #[error(
750 "token symbol value {0} cannot be less than {min}",
751 min = TokenSymbol::MIN_ENCODED_VALUE
752 )]
753 ValueTooSmall(u64),
754 #[error("token symbol should have length between 1 and 12 characters, but {0} was provided")]
755 InvalidLength(usize),
756 #[error("token symbol contains a character that is not uppercase ASCII")]
757 InvalidCharacter,
758 #[error("token symbol data left after decoding the specified number of characters")]
759 DataNotFullyDecoded,
760}
761
762impl From<ShortCapitalStringError> for TokenSymbolError {
763 fn from(value: ShortCapitalStringError) -> Self {
764 match value {
765 ShortCapitalStringError::ValueTooLarge(v) => Self::ValueTooLarge(v),
766 ShortCapitalStringError::ValueTooSmall(v) => Self::ValueTooSmall(v),
767 ShortCapitalStringError::InvalidLength(v) => Self::InvalidLength(v),
768 ShortCapitalStringError::InvalidCharacter => Self::InvalidCharacter,
769 ShortCapitalStringError::DataNotFullyDecoded => Self::DataNotFullyDecoded,
770 }
771 }
772}
773
774#[derive(Debug, Error)]
778pub enum RoleSymbolError {
779 #[error("role symbol value {0} cannot exceed {max}", max = RoleSymbol::MAX_ENCODED_VALUE)]
780 ValueTooLarge(u64),
781 #[error("role symbol value {0} cannot be less than {min}", min = RoleSymbol::MIN_ENCODED_VALUE)]
782 ValueTooSmall(u64),
783 #[error("role symbol should have length between 1 and 12 characters, but {0} was provided")]
784 InvalidLength(usize),
785 #[error("role symbol contains a character that is not uppercase ASCII or underscore")]
786 InvalidCharacter,
787 #[error("role symbol data left after decoding the specified number of characters")]
788 DataNotFullyDecoded,
789}
790
791impl From<ShortCapitalStringError> for RoleSymbolError {
792 fn from(value: ShortCapitalStringError) -> Self {
793 match value {
794 ShortCapitalStringError::ValueTooLarge(v) => Self::ValueTooLarge(v),
795 ShortCapitalStringError::ValueTooSmall(v) => Self::ValueTooSmall(v),
796 ShortCapitalStringError::InvalidLength(v) => Self::InvalidLength(v),
797 ShortCapitalStringError::InvalidCharacter => Self::InvalidCharacter,
798 ShortCapitalStringError::DataNotFullyDecoded => Self::DataNotFullyDecoded,
799 }
800 }
801}
802
803#[derive(Debug, Error)]
807pub(crate) enum ShortCapitalStringError {
808 #[error("short capital string value {0} is too large")]
809 ValueTooLarge(u64),
810 #[error("short capital string value {0} is too small")]
811 ValueTooSmall(u64),
812 #[error(
813 "short capital string should have length between 1 and 12 characters, but {0} was provided"
814 )]
815 InvalidLength(usize),
816 #[error("short capital string contains an invalid character")]
817 InvalidCharacter,
818 #[error("short capital string data left after decoding the specified number of characters")]
819 DataNotFullyDecoded,
820}
821
822#[derive(Debug, Error)]
826pub enum AssetVaultError {
827 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
828 AddFungibleAssetBalanceError(#[source] AssetError),
829 #[error("provided assets contain duplicates")]
830 DuplicateAsset(#[source] MerkleError),
831 #[error("non fungible asset {0} already exists in the vault")]
832 DuplicateNonFungibleAsset(Asset),
833 #[error("fungible asset {0} does not exist in the vault")]
834 FungibleAssetNotFound(FungibleAsset),
835 #[error("non fungible asset {0} does not exist in the vault")]
836 NonFungibleAssetNotFound(Asset),
837 #[error("subtracting fungible asset amounts would underflow")]
838 SubtractFungibleAssetBalanceError(#[source] AssetError),
839 #[error("maximum number of asset vault leaves exceeded")]
840 MaxLeafEntriesExceeded(#[source] MerkleError),
841}
842
843#[derive(Debug, Error)]
847pub enum PartialAssetVaultError {
848 #[error("duplicate asset ID {0} in partial vault")]
849 DuplicateAssetId(AssetId),
850 #[error("partial vault contains invalid asset value {value} at ID {id}")]
851 InvalidAssetForId {
852 id: AssetId,
853 value: Word,
854 #[source]
855 source: AssetError,
856 },
857 #[error("failed to add asset proof")]
858 FailedToAddProof(#[source] MerkleError),
859 #[error("asset is not tracked in the partial vault")]
860 UntrackedAsset(#[source] MerkleError),
861}
862
863#[derive(Debug, Error)]
867pub enum NoteError {
868 #[error("error while creating note script: {0}")]
869 MastForestScript(#[source] MastForestScriptError),
870 #[error("note tag length {0} exceeds the maximum of {max}", max = NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)]
871 NoteTagLengthTooLarge(u8),
872 #[error("duplicate fungible asset from issuer {0} in note")]
873 DuplicateFungibleAsset(AccountId),
874 #[error("duplicate non fungible asset {0} in note")]
875 DuplicateNonFungibleAsset(Asset),
876 #[error("note type {0} is inconsistent with note tag {1}")]
877 InconsistentNoteTag(NoteType, u64),
878 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
879 AddFungibleAssetBalanceError(#[source] AssetError),
880 #[error("note sender is not a valid account ID")]
881 NoteSenderInvalidAccountId(#[source] AccountIdError),
882 #[error("note execution hint after block variant cannot contain u32::MAX")]
883 NoteExecutionHintAfterBlockCannotBeU32Max,
884 #[error("invalid note execution hint payload {1} for tag {0}")]
885 InvalidNoteExecutionHintPayload(u8, u32),
886 #[error(
887 "note type {0} does not match any of the valid note types {public} or {private}",
888 public = NoteType::Public,
889 private = NoteType::Private,
890 )]
891 UnknownNoteType(Box<str>),
892 #[error("block note tree index {block_note_tree_index} is out of bounds 0..={highest_index}")]
893 BlockNoteTreeIndexOutOfBounds {
894 block_note_tree_index: u16,
895 highest_index: usize,
896 },
897 #[error("note network execution requires a public note but note is of type {0}")]
898 NetworkExecutionRequiresPublicNote(NoteType),
899 #[error("failed to assemble note script:\n{}", PrintDiagnostic::new(.0))]
900 NoteScriptAssemblyError(Report),
901 #[error("failed to deserialize note script")]
902 NoteScriptDeserializationError(#[source] DeserializationError),
903 #[error("note contains {0} assets which exceeds the maximum of {max}", max = NoteAssets::MAX_NUM_ASSETS)]
904 TooManyAssets(usize),
905 #[error("note contains {0} storage items which exceeds the maximum of {max}", max = MAX_NOTE_STORAGE_ITEMS)]
906 TooManyStorageItems(usize),
907 #[error("invalid note storage length: expected {expected} items, got {actual}")]
908 InvalidNoteStorageLength { expected: usize, actual: usize },
909 #[error("note tag requires a public note but the note is of type {0}")]
910 PublicNoteRequired(NoteType),
911 #[error("note attachment content must have at least one word")]
912 NoteAttachmentContentEmpty,
913 #[error(
914 "note attachment content contains {0} words, but the maximum is {max} words",
915 max = NoteAttachment::MAX_NUM_WORDS
916 )]
917 NoteAttachmentContentTooManyWords(usize),
918 #[error(
919 "note attachments contain a total of {0} words, but the maximum allowed is {max} words",
920 max = NoteAttachments::MAX_NUM_WORDS
921 )]
922 NoteAttachmentsTooManyWords(usize),
923 #[error(
924 "attachment size {0} exceeds maximum {max}",
925 max = NoteAttachment::MAX_NUM_WORDS
926 )]
927 NoteAttachmentHeaderSizeExceeded(u8),
928 #[error("{0} attachments were provided but maximum is {max}", max = NoteAttachments::MAX_COUNT)]
929 TooManyAttachments(usize),
930 #[error("attachment scheme {0} exceeds maximum value of {max}", max = NoteAttachmentScheme::MAX)]
931 NoteAttachmentSchemeExceeded(u32),
932 #[error("attachment scheme value 0 is reserved")]
933 NoteAttachmentSchemeZeroReserved,
934 #[error("{error_msg}")]
935 Other {
936 error_msg: Box<str>,
937 source: Option<Box<dyn Error + Send + Sync + 'static>>,
939 },
940}
941
942impl NoteError {
943 pub fn other(message: impl Into<String>) -> Self {
945 let message: String = message.into();
946 Self::Other { error_msg: message.into(), source: None }
947 }
948
949 pub fn other_with_source(
952 message: impl Into<String>,
953 source: impl Error + Send + Sync + 'static,
954 ) -> Self {
955 let message: String = message.into();
956 Self::Other {
957 error_msg: message.into(),
958 source: Some(Box::new(source)),
959 }
960 }
961}
962
963#[derive(Debug, Error)]
967pub enum PartialBlockchainError {
968 #[error(
969 "block num {block_num} exceeds chain length {chain_length} implied by the partial blockchain"
970 )]
971 BlockNumTooBig {
972 chain_length: usize,
973 block_num: BlockNumber,
974 },
975
976 #[error("duplicate block {block_num} in partial blockchain")]
977 DuplicateBlock { block_num: BlockNumber },
978
979 #[error("partial blockchain does not track authentication paths for block {block_num}")]
980 UntrackedBlock { block_num: BlockNumber },
981
982 #[error(
983 "provided block header with number {block_num} and commitment {block_commitment} is not tracked by partial MMR"
984 )]
985 BlockHeaderCommitmentMismatch {
986 block_num: BlockNumber,
987 block_commitment: Word,
988 source: MmrError,
989 },
990}
991
992impl PartialBlockchainError {
993 pub fn block_num_too_big(chain_length: usize, block_num: BlockNumber) -> Self {
994 Self::BlockNumTooBig { chain_length, block_num }
995 }
996
997 pub fn duplicate_block(block_num: BlockNumber) -> Self {
998 Self::DuplicateBlock { block_num }
999 }
1000
1001 pub fn untracked_block(block_num: BlockNumber) -> Self {
1002 Self::UntrackedBlock { block_num }
1003 }
1004}
1005
1006#[derive(Debug, Error)]
1010pub enum TransactionInputError {
1011 #[error("transaction input note with nullifier {0} is a duplicate")]
1012 DuplicateInputNote(Nullifier),
1013 #[error("partial blockchain has length {actual} which does not match block number {expected}")]
1014 InconsistentChainLength {
1015 expected: BlockNumber,
1016 actual: BlockNumber,
1017 },
1018 #[error(
1019 "partial blockchain has commitment {actual} which does not match the block header's chain commitment {expected}"
1020 )]
1021 InconsistentChainCommitment { expected: Word, actual: Word },
1022 #[error(
1023 "protocol config has commitment {actual} which does not match the block header's protocol config commitment {expected}"
1024 )]
1025 InconsistentProtocolConfig { expected: Word, actual: Word },
1026 #[error("block in which input note with id {0} was created is not in partial blockchain")]
1027 InputNoteBlockNotInPartialBlockchain(NoteId),
1028 #[error("input note with id {0} was not created in block {1}")]
1029 InputNoteNotInBlock(NoteId, BlockNumber),
1030 #[error(
1031 "total number of input notes is {0} which exceeds the maximum of {MAX_INPUT_NOTES_PER_TX}"
1032 )]
1033 TooManyInputNotes(usize),
1034}
1035
1036#[derive(Debug, Error)]
1040pub enum TransactionInputsExtractionError {
1041 #[error("specified foreign account id matches the transaction input's account id")]
1042 AccountNotForeign,
1043 #[error("foreign account data not found in advice map for account {0}")]
1044 ForeignAccountNotFound(AccountId),
1045 #[error("foreign account code not found for account {0}")]
1046 ForeignAccountCodeNotFound(AccountId),
1047 #[error("storage header data not found in advice map for account {0}")]
1048 StorageHeaderNotFound(AccountId),
1049 #[error("failed to handle account data")]
1050 AccountError(#[from] AccountError),
1051 #[error("failed to handle merkle data")]
1052 MerkleError(#[from] MerkleError),
1053 #[error("failed to handle account tree data")]
1054 AccountTreeError(#[from] AccountTreeError),
1055 #[error("missing vault root from Merkle store")]
1056 MissingVaultRoot,
1057 #[error("missing storage map root from Merkle store")]
1058 MissingMapRoot,
1059 #[error("failed to construct SMT proof")]
1060 SmtProofError(#[from] SmtProofError),
1061 #[error("failed to construct an asset")]
1062 AssetError(#[from] AssetError),
1063 #[error("failed to handle storage map data")]
1064 StorageMapError(#[from] StorageMapError),
1065 #[error("failed to convert elements to leaf index: {0}")]
1066 LeafConversionError(String),
1067 #[error("failed to construct SMT leaf")]
1068 SmtLeafError(#[from] SmtLeafError),
1069}
1070
1071#[derive(Debug, Error)]
1075pub enum TransactionOutputError {
1076 #[error("transaction output note with id {0} is a duplicate")]
1077 DuplicateOutputNote(NoteId),
1078 #[error("final account commitment is not in the advice map")]
1079 FinalAccountCommitmentMissingInAdviceMap,
1080 #[error("failed to parse final account header")]
1081 FinalAccountHeaderParseFailure(#[source] AccountError),
1082 #[error(
1083 "output notes commitment {expected} from kernel does not match computed commitment {actual}"
1084 )]
1085 OutputNotesCommitmentInconsistent { expected: Word, actual: Word },
1086 #[error("transaction kernel output stack is invalid: {0}")]
1087 OutputStackInvalid(String),
1088 #[error(
1089 "total number of output notes is {0} which exceeds the maximum of {MAX_OUTPUT_NOTES_PER_TX}"
1090 )]
1091 TooManyOutputNotes(usize),
1092 #[error("failed to process account update commitment: {0}")]
1093 AccountUpdateCommitment(Box<str>),
1094}
1095
1096#[derive(Debug, Error)]
1103pub enum OutputNoteError {
1104 #[error("attachment headers do not match attachments for private note with id {0}")]
1105 AttachmentHeadersMismatch(NoteId),
1106 #[error("attachments commitment does not match attachments for private note with id {0}")]
1107 AttachmentsCommitmentMismatch(NoteId),
1108 #[error("note with id {0} is private but expected a public note")]
1109 NoteIsPrivate(NoteId),
1110 #[error("note with id {0} is public but expected a private note")]
1111 NoteIsPublic(NoteId),
1112 #[error(
1113 "public note with id {note_id} has size {note_size} bytes which exceeds maximum note size of {NOTE_MAX_SIZE}"
1114 )]
1115 NoteSizeLimitExceeded { note_id: NoteId, note_size: usize },
1116}
1117
1118#[derive(Debug, Error)]
1122pub enum TransactionSummaryError {
1123 #[error(
1124 "transaction summary preimage contains {actual} elements but expected {expected} elements"
1125 )]
1126 InvalidPreimageLength { actual: usize, expected: usize },
1127 #[error("transaction summary metadata element {0} sets bits above the packed fields")]
1128 MetadataOutOfRange(Felt),
1129 #[error(
1130 "transaction summary layout version is {actual} but only version {expected} is supported"
1131 )]
1132 UnsupportedVersion { actual: Felt, expected: u8 },
1133}
1134
1135#[derive(Debug, Error)]
1139pub enum TransactionEventError {
1140 #[error("event id {0} is not a valid transaction event")]
1141 InvalidTransactionEvent(EventId),
1142}
1143
1144#[derive(Debug, Error)]
1148pub enum TransactionTraceParsingError {
1149 #[error("trace id {0} is an unknown transaction kernel trace")]
1150 UnknownTransactionTrace(u32),
1151}
1152
1153#[derive(Debug, Error)]
1157pub enum ProvenTransactionError {
1158 #[error(
1159 "proven transaction's final account commitment {tx_final_commitment} and account details commitment {details_commitment} must match"
1160 )]
1161 AccountFinalCommitmentMismatch {
1162 tx_final_commitment: Word,
1163 details_commitment: Word,
1164 },
1165 #[error(
1166 "proven transaction's final account ID {tx_account_id} and account details id {details_account_id} must match"
1167 )]
1168 AccountIdMismatch {
1169 tx_account_id: AccountId,
1170 details_account_id: AccountId,
1171 },
1172 #[error("failed to construct input notes for proven transaction")]
1173 InputNotesError(TransactionInputError),
1174 #[error("private account {0} should not have account details")]
1175 PrivateAccountWithDetails(AccountId),
1176 #[error("account {0} with public state is missing its account details")]
1177 PublicStateAccountMissingDetails(AccountId),
1178 #[error("new account {id} with public state must be accompanied by a full state patch")]
1179 NewPublicStateAccountRequiresFullStatePatch { id: AccountId, source: AccountError },
1180 #[error(
1181 "existing account {0} with public state should only provide delta updates instead of full details"
1182 )]
1183 ExistingPublicStateAccountRequiresDeltaDetails(AccountId),
1184 #[error("failed to construct output notes for proven transaction")]
1185 OutputNotesError(#[source] TransactionOutputError),
1186 #[error(
1187 "account update of size {update_size} for account {account_id} exceeds maximum update size of {ACCOUNT_UPDATE_MAX_SIZE}"
1188 )]
1189 AccountUpdateSizeLimitExceeded {
1190 account_id: AccountId,
1191 update_size: usize,
1192 },
1193 #[error("proven transaction neither changed the account state, nor consumed any notes")]
1194 EmptyTransaction,
1195 #[error(
1196 "expected account patch commitment {expected_patch_commitment} but found {actual_patch_commitment}"
1197 )]
1198 AccountPatchCommitmentMismatch {
1199 expected_patch_commitment: Word,
1200 actual_patch_commitment: Word,
1201 },
1202 #[error("note with id {0} is both created and consumed by the transaction")]
1203 NoteCreatedAndConsumed(NoteId),
1204}
1205
1206#[derive(Debug, Error)]
1211#[non_exhaustive]
1212pub enum TransactionHeaderError {
1213 #[error("input note with nullifier {0} appears twice in the transaction header")]
1214 DuplicateInputNote(Nullifier),
1215 #[error("output note {0} appears twice in the transaction header")]
1216 DuplicateOutputNote(NoteId),
1217 #[error("note with id {0} is both created and consumed by the transaction header")]
1218 NoteCreatedAndConsumed(NoteId),
1219}
1220
1221impl From<AccountUpdateDetailsValidationError> for ProvenTransactionError {
1222 fn from(error: AccountUpdateDetailsValidationError) -> Self {
1223 match error {
1224 AccountUpdateDetailsValidationError::PrivateAccountWithDetails(account_id) => {
1225 Self::PrivateAccountWithDetails(account_id)
1226 },
1227 AccountUpdateDetailsValidationError::PublicStateAccountMissingDetails(account_id) => {
1228 Self::PublicStateAccountMissingDetails(account_id)
1229 },
1230 AccountUpdateDetailsValidationError::AccountIdMismatch {
1231 account_id,
1232 patch_account_id,
1233 } => Self::AccountIdMismatch {
1234 tx_account_id: account_id,
1235 details_account_id: patch_account_id,
1236 },
1237 }
1238 }
1239}
1240
1241impl From<AccountUpdateSizeValidationError> for ProvenTransactionError {
1242 fn from(error: AccountUpdateSizeValidationError) -> Self {
1243 Self::AccountUpdateSizeLimitExceeded {
1244 account_id: error.account_id,
1245 update_size: error.update_size,
1246 }
1247 }
1248}
1249
1250impl From<AccountUpdateSizeValidationError> for BatchAccountUpdateError {
1251 fn from(error: AccountUpdateSizeValidationError) -> Self {
1252 Self::AccountUpdateSizeLimitExceeded {
1253 account_id: error.account_id,
1254 update_size: error.update_size,
1255 }
1256 }
1257}
1258
1259impl From<AccountUpdateDetailsValidationError> for BatchAccountUpdateError {
1260 fn from(error: AccountUpdateDetailsValidationError) -> Self {
1261 match error {
1262 AccountUpdateDetailsValidationError::PrivateAccountWithDetails(account_id) => {
1263 Self::PrivateAccountWithDetails(account_id)
1264 },
1265 AccountUpdateDetailsValidationError::PublicStateAccountMissingDetails(account_id) => {
1266 Self::PublicStateAccountMissingDetails(account_id)
1267 },
1268 AccountUpdateDetailsValidationError::AccountIdMismatch {
1269 account_id,
1270 patch_account_id,
1271 } => Self::AccountIdMismatch { account_id, patch_account_id },
1272 }
1273 }
1274}
1275
1276impl From<AccountUpdateDetailsValidationError> for BlockAccountUpdateError {
1277 fn from(error: AccountUpdateDetailsValidationError) -> Self {
1278 match error {
1279 AccountUpdateDetailsValidationError::PrivateAccountWithDetails(account_id) => {
1280 Self::PrivateAccountWithDetails(account_id)
1281 },
1282 AccountUpdateDetailsValidationError::PublicStateAccountMissingDetails(account_id) => {
1283 Self::PublicStateAccountMissingDetails(account_id)
1284 },
1285 AccountUpdateDetailsValidationError::AccountIdMismatch {
1286 account_id,
1287 patch_account_id,
1288 } => Self::AccountIdMismatch { account_id, patch_account_id },
1289 }
1290 }
1291}
1292
1293impl From<NewPublicAccountValidationError> for ProvenTransactionError {
1294 fn from(error: NewPublicAccountValidationError) -> Self {
1295 match error {
1296 NewPublicAccountValidationError::RequiresFullStatePatch { id, source } => {
1297 Self::NewPublicStateAccountRequiresFullStatePatch { id, source }
1298 },
1299 NewPublicAccountValidationError::FinalCommitmentMismatch {
1300 final_state_commitment,
1301 account_commitment,
1302 } => Self::AccountFinalCommitmentMismatch {
1303 tx_final_commitment: final_state_commitment,
1304 details_commitment: account_commitment,
1305 },
1306 }
1307 }
1308}
1309
1310impl From<NewPublicAccountValidationError> for BatchAccountUpdateError {
1311 fn from(error: NewPublicAccountValidationError) -> Self {
1312 match error {
1313 NewPublicAccountValidationError::RequiresFullStatePatch { id, source } => {
1314 Self::NewPublicStateAccountRequiresFullStatePatch { id, source }
1315 },
1316 NewPublicAccountValidationError::FinalCommitmentMismatch {
1317 final_state_commitment,
1318 account_commitment,
1319 } => Self::AccountFinalCommitmentMismatch {
1320 final_state_commitment,
1321 account_commitment,
1322 },
1323 }
1324 }
1325}
1326
1327impl From<NewPublicAccountValidationError> for BlockAccountUpdateError {
1328 fn from(error: NewPublicAccountValidationError) -> Self {
1329 match error {
1330 NewPublicAccountValidationError::RequiresFullStatePatch { id, source } => {
1331 Self::NewPublicStateAccountRequiresFullStatePatch { id, source }
1332 },
1333 NewPublicAccountValidationError::FinalCommitmentMismatch {
1334 final_state_commitment,
1335 account_commitment,
1336 } => Self::AccountFinalCommitmentMismatch {
1337 final_state_commitment,
1338 account_commitment,
1339 },
1340 }
1341 }
1342}
1343
1344#[derive(Debug, Error)]
1348pub enum ProposedBatchError {
1349 #[error("failed to verify transaction {transaction_id} in transaction batch")]
1350 TransactionVerificationFailed {
1351 transaction_id: TransactionId,
1352 source: TransactionVerifierError,
1353 },
1354
1355 #[error(
1356 "transaction batch has {0} input notes but at most {MAX_INPUT_NOTES_PER_BATCH} are allowed"
1357 )]
1358 TooManyInputNotes(usize),
1359
1360 #[error(
1361 "transaction batch has {0} output notes but at most {MAX_OUTPUT_NOTES_PER_BATCH} are allowed"
1362 )]
1363 TooManyOutputNotes(usize),
1364
1365 #[error(
1366 "transaction batch has {0} account updates but at most {MAX_ACCOUNTS_PER_BATCH} are allowed"
1367 )]
1368 TooManyAccountUpdates(usize),
1369
1370 #[error(
1371 "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}"
1372 )]
1373 ExpiredTransaction {
1374 transaction_id: TransactionId,
1375 transaction_expiration_num: BlockNumber,
1376 reference_block_num: BlockNumber,
1377 },
1378
1379 #[error("transaction batch must contain at least one transaction")]
1380 EmptyTransactionBatch,
1381
1382 #[error("transaction {transaction_id} appears twice in the proposed batch input")]
1383 DuplicateTransaction { transaction_id: TransactionId },
1384
1385 #[error(
1386 "transaction {second_transaction_id} consumes the note with nullifier {note_nullifier} that is also consumed by another transaction {first_transaction_id} in the batch"
1387 )]
1388 DuplicateInputNote {
1389 note_nullifier: Nullifier,
1390 first_transaction_id: TransactionId,
1391 second_transaction_id: TransactionId,
1392 },
1393
1394 #[error(
1395 "transaction {second_transaction_id} creates the note with id {note_id} that is also created by another transaction {first_transaction_id} in the batch"
1396 )]
1397 DuplicateOutputNote {
1398 note_id: NoteId,
1399 first_transaction_id: TransactionId,
1400 second_transaction_id: TransactionId,
1401 },
1402
1403 #[error(
1404 "transaction {consumed_by} that consumes the note with ID {note_id} must be ordered before transaction {created_by} that creates the note"
1405 )]
1406 NoteConsumedBeforeCreated {
1407 note_id: NoteId,
1408 consumed_by: TransactionId,
1409 created_by: TransactionId,
1410 },
1411
1412 #[error("failed to merge transaction patch into account {account_id}")]
1413 AccountUpdateError {
1414 account_id: AccountId,
1415 source: BatchAccountUpdateError,
1416 },
1417
1418 #[error(
1419 "unable to prove unauthenticated note inclusion because block {block_number} in which note with id {note_id} was created is not in partial blockchain"
1420 )]
1421 UnauthenticatedInputNoteBlockNotInPartialBlockchain {
1422 block_number: BlockNumber,
1423 note_id: NoteId,
1424 },
1425
1426 #[error(
1427 "unable to prove unauthenticated note inclusion of note {note_id} in block {block_num}"
1428 )]
1429 UnauthenticatedNoteAuthenticationFailed {
1430 note_id: NoteId,
1431 block_num: BlockNumber,
1432 source: MerkleError,
1433 },
1434
1435 #[error("partial blockchain has length {actual} which does not match block number {expected}")]
1436 InconsistentChainLength {
1437 expected: BlockNumber,
1438 actual: BlockNumber,
1439 },
1440
1441 #[error(
1442 "partial blockchain has root {actual} which does not match block header's root {expected}"
1443 )]
1444 InconsistentChainRoot { expected: Word, actual: Word },
1445
1446 #[error(
1447 "block {block_num} referenced by transaction {transaction_id} is not in the partial blockchain"
1448 )]
1449 MissingTransactionReferenceBlock {
1450 transaction_id: TransactionId,
1451 block_num: BlockNumber,
1452 },
1453
1454 #[error(
1455 "transaction {transaction_id} references block {block_num} with commitment {actual_block_commitment}, but the block in the chain with the same number has commitment {expected_block_commitment}"
1456 )]
1457 TransactionReferenceBlockCommitmentMismatch {
1458 transaction_id: TransactionId,
1459 block_num: BlockNumber,
1460 expected_block_commitment: Word,
1461 actual_block_commitment: Word,
1462 },
1463}
1464
1465#[derive(Debug, Error)]
1469pub enum ProvenBatchError {
1470 #[error("transaction batch must contain at least one transaction")]
1471 EmptyTransactionBatch,
1472 #[error("transaction {0} appears twice in the proven batch")]
1473 DuplicateTransaction(TransactionId),
1474 #[error(
1475 "transaction batch has {0} input notes but at most {MAX_INPUT_NOTES_PER_BATCH} are allowed"
1476 )]
1477 TooManyInputNotes(usize),
1478 #[error("input note with nullifier {0} appears twice in the proven batch")]
1479 DuplicateInputNote(Nullifier),
1480 #[error(
1481 "transaction batch has {0} output notes but at most {MAX_OUTPUT_NOTES_PER_BATCH} are allowed"
1482 )]
1483 TooManyOutputNotes(usize),
1484 #[error(
1485 "transaction batch has at least {0} account updates but at most {MAX_ACCOUNTS_PER_BATCH} are allowed"
1486 )]
1487 TooManyAccountUpdates(usize),
1488 #[error("output note {0} appears twice in the proven batch")]
1489 DuplicateOutputNote(NoteId),
1490 #[error("note with id {0} is both created and consumed by the proven batch")]
1491 NoteCreatedAndConsumed(NoteId),
1492 #[error("account {0} is updated more than once in the proven batch")]
1493 DuplicateAccountUpdate(AccountId),
1494 #[error("account update for {0} is missing from the proven batch")]
1495 MissingAccountUpdate(AccountId),
1496 #[error("account update for {0} has no corresponding transaction in the proven batch")]
1497 UnexpectedAccountUpdate(AccountId),
1498 #[error(
1499 "transaction {transaction_id} for account {account_id} starts from state {actual_initial_state_commitment}, but the previous transaction ends at state {expected_initial_state_commitment}"
1500 )]
1501 TransactionAccountStateMismatch {
1502 account_id: AccountId,
1503 transaction_id: TransactionId,
1504 expected_initial_state_commitment: Word,
1505 actual_initial_state_commitment: Word,
1506 },
1507 #[error(
1508 "account update for {account_id} starts from state {actual}, but its first transaction starts from state {expected}"
1509 )]
1510 AccountUpdateInitialStateMismatch {
1511 account_id: AccountId,
1512 expected: Word,
1513 actual: Word,
1514 },
1515 #[error(
1516 "account update for {account_id} ends at state {actual}, but its last transaction ends at state {expected}"
1517 )]
1518 AccountUpdateFinalStateMismatch {
1519 account_id: AccountId,
1520 expected: Word,
1521 actual: Word,
1522 },
1523 #[error(
1524 "batch expiration block number {batch_expiration_block_num} is not greater than the reference block number {reference_block_num}"
1525 )]
1526 InvalidBatchExpirationBlockNum {
1527 batch_expiration_block_num: BlockNumber,
1528 reference_block_num: BlockNumber,
1529 },
1530 #[error("batch kernel execution failed")]
1531 BatchKernelExecutionFailed(#[source] ExecutionError),
1532 #[error("batch kernel proving failed")]
1533 BatchKernelProvingFailed(#[source] ExecutionError),
1534 #[error("precompile proving failed")]
1535 PrecompileProvingFailed(#[source] ExecutionError),
1536 #[error("batch proof contains precompiles")]
1537 BatchProofContainsPrecompiles,
1538 #[error("batch kernel produced an invalid output stack")]
1539 BatchKernelOutputInvalid(#[source] BatchOutputError),
1540}
1541
1542#[derive(Debug, Error)]
1546pub enum BatchOutputError {
1547 #[error("batch kernel output stack is invalid: {0}")]
1548 OutputStackInvalid(String),
1549 #[error("batch expiration block number {0} does not fit into a u32")]
1550 ExpirationBlockNumberTooLarge(Felt),
1551}
1552
1553#[derive(Debug, Error)]
1557pub enum BlockOutputError {
1558 #[error(
1559 "block kernel output stack has a non-zero element at index {index}, but everything past the nullifier commitment must be zero padding"
1560 )]
1561 PaddingNotZero { index: usize },
1562}
1563
1564#[derive(Debug, Error)]
1568pub enum ProposedBlockError {
1569 #[error("block must contain at least one transaction batch")]
1570 EmptyBlock,
1571
1572 #[error("block must contain at most {MAX_BATCHES_PER_BLOCK} transaction batches")]
1573 TooManyBatches,
1574
1575 #[error(
1576 "batch {batch_id} expired at block {batch_expiration_block_num} but the current block number is {current_block_num}"
1577 )]
1578 ExpiredBatch {
1579 batch_id: BatchId,
1580 batch_expiration_block_num: BlockNumber,
1581 current_block_num: BlockNumber,
1582 },
1583
1584 #[error("batch {batch_id} appears twice in the block inputs")]
1585 DuplicateBatch { batch_id: BatchId },
1586
1587 #[error(
1588 "batch {second_batch_id} consumes the note with nullifier {note_nullifier} that is also consumed by another batch {first_batch_id} in the block"
1589 )]
1590 DuplicateInputNote {
1591 note_nullifier: Nullifier,
1592 first_batch_id: BatchId,
1593 second_batch_id: BatchId,
1594 },
1595
1596 #[error(
1597 "batch {second_batch_id} creates the note with ID {note_id} that is also created by another batch {first_batch_id} in the block"
1598 )]
1599 DuplicateOutputNote {
1600 note_id: NoteId,
1601 first_batch_id: BatchId,
1602 second_batch_id: BatchId,
1603 },
1604
1605 #[error(
1606 "batch {consumed_by} that consumes the note with ID {note_id} must be ordered before batch {created_by} that creates the note"
1607 )]
1608 NoteConsumedBeforeCreated {
1609 note_id: NoteId,
1610 consumed_by: BatchId,
1611 created_by: BatchId,
1612 },
1613
1614 #[error(
1615 "timestamp {provided_timestamp} does not increase monotonically compared to timestamp {previous_timestamp} from the previous block header"
1616 )]
1617 TimestampDoesNotIncreaseMonotonically {
1618 provided_timestamp: u32,
1619 previous_timestamp: u32,
1620 },
1621
1622 #[error(
1623 "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}"
1624 )]
1625 ConflictingBatchesUpdateSameAccount {
1626 account_id: AccountId,
1627 initial_state_commitment: Word,
1628 first_batch_id: BatchId,
1629 second_batch_id: BatchId,
1630 },
1631
1632 #[error(
1633 "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"
1634 )]
1635 ChainLengthNotEqualToPreviousBlockNumber {
1636 chain_length: BlockNumber,
1637 prev_block_num: BlockNumber,
1638 },
1639
1640 #[error(
1641 "partial blockchain has commitment {chain_commitment} which does not match the chain commitment {prev_block_chain_commitment} of the previous block {prev_block_num}"
1642 )]
1643 ChainRootNotEqualToPreviousBlockChainCommitment {
1644 chain_commitment: Word,
1645 prev_block_chain_commitment: Word,
1646 prev_block_num: BlockNumber,
1647 },
1648
1649 #[error(
1650 "partial blockchain is missing block {reference_block_num} referenced by batch {batch_id} in the block"
1651 )]
1652 BatchReferenceBlockMissingFromChain {
1653 reference_block_num: BlockNumber,
1654 batch_id: BatchId,
1655 },
1656
1657 #[error(
1658 "failed to prove unauthenticated note inclusion because block {block_number} in which note with id {note_id} was created is not in partial blockchain"
1659 )]
1660 UnauthenticatedInputNoteBlockNotInPartialBlockchain {
1661 block_number: BlockNumber,
1662 note_id: NoteId,
1663 },
1664
1665 #[error(
1666 "failed to prove unauthenticated note inclusion of note {note_id} in block {block_num}"
1667 )]
1668 UnauthenticatedNoteAuthenticationFailed {
1669 note_id: NoteId,
1670 block_num: BlockNumber,
1671 source: MerkleError,
1672 },
1673
1674 #[error(
1675 "unauthenticated note with nullifier {nullifier} was not created in the same block and no inclusion proof to authenticate it was provided"
1676 )]
1677 UnauthenticatedNoteConsumed { nullifier: Nullifier },
1678
1679 #[error("block inputs do not contain a proof of inclusion for account {0}")]
1680 MissingAccountWitness(AccountId),
1681
1682 #[error(
1683 "account {account_id} with state {state_commitment} cannot transition to any of the remaining states {}",
1684 remaining_state_commitments.iter().map(Word::to_hex).collect::<Vec<_>>().join(", ")
1685 )]
1686 InconsistentAccountStateTransition {
1687 account_id: AccountId,
1688 state_commitment: Word,
1689 remaining_state_commitments: Vec<Word>,
1690 },
1691
1692 #[error("no proof for nullifier {0} was provided")]
1693 NullifierProofMissing(Nullifier),
1694
1695 #[error("note with nullifier {0} is already spent")]
1696 NullifierSpent(Nullifier),
1697
1698 #[error("failed to merge transaction patch into account {account_id}")]
1699 AccountUpdateError {
1700 account_id: AccountId,
1701 source: Box<AccountPatchError>,
1702 },
1703
1704 #[error("failed to track account witness")]
1705 AccountWitnessTracking { source: AccountTreeError },
1706
1707 #[error(
1708 "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"
1709 )]
1710 StaleAccountTreeRoot {
1711 prev_block_account_root: Word,
1712 stale_account_root: Word,
1713 },
1714
1715 #[error("account ID prefix already exists in the tree")]
1716 AccountIdPrefixDuplicate { source: AccountTreeError },
1717
1718 #[error(
1719 "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"
1720 )]
1721 StaleNullifierTreeRoot {
1722 prev_block_nullifier_root: Word,
1723 stale_nullifier_root: Word,
1724 },
1725
1726 #[error("nullifier witness has a different root than the current nullifier tree root")]
1727 NullifierWitnessRootMismatch(NullifierTreeError),
1728}
1729
1730#[derive(Debug, thiserror::Error)]
1735#[non_exhaustive]
1736pub enum ProtocolConfigError {
1737 #[error("fee asset composition {0:?} is not supported, it must be fungible")]
1738 FeeAssetMustBeFungible(AssetComposition),
1739 #[error("minimum proof security must be at least one bit")]
1740 MinimumSecurityBitsMustBeNonZero,
1741 #[error("next protocol config cannot become effective at the genesis block")]
1742 NextConfigEffectiveAtGenesis,
1743 #[error(
1744 "kernel config contains {count} procedures but must contain at most {max}",
1745 max = KernelConfig::MAX_NUM_KERNEL_PROCEDURES,
1746 )]
1747 TooManyKernelProcedures { count: usize },
1748}
1749
1750#[derive(Debug, thiserror::Error)]
1755#[non_exhaustive]
1756pub enum ValidatorConfigError {
1757 #[error("validator set must contain at least one key")]
1758 EmptySet,
1759 #[error(
1760 "validator set contains {count} keys but must contain at most {max}",
1761 max = ValidatorConfig::MAX_VALIDATORS,
1762 )]
1763 TooManyKeys { count: usize },
1764 #[error("validator set contains duplicate public keys")]
1765 DuplicateKey,
1766 #[error("quorum is {quorum} but must equal the validator count of {count}")]
1767 QuorumMustEqualValidatorCount { quorum: u16, count: usize },
1768}
1769
1770#[derive(Debug, Error)]
1774pub enum NullifierTreeError {
1775 #[error(
1776 "entries passed to nullifier tree contain multiple block numbers for the same nullifier"
1777 )]
1778 DuplicateNullifierBlockNumbers(#[source] MerkleError),
1779
1780 #[error("attempt to mark nullifier {0} as spent but it is already spent")]
1781 NullifierAlreadySpent(Nullifier),
1782
1783 #[error("maximum number of nullifier tree leaves exceeded")]
1784 MaxLeafEntriesExceeded(#[source] MerkleError),
1785
1786 #[error("nullifier {nullifier} is not tracked by the partial nullifier tree")]
1787 UntrackedNullifier {
1788 nullifier: Nullifier,
1789 source: MerkleError,
1790 },
1791
1792 #[error("new tree root after nullifier witness insertion does not match previous tree root")]
1793 TreeRootConflict(#[source] MerkleError),
1794
1795 #[error("failed to compute nullifier tree mutations")]
1796 ComputeMutations(#[source] MerkleError),
1797
1798 #[error("invalid nullifier block number")]
1799 InvalidNullifierBlockNumber(Word),
1800}
1801
1802#[derive(Debug, Error)]
1806pub enum AuthSchemeError {
1807 #[error("auth scheme identifier `{0}` is not valid")]
1808 InvalidAuthSchemeIdentifier(String),
1809}
1810
1811#[derive(Debug, Error)]
1815pub enum TransactionVerifierError {
1816 #[error("failed to verify transaction")]
1817 TransactionVerificationFailed(#[source] VerificationError),
1818 #[error("transaction proof contains settled precompile work")]
1819 TransactionProofContainsPrecompiles,
1820 #[error("transaction proof security level is {actual} but must be at least {expected_minimum}")]
1821 InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 },
1822}