1use std::{collections::BTreeMap, convert::AsRef, fmt::Debug};
7
8use iota_sdk_types::{
9 Address, CheckpointContentsDigest, CommandArgumentError, ObjectDigest, ObjectId,
10 ObjectReference, Owner, TransactionDigest, TransactionEffectsDigest, Version,
11};
12use serde::{Deserialize, Serialize};
13use strum::{AsRefStr, IntoStaticStr};
14use thiserror::Error;
15#[cfg(not(target_arch = "wasm32"))]
16use tonic::Status;
17use typed_store_error::TypedStoreError;
18
19use crate::{
20 base_types::*,
21 committee::{Committee, EpochId, StakeUnit},
22 messages_checkpoint::CheckpointSequenceNumber,
23};
24
25#[cfg(test)]
26#[path = "unit_tests/error_codec_tests.rs"]
27mod error_codec_tests;
28
29pub const TRANSACTION_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transaction";
30pub const TRANSACTIONS_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transactions";
31
32#[macro_export]
33macro_rules! fp_bail {
34 ($e:expr) => {
35 return Err($e)
36 };
37}
38
39#[macro_export(local_inner_macros)]
40macro_rules! fp_ensure {
41 ($cond:expr, $e:expr) => {
42 if !($cond) {
43 fp_bail!($e);
44 }
45 };
46}
47
48use iota_sdk_types::ExecutionError as ExecutionFailureStatus;
49
50#[macro_export]
51macro_rules! exit_main {
52 ($result:expr) => {
53 match $result {
54 Ok(_) => (),
55 Err(err) => {
56 let err = format!("{:?}", err);
57 println!("{}", err.bold().red());
58 std::process::exit(1);
59 }
60 }
61 };
62}
63
64#[macro_export]
65macro_rules! make_invariant_violation {
66 ($($args:expr),* $(,)?) => {{
67 if cfg!(debug_assertions) {
68 panic!($($args),*)
69 }
70 ExecutionError::invariant_violation(format!($($args),*))
71 }}
72}
73
74#[macro_export]
75macro_rules! invariant_violation {
76 ($($args:expr),* $(,)?) => {
77 return Err(make_invariant_violation!($($args),*).into())
78 };
79}
80
81#[macro_export]
82macro_rules! assert_invariant {
83 ($cond:expr, $($args:expr),* $(,)?) => {{
84 if !$cond {
85 invariant_violation!($($args),*)
86 }
87 }};
88}
89
90#[cfg_attr(test, derive(iota_macros::EnumVariantOrder))]
96#[derive(
97 Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr,
98)]
99pub enum UserInputError {
100 #[error("Mutable object {object_id} cannot appear more than once in one transaction")]
101 MutableObjectUsedMoreThanOnce { object_id: ObjectId },
102 #[error("Wrong number of parameters for the transaction")]
103 ObjectInputArityViolation,
104 #[error("Could not find the referenced object {object_id} at version {version:?}")]
105 ObjectNotFound {
106 object_id: ObjectId,
107 version: Option<Version>,
108 },
109 #[error(
110 "Object ID {} Version {} Digest {} is not available for consumption, current version: {current_version}",
111 .provided_obj_ref.object_id, .provided_obj_ref.version, .provided_obj_ref.digest
112 )]
113 ObjectVersionUnavailableForConsumption {
114 provided_obj_ref: ObjectReference,
115 current_version: Version,
116 },
117 #[error("Package verification failed: {err}")]
118 PackageVerificationTimedout { err: String },
119 #[error("Dependent package not found on-chain: {package_id}")]
120 DependentPackageNotFound { package_id: ObjectId },
121 #[error("Mutable parameter provided, immutable parameter expected")]
122 ImmutableParameterExpected { object_id: ObjectId },
123 #[error("Size limit exceeded: {limit} is {value}")]
124 SizeLimitExceeded { limit: String, value: String },
125 #[error(
126 "Object {child_id} is owned by object {parent_id}. \
127 Objects owned by other objects cannot be used as input arguments"
128 )]
129 InvalidChildObjectArgument {
130 child_id: ObjectId,
131 parent_id: ObjectId,
132 },
133 #[error("Invalid Object digest for object {object_id}. Expected digest : {expected_digest}")]
134 InvalidObjectDigest {
135 object_id: ObjectId,
136 expected_digest: ObjectDigest,
137 },
138 #[error("Sequence numbers above the maximal value are not usable for transfers")]
139 InvalidSequenceNumber,
140 #[error("A move object is expected, instead a move package is passed: {object_id}")]
141 MovePackageAsObject { object_id: ObjectId },
142 #[error("A move package is expected, instead a move object is passed: {object_id}")]
143 MoveObjectAsPackage { object_id: ObjectId },
144 #[error("Transaction was not signed by the correct sender: {}", error)]
145 IncorrectUserSignature { error: String },
146
147 #[error("Object used as shared is not shared")]
148 NotSharedObject,
149 #[error("The transaction inputs contain duplicated ObjectReference's")]
150 DuplicateObjectRefInput,
151 #[error("A transaction input {object_id} is inconsistent")]
152 InconsistentInput { object_id: ObjectId },
153
154 #[error("Transaction gas payment missing")]
156 MissingGasPayment,
157 #[error("Gas object is not an owned object with owner: {}", owner)]
158 GasObjectNotOwnedObject { owner: Owner },
159 #[error("Gas budget: {} is higher than max: {}", gas_budget, max_budget)]
160 GasBudgetTooHigh { gas_budget: u64, max_budget: u64 },
161 #[error("Gas budget: {} is lower than min: {}", gas_budget, min_budget)]
162 GasBudgetTooLow { gas_budget: u64, min_budget: u64 },
163 #[error(
164 "Balance of gas object {} is lower than the needed amount: {}",
165 gas_balance,
166 needed_gas_amount
167 )]
168 GasBalanceTooLow {
169 gas_balance: u128,
170 needed_gas_amount: u128,
171 },
172 #[error("Transaction kind does not support Sponsored Transaction")]
173 UnsupportedSponsoredTransactionKind,
174 #[error(
175 "Gas price {} under reference gas price (RGP) {}",
176 gas_price,
177 reference_gas_price
178 )]
179 GasPriceUnderRGP {
180 gas_price: u64,
181 reference_gas_price: u64,
182 },
183 #[error("Gas price cannot exceed {} nanos", max_gas_price)]
184 GasPriceTooHigh { max_gas_price: u64 },
185 #[error("Object {object_id} is not a gas object")]
186 InvalidGasObject { object_id: ObjectId },
187 #[error("Gas object does not have enough balance to cover minimal gas spend")]
188 InsufficientBalanceToCoverMinimalGas,
189
190 #[error(
191 "Could not find the referenced object {} as the asked version {} is higher than the latest {}",
192 object_id,
193 asked_version,
194 latest_version
195 )]
196 ObjectSequenceNumberTooHigh {
197 object_id: ObjectId,
198 asked_version: Version,
199 latest_version: Version,
200 },
201 #[error("Object deleted at reference {:?}", object_ref)]
202 ObjectDeleted { object_ref: ObjectReference },
203 #[error("Invalid Batch Transaction: {}", error)]
204 InvalidBatchTransaction { error: String },
205 #[error("This Move function is currently disabled and not available for call")]
206 BlockedMoveFunction,
207 #[error("Empty input coins for Pay related transaction")]
208 EmptyInputCoins,
209 #[error("Invalid Move View Function call: {error}")]
210 InvalidMoveViewFunction { error: String },
211
212 #[error(
213 "IOTA payment transactions use first input coin for gas payment, but found a different gas object"
214 )]
215 UnexpectedGasPaymentObject,
216
217 #[error("Wrong initial version given for shared object")]
218 SharedObjectStartingVersionMismatch,
219
220 #[error("Wrong id given for shared object")]
221 SharedObjectIdMismatch,
222
223 #[error(
224 "Attempt to transfer object {object_id} that does not have public transfer. Object transfer must be done instead using a distinct Move function call"
225 )]
226 TransferObjectWithoutPublicTransfer { object_id: ObjectId },
227
228 #[error(
229 "TransferObjects, MergeCoin, and Publish cannot have empty arguments. \
230 If MakeMoveVec has empty arguments, it must have a type specified"
231 )]
232 EmptyCommandInput,
233
234 #[error("Transaction is denied: {error}")]
235 TransactionDenied { error: String },
236
237 #[error("Feature is not supported: {0}")]
238 Unsupported(String),
239
240 #[error("Query transactions with move function input error: {0}")]
241 MoveFunctionInput(String),
242
243 #[error("Verified checkpoint not found for sequence number: {0}")]
244 VerifiedCheckpointNotFound(CheckpointSequenceNumber),
245
246 #[error("Verified checkpoint not found for digest: {0}")]
247 VerifiedCheckpointDigestNotFound(String),
248
249 #[error("Latest checkpoint sequence number not found")]
250 LatestCheckpointSequenceNumberNotFound,
251
252 #[error("Checkpoint contents not found for digest: {0}")]
253 CheckpointContentsNotFound(CheckpointContentsDigest),
254
255 #[error("Genesis transaction not found")]
256 GenesisTransactionNotFound,
257
258 #[error("Transaction {0} not found")]
259 TransactionCursorNotFound(u64),
260
261 #[error("Object {object_id} is a system object and cannot be accessed by user transactions")]
262 InaccessibleSystemObject { object_id: ObjectId },
263 #[error(
264 "{max_publish_commands} max publish/upgrade commands allowed, {publish_count} provided"
265 )]
266 MaxPublishCountExceeded {
267 max_publish_commands: u64,
268 publish_count: u64,
269 },
270
271 #[error("Immutable parameter provided, mutable parameter expected for {object_id}")]
272 MutableParameterExpected { object_id: ObjectId },
273
274 #[error("Address {address} is denied for coin {coin_type}")]
275 AddressDeniedForCoin { address: Address, coin_type: String },
276
277 #[error("Commands following a command with Random can only be TransferObjects or MergeCoins")]
278 PostRandomCommandRestrictions,
279
280 #[error("Number of transactions exceeds the maximum allowed ({limit}) in a Soft Bundle")]
282 TooManyTransactionsInSoftBundle { limit: u64 },
283 #[error(
284 "Total transactions size ({size}) bytes exceeds the maximum allowed ({limit}) bytes in a Soft Bundle"
285 )]
286 SoftBundleTooLarge { size: u64, limit: u64 },
287 #[error("Transaction {} in Soft Bundle contains no shared objects", digest)]
288 NoSharedObject { digest: TransactionDigest },
289 #[error("Transaction {} in Soft Bundle has already been executed", digest)]
290 AlreadyExecuted { digest: TransactionDigest },
291 #[error("At least one certificate in Soft Bundle has already been processed")]
292 CertificateAlreadyProcessed,
293 #[error(
294 "Gas price for transaction {digest} in Soft Bundle mismatch: want {expected}, have {actual}"
295 )]
296 GasPriceMismatch {
297 digest: TransactionDigest,
298 expected: u64,
299 actual: u64,
300 },
301
302 #[error("Coin type is globally paused for use: {coin_type}")]
303 CoinTypeGlobalPause { coin_type: String },
304
305 #[error("Invalid identifier found in the transaction: {error}")]
306 InvalidIdentifier { error: String },
307
308 #[error(
310 "Account object {account_id} with version {account_version} was deleted in transaction {transaction_digest}"
311 )]
312 AccountObjectDeleted {
313 account_id: ObjectId,
314 account_version: Version,
315 transaction_digest: TransactionDigest,
316 },
317 #[error(
318 "Account object {account_id} with version {account_version} is used in a canceled transaction"
319 )]
320 AccountObjectInCanceledTransaction {
321 account_id: ObjectId,
322 account_version: Version,
323 },
324 #[error("Account object {object_id} is not a shared or immutable object that is unsupported")]
325 AccountObjectNotSupported { object_id: ObjectId },
326 #[error(
327 "The fetched account object version {actual_version} does not match the expected version {expected_version}, object id: {object_id}"
328 )]
329 AccountObjectVersionMismatch {
330 object_id: ObjectId,
331 expected_version: Version,
332 actual_version: Version,
333 },
334 #[error(
335 "The fetched account object digest {actual_digest} does not match the expected digest {expected_digest}, object id: {object_id}"
336 )]
337 InvalidAccountObjectDigest {
338 object_id: ObjectId,
339 expected_digest: ObjectDigest,
340 actual_digest: ObjectDigest,
341 },
342
343 #[error(
344 "AuthenticatorFunctionRef {authenticator_function_ref_id} not found for account {account_object_id} with version {account_object_version}"
345 )]
346 MoveAuthenticatorNotFound {
347 authenticator_function_ref_id: ObjectId,
348 account_object_id: ObjectId,
349 account_object_version: Version,
350 },
351 #[error("Unable to get a `MoveAuthenticator` object ID for account {account_object_id}")]
352 UnableToGetMoveAuthenticatorId { account_object_id: ObjectId },
353 #[error(
354 "Invalid authenticator function ref field value found for the account {account_object_id}"
355 )]
356 InvalidAuthenticatorFunctionRefField { account_object_id: ObjectId },
357
358 #[error("Package {package_id} is in the `MoveAuthenticator` input that is unsupported")]
359 PackageIsInMoveAuthenticatorInput { package_id: ObjectId },
360 #[error(
361 "Address-owned object {object_id} is in the `MoveAuthenticator` input that is unsupported"
362 )]
363 AddressOwnedIsInMoveAuthenticatorInput { object_id: ObjectId },
364 #[error(
365 "Object-owned object {object_id} is in the `MoveAuthenticator` input that is unsupported"
366 )]
367 ObjectOwnedIsInMoveAuthenticatorInput { object_id: ObjectId },
368 #[error(
369 "Mutable shared object {object_id} is in the `MoveAuthenticator` input that is unsupported"
370 )]
371 MutableSharedIsInMoveAuthenticatorInput { object_id: ObjectId },
372 #[error(
373 "Immutable account object {object_id} cannot authenticate a transaction, only a shared account object is supported"
374 )]
375 ImmutableAccountObjectNotSupported { object_id: ObjectId },
376 #[error(
377 "Randomness state object {object_id} is in the `MoveAuthenticator` input that is unsupported"
378 )]
379 RandomnessStateIsInMoveAuthenticatorInput { object_id: ObjectId },
380 #[error(
381 "Invalid argument at command {command_idx}, argument {argument_idx}: index {index} is out of bounds"
382 )]
383 InvalidArgumentIndex {
384 command_idx: usize,
385 argument_idx: usize,
386 index: u16,
387 },
388 #[error(
389 "Input object {object_id}{} can't be used: at the consensus commit that sequenced the transaction, it was already consumed, deleted, or didn't match what the transaction declared",
390 at_version(.version)
391 )]
392 InputObjectNotAvailableAtCommit {
393 object_id: ObjectId,
394 version: Option<Version>,
395 },
396}
397
398fn at_version(version: &Option<Version>) -> String {
401 version
402 .map(|version| format!(" at version {version}"))
403 .unwrap_or_default()
404}
405
406#[cfg_attr(test, derive(iota_macros::EnumVariantOrder))]
416#[derive(
417 Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr,
418)]
419pub enum IotaError {
420 #[error("Error checking transaction input objects: {error}")]
421 UserInput { error: UserInputError },
422
423 #[error("There are already {queue_len} transactions pending, above threshold of {threshold}")]
424 TooManyTransactionsPendingExecution { queue_len: usize, threshold: usize },
425
426 #[error("There are too many transactions pending in consensus")]
427 TooManyTransactionsPendingConsensus,
428
429 #[error(
430 "Input {object_id} already has {queue_len} transactions pending, above threshold of {threshold}"
431 )]
432 TooManyTransactionsPendingOnObject {
433 object_id: ObjectId,
434 queue_len: usize,
435 threshold: usize,
436 },
437
438 #[error(
439 "Input {object_id} has a transaction {txn_age_sec} seconds old pending, above threshold of {threshold} seconds"
440 )]
441 TooOldTransactionPendingOnObject {
442 object_id: ObjectId,
443 txn_age_sec: u64,
444 threshold: u64,
445 },
446
447 #[error("Soft bundle must only contain transactions of UserTransaction kind")]
448 InvalidTxKindInSoftBundle,
449
450 #[error("Signature is not valid: {}", error)]
452 InvalidSignature { error: String },
453 #[error("Required Signature from {expected} is absent {actual:?}")]
454 SignerSignatureAbsent {
455 expected: String,
456 actual: Vec<String>,
457 },
458 #[error("Expect {expected} signer signatures but got {actual}")]
459 SignerSignatureNumberMismatch { expected: usize, actual: usize },
460 #[error("Value was not signed by the correct sender: {}", error)]
461 IncorrectSigner { error: String },
462 #[error(
463 "Value was not signed by a known authority. signer: {:?}, index: {:?}, committee: {committee}",
464 signer,
465 index
466 )]
467 UnknownSigner {
468 signer: Option<String>,
469 index: Option<u32>,
470 committee: Box<Committee>,
471 },
472 #[error(
473 "Validator {signer:?} responded multiple signatures for the same message, conflicting: {conflicting_sig}"
474 )]
475 StakeAggregatorRepeatedSigner {
476 signer: AuthorityName,
477 conflicting_sig: bool,
478 },
479 #[error("Signature is not valid, but a retry may result in a valid one: {error}")]
482 PotentiallyTemporarilyInvalidSignature { error: String },
483
484 #[error(
486 "Signature or certificate from wrong epoch, expected {expected_epoch}, got {actual_epoch}"
487 )]
488 WrongEpoch {
489 expected_epoch: EpochId,
490 actual_epoch: EpochId,
491 },
492 #[error("Signatures in a certificate must form a quorum")]
493 CertificateRequiresQuorum,
494 #[error("Transaction certificate processing failed: {err}")]
495 ErrorWhileProcessingCertificate { err: String },
497 #[error(
498 "Failed to get a quorum of signed effects when processing transaction: {effects_map:?}"
499 )]
500 QuorumFailedToGetEffectsQuorumWhenProcessingTransaction {
501 effects_map: BTreeMap<TransactionEffectsDigest, (Vec<AuthorityName>, StakeUnit)>,
502 },
503 #[error(
504 "Failed to verify Tx certificate with executed effects, error: {error}, validator: {validator_name:?}"
505 )]
506 FailedToVerifyTxCertWithExecutedEffects {
507 validator_name: AuthorityName,
508 error: String,
509 },
510 #[error("Transaction is already finalized but with different user signatures")]
511 TxAlreadyFinalizedWithDifferentUserSigs,
512
513 #[error("Invalid authenticator")]
515 InvalidAuthenticator,
516 #[error("Invalid address")]
517 InvalidAddress,
518 #[error("Invalid transaction digest")]
519 InvalidTransactionDigest,
520 #[error("Invalid move authentication digest")]
521 InvalidMoveAuthenticatorDigest,
522
523 #[error("Invalid digest length. Expected {expected}, got {actual}")]
524 InvalidDigestLength { expected: usize, actual: usize },
525 #[error("Invalid DKG message size")]
526 InvalidDkgMessageSize,
527
528 #[error("Unexpected message")]
529 UnexpectedMessage,
530
531 #[error("Failed to execute the Move authenticator, reason: {error}")]
532 MoveAuthenticatorExecutionFailure { error: String },
533
534 #[error("Failed to verify the Move module, reason: {error}")]
536 ModuleVerificationFailure { error: String },
537 #[error("Failed to deserialize the Move module, reason: {error}")]
538 ModuleDeserializationFailure { error: String },
539 #[error("Failed to publish the Move module(s), reason: {error}")]
540 ModulePublishFailure { error: String },
541 #[error("Failed to build Move modules: {error}")]
542 ModuleBuildFailure { error: String },
543
544 #[error("Function resolution failure: {error}")]
546 FunctionNotFound { error: String },
547 #[error("Module not found in package: {module_name:?}")]
548 ModuleNotFound { module_name: String },
549 #[error("Type error while binding function arguments: {error}")]
550 Type { error: String },
551 #[error("Circular object ownership detected")]
552 CircularObjectOwnership,
553
554 #[error("Attempt to re-initialize a transaction lock for objects {refs:?}")]
556 ObjectLockAlreadyInitialized { refs: Vec<ObjectReference> },
557 #[error("Object {obj_ref:?} already locked by a different transaction: {pending_transaction}")]
558 ObjectLockConflict {
559 obj_ref: ObjectReference,
560 pending_transaction: TransactionDigest,
561 },
562 #[error(
563 "Objects {obj_refs:?} are already locked by a transaction from a future epoch {locked_epoch:?}), attempt to override with a transaction from epoch {new_epoch:?}"
564 )]
565 ObjectLockedAtFutureEpoch {
566 obj_refs: Vec<ObjectReference>,
567 locked_epoch: EpochId,
568 new_epoch: EpochId,
569 locked_by_tx: TransactionDigest,
570 },
571 #[error("Transaction {digest:?} was recently submitted; duplicate resubmission suppressed")]
572 RecentlyResubmitted { digest: TransactionDigest },
573 #[error("{TRANSACTION_NOT_FOUND_MSG_PREFIX} [{digest}]")]
574 TransactionNotFound { digest: TransactionDigest },
575 #[error("{TRANSACTIONS_NOT_FOUND_MSG_PREFIX} [{digests:?}]")]
576 TransactionsNotFound { digests: Vec<TransactionDigest> },
577 #[error("Could not find the referenced transaction events [{digest}]")]
578 TransactionEventsNotFound { digest: TransactionDigest },
579 #[error(
580 "Attempt to move to `Executed` state an transaction that has already been executed: {digest}"
581 )]
582 TransactionAlreadyExecuted { digest: TransactionDigest },
583 #[error("Object ID did not have the expected type")]
584 BadObjectType { error: String },
585 #[error("Fail to retrieve Object layout for {st}")]
586 FailObjectLayout { st: String },
587
588 #[error("Execution invariant violated")]
589 ExecutionInvariantViolation,
590 #[error("Validator {authority:?} is faulty in a Byzantine manner: {reason}")]
591 ByzantineAuthoritySuspicion {
592 authority: AuthorityName,
593 reason: String,
594 },
595 #[error(
596 "Attempted to access {object} through parent {given_parent}, \
597 but it's actual parent is {actual_owner}"
598 )]
599 InvalidChildObjectAccess {
600 object: ObjectId,
601 given_parent: ObjectId,
602 actual_owner: Owner,
603 },
604
605 #[error("Authority Error: {error}")]
606 GenericAuthority { error: String },
607
608 #[error("Failed to dispatch subscription: {error}")]
609 FailedToDispatchSubscription { error: String },
610
611 #[error("Failed to serialize Owner: {error}")]
612 OwnerFailedToSerialize { error: String },
613
614 #[error("Failed to deserialize fields into JSON: {error}")]
615 ExtraFieldFailedToDeserialize { error: String },
616
617 #[error("Failed to execute transaction locally by Orchestrator: {error}")]
618 TransactionOrchestratorLocalExecution { error: String },
619
620 #[error("Failure serializing transaction in the requested format: {error}")]
622 TransactionSerialization { error: String },
623 #[error("Failure serializing object in the requested format: {error}")]
624 ObjectSerialization { error: String },
625 #[error("Failure deserializing object in the requested format: {error}")]
626 ObjectDeserialization { error: String },
627 #[error("Failure deserializing runtime module metadata in the requested format: {error}")]
628 RuntimeModuleMetadataDeserialization { error: String },
629 #[error("Event store component is not active on this node")]
630 NoEventStore,
631
632 #[error("Too many authority errors were detected for {action}: {errors:?}")]
634 TooManyIncorrectAuthorities {
635 errors: Vec<(AuthorityName, IotaError)>,
636 action: String,
637 },
638 #[error("Invalid transaction range query to the fullnode: {error}")]
639 FullNodeInvalidTxRangeQuery { error: String },
640
641 #[error("Failed to submit transaction to consensus: {0}")]
643 FailedToSubmitToConsensus(String),
644 #[error("Failed to connect with consensus node: {0}")]
645 ConsensusConnectionBroken(String),
646 #[error("Failed to execute handle_consensus_transaction on Iota: {0}")]
647 HandleConsensusTransactionFailure(String),
648
649 #[error("Signature key generation error: {0}")]
651 SignatureKeyGen(String),
652 #[error("Key Conversion Error: {0}")]
653 KeyConversion(String),
654 #[error("Invalid Private Key provided")]
655 InvalidPrivateKey,
656
657 #[error("Fullnode does not support handle_certificate")]
659 FullNodeCantHandleCertificate,
660 #[error("Fullnode does not support ValidatorV2 endpoints")]
661 FullNodeCantHandleValidatorV2,
662 #[error("Fullnode does not support handle_authority_capabilities")]
663 FullNodeCantHandleAuthorityCapabilities,
664
665 #[error("Validator temporarily stopped processing transactions due to epoch change")]
667 ValidatorHaltedAtEpochEnd,
668 #[error("Operations for epoch {0} have ended")]
669 EpochEnded(EpochId),
670 #[error("Error when advancing epoch: {error}")]
671 AdvanceEpoch { error: String },
672
673 #[error("Transaction Expired")]
674 TransactionExpired,
675
676 #[error("{1} - {0}")]
679 Rpc(String, String),
680
681 #[error("Method not allowed")]
682 InvalidRpcMethod,
683
684 #[error("Use of disabled feature: {error}")]
686 UnsupportedFeature { error: String },
687
688 #[error("Unable to communicate with the Quorum Driver channel: {error}")]
689 QuorumDriverCommunication { error: String },
690
691 #[error("Operation timed out")]
692 Timeout,
693
694 #[error("Error executing {0}")]
695 Execution(String),
696
697 #[error("Invalid committee composition")]
698 InvalidCommittee(String),
699
700 #[error("Missing committee information for epoch {0}")]
701 MissingCommitteeAtEpoch(EpochId),
702
703 #[error("Index store not available on this Fullnode")]
704 IndexStoreNotAvailable,
705
706 #[error("Failed to read dynamic field from table in the object store: {0}")]
707 DynamicFieldRead(String),
708
709 #[error("Failed to read or deserialize system state related data structures on-chain: {0}")]
710 IotaSystemStateRead(String),
711
712 #[error("Unexpected version error: {0}")]
713 UnexpectedVersion(String),
714
715 #[error("Message version is not supported at the current protocol version: {error}")]
716 WrongMessageVersion { error: String },
717
718 #[error("unknown error: {0}")]
719 Unknown(String),
720
721 #[error("Failed to perform file operation: {0}")]
722 FileIO(String),
723
724 #[error("Failed to get JWK")]
725 JWKRetrieval,
726
727 #[error("Storage error: {0}")]
728 Storage(String),
729
730 #[error(
731 "Validator cannot handle the request at the moment. Please retry after at least {retry_after_secs} seconds"
732 )]
733 ValidatorOverloadedRetryAfter { retry_after_secs: u64 },
734
735 #[error("Too many requests")]
736 TooManyRequests,
737
738 #[error("The request did not contain a certificate")]
739 NoCertificateProvided,
740
741 #[error("Invalid admin request: {0}")]
742 InvalidAdminRequest(String),
743
744 #[error("Could not find the referenced transaction effects [{digest}]")]
745 TransactionEffectsNotFound { digest: TransactionDigest },
746
747 #[error("Dynamic field with key={key} and ID={id} does not exist on parent {parent_id}")]
748 DynamicFieldNotExists {
749 parent_id: ObjectId,
750 id: ObjectId,
751 key: String,
752 },
753}
754
755#[repr(u64)]
756#[expect(non_camel_case_types)]
757#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
758pub enum VMMVerifierErrorSubStatusCode {
762 MULTIPLE_RETURN_VALUES_NOT_ALLOWED = 0,
763 INVALID_OBJECT_CREATION = 1,
764}
765
766#[repr(u64)]
767#[expect(non_camel_case_types)]
768#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
769pub enum VMMemoryLimitExceededSubStatusCode {
772 EVENT_COUNT_LIMIT_EXCEEDED = 0,
773 EVENT_SIZE_LIMIT_EXCEEDED = 1,
774 NEW_ID_COUNT_LIMIT_EXCEEDED = 2,
775 DELETED_ID_COUNT_LIMIT_EXCEEDED = 3,
776 TRANSFER_ID_COUNT_LIMIT_EXCEEDED = 4,
777 OBJECT_RUNTIME_CACHE_LIMIT_EXCEEDED = 5,
778 OBJECT_RUNTIME_STORE_LIMIT_EXCEEDED = 6,
779 TOTAL_EVENT_SIZE_LIMIT_EXCEEDED = 7,
780}
781
782pub type IotaResult<T = ()> = Result<T, IotaError>;
783pub type UserInputResult<T = ()> = Result<T, UserInputError>;
784
785impl From<iota_protocol_config::Error> for IotaError {
786 fn from(error: iota_protocol_config::Error) -> Self {
787 IotaError::WrongMessageVersion { error: error.0 }
788 }
789}
790
791impl From<ExecutionError> for IotaError {
792 fn from(error: ExecutionError) -> Self {
793 IotaError::Execution(error.to_string())
794 }
795}
796
797impl From<iota_sdk_types::hash::MissingSignatureError> for IotaError {
798 fn from(error: iota_sdk_types::hash::MissingSignatureError) -> Self {
799 IotaError::InvalidSignature {
800 error: error.to_string(),
801 }
802 }
803}
804
805#[cfg(not(target_arch = "wasm32"))]
806impl From<Status> for IotaError {
807 fn from(status: Status) -> Self {
808 if status.message() == "Too many requests" {
809 return Self::TooManyRequests;
810 }
811 let result = bcs::from_bytes::<IotaError>(status.details());
812 if let Ok(iota_error) = result {
813 iota_error
814 } else {
815 Self::Rpc(
816 status.message().to_owned(),
817 status.code().description().to_owned(),
818 )
819 }
820 }
821}
822
823impl From<TypedStoreError> for IotaError {
824 fn from(e: TypedStoreError) -> Self {
825 Self::Storage(e.to_string())
826 }
827}
828
829impl From<crate::storage::error::Error> for IotaError {
830 fn from(e: crate::storage::error::Error) -> Self {
831 Self::Storage(e.to_string())
832 }
833}
834
835#[cfg(not(target_arch = "wasm32"))]
836impl From<IotaError> for Status {
837 fn from(error: IotaError) -> Self {
838 let bytes = bcs::to_bytes(&error).unwrap();
839 Status::with_details(tonic::Code::Internal, error.to_string(), bytes.into())
840 }
841}
842
843impl From<ExecutionErrorKind> for IotaError {
844 fn from(kind: ExecutionErrorKind) -> Self {
845 ExecutionError::from_kind(kind).into()
846 }
847}
848
849impl From<&str> for IotaError {
850 fn from(error: &str) -> Self {
851 IotaError::GenericAuthority {
852 error: error.to_string(),
853 }
854 }
855}
856
857impl From<String> for IotaError {
858 fn from(error: String) -> Self {
859 IotaError::GenericAuthority { error }
860 }
861}
862
863impl TryFrom<IotaError> for UserInputError {
864 type Error = anyhow::Error;
865
866 fn try_from(err: IotaError) -> Result<Self, Self::Error> {
867 match err {
868 IotaError::UserInput { error } => Ok(error),
869 other => anyhow::bail!("error `{other}` is not UserInput"),
870 }
871 }
872}
873
874impl From<UserInputError> for IotaError {
875 fn from(error: UserInputError) -> Self {
876 IotaError::UserInput { error }
877 }
878}
879
880impl IotaError {
881 pub fn individual_error_indicates_epoch_change(&self) -> bool {
882 matches!(
883 self,
884 IotaError::ValidatorHaltedAtEpochEnd | IotaError::MissingCommitteeAtEpoch(_)
885 )
886 }
887
888 pub fn is_retryable(&self) -> (bool, bool) {
894 let retryable = match self {
895 IotaError::Rpc { .. } => true,
896
897 IotaError::ValidatorHaltedAtEpochEnd => true,
899 IotaError::MissingCommitteeAtEpoch(..) => true,
900 IotaError::WrongEpoch { .. } => true,
901 IotaError::EpochEnded { .. } => true,
902
903 IotaError::UserInput { error } => {
904 match error {
905 UserInputError::ObjectNotFound { .. } => true,
907 UserInputError::DependentPackageNotFound { .. } => true,
908 UserInputError::InputObjectNotAvailableAtCommit { .. } => false,
911 _ => false,
912 }
913 }
914
915 IotaError::PotentiallyTemporarilyInvalidSignature { .. } => true,
916
917 IotaError::TooManyTransactionsPendingExecution { .. } => true,
919 IotaError::TooManyTransactionsPendingOnObject { .. } => true,
920 IotaError::TooOldTransactionPendingOnObject { .. } => true,
921 IotaError::TooManyTransactionsPendingConsensus => true,
922 IotaError::ValidatorOverloadedRetryAfter { .. } => true,
923
924 IotaError::FailedToSubmitToConsensus(..) => true,
926
927 IotaError::RecentlyResubmitted { .. } => true,
930
931 IotaError::Execution(..) => false,
933 IotaError::ByzantineAuthoritySuspicion { .. } => false,
934 IotaError::QuorumFailedToGetEffectsQuorumWhenProcessingTransaction { .. } => false,
935 IotaError::TxAlreadyFinalizedWithDifferentUserSigs => false,
936 IotaError::FailedToVerifyTxCertWithExecutedEffects { .. } => false,
937 IotaError::ObjectLockConflict { .. } => false,
938
939 IotaError::TooManyRequests => false,
943
944 IotaError::InvalidSignature { .. } => false,
946 IotaError::SignerSignatureAbsent { .. } => false,
947 IotaError::SignerSignatureNumberMismatch { .. } => false,
948 IotaError::IncorrectSigner { .. } => false,
949 IotaError::UnknownSigner { .. } => false,
950 IotaError::InvalidAuthenticator => false,
951
952 IotaError::TransactionExpired => false,
954
955 IotaError::StakeAggregatorRepeatedSigner { .. } => false,
957 IotaError::CertificateRequiresQuorum => false,
958
959 _ => return (false, false),
961 };
962
963 (retryable, true)
964 }
965
966 pub fn is_object_or_package_not_found(&self) -> bool {
967 match self {
968 IotaError::UserInput { error } => {
969 matches!(
970 error,
971 UserInputError::ObjectNotFound { .. }
972 | UserInputError::DependentPackageNotFound { .. }
973 )
974 }
975 _ => false,
976 }
977 }
978
979 pub fn is_overload(&self) -> bool {
980 matches!(
981 self,
982 IotaError::TooManyTransactionsPendingExecution { .. }
983 | IotaError::TooManyTransactionsPendingOnObject { .. }
984 | IotaError::TooOldTransactionPendingOnObject { .. }
985 | IotaError::TooManyTransactionsPendingConsensus
986 )
987 }
988
989 pub fn is_retryable_overload(&self) -> bool {
990 matches!(self, IotaError::ValidatorOverloadedRetryAfter { .. })
991 }
992
993 pub fn is_storage_or_epoch_error(&self) -> bool {
998 matches!(
999 self,
1000 IotaError::Storage(..)
1001 | IotaError::EpochEnded(..)
1002 | IotaError::ValidatorHaltedAtEpochEnd
1003 )
1004 }
1005
1006 pub fn retry_after_secs(&self) -> u64 {
1007 match self {
1008 IotaError::ValidatorOverloadedRetryAfter { retry_after_secs } => *retry_after_secs,
1009 _ => 0,
1010 }
1011 }
1012}
1013
1014pub fn categorize(error: &IotaError) -> ErrorCategory {
1016 match error {
1017 IotaError::UserInput { error } => match error {
1018 UserInputError::ObjectNotFound { .. } => ErrorCategory::Aborted,
1019 UserInputError::DependentPackageNotFound { .. } => ErrorCategory::Aborted,
1020 UserInputError::InputObjectNotAvailableAtCommit { .. } => {
1021 ErrorCategory::InvalidTransaction
1022 }
1023 _ => ErrorCategory::InvalidTransaction,
1024 },
1025 IotaError::InvalidSignature { .. }
1026 | IotaError::SignerSignatureAbsent { .. }
1027 | IotaError::SignerSignatureNumberMismatch { .. }
1028 | IotaError::IncorrectSigner { .. }
1029 | IotaError::UnknownSigner { .. }
1030 | IotaError::TransactionExpired => ErrorCategory::InvalidTransaction,
1031
1032 IotaError::ObjectLockConflict { .. } => ErrorCategory::LockConflict,
1033
1034 IotaError::TooManyTransactionsPendingExecution { .. }
1035 | IotaError::TooManyTransactionsPendingOnObject { .. }
1036 | IotaError::TooOldTransactionPendingOnObject { .. }
1037 | IotaError::TooManyTransactionsPendingConsensus
1038 | IotaError::ValidatorOverloadedRetryAfter { .. } => ErrorCategory::ValidatorOverloaded,
1039
1040 _ => ErrorCategory::Aborted,
1041 }
1042}
1043
1044#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, IntoStaticStr)]
1046pub enum ErrorCategory {
1047 Aborted,
1049 InvalidTransaction,
1051 LockConflict,
1053 Internal,
1057 ValidatorOverloaded,
1059 Unavailable,
1061}
1062
1063impl ErrorCategory {
1064 pub fn is_submission_retriable(&self) -> bool {
1066 matches!(
1067 self,
1068 ErrorCategory::Aborted
1069 | ErrorCategory::ValidatorOverloaded
1070 | ErrorCategory::Unavailable
1071 )
1072 }
1073}
1074
1075impl IotaError {
1076 pub fn categorize(&self) -> ErrorCategory {
1078 categorize(self)
1079 }
1080}
1081
1082impl Ord for IotaError {
1083 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1084 Ord::cmp(self.as_ref(), other.as_ref())
1085 }
1086}
1087
1088impl PartialOrd for IotaError {
1089 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1090 Some(self.cmp(other))
1091 }
1092}
1093
1094type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
1095
1096pub type ExecutionErrorKind = ExecutionFailureStatus;
1097
1098#[derive(Debug)]
1099pub struct ExecutionError {
1100 inner: Box<ExecutionErrorInner>,
1101}
1102
1103#[derive(Debug)]
1104struct ExecutionErrorInner {
1105 kind: ExecutionErrorKind,
1106 source: Option<BoxError>,
1107 command: Option<u64>,
1108}
1109
1110impl ExecutionError {
1111 pub fn new(kind: ExecutionErrorKind, source: Option<BoxError>) -> Self {
1112 Self {
1113 inner: Box::new(ExecutionErrorInner {
1114 kind,
1115 source,
1116 command: None,
1117 }),
1118 }
1119 }
1120
1121 pub fn new_with_source<E: Into<BoxError>>(kind: ExecutionErrorKind, source: E) -> Self {
1122 Self::new(kind, Some(source.into()))
1123 }
1124
1125 pub fn invariant_violation<E: Into<BoxError>>(source: E) -> Self {
1126 Self::new_with_source(ExecutionFailureStatus::InvariantViolation, source)
1127 }
1128
1129 pub fn with_command_index(mut self, command: u64) -> Self {
1130 self.inner.command = Some(command);
1131 self
1132 }
1133
1134 pub fn into_move_authentication_error(self) -> Self {
1141 let ExecutionErrorInner { kind, source, .. } = *self.inner;
1142 Self::new(
1143 ExecutionFailureStatus::MoveAuthentication {
1144 error: Box::new(kind),
1145 },
1146 source,
1147 )
1148 }
1149
1150 pub fn from_kind(kind: ExecutionErrorKind) -> Self {
1151 Self::new(kind, None)
1152 }
1153
1154 pub fn kind(&self) -> &ExecutionErrorKind {
1155 &self.inner.kind
1156 }
1157
1158 pub fn command(&self) -> Option<u64> {
1159 self.inner.command
1160 }
1161
1162 pub fn source(&self) -> &Option<BoxError> {
1163 &self.inner.source
1164 }
1165
1166 pub fn to_execution_status(&self) -> (ExecutionFailureStatus, Option<u64>) {
1167 (self.kind().clone(), self.command())
1168 }
1169}
1170
1171impl std::fmt::Display for ExecutionError {
1172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1173 write!(f, "{}: {}", self.inner.kind.as_ref(), self.inner.kind)?;
1174 if let Some(source) = self.inner.source.as_ref() {
1175 write!(f, "; caused by: {source}")?;
1176 }
1177 if let Some(command) = self.inner.command {
1178 write!(f, "; at command index: {command}")?;
1179 }
1180 Ok(())
1181 }
1182}
1183
1184impl std::error::Error for ExecutionError {
1185 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1186 self.inner.source.as_ref().map(|e| &**e as _)
1187 }
1188}
1189
1190impl From<ExecutionErrorKind> for ExecutionError {
1191 fn from(kind: ExecutionErrorKind) -> Self {
1192 Self::from_kind(kind)
1193 }
1194}
1195
1196pub fn command_argument_error(e: CommandArgumentError, arg_idx: usize) -> ExecutionError {
1197 ExecutionError::from_kind(ExecutionErrorKind::command_argument_error(
1198 e,
1199 arg_idx as u16,
1200 ))
1201}