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