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