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