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 one 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
312#[derive(
313 Eq,
314 PartialEq,
315 Clone,
316 Debug,
317 Serialize,
318 Deserialize,
319 Hash,
320 AsRefStr,
321 IntoStaticStr,
322 JsonSchema,
323 Error,
324)]
325#[serde(tag = "code", rename = "ObjectResponseError", rename_all = "camelCase")]
326pub enum IotaObjectResponseError {
327 #[error("Object {:?} does not exist", object_id)]
328 NotExists { object_id: ObjectID },
329 #[error("Cannot find dynamic field for parent object {:?}", parent_object_id)]
330 DynamicFieldNotFound { parent_object_id: ObjectID },
331 #[error(
332 "Object has been deleted object_id: {:?} at version: {:?} in digest {:?}",
333 object_id,
334 version,
335 digest
336 )]
337 Deleted {
338 object_id: ObjectID,
339 version: SequenceNumber,
341 digest: ObjectDigest,
343 },
344 #[error("Unknown Error")]
345 Unknown,
346 #[error("Display Error: {:?}", error)]
347 Display { error: String },
348 }
350
351#[derive(
353 Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr,
354)]
355pub enum IotaError {
356 #[error("Error checking transaction input objects: {:?}", error)]
357 UserInput { error: UserInputError },
358
359 #[error("Error checking transaction object: {:?}", error)]
360 IotaObjectResponse { error: IotaObjectResponseError },
361
362 #[error("Expecting a single owner, shared ownership found")]
363 UnexpectedOwnerType,
364
365 #[error("There are already {queue_len} transactions pending, above threshold of {threshold}")]
366 TooManyTransactionsPendingExecution { queue_len: usize, threshold: usize },
367
368 #[error("There are too many transactions pending in consensus")]
369 TooManyTransactionsPendingConsensus,
370
371 #[error(
372 "Input {object_id} already has {queue_len} transactions pending, above threshold of {threshold}"
373 )]
374 TooManyTransactionsPendingOnObject {
375 object_id: ObjectID,
376 queue_len: usize,
377 threshold: usize,
378 },
379
380 #[error(
381 "Input {object_id} has a transaction {txn_age_sec} seconds old pending, above threshold of {threshold} seconds"
382 )]
383 TooOldTransactionPendingOnObject {
384 object_id: ObjectID,
385 txn_age_sec: u64,
386 threshold: u64,
387 },
388
389 #[error("Soft bundle must only contain transactions of UserTransaction kind")]
390 InvalidTxKindInSoftBundle,
391
392 #[error("Signature is not valid: {}", error)]
394 InvalidSignature { error: String },
395 #[error("Required Signature from {expected} is absent {:?}", actual)]
396 SignerSignatureAbsent {
397 expected: String,
398 actual: Vec<String>,
399 },
400 #[error("Expect {expected} signer signatures but got {actual}")]
401 SignerSignatureNumberMismatch { expected: usize, actual: usize },
402 #[error("Value was not signed by the correct sender: {}", error)]
403 IncorrectSigner { error: String },
404 #[error(
405 "Value was not signed by a known authority. signer: {:?}, index: {:?}, committee: {committee}",
406 signer,
407 index
408 )]
409 UnknownSigner {
410 signer: Option<String>,
411 index: Option<u32>,
412 committee: Box<Committee>,
413 },
414 #[error(
415 "Validator {:?} responded multiple signatures for the same message, conflicting: {:?}",
416 signer,
417 conflicting_sig
418 )]
419 StakeAggregatorRepeatedSigner {
420 signer: AuthorityName,
421 conflicting_sig: bool,
422 },
423 #[error(
426 "Signature is not valid, but a retry may result in a valid one: {}",
427 error
428 )]
429 PotentiallyTemporarilyInvalidSignature { error: String },
430
431 #[error(
433 "Signature or certificate from wrong epoch, expected {expected_epoch}, got {actual_epoch}"
434 )]
435 WrongEpoch {
436 expected_epoch: EpochId,
437 actual_epoch: EpochId,
438 },
439 #[error("Signatures in a certificate must form a quorum")]
440 CertificateRequiresQuorum,
441 #[error("Transaction certificate processing failed: {err}")]
442 ErrorWhileProcessingCertificate { err: String },
443 #[error(
444 "Failed to get a quorum of signed effects when processing transaction: {effects_map:?}"
445 )]
446 QuorumFailedToGetEffectsQuorumWhenProcessingTransaction {
447 effects_map: BTreeMap<TransactionEffectsDigest, (Vec<AuthorityName>, StakeUnit)>,
448 },
449 #[error(
450 "Failed to verify Tx certificate with executed effects, error: {error:?}, validator: {validator_name:?}"
451 )]
452 FailedToVerifyTxCertWithExecutedEffects {
453 validator_name: AuthorityName,
454 error: String,
455 },
456 #[error("Transaction is already finalized but with different user signatures")]
457 TxAlreadyFinalizedWithDifferentUserSigs,
458
459 #[error("Invalid authenticator")]
461 InvalidAuthenticator,
462 #[error("Invalid address")]
463 InvalidAddress,
464 #[error("Invalid transaction digest.")]
465 InvalidTransactionDigest,
466
467 #[error("Invalid digest length. Expected {expected}, got {actual}")]
468 InvalidDigestLength { expected: usize, actual: usize },
469 #[error("Invalid DKG message size")]
470 InvalidDkgMessageSize,
471
472 #[error("Unexpected message.")]
473 UnexpectedMessage,
474
475 #[error("Failed to verify the Move module, reason: {error:?}.")]
477 ModuleVerificationFailure { error: String },
478 #[error("Failed to deserialize the Move module, reason: {error:?}.")]
479 ModuleDeserializationFailure { error: String },
480 #[error("Failed to publish the Move module(s), reason: {error}")]
481 ModulePublishFailure { error: String },
482 #[error("Failed to build Move modules: {error}.")]
483 ModuleBuildFailure { error: String },
484
485 #[error("Function resolution failure: {error:?}.")]
487 FunctionNotFound { error: String },
488 #[error("Module not found in package: {module_name:?}.")]
489 ModuleNotFound { module_name: String },
490 #[error("Type error while binding function arguments: {error:?}.")]
491 Type { error: String },
492 #[error("Circular object ownership detected")]
493 CircularObjectOwnership,
494
495 #[error("Attempt to re-initialize a transaction lock for objects {:?}.", refs)]
497 ObjectLockAlreadyInitialized { refs: Vec<ObjectRef> },
498 #[error(
499 "Object {obj_ref:?} already locked by a different transaction: {pending_transaction:?}"
500 )]
501 ObjectLockConflict {
502 obj_ref: ObjectRef,
503 pending_transaction: TransactionDigest,
504 },
505 #[error(
506 "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:?}"
507 )]
508 ObjectLockedAtFutureEpoch {
509 obj_refs: Vec<ObjectRef>,
510 locked_epoch: EpochId,
511 new_epoch: EpochId,
512 locked_by_tx: TransactionDigest,
513 },
514 #[error("{TRANSACTION_NOT_FOUND_MSG_PREFIX} [{:?}].", digest)]
515 TransactionNotFound { digest: TransactionDigest },
516 #[error("{TRANSACTIONS_NOT_FOUND_MSG_PREFIX} [{:?}].", digests)]
517 TransactionsNotFound { digests: Vec<TransactionDigest> },
518 #[error("Could not find the referenced transaction events [{digest:?}].")]
519 TransactionEventsNotFound { digest: TransactionEventsDigest },
520 #[error(
521 "Attempt to move to `Executed` state an transaction that has already been executed: {:?}.",
522 digest
523 )]
524 TransactionAlreadyExecuted { digest: TransactionDigest },
525 #[error("Object ID did not have the expected type")]
526 BadObjectType { error: String },
527 #[error("Fail to retrieve Object layout for {st}")]
528 FailObjectLayout { st: String },
529
530 #[error("Execution invariant violated")]
531 ExecutionInvariantViolation,
532 #[error("Validator {authority:?} is faulty in a Byzantine manner: {reason:?}")]
533 ByzantineAuthoritySuspicion {
534 authority: AuthorityName,
535 reason: String,
536 },
537 #[error(
538 "Attempted to access {object} through parent {given_parent}, \
539 but it's actual parent is {actual_owner}"
540 )]
541 InvalidChildObjectAccess {
542 object: ObjectID,
543 given_parent: ObjectID,
544 actual_owner: Owner,
545 },
546
547 #[error("Authority Error: {error:?}")]
548 GenericAuthority { error: String },
549
550 #[error("Generic Bridge Error: {error:?}")]
551 GenericBridge { 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("Failed to read or deserialize bridge related data structures on-chain: {0}")]
652 IotaBridgeRead(String),
653
654 #[error("Unexpected version error: {0}")]
655 UnexpectedVersion(String),
656
657 #[error("Message version is not supported at the current protocol version: {error}")]
658 WrongMessageVersion { error: String },
659
660 #[error("unknown error: {0}")]
661 Unknown(String),
662
663 #[error("Failed to perform file operation: {0}")]
664 FileIO(String),
665
666 #[error("Failed to get JWK")]
667 JWKRetrieval,
668
669 #[error("Storage error: {0}")]
670 Storage(String),
671
672 #[error(
673 "Validator cannot handle the request at the moment. Please retry after at least {retry_after_secs} seconds."
674 )]
675 ValidatorOverloadedRetryAfter { retry_after_secs: u64 },
676
677 #[error("Too many requests")]
678 TooManyRequests,
679
680 #[error("The request did not contain a certificate")]
681 NoCertificateProvided,
682}
683
684#[repr(u64)]
685#[expect(non_camel_case_types)]
686#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
687pub enum VMMVerifierErrorSubStatusCode {
691 MULTIPLE_RETURN_VALUES_NOT_ALLOWED = 0,
692 INVALID_OBJECT_CREATION = 1,
693}
694
695#[repr(u64)]
696#[expect(non_camel_case_types)]
697#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
698pub enum VMMemoryLimitExceededSubStatusCode {
701 EVENT_COUNT_LIMIT_EXCEEDED = 0,
702 EVENT_SIZE_LIMIT_EXCEEDED = 1,
703 NEW_ID_COUNT_LIMIT_EXCEEDED = 2,
704 DELETED_ID_COUNT_LIMIT_EXCEEDED = 3,
705 TRANSFER_ID_COUNT_LIMIT_EXCEEDED = 4,
706 OBJECT_RUNTIME_CACHE_LIMIT_EXCEEDED = 5,
707 OBJECT_RUNTIME_STORE_LIMIT_EXCEEDED = 6,
708 TOTAL_EVENT_SIZE_LIMIT_EXCEEDED = 7,
709}
710
711pub type IotaResult<T = ()> = Result<T, IotaError>;
712pub type UserInputResult<T = ()> = Result<T, UserInputError>;
713
714impl From<iota_protocol_config::Error> for IotaError {
715 fn from(error: iota_protocol_config::Error) -> Self {
716 IotaError::WrongMessageVersion { error: error.0 }
717 }
718}
719
720impl From<ExecutionError> for IotaError {
721 fn from(error: ExecutionError) -> Self {
722 IotaError::Execution(error.to_string())
723 }
724}
725
726impl From<Status> for IotaError {
727 fn from(status: Status) -> Self {
728 if status.message() == "Too many requests" {
729 return Self::TooManyRequests;
730 }
731 let result = bcs::from_bytes::<IotaError>(status.details());
732 if let Ok(iota_error) = result {
733 iota_error
734 } else {
735 Self::Rpc(
736 status.message().to_owned(),
737 status.code().description().to_owned(),
738 )
739 }
740 }
741}
742
743impl From<TypedStoreError> for IotaError {
744 fn from(e: TypedStoreError) -> Self {
745 Self::Storage(e.to_string())
746 }
747}
748
749impl From<crate::storage::error::Error> for IotaError {
750 fn from(e: crate::storage::error::Error) -> Self {
751 Self::Storage(e.to_string())
752 }
753}
754
755impl From<IotaError> for Status {
756 fn from(error: IotaError) -> Self {
757 let bytes = bcs::to_bytes(&error).unwrap();
758 Status::with_details(tonic::Code::Internal, error.to_string(), bytes.into())
759 }
760}
761
762impl From<ExecutionErrorKind> for IotaError {
763 fn from(kind: ExecutionErrorKind) -> Self {
764 ExecutionError::from_kind(kind).into()
765 }
766}
767
768impl From<&str> for IotaError {
769 fn from(error: &str) -> Self {
770 IotaError::GenericAuthority {
771 error: error.to_string(),
772 }
773 }
774}
775
776impl From<String> for IotaError {
777 fn from(error: String) -> Self {
778 IotaError::GenericAuthority { error }
779 }
780}
781
782impl TryFrom<IotaError> for UserInputError {
783 type Error = anyhow::Error;
784
785 fn try_from(err: IotaError) -> Result<Self, Self::Error> {
786 match err {
787 IotaError::UserInput { error } => Ok(error),
788 other => anyhow::bail!("error {:?} is not UserInput", other),
789 }
790 }
791}
792
793impl From<UserInputError> for IotaError {
794 fn from(error: UserInputError) -> Self {
795 IotaError::UserInput { error }
796 }
797}
798
799impl From<IotaObjectResponseError> for IotaError {
800 fn from(error: IotaObjectResponseError) -> Self {
801 IotaError::IotaObjectResponse { error }
802 }
803}
804
805impl IotaError {
806 pub fn individual_error_indicates_epoch_change(&self) -> bool {
807 matches!(
808 self,
809 IotaError::ValidatorHaltedAtEpochEnd | IotaError::MissingCommitteeAtEpoch(_)
810 )
811 }
812
813 pub fn is_retryable(&self) -> (bool, bool) {
819 let retryable = match self {
820 IotaError::Rpc { .. } => true,
821
822 IotaError::ValidatorHaltedAtEpochEnd => true,
824 IotaError::MissingCommitteeAtEpoch(..) => true,
825 IotaError::WrongEpoch { .. } => true,
826 IotaError::EpochEnded { .. } => true,
827
828 IotaError::UserInput { error } => {
829 match error {
830 UserInputError::ObjectNotFound { .. } => true,
832 UserInputError::DependentPackageNotFound { .. } => true,
833 _ => false,
834 }
835 }
836
837 IotaError::PotentiallyTemporarilyInvalidSignature { .. } => true,
838
839 IotaError::TooManyTransactionsPendingExecution { .. } => true,
841 IotaError::TooManyTransactionsPendingOnObject { .. } => true,
842 IotaError::TooOldTransactionPendingOnObject { .. } => true,
843 IotaError::TooManyTransactionsPendingConsensus => true,
844 IotaError::ValidatorOverloadedRetryAfter { .. } => true,
845
846 IotaError::Execution(..) => false,
848 IotaError::ByzantineAuthoritySuspicion { .. } => false,
849 IotaError::QuorumFailedToGetEffectsQuorumWhenProcessingTransaction { .. } => false,
850 IotaError::TxAlreadyFinalizedWithDifferentUserSigs => false,
851 IotaError::FailedToVerifyTxCertWithExecutedEffects { .. } => false,
852 IotaError::ObjectLockConflict { .. } => false,
853
854 IotaError::TooManyRequests => false,
858
859 _ => return (false, false),
861 };
862
863 (retryable, true)
864 }
865
866 pub fn is_object_or_package_not_found(&self) -> bool {
867 match self {
868 IotaError::UserInput { error } => {
869 matches!(
870 error,
871 UserInputError::ObjectNotFound { .. }
872 | UserInputError::DependentPackageNotFound { .. }
873 )
874 }
875 _ => false,
876 }
877 }
878
879 pub fn is_overload(&self) -> bool {
880 matches!(
881 self,
882 IotaError::TooManyTransactionsPendingExecution { .. }
883 | IotaError::TooManyTransactionsPendingOnObject { .. }
884 | IotaError::TooOldTransactionPendingOnObject { .. }
885 | IotaError::TooManyTransactionsPendingConsensus
886 )
887 }
888
889 pub fn is_retryable_overload(&self) -> bool {
890 matches!(self, IotaError::ValidatorOverloadedRetryAfter { .. })
891 }
892
893 pub fn retry_after_secs(&self) -> u64 {
894 match self {
895 IotaError::ValidatorOverloadedRetryAfter { retry_after_secs } => *retry_after_secs,
896 _ => 0,
897 }
898 }
899}
900
901impl Ord for IotaError {
902 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
903 Ord::cmp(self.as_ref(), other.as_ref())
904 }
905}
906
907impl PartialOrd for IotaError {
908 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
909 Some(self.cmp(other))
910 }
911}
912
913type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
914
915pub type ExecutionErrorKind = ExecutionFailureStatus;
916
917#[derive(Debug)]
918pub struct ExecutionError {
919 inner: Box<ExecutionErrorInner>,
920}
921
922#[derive(Debug)]
923struct ExecutionErrorInner {
924 kind: ExecutionErrorKind,
925 source: Option<BoxError>,
926 command: Option<CommandIndex>,
927}
928
929impl ExecutionError {
930 pub fn new(kind: ExecutionErrorKind, source: Option<BoxError>) -> Self {
931 Self {
932 inner: Box::new(ExecutionErrorInner {
933 kind,
934 source,
935 command: None,
936 }),
937 }
938 }
939
940 pub fn new_with_source<E: Into<BoxError>>(kind: ExecutionErrorKind, source: E) -> Self {
941 Self::new(kind, Some(source.into()))
942 }
943
944 pub fn invariant_violation<E: Into<BoxError>>(source: E) -> Self {
945 Self::new_with_source(ExecutionFailureStatus::InvariantViolation, source)
946 }
947
948 pub fn with_command_index(mut self, command: CommandIndex) -> Self {
949 self.inner.command = Some(command);
950 self
951 }
952
953 pub fn from_kind(kind: ExecutionErrorKind) -> Self {
954 Self::new(kind, None)
955 }
956
957 pub fn kind(&self) -> &ExecutionErrorKind {
958 &self.inner.kind
959 }
960
961 pub fn command(&self) -> Option<CommandIndex> {
962 self.inner.command
963 }
964
965 pub fn source(&self) -> &Option<BoxError> {
966 &self.inner.source
967 }
968
969 pub fn to_execution_status(&self) -> (ExecutionFailureStatus, Option<CommandIndex>) {
970 (self.kind().clone(), self.command())
971 }
972}
973
974impl std::fmt::Display for ExecutionError {
975 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
976 write!(f, "ExecutionError: {:?}", self)
977 }
978}
979
980impl std::error::Error for ExecutionError {
981 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
982 self.inner.source.as_ref().map(|e| &**e as _)
983 }
984}
985
986impl From<ExecutionErrorKind> for ExecutionError {
987 fn from(kind: ExecutionErrorKind) -> Self {
988 Self::from_kind(kind)
989 }
990}
991
992pub fn command_argument_error(e: CommandArgumentError, arg_idx: usize) -> ExecutionError {
993 ExecutionError::from_kind(ExecutionErrorKind::command_argument_error(
994 e,
995 arg_idx as u16,
996 ))
997}