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