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