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