sui_types/
execution_status.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::ObjectID;
5use crate::base_types::SuiAddress;
6use move_binary_format::file_format::{CodeOffset, TypeParameterIndex};
7use move_core_types::language_storage::ModuleId;
8use serde::{Deserialize, Serialize};
9use std::fmt::{self, Display, Formatter};
10use sui_macros::EnumVariantOrder;
11use thiserror::Error;
12
13#[cfg(test)]
14#[path = "unit_tests/execution_status_tests.rs"]
15mod execution_status_tests;
16
17#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
18pub enum ExecutionStatus {
19    Success,
20    /// Gas used in the failed case, and the error.
21    Failure {
22        /// The error
23        error: ExecutionFailureStatus,
24        /// Which command the error occurred
25        command: Option<CommandIndex>,
26    },
27}
28
29#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
30pub struct CongestedObjects(pub Vec<ObjectID>);
31
32impl fmt::Display for CongestedObjects {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
34        for obj in &self.0 {
35            write!(f, "{}, ", obj)?;
36        }
37        Ok(())
38    }
39}
40
41#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, EnumVariantOrder)]
42pub enum ExecutionFailureStatus {
43    //
44    // General transaction errors
45    //
46    #[error("Insufficient Gas.")]
47    InsufficientGas,
48    #[error("Invalid Gas Object. Possibly not address-owned or possibly not a SUI coin.")]
49    InvalidGasObject,
50    #[error("INVARIANT VIOLATION.")]
51    InvariantViolation,
52    #[error("Attempted to used feature that is not supported yet")]
53    FeatureNotYetSupported,
54    #[error(
55        "Move object with size {object_size} is larger \
56        than the maximum object size {max_object_size}"
57    )]
58    MoveObjectTooBig {
59        object_size: u64,
60        max_object_size: u64,
61    },
62    #[error(
63        "Move package with size {object_size} is larger than the \
64        maximum object size {max_object_size}"
65    )]
66    MovePackageTooBig {
67        object_size: u64,
68        max_object_size: u64,
69    },
70    #[error("Circular Object Ownership, including object {object}.")]
71    CircularObjectOwnership { object: ObjectID },
72
73    //
74    // Coin errors
75    //
76    #[error("Insufficient coin balance for operation.")]
77    InsufficientCoinBalance,
78    #[error("The coin balance overflows u64")]
79    CoinBalanceOverflow,
80
81    //
82    // Publish/Upgrade errors
83    //
84    #[error(
85        "Publish Error, Non-zero Address. \
86        The modules in the package must have their self-addresses set to zero."
87    )]
88    PublishErrorNonZeroAddress,
89
90    #[error(
91        "Sui Move Bytecode Verification Error. \
92        Please run the Sui Move Verifier for more information."
93    )]
94    SuiMoveVerificationError,
95
96    //
97    // Errors from the Move VM
98    //
99    // Indicates an error from a non-abort instruction
100    #[error(
101        "Move Primitive Runtime Error. Location: {0}. \
102        Arithmetic error, stack overflow, max value depth, etc."
103    )]
104    MovePrimitiveRuntimeError(MoveLocationOpt),
105    #[error("Move Runtime Abort. Location: {0}, Abort Code: {1}")]
106    MoveAbort(MoveLocation, u64),
107    #[error(
108        "Move Bytecode Verification Error. \
109        Please run the Bytecode Verifier for more information."
110    )]
111    VMVerificationOrDeserializationError,
112    #[error("MOVE VM INVARIANT VIOLATION.")]
113    VMInvariantViolation,
114
115    //
116    // Programmable Transaction Errors
117    //
118    #[error("Function Not Found.")]
119    FunctionNotFound,
120    #[error(
121        "Arity mismatch for Move function. \
122        The number of arguments does not match the number of parameters"
123    )]
124    ArityMismatch,
125    #[error(
126        "Type arity mismatch for Move function. \
127        Mismatch between the number of actual versus expected type arguments."
128    )]
129    TypeArityMismatch,
130    #[error("Non Entry Function Invoked. Move Call must start with an entry function")]
131    NonEntryFunctionInvoked,
132    #[error("Invalid command argument at {arg_idx}. {kind}")]
133    CommandArgumentError {
134        arg_idx: u16,
135        kind: CommandArgumentError,
136    },
137    #[error("Error for type argument at index {argument_idx}: {kind}")]
138    TypeArgumentError {
139        argument_idx: TypeParameterIndex,
140        kind: TypeArgumentError,
141    },
142    #[error(
143        "Unused result without the drop ability. \
144        Command result {result_idx}, return value {secondary_idx}"
145    )]
146    UnusedValueWithoutDrop { result_idx: u16, secondary_idx: u16 },
147    #[error(
148        "Invalid public Move function signature. \
149        Unsupported return type for return value {idx}"
150    )]
151    InvalidPublicFunctionReturnType { idx: u16 },
152    #[error("Invalid Transfer Object, object does not have public transfer.")]
153    InvalidTransferObject,
154
155    //
156    // Post-execution errors
157    //
158    // Indicates the effects from the transaction are too large
159    #[error(
160        "Effects of size {current_size} bytes too large. \
161    Limit is {max_size} bytes"
162    )]
163    EffectsTooLarge { current_size: u64, max_size: u64 },
164
165    #[error(
166        "Publish/Upgrade Error, Missing dependency. \
167         A dependency of a published or upgraded package has not been assigned an on-chain \
168         address."
169    )]
170    PublishUpgradeMissingDependency,
171
172    #[error(
173        "Publish/Upgrade Error, Dependency downgrade. \
174         Indirect (transitive) dependency of published or upgraded package has been assigned an \
175         on-chain version that is less than the version required by one of the package's \
176         transitive dependencies."
177    )]
178    PublishUpgradeDependencyDowngrade,
179
180    #[error("Invalid package upgrade. {upgrade_error}")]
181    PackageUpgradeError { upgrade_error: PackageUpgradeError },
182
183    // Indicates the transaction tried to write objects too large to storage
184    #[error(
185        "Written objects of {current_size} bytes too large. \
186    Limit is {max_size} bytes"
187    )]
188    WrittenObjectsTooLarge { current_size: u64, max_size: u64 },
189
190    #[error("Certificate is on the deny list")]
191    CertificateDenied,
192
193    #[error(
194        "Sui Move Bytecode Verification Timeout. \
195        Please run the Sui Move Verifier for more information."
196    )]
197    SuiMoveVerificationTimedout,
198
199    #[error("The shared object operation is not allowed.")]
200    SharedObjectOperationNotAllowed,
201
202    #[error(
203        "Certificate cannot be executed due to a dependency on a deleted shared object or an object that was transferred out of consensus"
204    )]
205    InputObjectDeleted,
206
207    #[error("Certificate is cancelled due to congestion on shared objects: {congested_objects}")]
208    ExecutionCancelledDueToSharedObjectCongestion { congested_objects: CongestedObjects },
209
210    #[error("Address {address:?} is denied for coin {coin_type}")]
211    AddressDeniedForCoin {
212        address: SuiAddress,
213        coin_type: String,
214    },
215
216    #[error("Coin type is globally paused for use: {coin_type}")]
217    CoinTypeGlobalPause { coin_type: String },
218
219    #[error("Certificate is cancelled because randomness could not be generated this epoch")]
220    ExecutionCancelledDueToRandomnessUnavailable,
221
222    #[error(
223        "Move vector element (passed to MakeMoveVec) with size {value_size} is larger \
224        than the maximum size {max_scaled_size}. Note that this maximum is scaled based on the \
225        type of the vector element."
226    )]
227    MoveVectorElemTooBig {
228        value_size: u64,
229        max_scaled_size: u64,
230    },
231
232    #[error(
233        "Move value (possibly an upgrade ticket or a dev-inspect value) with size {value_size} \
234        is larger than the maximum size  {max_scaled_size}. Note that this maximum is scaled based \
235        on the type of the value."
236    )]
237    MoveRawValueTooBig {
238        value_size: u64,
239        max_scaled_size: u64,
240    },
241
242    #[error("A valid linkage was unable to be determined for the transaction")]
243    InvalidLinkage,
244
245    #[error("Insufficient balance for transaction withdrawal")]
246    InsufficientBalanceForWithdraw,
247
248    #[error("Non-exclusive write input object {id} has been modified")]
249    NonExclusiveWriteInputObjectModified { id: ObjectID },
250    // NOTE: if you want to add a new enum,
251    // please add it at the end for Rust SDK backward compatibility.
252}
253
254#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
255pub struct MoveLocation {
256    pub module: ModuleId,
257    pub function: u16,
258    pub instruction: CodeOffset,
259    pub function_name: Option<String>,
260}
261
262#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
263pub struct MoveLocationOpt(pub Option<MoveLocation>);
264
265#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize, Hash, Error)]
266pub enum CommandArgumentError {
267    #[error("The type of the value does not match the expected type")]
268    TypeMismatch,
269    #[error("The argument cannot be deserialized into a value of the specified type")]
270    InvalidBCSBytes,
271    #[error("The argument cannot be instantiated from raw bytes")]
272    InvalidUsageOfPureArg,
273    #[error(
274        "Invalid argument to private entry function. \
275        These functions cannot take arguments from other Move functions"
276    )]
277    InvalidArgumentToPrivateEntryFunction,
278    #[error("Out of bounds access to input or result vector {idx}")]
279    IndexOutOfBounds { idx: u16 },
280    #[error(
281        "Out of bounds secondary access to result vector \
282        {result_idx} at secondary index {secondary_idx}"
283    )]
284    SecondaryIndexOutOfBounds { result_idx: u16, secondary_idx: u16 },
285    #[error(
286        "Invalid usage of result {result_idx}, \
287        expected a single result but found either no return values or multiple."
288    )]
289    InvalidResultArity { result_idx: u16 },
290    #[error(
291        "Invalid taking of the Gas coin. \
292        It can only be used by-value with TransferObjects"
293    )]
294    InvalidGasCoinUsage,
295    #[error(
296        "Invalid usage of value. \
297        Mutably borrowed values require unique usage. \
298        Immutably borrowed values cannot be taken or borrowed mutably. \
299        Taken values cannot be used again."
300    )]
301    InvalidValueUsage,
302    #[error("Immutable objects cannot be passed by-value.")]
303    InvalidObjectByValue,
304    #[error("Immutable objects cannot be passed by mutable reference, &mut.")]
305    InvalidObjectByMutRef,
306    #[error(
307        "Shared object operations such a wrapping, freezing, or converting to owned are not \
308        allowed."
309    )]
310    SharedObjectOperationNotAllowed,
311    #[error(
312        "Invalid argument arity. Expected a single argument but found a result that expanded to \
313        multiple arguments."
314    )]
315    InvalidArgumentArity,
316    #[error(
317        "Object passed to TransferObject does not have public transfer, i.e. the `store` \
318        ability"
319    )]
320    InvalidTransferObject,
321    #[error(
322        "First argument to MakeMoveVec is not an object. If no type is specified for MakeMoveVec, all arguments must be the same object type."
323    )]
324    InvalidMakeMoveVecNonObjectArgument,
325    #[error("Specified argument location does not have a value and cannot be used")]
326    ArgumentWithoutValue,
327    #[error(
328        "Cannot move a borrowed value. The value's type does resulted in this argument usage \
329        being inferred as a move. This is likely due to the type not having the `copy` ability; \
330        although in rare cases, it could also be this is the last usage of a value without the \
331        `drop` ability."
332    )]
333    CannotMoveBorrowedValue,
334    #[error(
335        "Cannot write to an argument location that is still borrowed, and where that borrow \
336        is an extension of that reference. This is likely due to this argument being used in a \
337        Move call that returns a reference, and that reference is used in a later command."
338    )]
339    CannotWriteToExtendedReference,
340    #[error(
341        "The argument specified cannot be used as a reference argument in the Move call. Either \
342        the argument is a mutable reference and it conflicts with another argument to the call, \
343        or the argument is mutable and another reference extends it and will be used in a later \
344        command."
345    )]
346    InvalidReferenceArgument,
347}
348
349#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash, Error)]
350pub enum PackageUpgradeError {
351    #[error("Unable to fetch package at {package_id}")]
352    UnableToFetchPackage { package_id: ObjectID },
353    #[error("Object {object_id} is not a package")]
354    NotAPackage { object_id: ObjectID },
355    #[error("New package is incompatible with previous version")]
356    IncompatibleUpgrade,
357    #[error("Digest in upgrade ticket and computed digest disagree")]
358    DigestDoesNotMatch { digest: Vec<u8> },
359    #[error("Upgrade policy {policy} is not a valid upgrade policy")]
360    UnknownUpgradePolicy { policy: u8 },
361    #[error("Package ID {package_id} does not match package ID in upgrade ticket {ticket_id}")]
362    PackageIDDoesNotMatch {
363        package_id: ObjectID,
364        ticket_id: ObjectID,
365    },
366}
367
368#[derive(Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize, Hash, Error)]
369pub enum TypeArgumentError {
370    #[error("A type was not found in the module specified.")]
371    TypeNotFound,
372    #[error("A type provided did not match the specified constraints.")]
373    ConstraintNotSatisfied,
374}
375
376impl ExecutionFailureStatus {
377    pub fn command_argument_error(kind: CommandArgumentError, arg_idx: u16) -> Self {
378        Self::CommandArgumentError { arg_idx, kind }
379    }
380}
381
382impl Display for MoveLocationOpt {
383    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
384        match &self.0 {
385            None => write!(f, "UNKNOWN"),
386            Some(l) => write!(f, "{l}"),
387        }
388    }
389}
390
391impl Display for MoveLocation {
392    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
393        let Self {
394            module,
395            function,
396            instruction,
397            function_name,
398        } = self;
399        if let Some(fname) = function_name {
400            write!(
401                f,
402                "{module}::{fname} (function index {function}) at offset {instruction}"
403            )
404        } else {
405            write!(
406                f,
407                "{module} in function definition {function} at offset {instruction}"
408            )
409        }
410    }
411}
412
413impl ExecutionStatus {
414    pub fn new_failure(
415        error: ExecutionFailureStatus,
416        command: Option<CommandIndex>,
417    ) -> ExecutionStatus {
418        ExecutionStatus::Failure { error, command }
419    }
420
421    pub fn is_ok(&self) -> bool {
422        matches!(self, ExecutionStatus::Success)
423    }
424
425    pub fn is_err(&self) -> bool {
426        matches!(self, ExecutionStatus::Failure { .. })
427    }
428
429    pub fn unwrap(&self) {
430        match self {
431            ExecutionStatus::Success => {}
432            ExecutionStatus::Failure { .. } => {
433                panic!("Unable to unwrap() on {:?}", self);
434            }
435        }
436    }
437
438    pub fn unwrap_err(self) -> (ExecutionFailureStatus, Option<CommandIndex>) {
439        match self {
440            ExecutionStatus::Success => {
441                panic!("Unable to unwrap() on {:?}", self);
442            }
443            ExecutionStatus::Failure { error, command } => (error, command),
444        }
445    }
446
447    pub fn get_congested_objects(&self) -> Option<&CongestedObjects> {
448        if let ExecutionStatus::Failure {
449            error:
450                ExecutionFailureStatus::ExecutionCancelledDueToSharedObjectCongestion {
451                    congested_objects,
452                },
453            ..
454        } = self
455        {
456            Some(congested_objects)
457        } else {
458            None
459        }
460    }
461
462    pub fn is_cancelled(&self) -> bool {
463        matches!(
464            self,
465            ExecutionStatus::Failure {
466                error: ExecutionFailureStatus::ExecutionCancelledDueToSharedObjectCongestion { .. },
467                ..
468            }
469        )
470    }
471}
472
473pub type CommandIndex = usize;