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