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("patch is for account ID {patch_id} but is being applied to account {account_id}")]
203 PatchAccountIdMismatch {
204 account_id: AccountId,
205 patch_id: AccountId,
206 },
207 #[error("a new account must have code and a nonce")]
208 NewAccountRequiresCodeAndNonce,
209 #[error("the storage of a new account must only contain storage create operations")]
210 NewAccountStorageRequiresCreateOps,
211 #[error("assets cannot be removed from a new account with an empty asset vault")]
212 AssetsRemovedFromNewAccount,
213 #[error("maximum number of storage map leaves exceeded")]
214 MaxNumStorageMapLeavesExceeded(#[source] MerkleError),
215 #[error("unknown storage patch operation tag {0}")]
216 UnknownStoragePatchOperation(u8),
217 #[error("{error_msg}")]
220 Other {
221 error_msg: Box<str>,
222 source: Option<Box<dyn Error + Send + Sync + 'static>>,
224 },
225}
226
227impl AccountError {
228 pub fn other(message: impl Into<String>) -> Self {
230 let message: String = message.into();
231 Self::Other { error_msg: message.into(), source: None }
232 }
233
234 pub fn other_with_source(
237 message: impl Into<String>,
238 source: impl Error + Send + Sync + 'static,
239 ) -> Self {
240 let message: String = message.into();
241 Self::Other {
242 error_msg: message.into(),
243 source: Some(Box::new(source)),
244 }
245 }
246}
247
248#[derive(Debug)]
250pub(crate) enum AccountUpdateDetailsValidationError {
251 PrivateAccountWithDetails(AccountId),
252 PublicStateAccountMissingDetails(AccountId),
253 AccountIdMismatch {
254 account_id: AccountId,
255 patch_account_id: AccountId,
256 },
257}
258
259#[derive(Debug)]
261pub(crate) struct AccountUpdateSizeValidationError {
262 pub(crate) account_id: AccountId,
263 pub(crate) update_size: usize,
264}
265
266#[derive(Debug)]
268pub(crate) enum NewPublicAccountValidationError {
269 NotACreationPatch {
270 id: AccountId,
271 source: AccountError,
272 },
273 FinalCommitmentMismatch {
274 final_state_commitment: Word,
275 account_commitment: Word,
276 },
277}
278
279#[derive(Debug, Error)]
283pub enum AccountIdError {
284 #[error("failed to convert bytes into account ID prefix field element")]
285 AccountIdInvalidPrefixFieldElement(#[source] DeserializationError),
286 #[error("failed to convert bytes into account ID suffix field element")]
287 AccountIdInvalidSuffixFieldElement(#[source] DeserializationError),
288 #[error("`{0}` is not a known account type")]
289 UnknownAccountType(Box<str>),
290 #[error("failed to parse hex string into account ID")]
291 AccountIdHexParseError(#[source] HexParseError),
292 #[error("`{0}` is not a known account ID version")]
293 UnknownAccountIdVersion(u8),
294 #[error("most significant bit of account ID suffix must be zero")]
295 AccountIdSuffixMostSignificantBitMustBeZero,
296 #[error("least significant byte of account ID suffix must be zero")]
297 AccountIdSuffixLeastSignificantByteMustBeZero,
298 #[error("failed to decode bech32 string into account ID")]
299 Bech32DecodeError(#[source] Bech32Error),
300}
301
302#[derive(Debug, Error)]
306pub enum StorageSlotNameError {
307 #[error("slot name must only contain characters a..z, A..Z, 0..9, double colon or underscore")]
308 InvalidCharacter,
309 #[error("slot names must be separated by double colons")]
310 UnexpectedColon,
311 #[error("slot name components must not start with an underscore")]
312 UnexpectedUnderscore,
313 #[error(
314 "slot names must contain at least {} components separated by double colons",
315 StorageSlotName::MIN_NUM_COMPONENTS
316 )]
317 TooShort,
318 #[error("slot names must contain at most {} characters", StorageSlotName::MAX_LENGTH)]
319 TooLong,
320}
321
322#[derive(Debug, Error)]
326pub enum AccountCodeInterfaceError {
327 #[error(
328 "account code interface must contain at least {} procedures, but only {actual} were given",
329 AccountCode::MIN_NUM_PROCEDURES
330 )]
331 TooFewProcedures { actual: usize },
332 #[error(
333 "account code interface contains {actual} procedures but it may contain at most {} procedures",
334 AccountCode::MAX_NUM_PROCEDURES
335 )]
336 TooManyProcedures { actual: usize },
337}
338
339#[derive(Debug, Error)]
343pub enum AccountComponentNameError {
344 #[error(
345 "account component name must only contain characters a..z, A..Z, 0..9, double colon or underscore"
346 )]
347 InvalidCharacter,
348 #[error("account component names must be separated by double colons")]
349 UnexpectedColon,
350 #[error("account component name components must not start with an underscore")]
351 UnexpectedUnderscore,
352 #[error(
353 "account component names must contain at least {} components separated by double colons",
354 StorageSlotName::MIN_NUM_COMPONENTS
355 )]
356 TooShort,
357 #[error(
358 "account component names must contain at most {} characters",
359 StorageSlotName::MAX_LENGTH
360 )]
361 TooLong,
362}
363
364#[derive(Debug, Error)]
368pub enum AccountTreeError {
369 #[error(
370 "account tree contains multiple account IDs that share the same prefix {duplicate_prefix}"
371 )]
372 DuplicateIdPrefix { duplicate_prefix: AccountIdPrefix },
373 #[error(
374 "entries passed to account tree contain multiple state commitments for the same account ID prefix {prefix}"
375 )]
376 DuplicateStateCommitments { prefix: AccountIdPrefix },
377 #[error("untracked account ID {id} used in partial account tree")]
378 UntrackedAccountId { id: AccountId, source: MerkleError },
379 #[error("new tree root after account witness insertion does not match previous tree root")]
380 TreeRootConflict(#[source] MerkleError),
381 #[error("failed to apply mutations to account tree")]
382 ApplyMutations(#[source] MerkleError),
383 #[error("failed to compute account tree mutations")]
384 ComputeMutations(#[source] MerkleError),
385 #[error("provided smt contains an invalid account ID in key {key}")]
386 InvalidAccountIdKey { key: Word, source: AccountIdError },
387 #[error("smt leaf's index is not a valid account ID prefix")]
388 InvalidAccountIdPrefix(#[source] AccountIdError),
389 #[error("account witness merkle path depth {0} does not match AccountTree::DEPTH")]
390 WitnessMerklePathDepthDoesNotMatchAccountTreeDepth(usize),
391}
392
393#[derive(Debug, Error)]
397pub enum AddressError {
398 #[error("tag length {0} is too large, must be less than or equal to {max}",
399 max = NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH
400 )]
401 TagLengthTooLarge(u8),
402 #[error("unknown address interface `{0}`")]
403 UnknownAddressInterface(u16),
404 #[error("failed to decode account ID")]
405 AccountIdDecodeError(#[source] AccountIdError),
406 #[error("address separator must not be included without routing parameters")]
407 TrailingSeparator,
408 #[error("failed to decode bech32 string into an address")]
409 Bech32DecodeError(#[source] Bech32Error),
410 #[error("{error_msg}")]
411 DecodeError {
412 error_msg: Box<str>,
413 source: Option<Box<dyn Error + Send + Sync + 'static>>,
415 },
416 #[error("found unknown routing parameter key {0}")]
417 UnknownRoutingParameterKey(u8),
418}
419
420impl AddressError {
421 pub fn decode_error(message: impl Into<String>) -> Self {
423 let message: String = message.into();
424 Self::DecodeError { error_msg: message.into(), source: None }
425 }
426
427 pub fn decode_error_with_source(
430 message: impl Into<String>,
431 source: impl Error + Send + Sync + 'static,
432 ) -> Self {
433 let message: String = message.into();
434 Self::DecodeError {
435 error_msg: message.into(),
436 source: Some(Box::new(source)),
437 }
438 }
439}
440
441#[derive(Debug, Error)]
445pub enum Bech32Error {
446 #[error(transparent)]
447 DecodeError(Box<dyn Error + Send + Sync + 'static>),
448 #[error("found unknown address type {0} which is not the expected {account_addr} account ID address type",
449 account_addr = AddressType::AccountId as u8
450 )]
451 UnknownAddressType(u8),
452 #[error("expected bech32 data to be of length {expected} but it was of length {actual}")]
453 InvalidDataLength { expected: usize, actual: usize },
454}
455
456#[derive(Debug, Error)]
460pub enum NetworkIdError {
461 #[error("failed to parse string into a network ID")]
462 NetworkIdParseError(#[source] Box<dyn Error + Send + Sync + 'static>),
463}
464
465#[derive(Debug, Error)]
469pub enum AccountDeltaError {
470 #[error("storage slot {0} was used as different slot types")]
471 StorageSlotUsedAsDifferentTypes(StorageSlotName),
472 #[error("asset {0} is changed by more than one asset delta")]
473 DuplicateAssetDelta(AssetId),
474 #[error(
475 "number of {delta_op} operations in account vault delta is {num_ops} but max is {max}",
476 max = AccountVaultDelta::MAX_ASSETS_PER_DELTA_OP
477 )]
478 TooManyVaultAssetDeltas {
479 delta_op: AssetDeltaOperation,
480 num_ops: usize,
481 },
482 #[error(
483 "account update of type `{left_update_type}` cannot be merged with account update of type `{right_update_type}`"
484 )]
485 IncompatibleAccountUpdates {
486 left_update_type: &'static str,
487 right_update_type: &'static str,
488 },
489 #[error("account delta could not be applied to account {account_id}")]
490 AccountDeltaApplicationFailed {
491 account_id: AccountId,
492 source: AccountError,
493 },
494 #[error("non-empty account storage, vault or code delta with zero nonce delta is not allowed")]
495 NonEmptyDeltaWithZeroNonceDelta,
496}
497
498#[derive(Debug, Error)]
499pub enum AccountPatchError {
500 #[error("final nonce can never be set to zero")]
501 FinalNonceIsZero,
502
503 #[error(
504 "state change to an account (store, vault or code) require that the final nonce is incremented"
505 )]
506 StateChangeRequiresNonceUpdate,
507
508 #[error("account code must be provided for new accounts (with nonce = 1)")]
509 CodeMustBeProvidedForNewAccounts,
510
511 #[error("storage slot {0} was used as different slot types")]
512 StorageSlotUsedAsDifferentTypes(StorageSlotName),
513
514 #[error("storage slot name {0} is assigned to more than one slot patch")]
515 DuplicateStorageSlotName(StorageSlotName),
516
517 #[error("number of storage slot patches is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)]
518 TooManyStorageSlotPatches(usize),
519
520 #[error("failed to merge storage patch for slot {0}: cannot create a slot twice")]
521 StoragePatchMergeDoubleCreate(StorageSlotName),
522
523 #[error(
524 "failed to merge storage patch for slot {0}: cannot create a slot after it was updated, which indicates it already exists"
525 )]
526 StoragePatchMergeCreateAfterUpdate(StorageSlotName),
527
528 #[error("failed to merge storage patch for slot {0}: cannot update slot after it was removed")]
529 StoragePatchMergeUpdateAfterRemove(StorageSlotName),
530
531 #[error("failed to merge storage patch for slot {0}: cannot remove a slot twice")]
532 StoragePatchMergeDoubleRemove(StorageSlotName),
533
534 #[error(
535 "nonce in the patch being merged is {new} which is not greater than current patch nonce {current}"
536 )]
537 NonceMustIncrease { current: Felt, new: Felt },
538
539 #[error(
540 "patch is for account ID {actual} but is being merged into patch for account {expected}"
541 )]
542 AccountIdMismatch { expected: AccountId, actual: AccountId },
543
544 #[error(
545 "account update of type `{left_update_type}` cannot be merged with account update of type `{right_update_type}`"
546 )]
547 IncompatibleAccountUpdates {
548 left_update_type: &'static str,
549 right_update_type: &'static str,
550 },
551}
552
553#[derive(Debug, Error)]
557pub enum StorageMapError {
558 #[error("map entries contain key {key} twice with values {value0} and {value1}")]
559 DuplicateKey {
560 key: StorageMapKey,
561 value0: Word,
562 value1: Word,
563 },
564 #[error("map key {key} is not present in provided SMT proof")]
565 MissingKey { key: StorageMapKey },
566 #[error("maximum number of storage map leaf entries exceeded")]
567 MaxLeafEntriesExceeded(#[source] MerkleError),
568}
569
570#[derive(Debug, Error)]
574pub enum BatchAccountUpdateError {
575 #[error(
576 "account update of size {update_size} for account {account_id} exceeds maximum update size of {ACCOUNT_UPDATE_MAX_SIZE}"
577 )]
578 AccountUpdateSizeLimitExceeded {
579 account_id: AccountId,
580 update_size: usize,
581 },
582 #[error("private account {0} should not have account details")]
583 PrivateAccountWithDetails(AccountId),
584 #[error("account {0} with public state is missing its account details")]
585 PublicStateAccountMissingDetails(AccountId),
586 #[error(
587 "batch account update's account ID {account_id} and account patch ID {patch_account_id} must match"
588 )]
589 AccountIdMismatch {
590 account_id: AccountId,
591 patch_account_id: AccountId,
592 },
593 #[error("new account {id} with public state must be accompanied by a creation patch")]
594 NewPublicStateAccountRequiresCreationPatch { id: AccountId, source: AccountError },
595 #[error(
596 "batch account update's final commitment {final_state_commitment} and reconstructed account commitment {account_commitment} must match"
597 )]
598 AccountFinalCommitmentMismatch {
599 final_state_commitment: Word,
600 account_commitment: Word,
601 },
602 #[error(
603 "account update for account {expected_account_id} cannot be merged with update from transaction {transaction} which was executed against account {actual_account_id}"
604 )]
605 AccountUpdateIdMismatch {
606 transaction: TransactionId,
607 expected_account_id: AccountId,
608 actual_account_id: AccountId,
609 },
610 #[error(
611 "final state commitment in account update from transaction {0} does not match initial state of current update"
612 )]
613 AccountUpdateInitialStateMismatch(TransactionId),
614 #[error("failed to merge account patch from transaction {0}")]
615 TransactionUpdateMergeError(TransactionId, #[source] Box<AccountPatchError>),
616}
617
618#[derive(Debug, Error)]
622pub enum BlockAccountUpdateError {
623 #[error("private account {0} should not have account details")]
624 PrivateAccountWithDetails(AccountId),
625 #[error("account {0} with public state is missing its account details")]
626 PublicStateAccountMissingDetails(AccountId),
627 #[error(
628 "block account update's account ID {account_id} and account patch ID {patch_account_id} must match"
629 )]
630 AccountIdMismatch {
631 account_id: AccountId,
632 patch_account_id: AccountId,
633 },
634 #[error("new account {id} with public state must be accompanied by a creation patch")]
635 NewPublicStateAccountRequiresCreationPatch { id: AccountId, source: AccountError },
636 #[error(
637 "block account update's final commitment {final_state_commitment} and reconstructed account commitment {account_commitment} must match"
638 )]
639 AccountFinalCommitmentMismatch {
640 final_state_commitment: Word,
641 account_commitment: Word,
642 },
643}
644
645#[derive(Debug, Error)]
649pub enum BlockBodyError {
650 #[error("block has {0} account updates but at most {MAX_ACCOUNTS_PER_BLOCK} are allowed")]
651 TooManyAccountUpdates(usize),
652 #[error("block has {0} nullifiers but at most {MAX_INPUT_NOTES_PER_BLOCK} are allowed")]
653 TooManyNullifiers(usize),
654 #[error("block has {0} output note batches but at most {MAX_BATCHES_PER_BLOCK} are allowed")]
655 TooManyOutputNoteBatches(usize),
656 #[error(
657 "output note batch {batch_index} has {note_count} notes but at most {MAX_OUTPUT_NOTES_PER_BATCH} are allowed"
658 )]
659 TooManyOutputNotes { batch_index: usize, note_count: usize },
660 #[error("output note batch {batch_index} contains invalid note index {note_index}")]
661 InvalidOutputNoteIndex { batch_index: usize, note_index: usize },
662 #[error("output note batch {batch_index} contains note index {note_index} twice")]
663 DuplicateOutputNoteIndex { batch_index: usize, note_index: usize },
664 #[error("output note {0} appears twice in the block body")]
665 DuplicateOutputNote(NoteId),
666 #[error("account update for {0} appears twice in the block body")]
667 DuplicateAccountUpdate(AccountId),
668 #[error("account update for new account {account_id} is invalid")]
669 InvalidNewAccountUpdate {
670 account_id: AccountId,
671 source: BlockAccountUpdateError,
672 },
673 #[error("nullifier {0} appears twice in the block body")]
674 DuplicateNullifier(Nullifier),
675 #[error("transaction {0} appears twice in the block body")]
676 DuplicateTransaction(TransactionId),
677}
678
679#[derive(Debug, Error)]
683pub enum AssetError {
684 #[error(
685 "fungible asset amount {0} exceeds the max allowed amount of {max_amount}",
686 max_amount = FungibleAsset::MAX_AMOUNT
687 )]
688 FungibleAssetAmountTooBig(u64),
689 #[error("subtracting {subtrahend} from fungible asset amount {minuend} would underflow")]
690 FungibleAssetAmountNotSufficient { minuend: u64, subtrahend: u64 },
691 #[error(
692 "cannot combine fungible assets with different asset IDs: {original_id} and {other_id}"
693 )]
694 FungibleAssetInconsistentIds { original_id: AssetId, other_id: AssetId },
695 #[error("faucet account ID in asset is invalid")]
696 InvalidFaucetAccountId(#[source] Box<dyn Error + Send + Sync + 'static>),
697 #[error(
698 "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}"
699 )]
700 NonFungibleAssetClassMustMatchValue { asset_class: AssetClass, value: Word },
701 #[error("asset class prefix and suffix in a fungible asset ID must be zero but was {0}")]
702 FungibleAssetClassMustBeZero(AssetClass),
703 #[error(
704 "the three most significant elements in a fungible asset's value must be zero but provided value was {0}"
705 )]
706 FungibleAssetValueMostSignificantElementsMustBeZero(Word),
707 #[error("smt proof in asset witness contains invalid ID or value")]
708 AssetWitnessInvalid(#[source] Box<AssetError>),
709 #[error("asset ID {id} is not present in the provided asset witness SMT proof")]
710 AssetWitnessMissingId { id: AssetId },
711 #[error("unknown asset composition encoding: {0}")]
712 UnknownAssetComposition(u8),
713 #[error("unknown asset delta operation encoding: {0}")]
714 UnknownAssetDeltaOperation(u8),
715 #[error("asset composition {0:?} is not supported at this operational site")]
716 UnsupportedAssetComposition(AssetComposition),
717 #[error(
718 "asset composition mismatch for faucet {faucet_id}: expected {expected:?}, found {actual:?}"
719 )]
720 AssetCompositionMismatch {
721 faucet_id: AccountId,
722 expected: AssetComposition,
723 actual: AssetComposition,
724 },
725 #[error("asset metadata byte 0x{0:02x} has reserved bits set to non-zero values")]
726 ReservedAssetMetadata(u8),
727 #[error("unknown asset ID version: {0}")]
728 UnknownAssetIdVersion(u8),
729}
730
731#[derive(Debug, Error)]
735pub enum TokenSymbolError {
736 #[error("token symbol value {0} cannot exceed {max}", max = TokenSymbol::MAX_ENCODED_VALUE)]
737 ValueTooLarge(u64),
738 #[error(
739 "token symbol value {0} cannot be less than {min}",
740 min = TokenSymbol::MIN_ENCODED_VALUE
741 )]
742 ValueTooSmall(u64),
743 #[error("token symbol should have length between 1 and 12 characters, but {0} was provided")]
744 InvalidLength(usize),
745 #[error("token symbol contains a character that is not uppercase ASCII")]
746 InvalidCharacter,
747 #[error("token symbol data left after decoding the specified number of characters")]
748 DataNotFullyDecoded,
749}
750
751impl From<ShortCapitalStringError> for TokenSymbolError {
752 fn from(value: ShortCapitalStringError) -> Self {
753 match value {
754 ShortCapitalStringError::ValueTooLarge(v) => Self::ValueTooLarge(v),
755 ShortCapitalStringError::ValueTooSmall(v) => Self::ValueTooSmall(v),
756 ShortCapitalStringError::InvalidLength(v) => Self::InvalidLength(v),
757 ShortCapitalStringError::InvalidCharacter => Self::InvalidCharacter,
758 ShortCapitalStringError::DataNotFullyDecoded => Self::DataNotFullyDecoded,
759 }
760 }
761}
762
763#[derive(Debug, Error)]
767pub enum RoleSymbolError {
768 #[error("role symbol value {0} cannot exceed {max}", max = RoleSymbol::MAX_ENCODED_VALUE)]
769 ValueTooLarge(u64),
770 #[error("role symbol value {0} cannot be less than {min}", min = RoleSymbol::MIN_ENCODED_VALUE)]
771 ValueTooSmall(u64),
772 #[error("role symbol should have length between 1 and 12 characters, but {0} was provided")]
773 InvalidLength(usize),
774 #[error("role symbol contains a character that is not uppercase ASCII or underscore")]
775 InvalidCharacter,
776 #[error("role symbol data left after decoding the specified number of characters")]
777 DataNotFullyDecoded,
778}
779
780impl From<ShortCapitalStringError> for RoleSymbolError {
781 fn from(value: ShortCapitalStringError) -> Self {
782 match value {
783 ShortCapitalStringError::ValueTooLarge(v) => Self::ValueTooLarge(v),
784 ShortCapitalStringError::ValueTooSmall(v) => Self::ValueTooSmall(v),
785 ShortCapitalStringError::InvalidLength(v) => Self::InvalidLength(v),
786 ShortCapitalStringError::InvalidCharacter => Self::InvalidCharacter,
787 ShortCapitalStringError::DataNotFullyDecoded => Self::DataNotFullyDecoded,
788 }
789 }
790}
791
792#[derive(Debug, Error)]
796pub(crate) enum ShortCapitalStringError {
797 #[error("short capital string value {0} is too large")]
798 ValueTooLarge(u64),
799 #[error("short capital string value {0} is too small")]
800 ValueTooSmall(u64),
801 #[error(
802 "short capital string should have length between 1 and 12 characters, but {0} was provided"
803 )]
804 InvalidLength(usize),
805 #[error("short capital string contains an invalid character")]
806 InvalidCharacter,
807 #[error("short capital string data left after decoding the specified number of characters")]
808 DataNotFullyDecoded,
809}
810
811#[derive(Debug, Error)]
815pub enum AssetVaultError {
816 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
817 AddFungibleAssetBalanceError(#[source] AssetError),
818 #[error("provided assets contain duplicates")]
819 DuplicateAsset(#[source] MerkleError),
820 #[error("non fungible asset {0} already exists in the vault")]
821 DuplicateNonFungibleAsset(Asset),
822 #[error("fungible asset {0} does not exist in the vault")]
823 FungibleAssetNotFound(FungibleAsset),
824 #[error("non fungible asset {0} does not exist in the vault")]
825 NonFungibleAssetNotFound(Asset),
826 #[error("subtracting fungible asset amounts would underflow")]
827 SubtractFungibleAssetBalanceError(#[source] AssetError),
828 #[error("maximum number of asset vault leaves exceeded")]
829 MaxLeafEntriesExceeded(#[source] MerkleError),
830}
831
832#[derive(Debug, Error)]
836pub enum PartialAssetVaultError {
837 #[error("duplicate asset ID {0} in partial vault")]
838 DuplicateAssetId(AssetId),
839 #[error("partial vault contains invalid asset value {value} at ID {id}")]
840 InvalidAssetForId {
841 id: AssetId,
842 value: Word,
843 #[source]
844 source: AssetError,
845 },
846 #[error("failed to add asset proof")]
847 FailedToAddProof(#[source] MerkleError),
848 #[error("asset is not tracked in the partial vault")]
849 UntrackedAsset(#[source] MerkleError),
850}
851
852#[derive(Debug, Error)]
856pub enum NoteError {
857 #[error("error while creating note script: {0}")]
858 MastForestScript(#[source] MastForestScriptError),
859 #[error("note tag length {0} exceeds the maximum of {max}", max = NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)]
860 NoteTagLengthTooLarge(u8),
861 #[error("duplicate fungible asset from issuer {0} in note")]
862 DuplicateFungibleAsset(AccountId),
863 #[error("duplicate non fungible asset {0} in note")]
864 DuplicateNonFungibleAsset(Asset),
865 #[error("note type {0} is inconsistent with note tag {1}")]
866 InconsistentNoteTag(NoteType, u64),
867 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
868 AddFungibleAssetBalanceError(#[source] AssetError),
869 #[error("note sender is not a valid account ID")]
870 NoteSenderInvalidAccountId(#[source] AccountIdError),
871 #[error("note execution hint after block variant cannot contain u32::MAX")]
872 NoteExecutionHintAfterBlockCannotBeU32Max,
873 #[error("invalid note execution hint payload {1} for tag {0}")]
874 InvalidNoteExecutionHintPayload(u8, u32),
875 #[error(
876 "note type {0} does not match any of the valid note types {public} or {private}",
877 public = NoteType::Public,
878 private = NoteType::Private,
879 )]
880 UnknownNoteType(Box<str>),
881 #[error("block note tree index {block_note_tree_index} is out of bounds 0..={highest_index}")]
882 BlockNoteTreeIndexOutOfBounds {
883 block_note_tree_index: u16,
884 highest_index: usize,
885 },
886 #[error("note network execution requires a public note but note is of type {0}")]
887 NetworkExecutionRequiresPublicNote(NoteType),
888 #[error("failed to assemble note script:\n{}", PrintDiagnostic::new(.0))]
889 NoteScriptAssemblyError(Report),
890 #[error("failed to deserialize note script")]
891 NoteScriptDeserializationError(#[source] DeserializationError),
892 #[error("note contains {0} assets which exceeds the maximum of {max}", max = NoteAssets::MAX_NUM_ASSETS)]
893 TooManyAssets(usize),
894 #[error("note contains {0} storage items which exceeds the maximum of {max}", max = MAX_NOTE_STORAGE_ITEMS)]
895 TooManyStorageItems(usize),
896 #[error("invalid note storage length: expected {expected} items, got {actual}")]
897 InvalidNoteStorageLength { expected: usize, actual: usize },
898 #[error("note tag requires a public note but the note is of type {0}")]
899 PublicNoteRequired(NoteType),
900 #[error("note attachment content must have at least one word")]
901 NoteAttachmentContentEmpty,
902 #[error(
903 "note attachment content contains {0} words, but the maximum is {max} words",
904 max = NoteAttachment::MAX_NUM_WORDS
905 )]
906 NoteAttachmentContentTooManyWords(usize),
907 #[error(
908 "note attachments contain a total of {0} words, but the maximum allowed is {max} words",
909 max = NoteAttachments::MAX_NUM_WORDS
910 )]
911 NoteAttachmentsTooManyWords(usize),
912 #[error(
913 "attachment size {0} exceeds maximum {max}",
914 max = NoteAttachment::MAX_NUM_WORDS
915 )]
916 NoteAttachmentHeaderSizeExceeded(u8),
917 #[error("{0} attachments were provided but maximum is {max}", max = NoteAttachments::MAX_COUNT)]
918 TooManyAttachments(usize),
919 #[error("attachment scheme {0} exceeds maximum value of {max}", max = NoteAttachmentScheme::MAX)]
920 NoteAttachmentSchemeExceeded(u32),
921 #[error("attachment scheme value 0 is reserved")]
922 NoteAttachmentSchemeZeroReserved,
923 #[error("{error_msg}")]
924 Other {
925 error_msg: Box<str>,
926 source: Option<Box<dyn Error + Send + Sync + 'static>>,
928 },
929}
930
931impl NoteError {
932 pub fn other(message: impl Into<String>) -> Self {
934 let message: String = message.into();
935 Self::Other { error_msg: message.into(), source: None }
936 }
937
938 pub fn other_with_source(
941 message: impl Into<String>,
942 source: impl Error + Send + Sync + 'static,
943 ) -> Self {
944 let message: String = message.into();
945 Self::Other {
946 error_msg: message.into(),
947 source: Some(Box::new(source)),
948 }
949 }
950}
951
952#[derive(Debug, Error)]
956pub enum PartialBlockchainError {
957 #[error(
958 "block num {block_num} exceeds chain length {chain_length} implied by the partial blockchain"
959 )]
960 BlockNumTooBig {
961 chain_length: usize,
962 block_num: BlockNumber,
963 },
964
965 #[error("duplicate block {block_num} in partial blockchain")]
966 DuplicateBlock { block_num: BlockNumber },
967
968 #[error("partial blockchain does not track authentication paths for block {block_num}")]
969 UntrackedBlock { block_num: BlockNumber },
970
971 #[error(
972 "provided block header with number {block_num} and commitment {block_commitment} is not tracked by partial MMR"
973 )]
974 BlockHeaderCommitmentMismatch {
975 block_num: BlockNumber,
976 block_commitment: Word,
977 source: MmrError,
978 },
979}
980
981impl PartialBlockchainError {
982 pub fn block_num_too_big(chain_length: usize, block_num: BlockNumber) -> Self {
983 Self::BlockNumTooBig { chain_length, block_num }
984 }
985
986 pub fn duplicate_block(block_num: BlockNumber) -> Self {
987 Self::DuplicateBlock { block_num }
988 }
989
990 pub fn untracked_block(block_num: BlockNumber) -> Self {
991 Self::UntrackedBlock { block_num }
992 }
993}
994
995#[derive(Debug, Error)]
999pub enum TransactionInputError {
1000 #[error("transaction input note with nullifier {0} is a duplicate")]
1001 DuplicateInputNote(Nullifier),
1002 #[error("partial blockchain has length {actual} which does not match block number {expected}")]
1003 InconsistentChainLength {
1004 expected: BlockNumber,
1005 actual: BlockNumber,
1006 },
1007 #[error(
1008 "partial blockchain has commitment {actual} which does not match the block header's chain commitment {expected}"
1009 )]
1010 InconsistentChainCommitment { expected: Word, actual: Word },
1011 #[error(
1012 "protocol config has commitment {actual} which does not match the block header's protocol config commitment {expected}"
1013 )]
1014 InconsistentProtocolConfig { expected: Word, actual: Word },
1015 #[error("block in which input note with id {0} was created is not in partial blockchain")]
1016 InputNoteBlockNotInPartialBlockchain(NoteId),
1017 #[error("input note with id {0} was not created in block {1}")]
1018 InputNoteNotInBlock(NoteId, BlockNumber),
1019 #[error(
1020 "total number of input notes is {0} which exceeds the maximum of {MAX_INPUT_NOTES_PER_TX}"
1021 )]
1022 TooManyInputNotes(usize),
1023}
1024
1025#[derive(Debug, Error)]
1029pub enum TransactionInputsExtractionError {
1030 #[error("specified foreign account id matches the transaction input's account id")]
1031 AccountNotForeign,
1032 #[error("foreign account data not found in advice map for account {0}")]
1033 ForeignAccountNotFound(AccountId),
1034 #[error("foreign account code not found for account {0}")]
1035 ForeignAccountCodeNotFound(AccountId),
1036 #[error("storage header data not found in advice map for account {0}")]
1037 StorageHeaderNotFound(AccountId),
1038 #[error("failed to handle account data")]
1039 AccountError(#[from] AccountError),
1040 #[error("failed to handle merkle data")]
1041 MerkleError(#[from] MerkleError),
1042 #[error("failed to handle account tree data")]
1043 AccountTreeError(#[from] AccountTreeError),
1044 #[error("missing vault root from Merkle store")]
1045 MissingVaultRoot,
1046 #[error("missing storage map root from Merkle store")]
1047 MissingMapRoot,
1048 #[error("failed to construct SMT proof")]
1049 SmtProofError(#[from] SmtProofError),
1050 #[error("failed to construct an asset")]
1051 AssetError(#[from] AssetError),
1052 #[error("failed to handle storage map data")]
1053 StorageMapError(#[from] StorageMapError),
1054 #[error("failed to convert elements to leaf index: {0}")]
1055 LeafConversionError(String),
1056 #[error("failed to construct SMT leaf")]
1057 SmtLeafError(#[from] SmtLeafError),
1058}
1059
1060#[derive(Debug, Error)]
1064pub enum TransactionOutputError {
1065 #[error("transaction output note with id {0} is a duplicate")]
1066 DuplicateOutputNote(NoteId),
1067 #[error("final account commitment is not in the advice map")]
1068 FinalAccountCommitmentMissingInAdviceMap,
1069 #[error("failed to parse final account header")]
1070 FinalAccountHeaderParseFailure(#[source] AccountError),
1071 #[error(
1072 "output notes commitment {expected} from kernel does not match computed commitment {actual}"
1073 )]
1074 OutputNotesCommitmentInconsistent { expected: Word, actual: Word },
1075 #[error("transaction kernel output stack is invalid: {0}")]
1076 OutputStackInvalid(String),
1077 #[error(
1078 "total number of output notes is {0} which exceeds the maximum of {MAX_OUTPUT_NOTES_PER_TX}"
1079 )]
1080 TooManyOutputNotes(usize),
1081 #[error("failed to process account update commitment: {0}")]
1082 AccountUpdateCommitment(Box<str>),
1083}
1084
1085#[derive(Debug, Error)]
1092pub enum OutputNoteError {
1093 #[error("attachment headers do not match attachments for private note with id {0}")]
1094 AttachmentHeadersMismatch(NoteId),
1095 #[error("attachments commitment does not match attachments for private note with id {0}")]
1096 AttachmentsCommitmentMismatch(NoteId),
1097 #[error("note with id {0} is private but expected a public note")]
1098 NoteIsPrivate(NoteId),
1099 #[error("note with id {0} is public but expected a private note")]
1100 NoteIsPublic(NoteId),
1101 #[error(
1102 "public note with id {note_id} has size {note_size} bytes which exceeds maximum note size of {NOTE_MAX_SIZE}"
1103 )]
1104 NoteSizeLimitExceeded { note_id: NoteId, note_size: usize },
1105}
1106
1107#[derive(Debug, Error)]
1111pub enum TransactionSummaryError {
1112 #[error(
1113 "transaction summary preimage contains {actual} elements but expected {expected} elements"
1114 )]
1115 InvalidPreimageLength { actual: usize, expected: usize },
1116 #[error("transaction summary metadata element {0} sets bits above the packed fields")]
1117 MetadataOutOfRange(Felt),
1118 #[error(
1119 "transaction summary layout version is {actual} but only version {expected} is supported"
1120 )]
1121 UnsupportedVersion { actual: Felt, expected: u8 },
1122}
1123
1124#[derive(Debug, Error)]
1128pub enum TransactionEventError {
1129 #[error("event id {0} is not a valid transaction event")]
1130 InvalidTransactionEvent(EventId),
1131}
1132
1133#[derive(Debug, Error)]
1137pub enum TransactionTraceParsingError {
1138 #[error("trace id {0} is an unknown transaction kernel trace")]
1139 UnknownTransactionTrace(u32),
1140}
1141
1142#[derive(Debug, Error)]
1146pub enum ProvenTransactionError {
1147 #[error(
1148 "proven transaction's final account commitment {tx_final_commitment} and account details commitment {details_commitment} must match"
1149 )]
1150 AccountFinalCommitmentMismatch {
1151 tx_final_commitment: Word,
1152 details_commitment: Word,
1153 },
1154 #[error(
1155 "proven transaction's final account ID {tx_account_id} and account details id {details_account_id} must match"
1156 )]
1157 AccountIdMismatch {
1158 tx_account_id: AccountId,
1159 details_account_id: AccountId,
1160 },
1161 #[error("failed to construct input notes for proven transaction")]
1162 InputNotesError(TransactionInputError),
1163 #[error("private account {0} should not have account details")]
1164 PrivateAccountWithDetails(AccountId),
1165 #[error("account {0} with public state is missing its account details")]
1166 PublicStateAccountMissingDetails(AccountId),
1167 #[error("new account {id} with public state must be accompanied by a creation patch")]
1168 NewPublicStateAccountRequiresCreationPatch { id: AccountId, source: AccountError },
1169 #[error(
1170 "existing account {0} with public state should only provide delta updates instead of full details"
1171 )]
1172 ExistingPublicStateAccountRequiresDeltaDetails(AccountId),
1173 #[error("failed to construct output notes for proven transaction")]
1174 OutputNotesError(#[source] TransactionOutputError),
1175 #[error(
1176 "account update of size {update_size} for account {account_id} exceeds maximum update size of {ACCOUNT_UPDATE_MAX_SIZE}"
1177 )]
1178 AccountUpdateSizeLimitExceeded {
1179 account_id: AccountId,
1180 update_size: usize,
1181 },
1182 #[error("proven transaction neither changed the account state, nor consumed any notes")]
1183 EmptyTransaction,
1184 #[error(
1185 "expected account patch commitment {expected_patch_commitment} but found {actual_patch_commitment}"
1186 )]
1187 AccountPatchCommitmentMismatch {
1188 expected_patch_commitment: Word,
1189 actual_patch_commitment: Word,
1190 },
1191 #[error("note with id {0} is both created and consumed by the transaction")]
1192 NoteCreatedAndConsumed(NoteId),
1193}
1194
1195#[derive(Debug, Error)]
1200#[non_exhaustive]
1201pub enum TransactionHeaderError {
1202 #[error("input note with nullifier {0} appears twice in the transaction header")]
1203 DuplicateInputNote(Nullifier),
1204 #[error("output note {0} appears twice in the transaction header")]
1205 DuplicateOutputNote(NoteId),
1206 #[error("note with id {0} is both created and consumed by the transaction header")]
1207 NoteCreatedAndConsumed(NoteId),
1208}
1209
1210impl From<AccountUpdateDetailsValidationError> for ProvenTransactionError {
1211 fn from(error: AccountUpdateDetailsValidationError) -> Self {
1212 match error {
1213 AccountUpdateDetailsValidationError::PrivateAccountWithDetails(account_id) => {
1214 Self::PrivateAccountWithDetails(account_id)
1215 },
1216 AccountUpdateDetailsValidationError::PublicStateAccountMissingDetails(account_id) => {
1217 Self::PublicStateAccountMissingDetails(account_id)
1218 },
1219 AccountUpdateDetailsValidationError::AccountIdMismatch {
1220 account_id,
1221 patch_account_id,
1222 } => Self::AccountIdMismatch {
1223 tx_account_id: account_id,
1224 details_account_id: patch_account_id,
1225 },
1226 }
1227 }
1228}
1229
1230impl From<AccountUpdateSizeValidationError> for ProvenTransactionError {
1231 fn from(error: AccountUpdateSizeValidationError) -> Self {
1232 Self::AccountUpdateSizeLimitExceeded {
1233 account_id: error.account_id,
1234 update_size: error.update_size,
1235 }
1236 }
1237}
1238
1239impl From<AccountUpdateSizeValidationError> for BatchAccountUpdateError {
1240 fn from(error: AccountUpdateSizeValidationError) -> Self {
1241 Self::AccountUpdateSizeLimitExceeded {
1242 account_id: error.account_id,
1243 update_size: error.update_size,
1244 }
1245 }
1246}
1247
1248impl From<AccountUpdateDetailsValidationError> for BatchAccountUpdateError {
1249 fn from(error: AccountUpdateDetailsValidationError) -> Self {
1250 match error {
1251 AccountUpdateDetailsValidationError::PrivateAccountWithDetails(account_id) => {
1252 Self::PrivateAccountWithDetails(account_id)
1253 },
1254 AccountUpdateDetailsValidationError::PublicStateAccountMissingDetails(account_id) => {
1255 Self::PublicStateAccountMissingDetails(account_id)
1256 },
1257 AccountUpdateDetailsValidationError::AccountIdMismatch {
1258 account_id,
1259 patch_account_id,
1260 } => Self::AccountIdMismatch { account_id, patch_account_id },
1261 }
1262 }
1263}
1264
1265impl From<AccountUpdateDetailsValidationError> for BlockAccountUpdateError {
1266 fn from(error: AccountUpdateDetailsValidationError) -> Self {
1267 match error {
1268 AccountUpdateDetailsValidationError::PrivateAccountWithDetails(account_id) => {
1269 Self::PrivateAccountWithDetails(account_id)
1270 },
1271 AccountUpdateDetailsValidationError::PublicStateAccountMissingDetails(account_id) => {
1272 Self::PublicStateAccountMissingDetails(account_id)
1273 },
1274 AccountUpdateDetailsValidationError::AccountIdMismatch {
1275 account_id,
1276 patch_account_id,
1277 } => Self::AccountIdMismatch { account_id, patch_account_id },
1278 }
1279 }
1280}
1281
1282impl From<NewPublicAccountValidationError> for ProvenTransactionError {
1283 fn from(error: NewPublicAccountValidationError) -> Self {
1284 match error {
1285 NewPublicAccountValidationError::NotACreationPatch { id, source } => {
1286 Self::NewPublicStateAccountRequiresCreationPatch { id, source }
1287 },
1288 NewPublicAccountValidationError::FinalCommitmentMismatch {
1289 final_state_commitment,
1290 account_commitment,
1291 } => Self::AccountFinalCommitmentMismatch {
1292 tx_final_commitment: final_state_commitment,
1293 details_commitment: account_commitment,
1294 },
1295 }
1296 }
1297}
1298
1299impl From<NewPublicAccountValidationError> for BatchAccountUpdateError {
1300 fn from(error: NewPublicAccountValidationError) -> Self {
1301 match error {
1302 NewPublicAccountValidationError::NotACreationPatch { id, source } => {
1303 Self::NewPublicStateAccountRequiresCreationPatch { id, source }
1304 },
1305 NewPublicAccountValidationError::FinalCommitmentMismatch {
1306 final_state_commitment,
1307 account_commitment,
1308 } => Self::AccountFinalCommitmentMismatch {
1309 final_state_commitment,
1310 account_commitment,
1311 },
1312 }
1313 }
1314}
1315
1316impl From<NewPublicAccountValidationError> for BlockAccountUpdateError {
1317 fn from(error: NewPublicAccountValidationError) -> Self {
1318 match error {
1319 NewPublicAccountValidationError::NotACreationPatch { id, source } => {
1320 Self::NewPublicStateAccountRequiresCreationPatch { id, source }
1321 },
1322 NewPublicAccountValidationError::FinalCommitmentMismatch {
1323 final_state_commitment,
1324 account_commitment,
1325 } => Self::AccountFinalCommitmentMismatch {
1326 final_state_commitment,
1327 account_commitment,
1328 },
1329 }
1330 }
1331}
1332
1333#[derive(Debug, Error)]
1337pub enum ProposedBatchError {
1338 #[error("failed to verify transaction {transaction_id} in transaction batch")]
1339 TransactionVerificationFailed {
1340 transaction_id: TransactionId,
1341 source: TransactionVerifierError,
1342 },
1343
1344 #[error(
1345 "transaction batch has {0} input notes but at most {MAX_INPUT_NOTES_PER_BATCH} are allowed"
1346 )]
1347 TooManyInputNotes(usize),
1348
1349 #[error(
1350 "transaction batch has {0} output notes but at most {MAX_OUTPUT_NOTES_PER_BATCH} are allowed"
1351 )]
1352 TooManyOutputNotes(usize),
1353
1354 #[error(
1355 "transaction batch has {0} account updates but at most {MAX_ACCOUNTS_PER_BATCH} are allowed"
1356 )]
1357 TooManyAccountUpdates(usize),
1358
1359 #[error(
1360 "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}"
1361 )]
1362 ExpiredTransaction {
1363 transaction_id: TransactionId,
1364 transaction_expiration_num: BlockNumber,
1365 reference_block_num: BlockNumber,
1366 },
1367
1368 #[error("transaction batch must contain at least one transaction")]
1369 EmptyTransactionBatch,
1370
1371 #[error("transaction {transaction_id} appears twice in the proposed batch input")]
1372 DuplicateTransaction { transaction_id: TransactionId },
1373
1374 #[error(
1375 "transaction {second_transaction_id} consumes the note with nullifier {note_nullifier} that is also consumed by another transaction {first_transaction_id} in the batch"
1376 )]
1377 DuplicateInputNote {
1378 note_nullifier: Nullifier,
1379 first_transaction_id: TransactionId,
1380 second_transaction_id: TransactionId,
1381 },
1382
1383 #[error(
1384 "transaction {second_transaction_id} creates the note with id {note_id} that is also created by another transaction {first_transaction_id} in the batch"
1385 )]
1386 DuplicateOutputNote {
1387 note_id: NoteId,
1388 first_transaction_id: TransactionId,
1389 second_transaction_id: TransactionId,
1390 },
1391
1392 #[error(
1393 "transaction {consumed_by} that consumes the note with ID {note_id} must be ordered before transaction {created_by} that creates the note"
1394 )]
1395 NoteConsumedBeforeCreated {
1396 note_id: NoteId,
1397 consumed_by: TransactionId,
1398 created_by: TransactionId,
1399 },
1400
1401 #[error("failed to merge transaction patch into account {account_id}")]
1402 AccountUpdateError {
1403 account_id: AccountId,
1404 source: BatchAccountUpdateError,
1405 },
1406
1407 #[error(
1408 "unable to prove unauthenticated note inclusion because block {block_number} in which note with id {note_id} was created is not in partial blockchain"
1409 )]
1410 UnauthenticatedInputNoteBlockNotInPartialBlockchain {
1411 block_number: BlockNumber,
1412 note_id: NoteId,
1413 },
1414
1415 #[error(
1416 "unable to prove unauthenticated note inclusion of note {note_id} in block {block_num}"
1417 )]
1418 UnauthenticatedNoteAuthenticationFailed {
1419 note_id: NoteId,
1420 block_num: BlockNumber,
1421 source: MerkleError,
1422 },
1423
1424 #[error("partial blockchain has length {actual} which does not match block number {expected}")]
1425 InconsistentChainLength {
1426 expected: BlockNumber,
1427 actual: BlockNumber,
1428 },
1429
1430 #[error(
1431 "partial blockchain has root {actual} which does not match block header's root {expected}"
1432 )]
1433 InconsistentChainRoot { expected: Word, actual: Word },
1434
1435 #[error(
1436 "block {block_num} referenced by transaction {transaction_id} is not in the partial blockchain"
1437 )]
1438 MissingTransactionReferenceBlock {
1439 transaction_id: TransactionId,
1440 block_num: BlockNumber,
1441 },
1442
1443 #[error(
1444 "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}"
1445 )]
1446 TransactionReferenceBlockCommitmentMismatch {
1447 transaction_id: TransactionId,
1448 block_num: BlockNumber,
1449 expected_block_commitment: Word,
1450 actual_block_commitment: Word,
1451 },
1452}
1453
1454#[derive(Debug, Error)]
1458pub enum ProvenBatchError {
1459 #[error("transaction batch must contain at least one transaction")]
1460 EmptyTransactionBatch,
1461 #[error("transaction {0} appears twice in the proven batch")]
1462 DuplicateTransaction(TransactionId),
1463 #[error(
1464 "transaction batch has {0} input notes but at most {MAX_INPUT_NOTES_PER_BATCH} are allowed"
1465 )]
1466 TooManyInputNotes(usize),
1467 #[error("input note with nullifier {0} appears twice in the proven batch")]
1468 DuplicateInputNote(Nullifier),
1469 #[error(
1470 "transaction batch has {0} output notes but at most {MAX_OUTPUT_NOTES_PER_BATCH} are allowed"
1471 )]
1472 TooManyOutputNotes(usize),
1473 #[error(
1474 "transaction batch has at least {0} account updates but at most {MAX_ACCOUNTS_PER_BATCH} are allowed"
1475 )]
1476 TooManyAccountUpdates(usize),
1477 #[error("output note {0} appears twice in the proven batch")]
1478 DuplicateOutputNote(NoteId),
1479 #[error("note with id {0} is both created and consumed by the proven batch")]
1480 NoteCreatedAndConsumed(NoteId),
1481 #[error("account {0} is updated more than once in the proven batch")]
1482 DuplicateAccountUpdate(AccountId),
1483 #[error("account update for {0} is missing from the proven batch")]
1484 MissingAccountUpdate(AccountId),
1485 #[error("account update for {0} has no corresponding transaction in the proven batch")]
1486 UnexpectedAccountUpdate(AccountId),
1487 #[error(
1488 "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}"
1489 )]
1490 TransactionAccountStateMismatch {
1491 account_id: AccountId,
1492 transaction_id: TransactionId,
1493 expected_initial_state_commitment: Word,
1494 actual_initial_state_commitment: Word,
1495 },
1496 #[error(
1497 "account update for {account_id} starts from state {actual}, but its first transaction starts from state {expected}"
1498 )]
1499 AccountUpdateInitialStateMismatch {
1500 account_id: AccountId,
1501 expected: Word,
1502 actual: Word,
1503 },
1504 #[error(
1505 "account update for {account_id} ends at state {actual}, but its last transaction ends at state {expected}"
1506 )]
1507 AccountUpdateFinalStateMismatch {
1508 account_id: AccountId,
1509 expected: Word,
1510 actual: Word,
1511 },
1512 #[error(
1513 "batch expiration block number {batch_expiration_block_num} is not greater than the reference block number {reference_block_num}"
1514 )]
1515 InvalidBatchExpirationBlockNum {
1516 batch_expiration_block_num: BlockNumber,
1517 reference_block_num: BlockNumber,
1518 },
1519 #[error("batch kernel execution failed")]
1520 BatchKernelExecutionFailed(#[source] ExecutionError),
1521 #[error("batch kernel proving failed")]
1522 BatchKernelProvingFailed(#[source] ExecutionError),
1523 #[error("precompile proving failed")]
1524 PrecompileProvingFailed(#[source] ExecutionError),
1525 #[error("batch proof contains precompiles")]
1526 BatchProofContainsPrecompiles,
1527 #[error("batch kernel produced an invalid output stack")]
1528 BatchKernelOutputInvalid(#[source] BatchOutputError),
1529}
1530
1531#[derive(Debug, Error)]
1535pub enum BatchOutputError {
1536 #[error("batch kernel output stack is invalid: {0}")]
1537 OutputStackInvalid(String),
1538 #[error("batch expiration block number {0} does not fit into a u32")]
1539 ExpirationBlockNumberTooLarge(Felt),
1540}
1541
1542#[derive(Debug, Error)]
1546pub enum BlockOutputError {
1547 #[error(
1548 "block kernel output stack has a non-zero element at index {index}, but everything past the nullifier commitment must be zero padding"
1549 )]
1550 PaddingNotZero { index: usize },
1551}
1552
1553#[derive(Debug, Error)]
1557pub enum ProposedBlockError {
1558 #[error("block must contain at least one transaction batch")]
1559 EmptyBlock,
1560
1561 #[error("block must contain at most {MAX_BATCHES_PER_BLOCK} transaction batches")]
1562 TooManyBatches,
1563
1564 #[error(
1565 "batch {batch_id} expired at block {batch_expiration_block_num} but the current block number is {current_block_num}"
1566 )]
1567 ExpiredBatch {
1568 batch_id: BatchId,
1569 batch_expiration_block_num: BlockNumber,
1570 current_block_num: BlockNumber,
1571 },
1572
1573 #[error("batch {batch_id} appears twice in the block inputs")]
1574 DuplicateBatch { batch_id: BatchId },
1575
1576 #[error(
1577 "batch {second_batch_id} consumes the note with nullifier {note_nullifier} that is also consumed by another batch {first_batch_id} in the block"
1578 )]
1579 DuplicateInputNote {
1580 note_nullifier: Nullifier,
1581 first_batch_id: BatchId,
1582 second_batch_id: BatchId,
1583 },
1584
1585 #[error(
1586 "batch {second_batch_id} creates the note with ID {note_id} that is also created by another batch {first_batch_id} in the block"
1587 )]
1588 DuplicateOutputNote {
1589 note_id: NoteId,
1590 first_batch_id: BatchId,
1591 second_batch_id: BatchId,
1592 },
1593
1594 #[error(
1595 "batch {consumed_by} that consumes the note with ID {note_id} must be ordered before batch {created_by} that creates the note"
1596 )]
1597 NoteConsumedBeforeCreated {
1598 note_id: NoteId,
1599 consumed_by: BatchId,
1600 created_by: BatchId,
1601 },
1602
1603 #[error(
1604 "timestamp {provided_timestamp} does not increase monotonically compared to timestamp {previous_timestamp} from the previous block header"
1605 )]
1606 TimestampDoesNotIncreaseMonotonically {
1607 provided_timestamp: u32,
1608 previous_timestamp: u32,
1609 },
1610
1611 #[error(
1612 "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}"
1613 )]
1614 ConflictingBatchesUpdateSameAccount {
1615 account_id: AccountId,
1616 initial_state_commitment: Word,
1617 first_batch_id: BatchId,
1618 second_batch_id: BatchId,
1619 },
1620
1621 #[error(
1622 "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"
1623 )]
1624 ChainLengthNotEqualToPreviousBlockNumber {
1625 chain_length: BlockNumber,
1626 prev_block_num: BlockNumber,
1627 },
1628
1629 #[error(
1630 "partial blockchain has commitment {chain_commitment} which does not match the chain commitment {prev_block_chain_commitment} of the previous block {prev_block_num}"
1631 )]
1632 ChainRootNotEqualToPreviousBlockChainCommitment {
1633 chain_commitment: Word,
1634 prev_block_chain_commitment: Word,
1635 prev_block_num: BlockNumber,
1636 },
1637
1638 #[error(
1639 "partial blockchain is missing block {reference_block_num} referenced by batch {batch_id} in the block"
1640 )]
1641 BatchReferenceBlockMissingFromChain {
1642 reference_block_num: BlockNumber,
1643 batch_id: BatchId,
1644 },
1645
1646 #[error(
1647 "failed to prove unauthenticated note inclusion because block {block_number} in which note with id {note_id} was created is not in partial blockchain"
1648 )]
1649 UnauthenticatedInputNoteBlockNotInPartialBlockchain {
1650 block_number: BlockNumber,
1651 note_id: NoteId,
1652 },
1653
1654 #[error(
1655 "failed to prove unauthenticated note inclusion of note {note_id} in block {block_num}"
1656 )]
1657 UnauthenticatedNoteAuthenticationFailed {
1658 note_id: NoteId,
1659 block_num: BlockNumber,
1660 source: MerkleError,
1661 },
1662
1663 #[error(
1664 "unauthenticated note with nullifier {nullifier} was not created in the same block and no inclusion proof to authenticate it was provided"
1665 )]
1666 UnauthenticatedNoteConsumed { nullifier: Nullifier },
1667
1668 #[error("block inputs do not contain a proof of inclusion for account {0}")]
1669 MissingAccountWitness(AccountId),
1670
1671 #[error(
1672 "account {account_id} with state {state_commitment} cannot transition to any of the remaining states {}",
1673 remaining_state_commitments.iter().map(Word::to_hex).collect::<Vec<_>>().join(", ")
1674 )]
1675 InconsistentAccountStateTransition {
1676 account_id: AccountId,
1677 state_commitment: Word,
1678 remaining_state_commitments: Vec<Word>,
1679 },
1680
1681 #[error("no proof for nullifier {0} was provided")]
1682 NullifierProofMissing(Nullifier),
1683
1684 #[error("note with nullifier {0} is already spent")]
1685 NullifierSpent(Nullifier),
1686
1687 #[error("failed to merge transaction patch into account {account_id}")]
1688 AccountUpdateError {
1689 account_id: AccountId,
1690 source: Box<AccountPatchError>,
1691 },
1692
1693 #[error("failed to track account witness")]
1694 AccountWitnessTracking { source: AccountTreeError },
1695
1696 #[error(
1697 "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"
1698 )]
1699 StaleAccountTreeRoot {
1700 prev_block_account_root: Word,
1701 stale_account_root: Word,
1702 },
1703
1704 #[error("account ID prefix already exists in the tree")]
1705 AccountIdPrefixDuplicate { source: AccountTreeError },
1706
1707 #[error(
1708 "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"
1709 )]
1710 StaleNullifierTreeRoot {
1711 prev_block_nullifier_root: Word,
1712 stale_nullifier_root: Word,
1713 },
1714
1715 #[error("nullifier witness has a different root than the current nullifier tree root")]
1716 NullifierWitnessRootMismatch(NullifierTreeError),
1717}
1718
1719#[derive(Debug, thiserror::Error)]
1724#[non_exhaustive]
1725pub enum ProtocolConfigError {
1726 #[error("fee asset composition {0:?} is not supported, it must be fungible")]
1727 FeeAssetMustBeFungible(AssetComposition),
1728 #[error("minimum proof security must be at least one bit")]
1729 MinimumSecurityBitsMustBeNonZero,
1730 #[error("next protocol config cannot become effective at the genesis block")]
1731 NextConfigEffectiveAtGenesis,
1732 #[error(
1733 "kernel config contains {count} procedures but must contain at most {max}",
1734 max = KernelConfig::MAX_NUM_KERNEL_PROCEDURES,
1735 )]
1736 TooManyKernelProcedures { count: usize },
1737}
1738
1739#[derive(Debug, thiserror::Error)]
1744#[non_exhaustive]
1745pub enum ValidatorConfigError {
1746 #[error("validator set must contain at least one key")]
1747 EmptySet,
1748 #[error(
1749 "validator set contains {count} keys but must contain at most {max}",
1750 max = ValidatorConfig::MAX_VALIDATORS,
1751 )]
1752 TooManyKeys { count: usize },
1753 #[error("validator set contains duplicate public keys")]
1754 DuplicateKey,
1755 #[error("quorum is {quorum} but must equal the validator count of {count}")]
1756 QuorumMustEqualValidatorCount { quorum: u16, count: usize },
1757}
1758
1759#[derive(Debug, Error)]
1763pub enum NullifierTreeError {
1764 #[error(
1765 "entries passed to nullifier tree contain multiple block numbers for the same nullifier"
1766 )]
1767 DuplicateNullifierBlockNumbers(#[source] MerkleError),
1768
1769 #[error("attempt to mark nullifier {0} as spent but it is already spent")]
1770 NullifierAlreadySpent(Nullifier),
1771
1772 #[error("maximum number of nullifier tree leaves exceeded")]
1773 MaxLeafEntriesExceeded(#[source] MerkleError),
1774
1775 #[error("nullifier {nullifier} is not tracked by the partial nullifier tree")]
1776 UntrackedNullifier {
1777 nullifier: Nullifier,
1778 source: MerkleError,
1779 },
1780
1781 #[error("new tree root after nullifier witness insertion does not match previous tree root")]
1782 TreeRootConflict(#[source] MerkleError),
1783
1784 #[error("failed to compute nullifier tree mutations")]
1785 ComputeMutations(#[source] MerkleError),
1786
1787 #[error("invalid nullifier block number")]
1788 InvalidNullifierBlockNumber(Word),
1789}
1790
1791#[derive(Debug, Error)]
1795pub enum AuthSchemeError {
1796 #[error("auth scheme identifier `{0}` is not valid")]
1797 InvalidAuthSchemeIdentifier(String),
1798}
1799
1800#[derive(Debug, Error)]
1804pub enum TransactionVerifierError {
1805 #[error("failed to verify transaction")]
1806 TransactionVerificationFailed(#[source] VerificationError),
1807 #[error("transaction proof contains settled precompile work")]
1808 TransactionProofContainsPrecompiles,
1809 #[error("transaction proof security level is {actual} but must be at least {expected_minimum}")]
1810 InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 },
1811}