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