1use alloc::{boxed::Box, string::String, vec::Vec};
2use core::error::Error;
3
4use assembly::{Report, diagnostics::reporting::PrintDiagnostic};
5use miden_crypto::utils::HexParseError;
6use thiserror::Error;
7use vm_core::{Felt, FieldElement, mast::MastForestError};
8use vm_processor::DeserializationError;
9
10use super::{
11 Digest, MAX_BATCHES_PER_BLOCK, MAX_OUTPUT_NOTES_PER_BATCH, Word,
12 account::AccountId,
13 asset::{FungibleAsset, NonFungibleAsset},
14 crypto::merkle::MerkleError,
15 note::NoteId,
16};
17use crate::{
18 ACCOUNT_UPDATE_MAX_SIZE, MAX_ACCOUNTS_PER_BATCH, MAX_INPUT_NOTES_PER_BATCH,
19 MAX_INPUT_NOTES_PER_TX, MAX_INPUTS_PER_NOTE, MAX_OUTPUT_NOTES_PER_TX,
20 account::{
21 AccountCode, AccountIdPrefix, AccountStorage, AccountType, AddressType, StorageValueName,
22 StorageValueNameError, TemplateTypeError,
23 },
24 batch::BatchId,
25 block::BlockNumber,
26 note::{NoteAssets, NoteExecutionHint, NoteTag, NoteType, Nullifier},
27 transaction::TransactionId,
28};
29
30#[derive(Debug, Error)]
34pub enum AccountComponentTemplateError {
35 #[error("storage slot name `{0}` is duplicate")]
36 DuplicateEntryNames(StorageValueName),
37 #[error("storage placeholder name `{0}` is duplicate")]
38 DuplicatePlaceholderName(StorageValueName),
39 #[error("slot {0} is defined multiple times")]
40 DuplicateSlot(u8),
41 #[error("storage value name is incorrect: {0}")]
42 IncorrectStorageValueName(#[source] StorageValueNameError),
43 #[error("type `{0}` is not valid for `{1}` slots")]
44 InvalidType(String, String),
45 #[error("error deserializing component metadata: {0}")]
46 MetadataDeserializationError(String),
47 #[error("multi-slot entry should contain as many values as storage slot indices")]
48 MultiSlotArityMismatch,
49 #[error("multi-slot entry slot range should occupy more than one storage slot")]
50 MultiSlotSpansOneSlot,
51 #[error("component storage slots are not contiguous ({0} is followed by {1})")]
52 NonContiguousSlots(u8, u8),
53 #[error("storage value for placeholder `{0}` was not provided in the init storage data")]
54 PlaceholderValueNotProvided(StorageValueName),
55 #[error("error converting value into expected type: ")]
56 StorageValueParsingError(#[source] TemplateTypeError),
57 #[error("storage map contains duplicate keys")]
58 StorageMapHasDuplicateKeys(#[source] Box<dyn Error + Send + Sync + 'static>),
59 #[error("component storage slots have to start at 0, but they start at {0}")]
60 StorageSlotsDoNotStartAtZero(u8),
61 #[cfg(feature = "std")]
62 #[error("error trying to deserialize from toml")]
63 TomlDeserializationError(#[source] toml::de::Error),
64 #[cfg(feature = "std")]
65 #[error("error trying to deserialize from toml")]
66 TomlSerializationError(#[source] toml::ser::Error),
67}
68
69#[derive(Debug, Error)]
73pub enum AccountError {
74 #[error("failed to deserialize account code")]
75 AccountCodeDeserializationError(#[source] DeserializationError),
76 #[error("account code does not contain procedures but must contain at least one procedure")]
77 AccountCodeNoProcedures,
78 #[error("account code contains {0} procedures but it may contain at most {max} procedures", max = AccountCode::MAX_NUM_PROCEDURES)]
79 AccountCodeTooManyProcedures(usize),
80 #[error("account procedure {0}'s storage offset {1} does not fit into u8")]
81 AccountCodeProcedureStorageOffsetTooLarge(Digest, Felt),
82 #[error("account procedure {0}'s storage size {1} does not fit into u8")]
83 AccountCodeProcedureStorageSizeTooLarge(Digest, Felt),
84 #[error("account procedure {0}'s final two elements must be Felt::ZERO")]
85 AccountCodeProcedureInvalidPadding(Digest),
86 #[error("failed to assemble account component:\n{}", PrintDiagnostic::new(.0))]
87 AccountComponentAssemblyError(Report),
88 #[error("failed to merge components into one account code mast forest")]
89 AccountComponentMastForestMergeError(#[source] MastForestError),
90 #[error("procedure with MAST root {0} is present in multiple account components")]
91 AccountComponentDuplicateProcedureRoot(Digest),
92 #[error("failed to create account component")]
93 AccountComponentTemplateInstantiationError(#[source] AccountComponentTemplateError),
94 #[error("failed to update asset vault")]
95 AssetVaultUpdateError(#[source] AssetVaultError),
96 #[error("account build error: {0}")]
97 BuildError(String, #[source] Option<Box<AccountError>>),
98 #[error("faucet metadata decimals is {actual} which exceeds max value of {max}")]
99 FungibleFaucetTooManyDecimals { actual: u8, max: u8 },
100 #[error("faucet metadata max supply is {actual} which exceeds max value of {max}")]
101 FungibleFaucetMaxSupplyTooLarge { actual: u64, max: u64 },
102 #[error("account header data has length {actual} but it must be of length {expected}")]
103 HeaderDataIncorrectLength { actual: usize, expected: usize },
104 #[error("new account nonce {new} is less than the current nonce {current}")]
105 NonceNotMonotonicallyIncreasing { current: u64, new: u64 },
106 #[error(
107 "digest of the seed has {actual} trailing zeroes but must have at least {expected} trailing zeroes"
108 )]
109 SeedDigestTooFewTrailingZeros { expected: u32, actual: u32 },
110 #[error("storage slot at index {0} is not of type map")]
111 StorageSlotNotMap(u8),
112 #[error("storage slot at index {0} is not of type value")]
113 StorageSlotNotValue(u8),
114 #[error("storage slot index is {index} but the slots length is {slots_len}")]
115 StorageIndexOutOfBounds { slots_len: u8, index: u8 },
116 #[error("number of storage slots is {0} but max possible number is {max}", max = AccountStorage::MAX_NUM_STORAGE_SLOTS)]
117 StorageTooManySlots(u64),
118 #[error("procedure storage offset + size is {0} which exceeds the maximum value of {max}",
119 max = AccountStorage::MAX_NUM_STORAGE_SLOTS
120 )]
121 StorageOffsetPlusSizeOutOfBounds(u16),
122 #[error(
123 "procedure which does not access storage (storage size = 0) has non-zero storage offset"
124 )]
125 PureProcedureWithStorageOffset,
126 #[error(
127 "account component at index {component_index} is incompatible with account of type {account_type}"
128 )]
129 UnsupportedComponentForAccountType {
130 account_type: AccountType,
131 component_index: usize,
132 },
133 #[error("failed to parse account ID from final account header")]
134 FinalAccountHeaderIdParsingFailed(#[source] AccountIdError),
135 #[error("assumption violated: {0}")]
138 AssumptionViolated(String),
139}
140
141#[derive(Debug, Error)]
145pub enum AccountIdError {
146 #[error("failed to convert bytes into account ID prefix field element")]
147 AccountIdInvalidPrefixFieldElement(#[source] DeserializationError),
148 #[error("failed to convert bytes into account ID suffix field element")]
149 AccountIdInvalidSuffixFieldElement(#[source] DeserializationError),
150 #[error("`{0}` is not a known account storage mode")]
151 UnknownAccountStorageMode(Box<str>),
152 #[error(r#"`{0}` is not a known account type, expected one of "FungibleFaucet", "NonFungibleFaucet", "RegularAccountImmutableCode" or "RegularAccountUpdatableCode""#)]
153 UnknownAccountType(Box<str>),
154 #[error("failed to parse hex string into account ID")]
155 AccountIdHexParseError(#[source] HexParseError),
156 #[error("`{0}` is not a known account ID version")]
157 UnknownAccountIdVersion(u8),
158 #[error("anchor epoch in account ID must not be u16::MAX ({})", u16::MAX)]
159 AnchorEpochMustNotBeU16Max,
160 #[error("least significant byte of account ID suffix must be zero")]
161 AccountIdSuffixLeastSignificantByteMustBeZero,
162 #[error(
163 "anchor block must be an epoch block, that is, its block number must be a multiple of 2^{}",
164 BlockNumber::EPOCH_LENGTH_EXPONENT
165 )]
166 AnchorBlockMustBeEpochBlock,
167 #[error("failed to decode bech32 string into account ID")]
168 Bech32DecodeError(#[source] Bech32Error),
169}
170
171#[derive(Debug, Error)]
175pub enum Bech32Error {
176 #[error("failed to decode bech32 string")]
177 DecodeError(#[source] Box<dyn Error + Send + Sync + 'static>),
178 #[error("found unknown address type {0} which is not the expected {account_addr} account ID address type",
179 account_addr = AddressType::AccountId as u8
180 )]
181 UnknownAddressType(u8),
182 #[error("expected bech32 data to be of length {expected} but it was of length {actual}")]
183 InvalidDataLength { expected: usize, actual: usize },
184}
185
186#[derive(Debug, Error)]
190pub enum NetworkIdError {
191 #[error("failed to parse string into a network ID")]
192 NetworkIdParseError(#[source] Box<dyn Error + Send + Sync + 'static>),
193}
194
195#[derive(Debug, Error)]
199pub enum AccountDeltaError {
200 #[error("storage slot {0} was updated as a value and as a map")]
201 StorageSlotUsedAsDifferentTypes(u8),
202 #[error("non fungible vault can neither be added nor removed twice")]
203 DuplicateNonFungibleVaultUpdate(NonFungibleAsset),
204 #[error(
205 "fungible asset issued by faucet {faucet_id} has delta {delta} which overflows when added to current value {current}"
206 )]
207 FungibleAssetDeltaOverflow {
208 faucet_id: AccountId,
209 current: i64,
210 delta: i64,
211 },
212 #[error(
213 "account update of type `{left_update_type}` cannot be merged with account update of type `{right_update_type}`"
214 )]
215 IncompatibleAccountUpdates {
216 left_update_type: &'static str,
217 right_update_type: &'static str,
218 },
219 #[error("account delta could not be applied to account {account_id}")]
220 AccountDeltaApplicationFailed {
221 account_id: AccountId,
222 source: AccountError,
223 },
224 #[error("inconsistent nonce update: {0}")]
225 InconsistentNonceUpdate(String),
226 #[error("account ID {0} in fungible asset delta is not of type fungible faucet")]
227 NotAFungibleFaucetId(AccountId),
228}
229
230#[derive(Debug, Error)]
234pub enum BatchAccountUpdateError {
235 #[error(
236 "account update for account {expected_account_id} cannot be merged with update from transaction {transaction} which was executed against account {actual_account_id}"
237 )]
238 AccountUpdateIdMismatch {
239 transaction: TransactionId,
240 expected_account_id: AccountId,
241 actual_account_id: AccountId,
242 },
243 #[error(
244 "final state commitment in account update from transaction {0} does not match initial state of current update"
245 )]
246 AccountUpdateInitialStateMismatch(TransactionId),
247 #[error("failed to merge account delta from transaction {0}")]
248 TransactionUpdateMergeError(TransactionId, #[source] AccountDeltaError),
249}
250
251#[derive(Debug, Error)]
255pub enum AssetError {
256 #[error(
257 "fungible asset amount {0} exceeds the max allowed amount of {max_amount}",
258 max_amount = FungibleAsset::MAX_AMOUNT
259 )]
260 FungibleAssetAmountTooBig(u64),
261 #[error("subtracting {subtrahend} from fungible asset amount {minuend} would overflow")]
262 FungibleAssetAmountNotSufficient { minuend: u64, subtrahend: u64 },
263 #[error("fungible asset word {hex} does not contain expected ZERO at word index 1",
264 hex = vm_core::utils::to_hex(Felt::elements_as_bytes(.0))
265 )]
266 FungibleAssetExpectedZero(Word),
267 #[error(
268 "cannot add fungible asset with issuer {other_issuer} to fungible asset with issuer {original_issuer}"
269 )]
270 FungibleAssetInconsistentFaucetIds {
271 original_issuer: AccountId,
272 other_issuer: AccountId,
273 },
274 #[error("faucet account ID in asset is invalid")]
275 InvalidFaucetAccountId(#[source] Box<dyn Error + Send + Sync + 'static>),
276 #[error(
277 "faucet id {0} of type {id_type} must be of type {expected_ty} for fungible assets",
278 id_type = .0.account_type(),
279 expected_ty = AccountType::FungibleFaucet
280 )]
281 FungibleFaucetIdTypeMismatch(AccountId),
282 #[error(
283 "faucet id {0} of type {id_type} must be of type {expected_ty} for non fungible assets",
284 id_type = .0.account_type(),
285 expected_ty = AccountType::NonFungibleFaucet
286 )]
287 NonFungibleFaucetIdTypeMismatch(AccountIdPrefix),
288 #[error("{0}")]
289 TokenSymbolError(String),
290}
291
292#[derive(Debug, Error)]
296pub enum AssetVaultError {
297 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
298 AddFungibleAssetBalanceError(#[source] AssetError),
299 #[error("provided assets contain duplicates")]
300 DuplicateAsset(#[source] MerkleError),
301 #[error("non fungible asset {0} already exists in the vault")]
302 DuplicateNonFungibleAsset(NonFungibleAsset),
303 #[error("fungible asset {0} does not exist in the vault")]
304 FungibleAssetNotFound(FungibleAsset),
305 #[error("faucet id {0} is not a fungible faucet id")]
306 NotAFungibleFaucetId(AccountId),
307 #[error("non fungible asset {0} does not exist in the vault")]
308 NonFungibleAssetNotFound(NonFungibleAsset),
309 #[error("subtracting fungible asset amounts would underflow")]
310 SubtractFungibleAssetBalanceError(#[source] AssetError),
311}
312
313#[derive(Debug, Error)]
317pub enum NoteError {
318 #[error("duplicate fungible asset from issuer {0} in note")]
319 DuplicateFungibleAsset(AccountId),
320 #[error("duplicate non fungible asset {0} in note")]
321 DuplicateNonFungibleAsset(NonFungibleAsset),
322 #[error("note type {0:?} is inconsistent with note tag {1}")]
323 InconsistentNoteTag(NoteType, u64),
324 #[error("adding fungible asset amounts would exceed maximum allowed amount")]
325 AddFungibleAssetBalanceError(#[source] AssetError),
326 #[error("note sender is not a valid account ID")]
327 NoteSenderInvalidAccountId(#[source] AccountIdError),
328 #[error("note tag use case {0} must be less than 2^{exp}", exp = NoteTag::MAX_USE_CASE_ID_EXPONENT)]
329 NoteTagUseCaseTooLarge(u16),
330 #[error(
331 "note execution hint tag {0} must be in range {from}..={to}",
332 from = NoteExecutionHint::NONE_TAG,
333 to = NoteExecutionHint::ON_BLOCK_SLOT_TAG,
334 )]
335 NoteExecutionHintTagOutOfRange(u8),
336 #[error("note execution hint after block variant cannot contain u32::MAX")]
337 NoteExecutionHintAfterBlockCannotBeU32Max,
338 #[error("invalid note execution hint payload {1} for tag {0}")]
339 InvalidNoteExecutionHintPayload(u8, u32),
340 #[error("note type {0:b} does not match any of the valid note types {public}, {private} or {encrypted}",
341 public = NoteType::Public as u8,
342 private = NoteType::Private as u8,
343 encrypted = NoteType::Encrypted as u8,
344 )]
345 InvalidNoteType(u64),
346 #[error("note location index {node_index_in_block} is out of bounds 0..={highest_index}")]
347 NoteLocationIndexOutOfBounds {
348 node_index_in_block: u16,
349 highest_index: usize,
350 },
351 #[error("note network execution requires public accounts")]
352 NetworkExecutionRequiresPublicAccount,
353 #[error("note network execution requires a public note but note is of type {0:?}")]
354 NetworkExecutionRequiresPublicNote(NoteType),
355 #[error("failed to assemble note script:\n{}", PrintDiagnostic::new(.0))]
356 NoteScriptAssemblyError(Report),
357 #[error("failed to deserialize note script")]
358 NoteScriptDeserializationError(#[source] DeserializationError),
359 #[error("public use case requires a public note but note is of type {0:?}")]
360 PublicUseCaseRequiresPublicNote(NoteType),
361 #[error("note contains {0} assets which exceeds the maximum of {max}", max = NoteAssets::MAX_NUM_ASSETS)]
362 TooManyAssets(usize),
363 #[error("note contains {0} inputs which exceeds the maximum of {max}", max = MAX_INPUTS_PER_NOTE)]
364 TooManyInputs(usize),
365}
366
367#[derive(Debug, Error)]
371pub enum ChainMmrError {
372 #[error("block num {block_num} exceeds chain length {chain_length} implied by the chain MMR")]
373 BlockNumTooBig {
374 chain_length: usize,
375 block_num: BlockNumber,
376 },
377 #[error("duplicate block {block_num} in chain MMR")]
378 DuplicateBlock { block_num: BlockNumber },
379 #[error("chain MMR does not track authentication paths for block {block_num}")]
380 UntrackedBlock { block_num: BlockNumber },
381}
382
383impl ChainMmrError {
384 pub fn block_num_too_big(chain_length: usize, block_num: BlockNumber) -> Self {
385 Self::BlockNumTooBig { chain_length, block_num }
386 }
387
388 pub fn duplicate_block(block_num: BlockNumber) -> Self {
389 Self::DuplicateBlock { block_num }
390 }
391
392 pub fn untracked_block(block_num: BlockNumber) -> Self {
393 Self::UntrackedBlock { block_num }
394 }
395}
396
397#[derive(Debug, Error)]
401pub enum TransactionScriptError {
402 #[error("failed to assemble transaction script:\n{}", PrintDiagnostic::new(.0))]
403 AssemblyError(Report),
404}
405
406#[derive(Debug, Error)]
410pub enum TransactionInputError {
411 #[error("account seed must be provided for new accounts")]
412 AccountSeedNotProvidedForNewAccount,
413 #[error("account seed must not be provided for existing accounts")]
414 AccountSeedProvidedForExistingAccount,
415 #[error(
416 "anchor block header for epoch {0} (block number = {block_number}) must be provided in the chain mmr for the new account",
417 block_number = BlockNumber::from_epoch(*.0),
418 )]
419 AnchorBlockHeaderNotProvidedForNewAccount(u16),
420 #[error("transaction input note with nullifier {0} is a duplicate")]
421 DuplicateInputNote(Nullifier),
422 #[error(
423 "ID {expected} of the new account does not match the ID {actual} computed from the provided seed"
424 )]
425 InconsistentAccountSeed { expected: AccountId, actual: AccountId },
426 #[error("chain mmr has length {actual} which does not match block number {expected}")]
427 InconsistentChainLength {
428 expected: BlockNumber,
429 actual: BlockNumber,
430 },
431 #[error(
432 "chain mmr has commitment {actual} which does not match the block header's chain commitment {expected}"
433 )]
434 InconsistentChainCommitment { expected: Digest, actual: Digest },
435 #[error("block in which input note with id {0} was created is not in chain mmr")]
436 InputNoteBlockNotInChainMmr(NoteId),
437 #[error("input note with id {0} was not created in block {1}")]
438 InputNoteNotInBlock(NoteId, BlockNumber),
439 #[error("account ID computed from seed is invalid")]
440 InvalidAccountIdSeed(#[source] AccountIdError),
441 #[error(
442 "total number of input notes is {0} which exceeds the maximum of {MAX_INPUT_NOTES_PER_TX}"
443 )]
444 TooManyInputNotes(usize),
445}
446
447#[derive(Debug, Error)]
451pub enum TransactionOutputError {
452 #[error("transaction output note with id {0} is a duplicate")]
453 DuplicateOutputNote(NoteId),
454 #[error("final account commitment is not in the advice map")]
455 FinalAccountHashMissingInAdviceMap,
456 #[error("failed to parse final account header")]
457 FinalAccountHeaderParseFailure(#[source] AccountError),
458 #[error(
459 "output notes commitment {expected} from kernel does not match computed commitment {actual}"
460 )]
461 OutputNotesCommitmentInconsistent { expected: Digest, actual: Digest },
462 #[error("transaction kernel output stack is invalid: {0}")]
463 OutputStackInvalid(String),
464 #[error(
465 "total number of output notes is {0} which exceeds the maximum of {MAX_OUTPUT_NOTES_PER_TX}"
466 )]
467 TooManyOutputNotes(usize),
468}
469
470#[derive(Debug, Error)]
474pub enum ProvenTransactionError {
475 #[error(
476 "proven transaction's final account commitment {tx_final_commitment} and account details commitment {details_commitment} must match"
477 )]
478 AccountFinalCommitmentMismatch {
479 tx_final_commitment: Digest,
480 details_commitment: Digest,
481 },
482 #[error(
483 "proven transaction's final account ID {tx_account_id} and account details id {details_account_id} must match"
484 )]
485 AccountIdMismatch {
486 tx_account_id: AccountId,
487 details_account_id: AccountId,
488 },
489 #[error("failed to construct input notes for proven transaction")]
490 InputNotesError(TransactionInputError),
491 #[error("private account {0} should not have account details")]
492 PrivateAccountWithDetails(AccountId),
493 #[error("public account {0} is missing its account details")]
494 PublicAccountMissingDetails(AccountId),
495 #[error("new public account {0} is missing its account details")]
496 NewPublicAccountRequiresFullDetails(AccountId),
497 #[error(
498 "existing public account {0} should only provide delta updates instead of full details"
499 )]
500 ExistingPublicAccountRequiresDeltaDetails(AccountId),
501 #[error("failed to construct output notes for proven transaction")]
502 OutputNotesError(TransactionOutputError),
503 #[error(
504 "account update of size {update_size} for account {account_id} exceeds maximum update size of {ACCOUNT_UPDATE_MAX_SIZE}"
505 )]
506 AccountUpdateSizeLimitExceeded {
507 account_id: AccountId,
508 update_size: usize,
509 },
510}
511
512#[derive(Debug, Error)]
516pub enum ProposedBatchError {
517 #[error(
518 "transaction batch has {0} input notes but at most {MAX_INPUT_NOTES_PER_BATCH} are allowed"
519 )]
520 TooManyInputNotes(usize),
521
522 #[error(
523 "transaction batch has {0} output notes but at most {MAX_OUTPUT_NOTES_PER_BATCH} are allowed"
524 )]
525 TooManyOutputNotes(usize),
526
527 #[error(
528 "transaction batch has {0} account updates but at most {MAX_ACCOUNTS_PER_BATCH} are allowed"
529 )]
530 TooManyAccountUpdates(usize),
531
532 #[error(
533 "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}"
534 )]
535 ExpiredTransaction {
536 transaction_id: TransactionId,
537 transaction_expiration_num: BlockNumber,
538 reference_block_num: BlockNumber,
539 },
540
541 #[error("transaction batch must contain at least one transaction")]
542 EmptyTransactionBatch,
543
544 #[error("transaction {transaction_id} appears twice in the proposed batch input")]
545 DuplicateTransaction { transaction_id: TransactionId },
546
547 #[error(
548 "transaction {second_transaction_id} consumes the note with nullifier {note_nullifier} that is also consumed by another transaction {first_transaction_id} in the batch"
549 )]
550 DuplicateInputNote {
551 note_nullifier: Nullifier,
552 first_transaction_id: TransactionId,
553 second_transaction_id: TransactionId,
554 },
555
556 #[error(
557 "transaction {second_transaction_id} creates the note with id {note_id} that is also created by another transaction {first_transaction_id} in the batch"
558 )]
559 DuplicateOutputNote {
560 note_id: NoteId,
561 first_transaction_id: TransactionId,
562 second_transaction_id: TransactionId,
563 },
564
565 #[error(
566 "note commitment mismatch for note {id}: (input: {input_commitment}, output: {output_commitment})"
567 )]
568 NoteCommitmentMismatch {
569 id: NoteId,
570 input_commitment: Digest,
571 output_commitment: Digest,
572 },
573
574 #[error("failed to merge transaction delta into account {account_id}")]
575 AccountUpdateError {
576 account_id: AccountId,
577 source: BatchAccountUpdateError,
578 },
579
580 #[error(
581 "unable to prove unauthenticated note inclusion because block {block_number} in which note with id {note_id} was created is not in chain mmr"
582 )]
583 UnauthenticatedInputNoteBlockNotInChainMmr {
584 block_number: BlockNumber,
585 note_id: NoteId,
586 },
587
588 #[error(
589 "unable to prove unauthenticated note inclusion of note {note_id} in block {block_num}"
590 )]
591 UnauthenticatedNoteAuthenticationFailed {
592 note_id: NoteId,
593 block_num: BlockNumber,
594 source: MerkleError,
595 },
596
597 #[error("chain mmr has length {actual} which does not match block number {expected}")]
598 InconsistentChainLength {
599 expected: BlockNumber,
600 actual: BlockNumber,
601 },
602
603 #[error("chain mmr has root {actual} which does not match block header's root {expected}")]
604 InconsistentChainRoot { expected: Digest, actual: Digest },
605
606 #[error(
607 "block {block_reference} referenced by transaction {transaction_id} is not in the chain mmr"
608 )]
609 MissingTransactionBlockReference {
610 block_reference: Digest,
611 transaction_id: TransactionId,
612 },
613}
614
615#[derive(Debug, Error)]
619pub enum ProposedBlockError {
620 #[error("block must contain at least one transaction batch")]
621 EmptyBlock,
622
623 #[error("block must contain at most {MAX_BATCHES_PER_BLOCK} transaction batches")]
624 TooManyBatches,
625
626 #[error(
627 "batch {batch_id} expired at block {batch_expiration_block_num} but the current block number is {current_block_num}"
628 )]
629 ExpiredBatch {
630 batch_id: BatchId,
631 batch_expiration_block_num: BlockNumber,
632 current_block_num: BlockNumber,
633 },
634
635 #[error("batch {batch_id} appears twice in the block inputs")]
636 DuplicateBatch { batch_id: BatchId },
637
638 #[error(
639 "batch {second_batch_id} consumes the note with nullifier {note_nullifier} that is also consumed by another batch {first_batch_id} in the block"
640 )]
641 DuplicateInputNote {
642 note_nullifier: Nullifier,
643 first_batch_id: BatchId,
644 second_batch_id: BatchId,
645 },
646
647 #[error(
648 "batch {second_batch_id} creates the note with ID {note_id} that is also created by another batch {first_batch_id} in the block"
649 )]
650 DuplicateOutputNote {
651 note_id: NoteId,
652 first_batch_id: BatchId,
653 second_batch_id: BatchId,
654 },
655
656 #[error(
657 "timestamp {provided_timestamp} does not increase monotonically compared to timestamp {previous_timestamp} from the previous block header"
658 )]
659 TimestampDoesNotIncreaseMonotonically {
660 provided_timestamp: u32,
661 previous_timestamp: u32,
662 },
663
664 #[error(
665 "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}"
666 )]
667 ConflictingBatchesUpdateSameAccount {
668 account_id: AccountId,
669 initial_state_commitment: Digest,
670 first_batch_id: BatchId,
671 second_batch_id: BatchId,
672 },
673
674 #[error(
675 "chain mmr 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"
676 )]
677 ChainLengthNotEqualToPreviousBlockNumber {
678 chain_length: BlockNumber,
679 prev_block_num: BlockNumber,
680 },
681
682 #[error(
683 "chain mmr has commitment {chain_commitment} which does not match the chain commitment {prev_block_chain_commitment} of the previous block {prev_block_num}"
684 )]
685 ChainRootNotEqualToPreviousBlockChainCommitment {
686 chain_commitment: Digest,
687 prev_block_chain_commitment: Digest,
688 prev_block_num: BlockNumber,
689 },
690
691 #[error(
692 "chain mmr is missing block {reference_block_num} referenced by batch {batch_id} in the block"
693 )]
694 BatchReferenceBlockMissingFromChain {
695 reference_block_num: BlockNumber,
696 batch_id: BatchId,
697 },
698
699 #[error(
700 "note commitment mismatch for note {id}: (input: {input_commitment}, output: {output_commitment})"
701 )]
702 NoteCommitmentMismatch {
703 id: NoteId,
704 input_commitment: Digest,
705 output_commitment: Digest,
706 },
707
708 #[error(
709 "failed to prove unauthenticated note inclusion because block {block_number} in which note with id {note_id} was created is not in chain mmr"
710 )]
711 UnauthenticatedInputNoteBlockNotInChainMmr {
712 block_number: BlockNumber,
713 note_id: NoteId,
714 },
715
716 #[error(
717 "failed to prove unauthenticated note inclusion of note {note_id} in block {block_num}"
718 )]
719 UnauthenticatedNoteAuthenticationFailed {
720 note_id: NoteId,
721 block_num: BlockNumber,
722 source: MerkleError,
723 },
724
725 #[error(
726 "unauthenticated note with nullifier {nullifier} was not created in the same block and no inclusion proof to authenticate it was provided"
727 )]
728 UnauthenticatedNoteConsumed { nullifier: Nullifier },
729
730 #[error("block inputs do not contain a proof of inclusion for account {0}")]
731 MissingAccountWitness(AccountId),
732
733 #[error(
734 "account {account_id} with state {state_commitment} cannot transition to any of the remaining states {remaining_state_commitments:?}"
735 )]
736 InconsistentAccountStateTransition {
737 account_id: AccountId,
738 state_commitment: Digest,
739 remaining_state_commitments: Vec<Digest>,
740 },
741
742 #[error("no proof for nullifier {0} was provided")]
743 NullifierProofMissing(Nullifier),
744
745 #[error("note with nullifier {0} is already spent")]
746 NullifierSpent(Nullifier),
747
748 #[error("failed to merge transaction delta into account {account_id}")]
749 AccountUpdateError {
750 account_id: AccountId,
751 source: AccountDeltaError,
752 },
753}
754
755#[derive(Debug, Error)]
759pub enum NullifierTreeError {
760 #[error("attempt to mark nullifier {0} as spent but it is already spent")]
761 NullifierAlreadySpent(Nullifier),
762 #[error("nullifier {nullifier} is not tracked by the partial nullifier tree")]
763 UntrackedNullifier {
764 nullifier: Nullifier,
765 source: MerkleError,
766 },
767 #[error("new tree root after nullifier witness insertion does not match previous tree root")]
768 TreeRootConflict(#[source] MerkleError),
769}