1use crate::{
6 base_types::*,
7 committee::{Committee, EpochId, StakeUnit},
8 digests::CheckpointContentsDigest,
9 execution_status::CommandArgumentError,
10 messages_checkpoint::CheckpointSequenceNumber,
11 object::Owner,
12};
13
14use schemars::JsonSchema;
15use serde::{Deserialize, Serialize};
16use std::{collections::BTreeMap, fmt::Debug};
17use strum_macros::{AsRefStr, IntoStaticStr};
18use thiserror::Error;
19use tonic::Status;
20use typed_store_error::TypedStoreError;
21
22pub const TRANSACTION_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transaction";
23pub const TRANSACTIONS_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transactions";
24
25#[macro_export]
26macro_rules! fp_bail {
27 ($e:expr) => {
28 return Err($e)
29 };
30}
31
32#[macro_export(local_inner_macros)]
33macro_rules! fp_ensure {
34 ($cond:expr, $e:expr) => {
35 if !($cond) {
36 fp_bail!($e);
37 }
38 };
39}
40use crate::execution_status::{CommandIndex, ExecutionFailureStatus};
41pub(crate) use fp_ensure;
42
43#[macro_export]
44macro_rules! exit_main {
45 ($result:expr) => {
46 match $result {
47 Ok(_) => (),
48 Err(err) => {
49 let err = format!("{:?}", err);
50 println!("{}", err.bold().red());
51 std::process::exit(1);
52 }
53 }
54 };
55}
56
57#[macro_export]
58macro_rules! make_invariant_violation {
59 ($($args:expr),* $(,)?) => {{
60 if cfg!(debug_assertions) {
61 panic!($($args),*)
62 }
63 $crate::error::ExecutionError::invariant_violation(format!($($args),*))
64 }}
65}
66
67#[macro_export]
68macro_rules! invariant_violation {
69 ($($args:expr),* $(,)?) => {
70 return Err(make_invariant_violation!($($args),*).into())
71 };
72}
73
74#[macro_export]
75macro_rules! assert_invariant {
76 ($cond:expr, $($args:expr),* $(,)?) => {{
77 if !$cond {
78 invariant_violation!($($args),*)
79 }
80 }};
81}
82
83#[derive(
84 Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr,
85)]
86pub enum UserInputError {
87 #[error("Mutable object {object_id} cannot appear more than one in one transaction")]
88 MutableObjectUsedMoreThanOnce { object_id: ObjectID },
89 #[error("Wrong number of parameters for the transaction")]
90 ObjectInputArityViolation,
91 #[error(
92 "Could not find the referenced object {} at version {:?}",
93 object_id,
94 version
95 )]
96 ObjectNotFound {
97 object_id: ObjectID,
98 version: Option<SequenceNumber>,
99 },
100 #[error(
101 "Object ID {} Version {} Digest {} is not available for consumption, current version: {current_version}",
102 .provided_obj_ref.0, .provided_obj_ref.1, .provided_obj_ref.2
103 )]
104 ObjectVersionUnavailableForConsumption {
105 provided_obj_ref: ObjectRef,
106 current_version: SequenceNumber,
107 },
108 #[error("Package verification failed: {err}")]
109 PackageVerificationTimeout { err: String },
110 #[error("Dependent package not found on-chain: {package_id}")]
111 DependentPackageNotFound { package_id: ObjectID },
112 #[error("Mutable parameter provided, immutable parameter expected")]
113 ImmutableParameterExpectedError { object_id: ObjectID },
114 #[error("Size limit exceeded: {limit} is {value}")]
115 SizeLimitExceeded { limit: String, value: String },
116 #[error(
117 "Object {child_id} is owned by object {parent_id}. \
118 Objects owned by other objects cannot be used as input arguments"
119 )]
120 InvalidChildObjectArgument {
121 child_id: ObjectID,
122 parent_id: ObjectID,
123 },
124 #[error("Invalid Object digest for object {object_id}. Expected digest : {expected_digest}")]
125 InvalidObjectDigest {
126 object_id: ObjectID,
127 expected_digest: ObjectDigest,
128 },
129 #[error("Sequence numbers above the maximal value are not usable for transfers")]
130 InvalidSequenceNumber,
131 #[error("A move object is expected, instead a move package is passed: {object_id}")]
132 MovePackageAsObject { object_id: ObjectID },
133 #[error("A move package is expected, instead a move object is passed: {object_id}")]
134 MoveObjectAsPackage { object_id: ObjectID },
135 #[error("Transaction was not signed by the correct sender: {}", error)]
136 IncorrectUserSignature { error: String },
137
138 #[error("Object used as shared is not shared")]
139 NotSharedObjectError,
140 #[error("The transaction inputs contain duplicated ObjectRef's")]
141 DuplicateObjectRefInput,
142
143 #[error("Transaction gas payment missing")]
145 MissingGasPayment,
146 #[error("Gas object is not an owned object with owner: {:?}", owner)]
147 GasObjectNotOwnedObject { owner: Owner },
148 #[error("Gas budget: {gas_budget} is higher than max: {max_budget}")]
149 GasBudgetTooHigh { gas_budget: u64, max_budget: u64 },
150 #[error("Gas budget: {gas_budget} is lower than min: {min_budget}")]
151 GasBudgetTooLow { gas_budget: u64, min_budget: u64 },
152 #[error(
153 "Balance of gas object {gas_balance} is lower than the needed amount: {needed_gas_amount}"
154 )]
155 GasBalanceTooLow {
156 gas_balance: u128,
157 needed_gas_amount: u128,
158 },
159 #[error("Transaction kind does not support Sponsored Transaction")]
160 UnsupportedSponsoredTransactionKind,
161 #[error("Gas price {gas_price} under reference gas price (RGP) {reference_gas_price}")]
162 GasPriceUnderRGP {
163 gas_price: u64,
164 reference_gas_price: u64,
165 },
166 #[error("Gas price cannot exceed {max_gas_price} mist")]
167 GasPriceTooHigh { max_gas_price: u64 },
168 #[error("Object {object_id} is not a gas object")]
169 InvalidGasObject { object_id: ObjectID },
170 #[error("Gas object does not have enough balance to cover minimal gas spend")]
171 InsufficientBalanceToCoverMinimalGas,
172
173 #[error(
174 "Could not find the referenced object {object_id} as the asked version {asked_version:?} is higher than the latest {latest_version:?}"
175 )]
176 ObjectSequenceNumberTooHigh {
177 object_id: ObjectID,
178 asked_version: SequenceNumber,
179 latest_version: SequenceNumber,
180 },
181 #[error("Object deleted at reference ({}, {:?}, {})", object_ref.0, object_ref.1, object_ref.2)]
182 ObjectDeleted { object_ref: ObjectRef },
183 #[error("Invalid Batch Transaction: {error}")]
184 InvalidBatchTransaction { error: String },
185 #[error("This Move function is currently disabled and not available for call")]
186 BlockedMoveFunction,
187 #[error("Empty input coins for Pay related transaction")]
188 EmptyInputCoins,
189
190 #[error(
191 "SUI payment transactions use first input coin for gas payment, but found a different gas object"
192 )]
193 UnexpectedGasPaymentObject,
194
195 #[error("Wrong initial version given for shared object")]
196 SharedObjectStartingVersionMismatch,
197
198 #[error(
199 "Attempt to transfer object {object_id} that does not have public transfer. Object transfer must be done instead using a distinct Move function call"
200 )]
201 TransferObjectWithoutPublicTransferError { object_id: ObjectID },
202
203 #[error(
204 "TransferObjects, MergeCoin, and Publish cannot have empty arguments. \
205 If MakeMoveVec has empty arguments, it must have a type specified"
206 )]
207 EmptyCommandInput,
208
209 #[error("Transaction is denied: {error}")]
210 TransactionDenied { error: String },
211
212 #[error("Feature is not supported: {0}")]
213 Unsupported(String),
214
215 #[error("Query transactions with move function input error: {0}")]
216 MoveFunctionInputError(String),
217
218 #[error("Verified checkpoint not found for sequence number: {0}")]
219 VerifiedCheckpointNotFound(CheckpointSequenceNumber),
220
221 #[error("Verified checkpoint not found for digest: {0}")]
222 VerifiedCheckpointDigestNotFound(String),
223
224 #[error("Latest checkpoint sequence number not found")]
225 LatestCheckpointSequenceNumberNotFound,
226
227 #[error("Checkpoint contents not found for digest: {0}")]
228 CheckpointContentsNotFound(CheckpointContentsDigest),
229
230 #[error("Genesis transaction not found")]
231 GenesisTransactionNotFound,
232
233 #[error("Transaction {0} not found")]
234 TransactionCursorNotFound(u64),
235
236 #[error(
237 "Object {} is a system object and cannot be accessed by user transactions",
238 object_id
239 )]
240 InaccessibleSystemObject { object_id: ObjectID },
241 #[error(
242 "{max_publish_commands} max publish/upgrade commands allowed, {publish_count} provided"
243 )]
244 MaxPublishCountExceeded {
245 max_publish_commands: u64,
246 publish_count: u64,
247 },
248
249 #[error("Immutable parameter provided, mutable parameter expected")]
250 MutableParameterExpected { object_id: ObjectID },
251
252 #[error("Address {address} is denied for coin {coin_type}")]
253 AddressDeniedForCoin {
254 address: SuiAddress,
255 coin_type: String,
256 },
257
258 #[error("Commands following a command with Random can only be TransferObjects or MergeCoins")]
259 PostRandomCommandRestrictions,
260
261 #[error("Number of transactions ({size}) exceeds the maximum allowed ({limit}) in a batch")]
263 TooManyTransactionsInBatch { size: usize, limit: u64 },
264 #[error(
265 "Total transactions size ({size}) bytes exceeds the maximum allowed ({limit}) bytes in a Soft Bundle"
266 )]
267 TotalTransactionSizeTooLargeInBatch { size: usize, limit: u64 },
268 #[error("Transaction {digest} in Soft Bundle contains no shared objects")]
269 NoSharedObjectError { digest: TransactionDigest },
270 #[error("Transaction {digest} in Soft Bundle has already been executed")]
271 AlreadyExecutedInSoftBundleError { digest: TransactionDigest },
272 #[error("At least one certificate in Soft Bundle has already been processed")]
273 CertificateAlreadyProcessed,
274 #[error("Transaction {digest} was already executed")]
275 TransactionAlreadyExecuted { digest: TransactionDigest },
276 #[error(
277 "Gas price for transaction {digest} in Soft Bundle mismatch: want {expected}, have {actual}"
278 )]
279 GasPriceMismatchError {
280 digest: TransactionDigest,
281 expected: u64,
282 actual: u64,
283 },
284
285 #[error("Coin type is globally paused for use: {coin_type}")]
286 CoinTypeGlobalPause { coin_type: String },
287
288 #[error("Invalid identifier found in the transaction: {error}")]
289 InvalidIdentifier { error: String },
290
291 #[error("Object used as owned is not owned")]
292 NotOwnedObjectError,
293
294 #[error("Invalid withdraw reservation: {error}")]
295 InvalidWithdrawReservation { error: String },
296
297 #[error("Transaction with empty gas payment must specify an expiration.")]
298 MissingTransactionExpiration,
299
300 #[error("Invalid transaction expiration: {error}")]
301 InvalidExpiration { error: String },
302
303 #[error("Transaction chain ID {provided} does not match network chain ID {expected}.")]
304 InvalidChainId { provided: String, expected: String },
305}
306
307#[derive(
308 Eq,
309 PartialEq,
310 Clone,
311 Debug,
312 Serialize,
313 Deserialize,
314 Hash,
315 AsRefStr,
316 IntoStaticStr,
317 JsonSchema,
318 Error,
319)]
320#[serde(tag = "code", rename = "ObjectResponseError", rename_all = "camelCase")]
321pub enum SuiObjectResponseError {
322 #[error("Object {object_id} does not exist")]
323 NotExists { object_id: ObjectID },
324 #[error("Cannot find dynamic field for parent object {parent_object_id}")]
325 DynamicFieldNotFound { parent_object_id: ObjectID },
326 #[error(
327 "Object has been deleted object_id: {object_id} at version: {version:?} in digest {digest}"
328 )]
329 Deleted {
330 object_id: ObjectID,
331 version: SequenceNumber,
333 digest: ObjectDigest,
335 },
336 #[error("Unknown Error")]
337 Unknown,
338 #[error("Display Error: {error}")]
339 DisplayError { error: String },
340 }
342
343#[derive(Eq, PartialEq, Clone, Serialize, Deserialize, Error, Hash)]
345#[error(transparent)]
346pub struct SuiError(#[from] pub Box<SuiErrorKind>);
347
348#[derive(
350 Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr,
351)]
352pub enum SuiErrorKind {
353 #[error("Error checking transaction input objects: {error}")]
354 UserInputError { error: UserInputError },
355
356 #[error("Error checking transaction object: {error}")]
357 SuiObjectResponseError { error: SuiObjectResponseError },
358
359 #[error("Expecting a single owner, shared ownership found")]
360 UnexpectedOwnerType,
361
362 #[error("There are already {queue_len} transactions pending, above threshold of {threshold}")]
363 TooManyTransactionsPendingExecution { queue_len: usize, threshold: usize },
364
365 #[error("There are too many transactions pending in consensus")]
366 TooManyTransactionsPendingConsensus,
367
368 #[error(
369 "Input {object_id} already has {queue_len} transactions pending, above threshold of {threshold}"
370 )]
371 TooManyTransactionsPendingOnObject {
372 object_id: ObjectID,
373 queue_len: usize,
374 threshold: usize,
375 },
376
377 #[error(
378 "Input {object_id} has a transaction {txn_age_sec} seconds old pending, above threshold of {threshold} seconds"
379 )]
380 TooOldTransactionPendingOnObject {
381 object_id: ObjectID,
382 txn_age_sec: u64,
383 threshold: u64,
384 },
385
386 #[error("Soft bundle must only contain transactions of UserTransaction kind")]
387 InvalidTxKindInSoftBundle,
388
389 #[error("Signature is not valid: {}", error)]
391 InvalidSignature { error: String },
392 #[error("Required Signature from {expected} is absent {:?}", actual)]
393 SignerSignatureAbsent {
394 expected: String,
395 actual: Vec<String>,
396 },
397 #[error("Expect {expected} signer signatures but got {actual}")]
398 SignerSignatureNumberMismatch { expected: usize, actual: usize },
399 #[error("Value was not signed by the correct sender: {}", error)]
400 IncorrectSigner { error: String },
401 #[error(
402 "Value was not signed by a known authority. signer: {:?}, index: {:?}, committee: {committee}",
403 signer,
404 index
405 )]
406 UnknownSigner {
407 signer: Option<String>,
408 index: Option<u32>,
409 committee: Box<Committee>,
410 },
411 #[error(
412 "Validator {:?} responded multiple signatures for the same message, conflicting: {:?}",
413 signer,
414 conflicting_sig
415 )]
416 StakeAggregatorRepeatedSigner {
417 signer: AuthorityName,
418 conflicting_sig: bool,
419 },
420 #[error(
422 "Signature is not valid, but a retry may result in a valid one: {}",
423 error
424 )]
425 PotentiallyTemporarilyInvalidSignature { error: String },
426
427 #[error(
429 "Signature or certificate from wrong epoch, expected {expected_epoch}, got {actual_epoch}"
430 )]
431 WrongEpoch {
432 expected_epoch: EpochId,
433 actual_epoch: EpochId,
434 },
435 #[error("Signatures in a certificate must form a quorum")]
436 CertificateRequiresQuorum,
437 #[allow(non_camel_case_types)]
438 #[error("DEPRECATED")]
439 DEPRECATED_ErrorWhileProcessingCertificate,
440 #[error(
441 "Failed to get a quorum of signed effects when processing transaction: {effects_map:?}"
442 )]
443 QuorumFailedToGetEffectsQuorumWhenProcessingTransaction {
444 effects_map: BTreeMap<TransactionEffectsDigest, (Vec<AuthorityName>, StakeUnit)>,
445 },
446 #[error(
447 "Failed to verify Tx certificate with executed effects, error: {error:?}, validator: {validator_name:?}"
448 )]
449 FailedToVerifyTxCertWithExecutedEffects {
450 validator_name: AuthorityName,
451 error: String,
452 },
453 #[error("Transaction is already finalized but with different user signatures")]
454 TxAlreadyFinalizedWithDifferentUserSigs,
455
456 #[error("Invalid authenticator")]
458 InvalidAuthenticator,
459 #[error("Invalid address")]
460 InvalidAddress,
461 #[error("Invalid transaction digest.")]
462 InvalidTransactionDigest,
463
464 #[error("Invalid digest length. Expected {expected}, got {actual}")]
465 InvalidDigestLength { expected: usize, actual: usize },
466 #[error("Invalid DKG message size")]
467 InvalidDkgMessageSize,
468
469 #[error("Unexpected message: {0}")]
470 UnexpectedMessage(String),
471
472 #[error("Failed to verify the Move module, reason: {error}.")]
474 ModuleVerificationFailure { error: String },
475 #[error("Failed to deserialize the Move module, reason: {error}.")]
476 ModuleDeserializationFailure { error: String },
477 #[error("Failed to publish the Move module(s), reason: {error}")]
478 ModulePublishFailure { error: String },
479 #[error("Failed to build Move modules: {error}.")]
480 ModuleBuildFailure { error: String },
481
482 #[error("Function resolution failure: {error}.")]
484 FunctionNotFound { error: String },
485 #[error("Module not found in package: {module_name}.")]
486 ModuleNotFound { module_name: String },
487 #[error("Type error while binding function arguments: {error}.")]
488 TypeError { error: String },
489 #[error("Circular object ownership detected")]
490 CircularObjectOwnership,
491
492 #[error("Attempt to re-initialize a transaction lock for objects {:?}.", refs)]
494 ObjectLockAlreadyInitialized { refs: Vec<ObjectRef> },
495 #[error(
496 "Object {obj_ref:?} already locked by a different transaction: {pending_transaction:?}"
497 )]
498 ObjectLockConflict {
499 obj_ref: ObjectRef,
500 pending_transaction: TransactionDigest,
501 },
502 #[error(
503 "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:?}"
504 )]
505 ObjectLockedAtFutureEpoch {
506 obj_refs: Vec<ObjectRef>,
507 locked_epoch: EpochId,
508 new_epoch: EpochId,
509 locked_by_tx: TransactionDigest,
510 },
511 #[error("{TRANSACTION_NOT_FOUND_MSG_PREFIX} [{:?}].", digest)]
512 TransactionNotFound { digest: TransactionDigest },
513 #[error("{TRANSACTIONS_NOT_FOUND_MSG_PREFIX} [{:?}].", digests)]
514 TransactionsNotFound { digests: Vec<TransactionDigest> },
515 #[error("Could not find the referenced transaction events [{digest:?}].")]
516 TransactionEventsNotFound { digest: TransactionDigest },
517 #[error("Could not find the referenced transaction effects [{digest:?}].")]
518 TransactionEffectsNotFound { digest: TransactionDigest },
519 #[error(
520 "Attempt to move to `Executed` state an transaction that has already been executed: {:?}.",
521 digest
522 )]
523 TransactionAlreadyExecuted { digest: TransactionDigest },
524 #[error("Transaction reject reason not found for transaction {digest:?}")]
525 TransactionRejectReasonNotFound { digest: TransactionDigest },
526 #[error("Object ID did not have the expected type")]
527 BadObjectType { error: String },
528 #[error("Fail to retrieve Object layout for {st}")]
529 FailObjectLayout { st: String },
530
531 #[error("Execution invariant violated")]
532 ExecutionInvariantViolation,
533 #[error("Validator {authority:?} is faulty in a Byzantine manner: {reason:?}")]
534 ByzantineAuthoritySuspicion {
535 authority: AuthorityName,
536 reason: String,
537 },
538 #[allow(non_camel_case_types)]
539 #[serde(rename = "StorageError")]
540 #[error("DEPRECATED")]
541 DEPRECATED_StorageError,
542 #[allow(non_camel_case_types)]
543 #[serde(rename = "GenericStorageError")]
544 #[error("DEPRECATED")]
545 DEPRECATED_GenericStorageError,
546 #[error(
547 "Attempted to access {object} through parent {given_parent}, \
548 but it's actual parent is {actual_owner}"
549 )]
550 InvalidChildObjectAccess {
551 object: ObjectID,
552 given_parent: ObjectID,
553 actual_owner: Owner,
554 },
555
556 #[allow(non_camel_case_types)]
557 #[serde(rename = "StorageMissingFieldError")]
558 #[error("DEPRECATED")]
559 DEPRECATED_StorageMissingFieldError,
560 #[allow(non_camel_case_types)]
561 #[serde(rename = "StorageCorruptedFieldError")]
562 #[error("DEPRECATED")]
563 DEPRECATED_StorageCorruptedFieldError,
564
565 #[error("Authority Error: {error}")]
566 GenericAuthorityError { error: String },
567
568 #[error("Generic Bridge Error: {error}")]
569 GenericBridgeError { error: String },
570
571 #[error("Failed to dispatch subscription: {error}")]
572 FailedToDispatchSubscription { error: String },
573
574 #[error("Failed to serialize Owner: {error}")]
575 OwnerFailedToSerialize { error: String },
576
577 #[error("Failed to deserialize fields into JSON: {error}")]
578 ExtraFieldFailedToDeserialize { error: String },
579
580 #[error("Failed to execute transaction locally by Orchestrator: {error}")]
581 TransactionOrchestratorLocalExecutionError { error: String },
582
583 #[error("Failure serializing transaction in the requested format: {error}")]
585 TransactionSerializationError { error: String },
586 #[error("Failure deserializing transaction from the provided format: {error}")]
587 TransactionDeserializationError { error: String },
588 #[error("Failure serializing transaction effects from the provided format: {error}")]
589 TransactionEffectsSerializationError { error: String },
590 #[error("Failure deserializing transaction effects from the provided format: {error}")]
591 TransactionEffectsDeserializationError { error: String },
592 #[error("Failure serializing transaction events from the provided format: {error}")]
593 TransactionEventsSerializationError { error: String },
594 #[error("Failure deserializing transaction events from the provided format: {error}")]
595 TransactionEventsDeserializationError { error: String },
596 #[error("Failure serializing object in the requested format: {error}")]
597 ObjectSerializationError { error: String },
598 #[error("Failure deserializing object in the requested format: {error}")]
599 ObjectDeserializationError { error: String },
600 #[error("Event store component is not active on this node")]
601 NoEventStore,
602
603 #[error("Too many authority errors were detected for {}: {:?}", action, errors)]
605 TooManyIncorrectAuthorities {
606 errors: Vec<(AuthorityName, SuiError)>,
607 action: String,
608 },
609 #[error("Invalid transaction range query to the fullnode: {error}")]
610 FullNodeInvalidTxRangeQuery { error: String },
611
612 #[error("Failed to submit transaction to consensus: {0}")]
614 FailedToSubmitToConsensus(String),
615 #[error("Failed to connect with consensus node: {0}")]
616 ConsensusConnectionBroken(String),
617 #[error("Failed to execute handle_consensus_transaction on Sui: {0}")]
618 HandleConsensusTransactionFailure(String),
619
620 #[error("Signature key generation error: {0}")]
622 SignatureKeyGenError(String),
623 #[error("Key Conversion Error: {0}")]
624 KeyConversionError(String),
625 #[error("Invalid Private Key provided")]
626 InvalidPrivateKey,
627
628 #[error("Fullnode does not support handle_certificate")]
630 FullNodeCantHandleCertificate,
631
632 #[error("Validator temporarily stopped processing transactions due to epoch change")]
634 ValidatorHaltedAtEpochEnd,
635 #[error("Operations for epoch {0} have ended")]
636 EpochEnded(EpochId),
637 #[error("Error when advancing epoch: {error}")]
638 AdvanceEpochError { error: String },
639
640 #[error("Transaction Expired")]
641 TransactionExpired,
642
643 #[error("{1} - {0}")]
646 RpcError(String, String),
647
648 #[error("Method not allowed")]
649 InvalidRpcMethodError,
650
651 #[error("Use of disabled feature: {error}")]
652 UnsupportedFeatureError { error: String },
653
654 #[error("Unable to communicate with the Quorum Driver channel: {error}")]
655 QuorumDriverCommunicationError { error: String },
656
657 #[error("Operation timed out")]
658 TimeoutError,
659
660 #[error("Error executing {0}")]
661 ExecutionError(String),
662
663 #[error("Invalid committee composition")]
664 InvalidCommittee(String),
665
666 #[error("Missing committee information for epoch {0}")]
667 MissingCommitteeAtEpoch(EpochId),
668
669 #[error("Index store not available on this Fullnode.")]
670 IndexStoreNotAvailable,
671
672 #[error("Failed to read dynamic field from table in the object store: {0}")]
673 DynamicFieldReadError(String),
674
675 #[error("Failed to read or deserialize system state related data structures on-chain: {0}")]
676 SuiSystemStateReadError(String),
677
678 #[error("Failed to read or deserialize bridge related data structures on-chain: {0}")]
679 SuiBridgeReadError(String),
680
681 #[error("Unexpected version error: {0}")]
682 UnexpectedVersion(String),
683
684 #[error("Message version is not supported at the current protocol version: {error}")]
685 WrongMessageVersion { error: String },
686
687 #[error("unknown error: {0}")]
688 Unknown(String),
689
690 #[error("Failed to perform file operation: {0}")]
691 FileIOError(String),
692
693 #[error("Failed to get JWK")]
694 JWKRetrievalError,
695
696 #[error("Storage error: {0}")]
697 Storage(String),
698
699 #[error(
700 "Validator cannot handle the request at the moment. Please retry after at least {retry_after_secs} seconds."
701 )]
702 ValidatorOverloadedRetryAfter { retry_after_secs: u64 },
703
704 #[error("Too many requests")]
705 TooManyRequests,
706
707 #[error("The request did not contain a certificate")]
708 NoCertificateProvidedError,
709
710 #[error("Nitro attestation verify failed: {0}")]
711 NitroAttestationFailedToVerify(String),
712
713 #[error("Failed to serialize {type_info}, error: {error}")]
714 GrpcMessageSerializeError { type_info: String, error: String },
715
716 #[error("Failed to deserialize {type_info}, error: {error}")]
717 GrpcMessageDeserializeError { type_info: String, error: String },
718
719 #[error(
720 "Validator consensus rounds are lagging behind. last committed leader round: {last_committed_round}, requested round: {round}"
721 )]
722 ValidatorConsensusLagging {
723 round: u32,
724 last_committed_round: u32,
725 },
726
727 #[error("Invalid admin request: {0}")]
728 InvalidAdminRequest(String),
729
730 #[error("Invalid request: {0}")]
731 InvalidRequest(String),
732
733 #[error(
734 "The current set of aliases for a required signer changed after the transaction was submitted"
735 )]
736 AliasesChanged,
737}
738
739#[repr(u64)]
740#[allow(non_camel_case_types)]
741#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
742pub enum VMMVerifierErrorSubStatusCode {
745 MULTIPLE_RETURN_VALUES_NOT_ALLOWED = 0,
746 INVALID_OBJECT_CREATION = 1,
747}
748
749#[repr(u64)]
750#[allow(non_camel_case_types)]
751#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
752pub enum VMMemoryLimitExceededSubStatusCode {
754 EVENT_COUNT_LIMIT_EXCEEDED = 0,
755 EVENT_SIZE_LIMIT_EXCEEDED = 1,
756 NEW_ID_COUNT_LIMIT_EXCEEDED = 2,
757 DELETED_ID_COUNT_LIMIT_EXCEEDED = 3,
758 TRANSFER_ID_COUNT_LIMIT_EXCEEDED = 4,
759 OBJECT_RUNTIME_CACHE_LIMIT_EXCEEDED = 5,
760 OBJECT_RUNTIME_STORE_LIMIT_EXCEEDED = 6,
761 TOTAL_EVENT_SIZE_LIMIT_EXCEEDED = 7,
762}
763
764pub type SuiResult<T = ()> = Result<T, SuiError>;
765pub type UserInputResult<T = ()> = Result<T, UserInputError>;
766
767impl From<SuiErrorKind> for SuiError {
768 fn from(error: SuiErrorKind) -> Self {
769 SuiError(Box::new(error))
770 }
771}
772
773impl std::ops::Deref for SuiError {
774 type Target = SuiErrorKind;
775
776 fn deref(&self) -> &Self::Target {
777 &self.0
778 }
779}
780
781impl From<sui_protocol_config::Error> for SuiError {
782 fn from(error: sui_protocol_config::Error) -> Self {
783 SuiErrorKind::WrongMessageVersion { error: error.0 }.into()
784 }
785}
786
787impl From<ExecutionError> for SuiError {
788 fn from(error: ExecutionError) -> Self {
789 SuiErrorKind::ExecutionError(error.to_string()).into()
790 }
791}
792
793impl From<Status> for SuiError {
794 fn from(status: Status) -> Self {
795 if status.message() == "Too many requests" {
796 return SuiErrorKind::TooManyRequests.into();
797 }
798
799 let result = bcs::from_bytes::<SuiError>(status.details());
800 if let Ok(sui_error) = result {
801 sui_error
802 } else {
803 SuiErrorKind::RpcError(
804 status.message().to_owned(),
805 status.code().description().to_owned(),
806 )
807 .into()
808 }
809 }
810}
811
812impl From<TypedStoreError> for SuiError {
813 fn from(e: TypedStoreError) -> Self {
814 SuiErrorKind::Storage(e.to_string()).into()
815 }
816}
817
818impl From<crate::storage::error::Error> for SuiError {
819 fn from(e: crate::storage::error::Error) -> Self {
820 SuiErrorKind::Storage(e.to_string()).into()
821 }
822}
823
824impl From<SuiErrorKind> for Status {
825 fn from(error: SuiErrorKind) -> Self {
826 let bytes = bcs::to_bytes(&error).unwrap();
827 Status::with_details(tonic::Code::Internal, error.to_string(), bytes.into())
828 }
829}
830
831impl From<SuiError> for Status {
832 fn from(error: SuiError) -> Self {
833 Status::from(error.into_inner())
834 }
835}
836
837impl From<ExecutionErrorKind> for SuiError {
838 fn from(kind: ExecutionErrorKind) -> Self {
839 ExecutionError::from_kind(kind).into()
840 }
841}
842
843impl From<&str> for SuiError {
844 fn from(error: &str) -> Self {
845 SuiErrorKind::GenericAuthorityError {
846 error: error.to_string(),
847 }
848 .into()
849 }
850}
851
852impl From<String> for SuiError {
853 fn from(error: String) -> Self {
854 SuiErrorKind::GenericAuthorityError { error }.into()
855 }
856}
857
858impl TryFrom<SuiErrorKind> for UserInputError {
859 type Error = anyhow::Error;
860
861 fn try_from(err: SuiErrorKind) -> Result<Self, Self::Error> {
862 match err {
863 SuiErrorKind::UserInputError { error } => Ok(error),
864 other => anyhow::bail!("error {:?} is not UserInputError", other),
865 }
866 }
867}
868
869impl TryFrom<SuiError> for UserInputError {
870 type Error = anyhow::Error;
871
872 fn try_from(err: SuiError) -> Result<Self, Self::Error> {
873 err.into_inner().try_into()
874 }
875}
876
877impl From<UserInputError> for SuiError {
878 fn from(error: UserInputError) -> Self {
879 SuiErrorKind::UserInputError { error }.into()
880 }
881}
882
883impl From<SuiObjectResponseError> for SuiError {
884 fn from(error: SuiObjectResponseError) -> Self {
885 SuiErrorKind::SuiObjectResponseError { error }.into()
886 }
887}
888
889impl PartialEq<SuiErrorKind> for SuiError {
890 fn eq(&self, other: &SuiErrorKind) -> bool {
891 &*self.0 == other
892 }
893}
894
895impl PartialEq<SuiError> for SuiErrorKind {
896 fn eq(&self, other: &SuiError) -> bool {
897 self == &*other.0
898 }
899}
900
901impl SuiError {
902 pub fn as_inner(&self) -> &SuiErrorKind {
903 &self.0
904 }
905
906 pub fn into_inner(self) -> SuiErrorKind {
907 *self.0
908 }
909}
910
911impl SuiErrorKind {
912 pub fn to_variant_name(&self) -> &'static str {
914 match &self {
915 SuiErrorKind::UserInputError { error } => error.into(),
916 _ => self.into(),
917 }
918 }
919
920 pub fn individual_error_indicates_epoch_change(&self) -> bool {
921 matches!(
922 self,
923 SuiErrorKind::ValidatorHaltedAtEpochEnd | SuiErrorKind::MissingCommitteeAtEpoch(_)
924 )
925 }
926
927 pub fn is_retryable(&self) -> (bool, bool) {
932 let retryable = match self {
933 SuiErrorKind::RpcError { .. } => true,
935
936 SuiErrorKind::ValidatorHaltedAtEpochEnd => true,
938 SuiErrorKind::MissingCommitteeAtEpoch(..) => true,
939 SuiErrorKind::WrongEpoch { .. } => true,
940 SuiErrorKind::EpochEnded(..) => true,
941
942 SuiErrorKind::UserInputError { error } => {
943 match error {
944 UserInputError::ObjectNotFound { .. } => true,
946 UserInputError::DependentPackageNotFound { .. } => true,
947 _ => false,
948 }
949 }
950
951 SuiErrorKind::PotentiallyTemporarilyInvalidSignature { .. } => true,
952
953 SuiErrorKind::TooManyTransactionsPendingExecution { .. } => true,
955 SuiErrorKind::TooManyTransactionsPendingOnObject { .. } => true,
956 SuiErrorKind::TooOldTransactionPendingOnObject { .. } => true,
957 SuiErrorKind::TooManyTransactionsPendingConsensus => true,
958 SuiErrorKind::ValidatorOverloadedRetryAfter { .. } => true,
959
960 SuiErrorKind::ExecutionError(..) => false,
962 SuiErrorKind::ByzantineAuthoritySuspicion { .. } => false,
963 SuiErrorKind::QuorumFailedToGetEffectsQuorumWhenProcessingTransaction { .. } => false,
964 SuiErrorKind::TxAlreadyFinalizedWithDifferentUserSigs => false,
965 SuiErrorKind::FailedToVerifyTxCertWithExecutedEffects { .. } => false,
966 SuiErrorKind::ObjectLockConflict { .. } => false,
967
968 SuiErrorKind::TooManyRequests => false,
972
973 _ => return (false, false),
975 };
976
977 (retryable, true)
978 }
979
980 pub fn is_object_or_package_not_found(&self) -> bool {
981 match self {
982 SuiErrorKind::UserInputError { error } => {
983 matches!(
984 error,
985 UserInputError::ObjectNotFound { .. }
986 | UserInputError::DependentPackageNotFound { .. }
987 )
988 }
989 _ => false,
990 }
991 }
992
993 pub fn is_overload(&self) -> bool {
994 matches!(
995 self,
996 SuiErrorKind::TooManyTransactionsPendingExecution { .. }
997 | SuiErrorKind::TooManyTransactionsPendingOnObject { .. }
998 | SuiErrorKind::TooOldTransactionPendingOnObject { .. }
999 | SuiErrorKind::TooManyTransactionsPendingConsensus
1000 )
1001 }
1002
1003 pub fn is_retryable_overload(&self) -> bool {
1004 matches!(self, SuiErrorKind::ValidatorOverloadedRetryAfter { .. })
1005 }
1006
1007 pub fn retry_after_secs(&self) -> u64 {
1008 match self {
1009 SuiErrorKind::ValidatorOverloadedRetryAfter { retry_after_secs } => *retry_after_secs,
1010 _ => 0,
1011 }
1012 }
1013
1014 pub fn categorize(&self) -> ErrorCategory {
1016 match self {
1017 SuiErrorKind::UserInputError { error } => {
1018 match error {
1019 UserInputError::ObjectNotFound { .. } => ErrorCategory::Aborted,
1022 UserInputError::DependentPackageNotFound { .. } => ErrorCategory::Aborted,
1023 _ => ErrorCategory::InvalidTransaction,
1025 }
1026 }
1027
1028 SuiErrorKind::InvalidSignature { .. }
1029 | SuiErrorKind::SignerSignatureAbsent { .. }
1030 | SuiErrorKind::SignerSignatureNumberMismatch { .. }
1031 | SuiErrorKind::IncorrectSigner { .. }
1032 | SuiErrorKind::UnknownSigner { .. }
1033 | SuiErrorKind::TransactionExpired => ErrorCategory::InvalidTransaction,
1034
1035 SuiErrorKind::ObjectLockConflict { .. } => ErrorCategory::LockConflict,
1036
1037 SuiErrorKind::Unknown { .. }
1038 | SuiErrorKind::GrpcMessageSerializeError { .. }
1039 | SuiErrorKind::GrpcMessageDeserializeError { .. }
1040 | SuiErrorKind::ByzantineAuthoritySuspicion { .. }
1041 | SuiErrorKind::InvalidTxKindInSoftBundle
1042 | SuiErrorKind::UnsupportedFeatureError { .. }
1043 | SuiErrorKind::InvalidRequest { .. } => ErrorCategory::Internal,
1044
1045 SuiErrorKind::TooManyTransactionsPendingExecution { .. }
1046 | SuiErrorKind::TooManyTransactionsPendingOnObject { .. }
1047 | SuiErrorKind::TooOldTransactionPendingOnObject { .. }
1048 | SuiErrorKind::TooManyTransactionsPendingConsensus
1049 | SuiErrorKind::ValidatorOverloadedRetryAfter { .. } => {
1050 ErrorCategory::ValidatorOverloaded
1051 }
1052
1053 SuiErrorKind::TimeoutError => ErrorCategory::Unavailable,
1054
1055 _ => ErrorCategory::Aborted,
1057 }
1058 }
1059}
1060
1061impl Ord for SuiError {
1062 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1063 Ord::cmp(self.as_ref(), other.as_ref())
1064 }
1065}
1066
1067impl PartialOrd for SuiError {
1068 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1069 Some(self.cmp(other))
1070 }
1071}
1072
1073impl std::fmt::Debug for SuiError {
1074 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1075 self.as_inner().fmt(f)
1076 }
1077}
1078
1079type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
1080
1081pub type ExecutionErrorKind = ExecutionFailureStatus;
1082
1083#[derive(Debug)]
1084pub struct ExecutionError {
1085 inner: Box<ExecutionErrorInner>,
1086}
1087
1088#[derive(Debug)]
1089struct ExecutionErrorInner {
1090 kind: ExecutionErrorKind,
1091 source: Option<BoxError>,
1092 command: Option<CommandIndex>,
1093}
1094
1095impl ExecutionError {
1096 pub fn new(kind: ExecutionErrorKind, source: Option<BoxError>) -> Self {
1097 Self {
1098 inner: Box::new(ExecutionErrorInner {
1099 kind,
1100 source,
1101 command: None,
1102 }),
1103 }
1104 }
1105
1106 pub fn new_with_source<E: Into<BoxError>>(kind: ExecutionErrorKind, source: E) -> Self {
1107 Self::new(kind, Some(source.into()))
1108 }
1109
1110 pub fn invariant_violation<E: Into<BoxError>>(source: E) -> Self {
1111 Self::new_with_source(ExecutionFailureStatus::InvariantViolation, source)
1112 }
1113
1114 pub fn with_command_index(mut self, command: CommandIndex) -> Self {
1115 self.inner.command = Some(command);
1116 self
1117 }
1118
1119 pub fn from_kind(kind: ExecutionErrorKind) -> Self {
1120 Self::new(kind, None)
1121 }
1122
1123 pub fn kind(&self) -> &ExecutionErrorKind {
1124 &self.inner.kind
1125 }
1126
1127 pub fn command(&self) -> Option<CommandIndex> {
1128 self.inner.command
1129 }
1130
1131 pub fn source(&self) -> &Option<BoxError> {
1132 &self.inner.source
1133 }
1134
1135 pub fn to_execution_status(&self) -> (ExecutionFailureStatus, Option<CommandIndex>) {
1136 (self.kind().clone(), self.command())
1137 }
1138}
1139
1140impl std::fmt::Display for ExecutionError {
1141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1142 write!(f, "ExecutionError: {:?}", self)
1143 }
1144}
1145
1146impl std::error::Error for ExecutionError {
1147 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1148 self.inner.source.as_ref().map(|e| &**e as _)
1149 }
1150}
1151
1152impl From<ExecutionErrorKind> for ExecutionError {
1153 fn from(kind: ExecutionErrorKind) -> Self {
1154 Self::from_kind(kind)
1155 }
1156}
1157
1158pub fn command_argument_error(e: CommandArgumentError, arg_idx: usize) -> ExecutionError {
1159 ExecutionError::from_kind(ExecutionErrorKind::command_argument_error(
1160 e,
1161 arg_idx as u16,
1162 ))
1163}
1164
1165#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, IntoStaticStr)]
1167pub enum ErrorCategory {
1168 Aborted,
1170 InvalidTransaction,
1172 LockConflict,
1174 Internal,
1177 ValidatorOverloaded,
1179 Unavailable,
1181}
1182
1183impl ErrorCategory {
1184 pub fn is_submission_retriable(&self) -> bool {
1186 matches!(
1187 self,
1188 ErrorCategory::Aborted
1189 | ErrorCategory::ValidatorOverloaded
1190 | ErrorCategory::Unavailable
1191 )
1192 }
1193}