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