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
604 #[error("Validator temporarily stopped processing transactions due to epoch change")]
606 ValidatorHaltedAtEpochEnd,
607 #[error("Operations for epoch {0} have ended")]
608 EpochEnded(EpochId),
609 #[error("Error when advancing epoch: {:?}", error)]
610 AdvanceEpoch { error: String },
611
612 #[error("Transaction Expired")]
613 TransactionExpired,
614
615 #[error("{1} - {0}")]
618 Rpc(String, String),
619
620 #[error("Method not allowed")]
621 InvalidRpcMethod,
622
623 #[error("Use of disabled feature: {:?}", error)]
625 UnsupportedFeature { error: String },
626
627 #[error("Unable to communicate with the Quorum Driver channel: {:?}", error)]
628 QuorumDriverCommunication { error: String },
629
630 #[error("Operation timed out")]
631 Timeout,
632
633 #[error("Error executing {0}")]
634 Execution(String),
635
636 #[error("Invalid committee composition")]
637 InvalidCommittee(String),
638
639 #[error("Missing committee information for epoch {0}")]
640 MissingCommitteeAtEpoch(EpochId),
641
642 #[error("Index store not available on this Fullnode.")]
643 IndexStoreNotAvailable,
644
645 #[error("Failed to read dynamic field from table in the object store: {0}")]
646 DynamicFieldRead(String),
647
648 #[error("Failed to read or deserialize system state related data structures on-chain: {0}")]
649 IotaSystemStateRead(String),
650
651 #[error("Unexpected version error: {0}")]
652 UnexpectedVersion(String),
653
654 #[error("Message version is not supported at the current protocol version: {error}")]
655 WrongMessageVersion { error: String },
656
657 #[error("unknown error: {0}")]
658 Unknown(String),
659
660 #[error("Failed to perform file operation: {0}")]
661 FileIO(String),
662
663 #[error("Failed to get JWK")]
664 JWKRetrieval,
665
666 #[error("Storage error: {0}")]
667 Storage(String),
668
669 #[error(
670 "Validator cannot handle the request at the moment. Please retry after at least {retry_after_secs} seconds."
671 )]
672 ValidatorOverloadedRetryAfter { retry_after_secs: u64 },
673
674 #[error("Too many requests")]
675 TooManyRequests,
676
677 #[error("The request did not contain a certificate")]
678 NoCertificateProvided,
679}
680
681#[repr(u64)]
682#[expect(non_camel_case_types)]
683#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
684pub enum VMMVerifierErrorSubStatusCode {
688 MULTIPLE_RETURN_VALUES_NOT_ALLOWED = 0,
689 INVALID_OBJECT_CREATION = 1,
690}
691
692#[repr(u64)]
693#[expect(non_camel_case_types)]
694#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
695pub enum VMMemoryLimitExceededSubStatusCode {
698 EVENT_COUNT_LIMIT_EXCEEDED = 0,
699 EVENT_SIZE_LIMIT_EXCEEDED = 1,
700 NEW_ID_COUNT_LIMIT_EXCEEDED = 2,
701 DELETED_ID_COUNT_LIMIT_EXCEEDED = 3,
702 TRANSFER_ID_COUNT_LIMIT_EXCEEDED = 4,
703 OBJECT_RUNTIME_CACHE_LIMIT_EXCEEDED = 5,
704 OBJECT_RUNTIME_STORE_LIMIT_EXCEEDED = 6,
705 TOTAL_EVENT_SIZE_LIMIT_EXCEEDED = 7,
706}
707
708pub type IotaResult<T = ()> = Result<T, IotaError>;
709pub type UserInputResult<T = ()> = Result<T, UserInputError>;
710
711impl From<iota_protocol_config::Error> for IotaError {
712 fn from(error: iota_protocol_config::Error) -> Self {
713 IotaError::WrongMessageVersion { error: error.0 }
714 }
715}
716
717impl From<ExecutionError> for IotaError {
718 fn from(error: ExecutionError) -> Self {
719 IotaError::Execution(error.to_string())
720 }
721}
722
723impl From<Status> for IotaError {
724 fn from(status: Status) -> Self {
725 if status.message() == "Too many requests" {
726 return Self::TooManyRequests;
727 }
728 let result = bcs::from_bytes::<IotaError>(status.details());
729 if let Ok(iota_error) = result {
730 iota_error
731 } else {
732 Self::Rpc(
733 status.message().to_owned(),
734 status.code().description().to_owned(),
735 )
736 }
737 }
738}
739
740impl From<TypedStoreError> for IotaError {
741 fn from(e: TypedStoreError) -> Self {
742 Self::Storage(e.to_string())
743 }
744}
745
746impl From<crate::storage::error::Error> for IotaError {
747 fn from(e: crate::storage::error::Error) -> Self {
748 Self::Storage(e.to_string())
749 }
750}
751
752impl From<IotaError> for Status {
753 fn from(error: IotaError) -> Self {
754 let bytes = bcs::to_bytes(&error).unwrap();
755 Status::with_details(tonic::Code::Internal, error.to_string(), bytes.into())
756 }
757}
758
759impl From<ExecutionErrorKind> for IotaError {
760 fn from(kind: ExecutionErrorKind) -> Self {
761 ExecutionError::from_kind(kind).into()
762 }
763}
764
765impl From<&str> for IotaError {
766 fn from(error: &str) -> Self {
767 IotaError::GenericAuthority {
768 error: error.to_string(),
769 }
770 }
771}
772
773impl From<String> for IotaError {
774 fn from(error: String) -> Self {
775 IotaError::GenericAuthority { error }
776 }
777}
778
779impl TryFrom<IotaError> for UserInputError {
780 type Error = anyhow::Error;
781
782 fn try_from(err: IotaError) -> Result<Self, Self::Error> {
783 match err {
784 IotaError::UserInput { error } => Ok(error),
785 other => anyhow::bail!("error {:?} is not UserInput", other),
786 }
787 }
788}
789
790impl From<UserInputError> for IotaError {
791 fn from(error: UserInputError) -> Self {
792 IotaError::UserInput { error }
793 }
794}
795
796impl From<IotaObjectResponseError> for IotaError {
797 fn from(error: IotaObjectResponseError) -> Self {
798 IotaError::IotaObjectResponse { error }
799 }
800}
801
802impl IotaError {
803 pub fn individual_error_indicates_epoch_change(&self) -> bool {
804 matches!(
805 self,
806 IotaError::ValidatorHaltedAtEpochEnd | IotaError::MissingCommitteeAtEpoch(_)
807 )
808 }
809
810 pub fn is_retryable(&self) -> (bool, bool) {
816 let retryable = match self {
817 IotaError::Rpc { .. } => true,
818
819 IotaError::ValidatorHaltedAtEpochEnd => true,
821 IotaError::MissingCommitteeAtEpoch(..) => true,
822 IotaError::WrongEpoch { .. } => true,
823 IotaError::EpochEnded { .. } => true,
824
825 IotaError::UserInput { error } => {
826 match error {
827 UserInputError::ObjectNotFound { .. } => true,
829 UserInputError::DependentPackageNotFound { .. } => true,
830 _ => false,
831 }
832 }
833
834 IotaError::PotentiallyTemporarilyInvalidSignature { .. } => true,
835
836 IotaError::TooManyTransactionsPendingExecution { .. } => true,
838 IotaError::TooManyTransactionsPendingOnObject { .. } => true,
839 IotaError::TooOldTransactionPendingOnObject { .. } => true,
840 IotaError::TooManyTransactionsPendingConsensus => true,
841 IotaError::ValidatorOverloadedRetryAfter { .. } => true,
842
843 IotaError::Execution(..) => false,
845 IotaError::ByzantineAuthoritySuspicion { .. } => false,
846 IotaError::QuorumFailedToGetEffectsQuorumWhenProcessingTransaction { .. } => false,
847 IotaError::TxAlreadyFinalizedWithDifferentUserSigs => false,
848 IotaError::FailedToVerifyTxCertWithExecutedEffects { .. } => false,
849 IotaError::ObjectLockConflict { .. } => false,
850
851 IotaError::TooManyRequests => false,
855
856 _ => return (false, false),
858 };
859
860 (retryable, true)
861 }
862
863 pub fn is_object_or_package_not_found(&self) -> bool {
864 match self {
865 IotaError::UserInput { error } => {
866 matches!(
867 error,
868 UserInputError::ObjectNotFound { .. }
869 | UserInputError::DependentPackageNotFound { .. }
870 )
871 }
872 _ => false,
873 }
874 }
875
876 pub fn is_overload(&self) -> bool {
877 matches!(
878 self,
879 IotaError::TooManyTransactionsPendingExecution { .. }
880 | IotaError::TooManyTransactionsPendingOnObject { .. }
881 | IotaError::TooOldTransactionPendingOnObject { .. }
882 | IotaError::TooManyTransactionsPendingConsensus
883 )
884 }
885
886 pub fn is_retryable_overload(&self) -> bool {
887 matches!(self, IotaError::ValidatorOverloadedRetryAfter { .. })
888 }
889
890 pub fn retry_after_secs(&self) -> u64 {
891 match self {
892 IotaError::ValidatorOverloadedRetryAfter { retry_after_secs } => *retry_after_secs,
893 _ => 0,
894 }
895 }
896}
897
898impl Ord for IotaError {
899 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
900 Ord::cmp(self.as_ref(), other.as_ref())
901 }
902}
903
904impl PartialOrd for IotaError {
905 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
906 Some(self.cmp(other))
907 }
908}
909
910type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
911
912pub type ExecutionErrorKind = ExecutionFailureStatus;
913
914#[derive(Debug)]
915pub struct ExecutionError {
916 inner: Box<ExecutionErrorInner>,
917}
918
919#[derive(Debug)]
920struct ExecutionErrorInner {
921 kind: ExecutionErrorKind,
922 source: Option<BoxError>,
923 command: Option<CommandIndex>,
924}
925
926impl ExecutionError {
927 pub fn new(kind: ExecutionErrorKind, source: Option<BoxError>) -> Self {
928 Self {
929 inner: Box::new(ExecutionErrorInner {
930 kind,
931 source,
932 command: None,
933 }),
934 }
935 }
936
937 pub fn new_with_source<E: Into<BoxError>>(kind: ExecutionErrorKind, source: E) -> Self {
938 Self::new(kind, Some(source.into()))
939 }
940
941 pub fn invariant_violation<E: Into<BoxError>>(source: E) -> Self {
942 Self::new_with_source(ExecutionFailureStatus::InvariantViolation, source)
943 }
944
945 pub fn with_command_index(mut self, command: CommandIndex) -> Self {
946 self.inner.command = Some(command);
947 self
948 }
949
950 pub fn from_kind(kind: ExecutionErrorKind) -> Self {
951 Self::new(kind, None)
952 }
953
954 pub fn kind(&self) -> &ExecutionErrorKind {
955 &self.inner.kind
956 }
957
958 pub fn command(&self) -> Option<CommandIndex> {
959 self.inner.command
960 }
961
962 pub fn source(&self) -> &Option<BoxError> {
963 &self.inner.source
964 }
965
966 pub fn to_execution_status(&self) -> (ExecutionFailureStatus, Option<CommandIndex>) {
967 (self.kind().clone(), self.command())
968 }
969}
970
971impl std::fmt::Display for ExecutionError {
972 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
973 write!(f, "ExecutionError: {self:?}")
974 }
975}
976
977impl std::error::Error for ExecutionError {
978 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
979 self.inner.source.as_ref().map(|e| &**e as _)
980 }
981}
982
983impl From<ExecutionErrorKind> for ExecutionError {
984 fn from(kind: ExecutionErrorKind) -> Self {
985 Self::from_kind(kind)
986 }
987}
988
989pub fn command_argument_error(e: CommandArgumentError, arg_idx: usize) -> ExecutionError {
990 ExecutionError::from_kind(ExecutionErrorKind::command_argument_error(
991 e,
992 arg_idx as u16,
993 ))
994}