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