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