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