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