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