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