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