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