sui_types/
error.rs

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