1use alloc::{boxed::Box, string::String};
2use core::error::Error;
3
4use assembly::{diagnostics::reporting::PrintDiagnostic, Report};
5use miden_crypto::utils::HexParseError;
6use thiserror::Error;
7use vm_core::{mast::MastForestError, Felt, FieldElement};
8use vm_processor::DeserializationError;
9
10use super::{
11 account::AccountId,
12 asset::{FungibleAsset, NonFungibleAsset},
13 crypto::merkle::MerkleError,
14 note::NoteId,
15 Digest, Word, MAX_ACCOUNTS_PER_BLOCK, MAX_BATCHES_PER_BLOCK, MAX_INPUT_NOTES_PER_BLOCK,
16 MAX_OUTPUT_NOTES_PER_BATCH, MAX_OUTPUT_NOTES_PER_BLOCK,
17};
18use crate::{
19 account::{
20 AccountCode, AccountIdPrefix, AccountStorage, AccountType, PlaceholderType,
21 StoragePlaceholder,
22 },
23 block::BlockNumber,
24 note::{NoteAssets, NoteExecutionHint, NoteTag, NoteType, Nullifier},
25 ACCOUNT_UPDATE_MAX_SIZE, MAX_INPUTS_PER_NOTE, MAX_INPUT_NOTES_PER_TX, MAX_OUTPUT_NOTES_PER_TX,
26};
27
28#[derive(Debug, Error)]
32pub enum AccountComponentTemplateError {
33 #[cfg(feature = "std")]
34 #[error("error trying to deserialize from toml")]
35 DeserializationError(#[source] toml::de::Error),
36 #[error("slot {0} is defined multiple times")]
37 DuplicateSlot(u8),
38 #[error("storage value was not of the expected type {0}")]
39 IncorrectStorageValue(String),
40 #[error("multi-slot entry should contain as many values as storage slots indices")]
41 MultiSlotArityMismatch,
42 #[error("error deserializing component metadata: {0}")]
43 MetadataDeserializationError(String),
44 #[error("component storage slots are not contiguous ({0} is followed by {1})")]
45 NonContiguousSlots(u8, u8),
46 #[error("storage value for placeholder `{0}` was not provided in the map")]
47 PlaceholderValueNotProvided(StoragePlaceholder),
48 #[error("storage map contains duplicate key `{0}`")]
49 StorageMapHasDuplicateKeys(String),
50 #[error("component storage slots have to start at 0, but they start at {0}")]
51 StorageSlotsDoNotStartAtZero(u8),
52 #[error(
53 "storage placeholder `{0}` appears more than once representing different types `{0}` and `{1}`"
54 )]
55 StoragePlaceholderTypeMismatch(StoragePlaceholder, PlaceholderType, PlaceholderType),
56}
57
58#[derive(Debug, Error)]
62pub enum AccountError {
63 #[error("failed to deserialize account code")]
64 AccountCodeDeserializationError(#[source] DeserializationError),
65 #[error("account code does not contain procedures but must contain at least one procedure")]
66 AccountCodeNoProcedures,
67 #[error("account code contains {0} procedures but it may contain at most {max} procedures", max = AccountCode::MAX_NUM_PROCEDURES)]
68 AccountCodeTooManyProcedures(usize),
69 #[error("account procedure {0}'s storage offset {1} does not fit into u8")]
70 AccountCodeProcedureStorageOffsetTooLarge(Digest, Felt),
71 #[error("account procedure {0}'s storage size {1} does not fit into u8")]
72 AccountCodeProcedureStorageSizeTooLarge(Digest, Felt),
73 #[error("account procedure {0}'s final two elements must be Felt::ZERO")]
74 AccountCodeProcedureInvalidPadding(Digest),
75 #[error("failed to assemble account component:\n{}", PrintDiagnostic::new(.0))]
76 AccountComponentAssemblyError(Report),
77 #[error("failed to merge components into one account code mast forest")]
78 AccountComponentMastForestMergeError(#[source] MastForestError),
79 #[error("procedure with MAST root {0} is present in multiple account components")]
80 AccountComponentDuplicateProcedureRoot(Digest),
81 #[error("failed to create account component")]
82 AccountComponentTemplateInstantiationError(#[source] AccountComponentTemplateError),
83 #[error("failed to update asset vault")]
84 AssetVaultUpdateError(#[source] AssetVaultError),
85 #[error("account build error: {0}")]
86 BuildError(String, #[source] Option<Box<AccountError>>),
87 #[error("faucet metadata decimals is {actual} which exceeds max value of {max}")]
88 FungibleFaucetTooManyDecimals { actual: u8, max: u8 },
89 #[error("faucet metadata max supply is {actual} which exceeds max value of {max}")]
90 FungibleFaucetMaxSupplyTooLarge { actual: u64, max: u64 },
91 #[error("account header data has length {actual} but it must be of length {expected}")]
92 HeaderDataIncorrectLength { actual: usize, expected: usize },
93 #[error("new account nonce {new} is less than the current nonce {current}")]
94 NonceNotMonotonicallyIncreasing { current: u64, new: u64 },
95 #[error("digest of the seed has {actual} trailing zeroes but must have at least {expected} trailing zeroes")]
96 SeedDigestTooFewTrailingZeros { expected: u32, actual: u32 },
97 #[error("storage slot at index {0} is not of type map")]
98 StorageSlotNotMap(u8),
99 #[error("storage slot at index {0} is not of type value")]
100 StorageSlotNotValue(u8),
101 #[error("storage slot index is {index} but the slots length is {slots_len}")]
102 StorageIndexOutOfBounds { slots_len: u8, index: u8 },
103 #[error("number of storage slots is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)]
104 StorageTooManySlots(u64),
105 #[error("procedure storage offset + size is {0} which exceeds the maximum value of {max}",
106 max = AccountStorage::MAX_NUM_STORAGE_SLOTS
107 )]
108 StorageOffsetPlusSizeOutOfBounds(u16),
109 #[error(
110 "procedure which does not access storage (storage size = 0) has non-zero storage offset"
111 )]
112 PureProcedureWithStorageOffset,
113 #[error("account component at index {component_index} is incompatible with account of type {account_type}")]
114 UnsupportedComponentForAccountType {
115 account_type: AccountType,
116 component_index: usize,
117 },
118 #[error("failed to parse account ID from final account header")]
119 FinalAccountHeaderIdParsingFailed(#[source] AccountIdError),
120 #[error("assumption violated: {0}")]
123 AssumptionViolated(String),
124}
125
126#[derive(Debug, Error)]
130pub enum AccountIdError {
131 #[error("failed to convert bytes into account ID prefix field element")]
132 AccountIdInvalidPrefixFieldElement(#[source] DeserializationError),
133 #[error("failed to convert bytes into account ID suffix field element")]
134 AccountIdInvalidSuffixFieldElement(#[source] DeserializationError),
135 #[error("`{0}` is not a known account storage mode")]
136 UnknownAccountStorageMode(Box<str>),
137 #[error(r#"`{0}` is not a known account type, expected one of "FungibleFaucet", "NonFungibleFaucet", "RegularAccountImmutableCode" or "RegularAccountUpdatableCode""#)]
138 UnknownAccountType(Box<str>),
139 #[error("failed to parse hex string into account ID")]
140 AccountIdHexParseError(#[source] HexParseError),
141 #[error("`{0}` is not a known account ID version")]
142 UnknownAccountIdVersion(u8),
143 #[error("anchor epoch in account ID must not be u16::MAX ({})", u16::MAX)]
144 AnchorEpochMustNotBeU16Max,
145 #[error("least significant byte of account ID suffix must be zero")]
146 AccountIdSuffixLeastSignificantByteMustBeZero,
147 #[error(
148 "anchor block must be an epoch block, that is, its block number must be a multiple of 2^{}",
149 BlockNumber::EPOCH_LENGTH_EXPONENT
150 )]
151 AnchorBlockMustBeEpochBlock,
152}
153
154#[derive(Debug, Error)]
158pub enum AccountDeltaError {
159 #[error("storage slot {0} was updated as a value and as a map")]
160 StorageSlotUsedAsDifferentTypes(u8),
161 #[error("non fungible vault can neither be added nor removed twice")]
162 DuplicateNonFungibleVaultUpdate(NonFungibleAsset),
163 #[error("fungible asset issued by faucet {faucet_id} has delta {delta} which overflows when added to current value {current}")]
164 FungibleAssetDeltaOverflow {
165 faucet_id: AccountId,
166 current: i64,
167 delta: i64,
168 },
169 #[error("account update of type `{left_update_type}` cannot be merged with account update of type `{right_update_type}`")]
170 IncompatibleAccountUpdates {
171 left_update_type: &'static str,
172 right_update_type: &'static str,
173 },
174 #[error("account delta could not be applied to account {account_id}")]
175 AccountDeltaApplicationFailed {
176 account_id: AccountId,
177 source: AccountError,
178 },
179 #[error("inconsistent nonce update: {0}")]
180 InconsistentNonceUpdate(String),
181 #[error("account ID {0} in fungible asset delta is not of type fungible faucet")]
182 NotAFungibleFaucetId(AccountId),
183}
184
185#[derive(Debug, Error)]
189pub enum AssetError {
190 #[error(
191 "fungible asset amount {0} exceeds the max allowed amount of {max_amount}",
192 max_amount = FungibleAsset::MAX_AMOUNT
193 )]
194 FungibleAssetAmountTooBig(u64),
195 #[error("subtracting {subtrahend} from fungible asset amount {minuend} would overflow")]
196 FungibleAssetAmountNotSufficient { minuend: u64, subtrahend: u64 },
197 #[error("fungible asset word {hex} does not contain expected ZERO at word index 1",
198 hex = vm_core::utils::to_hex(Felt::elements_as_bytes(.0))
199 )]
200 FungibleAssetExpectedZero(Word),
201 #[error("cannot add fungible asset with issuer {other_issuer} to fungible asset with issuer {original_issuer}")]
202 FungibleAssetInconsistentFaucetIds {
203 original_issuer: AccountId,
204 other_issuer: AccountId,
205 },
206 #[error("faucet account ID in asset is invalid")]
207 InvalidFaucetAccountId(#[source] Box<dyn Error + Send + Sync + 'static>),
208 #[error(
209 "faucet id {0} of type {id_type} must be of type {expected_ty} for fungible assets",
210 id_type = .0.account_type(),
211 expected_ty = AccountType::FungibleFaucet
212 )]
213 FungibleFaucetIdTypeMismatch(AccountId),
214 #[error(
215 "faucet id {0} of type {id_type} must be of type {expected_ty} for non fungible assets",
216 id_type = .0.account_type(),
217 expected_ty = AccountType::NonFungibleFaucet
218 )]
219 NonFungibleFaucetIdTypeMismatch(AccountIdPrefix),
220 #[error("{0}")]
221 TokenSymbolError(String),
222}
223
224#[derive(Debug, Error)]
228pub enum AssetVaultError {
229 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
230 AddFungibleAssetBalanceError(#[source] AssetError),
231 #[error("provided assets contain duplicates")]
232 DuplicateAsset(#[source] MerkleError),
233 #[error("non fungible asset {0} already exists in the vault")]
234 DuplicateNonFungibleAsset(NonFungibleAsset),
235 #[error("fungible asset {0} does not exist in the vault")]
236 FungibleAssetNotFound(FungibleAsset),
237 #[error("faucet id {0} is not a fungible faucet id")]
238 NotAFungibleFaucetId(AccountId),
239 #[error("non fungible asset {0} does not exist in the vault")]
240 NonFungibleAssetNotFound(NonFungibleAsset),
241 #[error("subtracting fungible asset amounts would underflow")]
242 SubtractFungibleAssetBalanceError(#[source] AssetError),
243}
244
245#[derive(Debug, Error)]
249pub enum NoteError {
250 #[error("duplicate fungible asset from issuer {0} in note")]
251 DuplicateFungibleAsset(AccountId),
252 #[error("duplicate non fungible asset {0} in note")]
253 DuplicateNonFungibleAsset(NonFungibleAsset),
254 #[error("note type {0:?} is inconsistent with note tag {1}")]
255 InconsistentNoteTag(NoteType, u64),
256 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
257 AddFungibleAssetBalanceError(#[source] AssetError),
258 #[error("note sender is not a valid account ID")]
259 NoteSenderInvalidAccountId(#[source] AccountIdError),
260 #[error("note tag use case {0} must be less than 2^{exp}", exp = NoteTag::MAX_USE_CASE_ID_EXPONENT)]
261 NoteTagUseCaseTooLarge(u16),
262 #[error(
263 "note execution hint tag {0} must be in range {from}..={to}",
264 from = NoteExecutionHint::NONE_TAG,
265 to = NoteExecutionHint::ON_BLOCK_SLOT_TAG,
266 )]
267 NoteExecutionHintTagOutOfRange(u8),
268 #[error("note execution hint after block variant cannot contain u32::MAX")]
269 NoteExecutionHintAfterBlockCannotBeU32Max,
270 #[error("invalid note execution hint payload {1} for tag {0}")]
271 InvalidNoteExecutionHintPayload(u8, u32),
272 #[error("note type {0:b} does not match any of the valid note types {public}, {private} or {encrypted}",
273 public = NoteType::Public as u8,
274 private = NoteType::Private as u8,
275 encrypted = NoteType::Encrypted as u8,
276 )]
277 InvalidNoteType(u64),
278 #[error("note location index {node_index_in_block} is out of bounds 0..={highest_index}")]
279 NoteLocationIndexOutOfBounds {
280 node_index_in_block: u16,
281 highest_index: usize,
282 },
283 #[error("note network execution requires account stored on chain")]
284 NetworkExecutionRequiresOnChainAccount,
285 #[error("note network execution requires a public note but note is of type {0:?}")]
286 NetworkExecutionRequiresPublicNote(NoteType),
287 #[error("failed to assemble note script:\n{}", PrintDiagnostic::new(.0))]
288 NoteScriptAssemblyError(Report),
289 #[error("failed to deserialize note script")]
290 NoteScriptDeserializationError(#[source] DeserializationError),
291 #[error("public use case requires a public note but note is of type {0:?}")]
292 PublicUseCaseRequiresPublicNote(NoteType),
293 #[error("note contains {0} assets which exceeds the maximum of {max}", max = NoteAssets::MAX_NUM_ASSETS)]
294 TooManyAssets(usize),
295 #[error("note contains {0} inputs which exceeds the maximum of {max}", max = MAX_INPUTS_PER_NOTE)]
296 TooManyInputs(usize),
297}
298
299#[derive(Debug, Error)]
303pub enum ChainMmrError {
304 #[error("block num {block_num} exceeds chain length {chain_length} implied by the chain MMR")]
305 BlockNumTooBig {
306 chain_length: usize,
307 block_num: BlockNumber,
308 },
309 #[error("duplicate block {block_num} in chain MMR")]
310 DuplicateBlock { block_num: BlockNumber },
311 #[error("chain MMR does not track authentication paths for block {block_num}")]
312 UntrackedBlock { block_num: BlockNumber },
313}
314
315impl ChainMmrError {
316 pub fn block_num_too_big(chain_length: usize, block_num: BlockNumber) -> Self {
317 Self::BlockNumTooBig { chain_length, block_num }
318 }
319
320 pub fn duplicate_block(block_num: BlockNumber) -> Self {
321 Self::DuplicateBlock { block_num }
322 }
323
324 pub fn untracked_block(block_num: BlockNumber) -> Self {
325 Self::UntrackedBlock { block_num }
326 }
327}
328
329#[derive(Debug, Error)]
333pub enum TransactionScriptError {
334 #[error("failed to assemble transaction script:\n{}", PrintDiagnostic::new(.0))]
335 AssemblyError(Report),
336}
337
338#[derive(Debug, Error)]
342pub enum TransactionInputError {
343 #[error("account seed must be provided for new accounts")]
344 AccountSeedNotProvidedForNewAccount,
345 #[error("account seed must not be provided for existing accounts")]
346 AccountSeedProvidedForExistingAccount,
347 #[error(
348 "anchor block header for epoch {0} (block number = {block_number}) must be provided in the chain mmr for the new account",
349 block_number = BlockNumber::from_epoch(*.0),
350 )]
351 AnchorBlockHeaderNotProvidedForNewAccount(u16),
352 #[error("transaction input note with nullifier {0} is a duplicate")]
353 DuplicateInputNote(Nullifier),
354 #[error("ID {expected} of the new account does not match the ID {actual} computed from the provided seed")]
355 InconsistentAccountSeed { expected: AccountId, actual: AccountId },
356 #[error("chain mmr has length {actual} which does not match block number {expected} ")]
357 InconsistentChainLength {
358 expected: BlockNumber,
359 actual: BlockNumber,
360 },
361 #[error("chain mmr has root {actual} which does not match block header's root {expected}")]
362 InconsistentChainRoot { expected: Digest, actual: Digest },
363 #[error("block in which input note with id {0} was created is not in chain mmr")]
364 InputNoteBlockNotInChainMmr(NoteId),
365 #[error("input note with id {0} was not created in block {1}")]
366 InputNoteNotInBlock(NoteId, BlockNumber),
367 #[error("account ID computed from seed is invalid")]
368 InvalidAccountIdSeed(#[source] AccountIdError),
369 #[error(
370 "total number of input notes is {0} which exceeds the maximum of {MAX_INPUT_NOTES_PER_TX}"
371 )]
372 TooManyInputNotes(usize),
373}
374
375#[derive(Debug, Error)]
379pub enum TransactionOutputError {
380 #[error("transaction output note with id {0} is a duplicate")]
381 DuplicateOutputNote(NoteId),
382 #[error("final account hash is not in the advice map")]
383 FinalAccountHashMissingInAdviceMap,
384 #[error("failed to parse final account header")]
385 FinalAccountHeaderParseFailure(#[source] AccountError),
386 #[error("output notes commitment {expected} from kernel does not match computed commitment {actual}")]
387 OutputNotesCommitmentInconsistent { expected: Digest, actual: Digest },
388 #[error("transaction kernel output stack is invalid: {0}")]
389 OutputStackInvalid(String),
390 #[error("total number of output notes is {0} which exceeds the maximum of {MAX_OUTPUT_NOTES_PER_TX}")]
391 TooManyOutputNotes(usize),
392}
393
394#[derive(Debug, Error)]
398pub enum ProvenTransactionError {
399 #[error("proven transaction's final account hash {tx_final_hash} and account details hash {details_hash} must match")]
400 AccountFinalHashMismatch {
401 tx_final_hash: Digest,
402 details_hash: Digest,
403 },
404 #[error("proven transaction's final account ID {tx_account_id} and account details id {details_account_id} must match")]
405 AccountIdMismatch {
406 tx_account_id: AccountId,
407 details_account_id: AccountId,
408 },
409 #[error("failed to construct input notes for proven transaction")]
410 InputNotesError(TransactionInputError),
411 #[error("off-chain account {0} should not have account details")]
412 OffChainAccountWithDetails(AccountId),
413 #[error("on-chain account {0} is missing its account details")]
414 OnChainAccountMissingDetails(AccountId),
415 #[error("new on-chain account {0} is missing its account details")]
416 NewOnChainAccountRequiresFullDetails(AccountId),
417 #[error(
418 "existing on-chain account {0} should only provide delta updates instead of full details"
419 )]
420 ExistingOnChainAccountRequiresDeltaDetails(AccountId),
421 #[error("failed to construct output notes for proven transaction")]
422 OutputNotesError(TransactionOutputError),
423 #[error(
424 "account update of size {update_size} for account {account_id} exceeds maximum update size of {ACCOUNT_UPDATE_MAX_SIZE}",
425 )]
426 AccountUpdateSizeLimitExceeded {
427 account_id: AccountId,
428 update_size: usize,
429 },
430}
431
432#[derive(Debug, Error)]
436pub enum BlockError {
437 #[error("duplicate note with id {0} in the block")]
438 DuplicateNoteFound(NoteId),
439 #[error("too many accounts updated in the block (max: {MAX_ACCOUNTS_PER_BLOCK}, actual: {0})")]
440 TooManyAccountUpdates(usize),
441 #[error("too many notes in the batch (max: {MAX_OUTPUT_NOTES_PER_BATCH}, actual: {0})")]
442 TooManyNotesInBatch(usize),
443 #[error("too many notes in the block (max: {MAX_OUTPUT_NOTES_PER_BLOCK}, actual: {0})")]
444 TooManyNotesInBlock(usize),
445 #[error("too many nullifiers in the block (max: {MAX_INPUT_NOTES_PER_BLOCK}, actual: {0})")]
446 TooManyNullifiersInBlock(usize),
447 #[error(
448 "too many transaction batches in the block (max: {MAX_BATCHES_PER_BLOCK}, actual: {0})"
449 )]
450 TooManyTransactionBatches(usize),
451}