outmove_common/types/
vm_status.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4#![allow(clippy::unit_arg)]
5
6use super::language_storage::ModuleId;
7use anyhow::Result;
8use serde::{de, ser, Deserialize, Serialize};
9use std::{convert::TryFrom, fmt};
10
11/// The minimum status code for validation statuses
12pub static VALIDATION_STATUS_MIN_CODE: u64 = 0;
13
14/// The maximum status code for validation statuses
15pub static VALIDATION_STATUS_MAX_CODE: u64 = 999;
16
17/// The minimum status code for verification statuses
18pub static VERIFICATION_STATUS_MIN_CODE: u64 = 1000;
19
20/// The maximum status code for verification statuses
21pub static VERIFICATION_STATUS_MAX_CODE: u64 = 1999;
22
23/// The minimum status code for invariant violation statuses
24pub static INVARIANT_VIOLATION_STATUS_MIN_CODE: u64 = 2000;
25
26/// The maximum status code for invariant violation statuses
27pub static INVARIANT_VIOLATION_STATUS_MAX_CODE: u64 = 2999;
28
29/// The minimum status code for deserialization statuses
30pub static DESERIALIZATION_STATUS_MIN_CODE: u64 = 3000;
31
32/// The maximum status code for deserialization statuses
33pub static DESERIALIZATION_STATUS_MAX_CODE: u64 = 3999;
34
35/// The minimum status code for runtime statuses
36pub static EXECUTION_STATUS_MIN_CODE: u64 = 4000;
37
38/// The maximum status code for runtim statuses
39pub static EXECUTION_STATUS_MAX_CODE: u64 = 4999;
40
41/// A `VMStatus` is represented as either
42/// - `Executed` indicating successful execution
43/// - `Error` indicating an error from the VM itself
44/// - `MoveAbort` indicating an `abort` ocurred inside of a Move program
45#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
46pub enum VMStatus {
47    /// The VM status corresponding to an EXECUTED status code
48    Executed,
49
50    /// Indicates an error from the VM, e.g. OUT_OF_GAS, INVALID_AUTH_KEY, RET_TYPE_MISMATCH_ERROR
51    /// etc.
52    /// The code will neither EXECUTED nor ABORTED
53    Error(StatusCode),
54
55    /// Indicates an `abort` from inside Move code. Contains the location of the abort and the code
56    MoveAbort(AbortLocation, /* code */ u64),
57
58    /// Indicates an failure from inside Move code, where the VM could not continue exection, e.g.
59    /// dividing by zero or a missing resource
60    ExecutionFailure {
61        status_code: StatusCode,
62        location: AbortLocation,
63        function: u16,
64        code_offset: u16,
65    },
66}
67
68#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
69pub enum KeptVMStatus {
70    Executed,
71    OutOfGas,
72    MoveAbort(AbortLocation, /* code */ u64),
73    ExecutionFailure {
74        location: AbortLocation,
75        function: u16,
76        code_offset: u16,
77    },
78    MiscellaneousError,
79}
80
81pub type DiscardedVMStatus = StatusCode;
82
83/// An `AbortLocation` specifies where a Move program `abort` occurred, either in a function in
84/// a module, or in a script
85#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
86pub enum AbortLocation {
87    /// Indicates `abort` occurred in the specified module
88    Module(ModuleId),
89    /// Indicates the `abort` occurred in a script
90    Script,
91}
92
93/// A status type is one of 5 different variants, along with a fallback variant in the case that we
94/// don't recognize the status code.
95#[derive(Clone, PartialEq, Eq, Debug, Hash)]
96pub enum StatusType {
97    Validation,
98    Verification,
99    InvariantViolation,
100    Deserialization,
101    Execution,
102    Unknown,
103}
104
105impl VMStatus {
106    /// Return the status code for the `VMStatus`
107    pub fn status_code(&self) -> StatusCode {
108        match self {
109            Self::Executed => StatusCode::EXECUTED,
110            Self::MoveAbort(_, _) => StatusCode::ABORTED,
111            Self::ExecutionFailure { status_code, .. } => *status_code,
112            Self::Error(code) => {
113                let code = *code;
114                debug_assert!(code != StatusCode::EXECUTED);
115                debug_assert!(code != StatusCode::ABORTED);
116                debug_assert!(code.status_type() != StatusType::Execution);
117                code
118            }
119        }
120    }
121
122    /// Returns the Move abort code if the status is `MoveAbort`, and `None` otherwise
123    pub fn move_abort_code(&self) -> Option<u64> {
124        match self {
125            Self::MoveAbort(_, code) => Some(*code),
126            Self::Error(_) | Self::ExecutionFailure { .. } | Self::Executed => None,
127        }
128    }
129
130    /// Return the status type for this `VMStatus`. This is solely determined by the `status_code`
131    pub fn status_type(&self) -> StatusType {
132        self.status_code().status_type()
133    }
134
135    /// Returns `Ok` with a recorded status if it should be kept, `Err` of the error code if it
136    /// should be discarded
137    pub fn keep_or_discard(self) -> Result<KeptVMStatus, DiscardedVMStatus> {
138        match self {
139            VMStatus::Executed => Ok(KeptVMStatus::Executed),
140            VMStatus::MoveAbort(location, code) => Ok(KeptVMStatus::MoveAbort(location, code)),
141            VMStatus::ExecutionFailure {
142                status_code: StatusCode::OUT_OF_GAS,
143                ..
144            }
145            | VMStatus::Error(StatusCode::OUT_OF_GAS) => Ok(KeptVMStatus::OutOfGas),
146            VMStatus::ExecutionFailure {
147                status_code: _status_code,
148                location,
149                function,
150                code_offset,
151            } => Ok(KeptVMStatus::ExecutionFailure {
152                location,
153                function,
154                code_offset,
155            }),
156            VMStatus::Error(code) => {
157                match code.status_type() {
158                    // Any unknown error should be discarded
159                    StatusType::Unknown => Err(code),
160                    // Any error that is a validation status (i.e. an error arising from the prologue)
161                    // causes the transaction to not be included.
162                    StatusType::Validation => Err(code),
163                    // If the VM encountered an invalid internal state, we should discard the transaction.
164                    StatusType::InvariantViolation => Err(code),
165                    // A transaction that publishes code that cannot be verified will be charged.
166                    StatusType::Verification => Ok(KeptVMStatus::MiscellaneousError),
167                    // If we are able to decode the`SignedTransaction`, but failed to decode
168                    // `SingedTransaction.raw_transaction.payload` (i.e., the transaction script),
169                    // there should be a charge made to that user's account for the gas fees related
170                    // to decoding, running the prologue etc.
171                    StatusType::Deserialization => Ok(KeptVMStatus::MiscellaneousError),
172                    // Any error encountered during the execution of the transaction will charge gas.
173                    StatusType::Execution => Ok(KeptVMStatus::ExecutionFailure {
174                        location: AbortLocation::Script,
175                        function: 0,
176                        code_offset: 0,
177                    }),
178                }
179            }
180        }
181    }
182}
183
184impl fmt::Display for StatusType {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        let string = match self {
187            StatusType::Validation => "Validation",
188            StatusType::Verification => "Verification",
189            StatusType::InvariantViolation => "Invariant violation",
190            StatusType::Deserialization => "Deserialization",
191            StatusType::Execution => "Execution",
192            StatusType::Unknown => "Unknown",
193        };
194        write!(f, "{}", string)
195    }
196}
197
198impl fmt::Display for VMStatus {
199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200        let status_type = self.status_type();
201        let mut status = format!("status {:#?} of type {}", self.status_code(), status_type);
202
203        if let VMStatus::MoveAbort(_, code) = self {
204            status = format!("{} with sub status {}", status, code);
205        }
206
207        write!(f, "{}", status)
208    }
209}
210
211impl fmt::Display for KeptVMStatus {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        write!(f, "status ")?;
214        match self {
215            KeptVMStatus::Executed => write!(f, "EXECUTED"),
216            KeptVMStatus::OutOfGas => write!(f, "OUT_OF_GAS"),
217            KeptVMStatus::MiscellaneousError => write!(f, "MISCELLANEOUS_ERROR"),
218            KeptVMStatus::MoveAbort(location, code) => {
219                write!(f, "ABORTED with code {} in {}", code, location)
220            }
221            KeptVMStatus::ExecutionFailure {
222                location,
223                function,
224                code_offset,
225            } => write!(
226                f,
227                "EXECUTION_FAILURE at bytecode offset {} in function index {} in {}",
228                code_offset, function, location
229            ),
230        }
231    }
232}
233
234impl fmt::Debug for VMStatus {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        match self {
237            VMStatus::Executed => write!(f, "EXECUTED"),
238            VMStatus::Error(code) => f.debug_struct("ERROR").field("status_code", code).finish(),
239            VMStatus::MoveAbort(location, code) => f
240                .debug_struct("ABORTED")
241                .field("code", code)
242                .field("location", location)
243                .finish(),
244            VMStatus::ExecutionFailure {
245                status_code,
246                location,
247                function,
248                code_offset,
249            } => f
250                .debug_struct("EXECUTION_FAILURE")
251                .field("status_code", status_code)
252                .field("location", location)
253                .field("function_definition", function)
254                .field("code_offset", code_offset)
255                .finish(),
256        }
257    }
258}
259
260impl fmt::Debug for KeptVMStatus {
261    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262        match self {
263            KeptVMStatus::Executed => write!(f, "EXECUTED"),
264            KeptVMStatus::OutOfGas => write!(f, "OUT_OF_GAS"),
265            KeptVMStatus::MoveAbort(location, code) => f
266                .debug_struct("ABORTED")
267                .field("code", code)
268                .field("location", location)
269                .finish(),
270            KeptVMStatus::ExecutionFailure {
271                location,
272                function,
273                code_offset,
274            } => f
275                .debug_struct("EXECUTION_FAILURE")
276                .field("location", location)
277                .field("function_definition", function)
278                .field("code_offset", code_offset)
279                .finish(),
280            KeptVMStatus::MiscellaneousError => write!(f, "MISCELLANEOUS_ERROR"),
281        }
282    }
283}
284
285impl fmt::Display for AbortLocation {
286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        match self {
288            AbortLocation::Script => write!(f, "Script"),
289            AbortLocation::Module(id) => write!(f, "{}", id),
290        }
291    }
292}
293
294impl fmt::Debug for AbortLocation {
295    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        write!(f, "{}", self)
297    }
298}
299
300impl std::error::Error for VMStatus {}
301
302pub mod known_locations {
303    use crate::types::{
304        identifier::Identifier,
305        language_storage::{ModuleId, CORE_CODE_ADDRESS},
306        vm_status::AbortLocation,
307    };
308    use once_cell::sync::Lazy;
309
310    /// The name of the Account module.
311    pub const ACCOUNT_MODULE_NAME: &str = "DiemAccount";
312    /// The Identifier for the Account module.
313    pub static ACCOUNT_MODULE_IDENTIFIER: Lazy<Identifier> =
314        Lazy::new(|| Identifier::new(ACCOUNT_MODULE_NAME).unwrap());
315    /// The ModuleId for the Account module.
316    pub static ACCOUNT_MODULE: Lazy<ModuleId> =
317        Lazy::new(|| ModuleId::new(CORE_CODE_ADDRESS, ACCOUNT_MODULE_IDENTIFIER.clone()));
318    /// Location for an abort in the Account module
319    pub fn account_module_abort() -> AbortLocation {
320        AbortLocation::Module(ACCOUNT_MODULE.clone())
321    }
322
323    /// The name of the Diem module.
324    pub const DIEM_MODULE_NAME: &str = "Diem";
325    /// The Identifier for the Diem module.
326    pub static DIEM_MODULE_IDENTIFIER: Lazy<Identifier> =
327        Lazy::new(|| Identifier::new(DIEM_MODULE_NAME).unwrap());
328    /// The ModuleId for the Diem module.
329    pub static DIEM_MODULE: Lazy<ModuleId> =
330        Lazy::new(|| ModuleId::new(CORE_CODE_ADDRESS, DIEM_MODULE_IDENTIFIER.clone()));
331    pub fn diem_module_abort() -> AbortLocation {
332        AbortLocation::Module(DIEM_MODULE.clone())
333    }
334
335    /// The name of the Designated Dealer module.
336    pub const DESIGNATED_DEALER_MODULE_NAME: &str = "DesignatedDealer";
337    /// The Identifier for the Designated Dealer module.
338    pub static DESIGNATED_DEALER_MODULE_IDENTIFIER: Lazy<Identifier> =
339        Lazy::new(|| Identifier::new(DESIGNATED_DEALER_MODULE_NAME).unwrap());
340    /// The ModuleId for the Designated Dealer module.
341    pub static DESIGNATED_DEALER_MODULE: Lazy<ModuleId> = Lazy::new(|| {
342        ModuleId::new(
343            CORE_CODE_ADDRESS,
344            DESIGNATED_DEALER_MODULE_IDENTIFIER.clone(),
345        )
346    });
347    pub fn designated_dealer_module_abort() -> AbortLocation {
348        AbortLocation::Module(DESIGNATED_DEALER_MODULE.clone())
349    }
350}
351
352macro_rules! derive_status_try_from_repr {
353    (
354        #[repr($repr_ty:ident)]
355        $( #[$metas:meta] )*
356        $vis:vis enum $enum_name:ident {
357            $(
358                $variant:ident = $value: expr
359            ),*
360            $( , )?
361        }
362    ) => {
363        #[repr($repr_ty)]
364        $( #[$metas] )*
365        $vis enum $enum_name {
366            $(
367                $variant = $value
368            ),*
369        }
370
371        impl std::convert::TryFrom<$repr_ty> for $enum_name {
372            type Error = &'static str;
373            fn try_from(value: $repr_ty) -> Result<Self, Self::Error> {
374                match value {
375                    $(
376                        $value => Ok($enum_name::$variant),
377                    )*
378                    _ => Err("invalid StatusCode"),
379                }
380            }
381        }
382
383        #[cfg(any(test, feature = "fuzzing"))]
384        const STATUS_CODE_VALUES: &'static [$repr_ty] = &[
385            $($value),*
386        ];
387    };
388}
389
390derive_status_try_from_repr! {
391#[repr(u64)]
392#[allow(non_camel_case_types)]
393#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
394/// We don't derive Arbitrary on this enum because it is too large and breaks proptest. It is
395/// written for a subset of these in proptest_types. We test conversion between this and protobuf
396/// with a hand-written test.
397pub enum StatusCode {
398    // The status of a transaction as determined by the prologue.
399    // Validation Errors: 0-999
400    // We don't want the default value to be valid
401    UNKNOWN_VALIDATION_STATUS = 0,
402    // The transaction has a bad signature
403    INVALID_SIGNATURE = 1,
404    // Bad account authentication key
405    INVALID_AUTH_KEY = 2,
406    // Sequence number is too old
407    SEQUENCE_NUMBER_TOO_OLD = 3,
408    // Sequence number is too new
409    SEQUENCE_NUMBER_TOO_NEW = 4,
410    // Insufficient balance to pay minimum transaction fee
411    INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE = 5,
412    // The transaction has expired
413    TRANSACTION_EXPIRED = 6,
414    // The sending account does not exist
415    SENDING_ACCOUNT_DOES_NOT_EXIST = 7,
416    // This write set transaction was rejected because it did not meet the
417    // requirements for one.
418    REJECTED_WRITE_SET = 8,
419    // This write set transaction cannot be applied to the current state.
420    INVALID_WRITE_SET = 9,
421    // Length of program field in raw transaction exceeded max length
422    EXCEEDED_MAX_TRANSACTION_SIZE = 10,
423    // This script is not in our allowlist of scripts.
424    UNKNOWN_SCRIPT = 11,
425    // Transaction is trying to publish a new module.
426    UNKNOWN_MODULE = 12,
427    // Max gas units submitted with transaction exceeds max gas units bound
428    // in VM
429    MAX_GAS_UNITS_EXCEEDS_MAX_GAS_UNITS_BOUND = 13,
430    // Max gas units submitted with transaction not enough to cover the
431    // intrinsic cost of the transaction.
432    MAX_GAS_UNITS_BELOW_MIN_TRANSACTION_GAS_UNITS = 14,
433    // Gas unit price submitted with transaction is below minimum gas price
434    // set in the VM.
435    GAS_UNIT_PRICE_BELOW_MIN_BOUND = 15,
436    // Gas unit price submitted with the transaction is above the maximum
437    // gas price set in the VM.
438    GAS_UNIT_PRICE_ABOVE_MAX_BOUND = 16,
439    // Gas specifier submitted is either malformed (not a valid identifier),
440    // or does not refer to an accepted gas specifier
441    INVALID_GAS_SPECIFIER = 17,
442    // The sending account is frozen
443    SENDING_ACCOUNT_FROZEN = 18,
444    // Unable to deserialize the account blob
445    UNABLE_TO_DESERIALIZE_ACCOUNT = 19,
446    // The currency info was unable to be found
447    CURRENCY_INFO_DOES_NOT_EXIST = 20,
448    // The account sender doesn't have permissions to publish modules
449    INVALID_MODULE_PUBLISHER = 21,
450    // The sending account has no role
451    NO_ACCOUNT_ROLE = 22,
452    // The transaction's chain_id does not match the one published on-chain
453    BAD_CHAIN_ID = 23,
454    // The sequence number is too large and would overflow if the transaction were executed
455    SEQUENCE_NUMBER_TOO_BIG = 24,
456    // The gas currency is not registered as a TransactionFee currency
457    BAD_TRANSACTION_FEE_CURRENCY = 25,
458
459    // When a code module/script is published it is verified. These are the
460    // possible errors that can arise from the verification process.
461    // Verification Errors: 1000-1999
462    UNKNOWN_VERIFICATION_ERROR = 1000,
463    INDEX_OUT_OF_BOUNDS = 1001,
464    INVALID_SIGNATURE_TOKEN = 1003,
465    RECURSIVE_STRUCT_DEFINITION = 1005,
466    INVALID_RESOURCE_FIELD = 1006,
467    INVALID_FALL_THROUGH = 1007,
468    NEGATIVE_STACK_SIZE_WITHIN_BLOCK = 1009,
469    INVALID_MAIN_FUNCTION_SIGNATURE = 1011,
470    DUPLICATE_ELEMENT = 1012,
471    INVALID_MODULE_HANDLE = 1013,
472    UNIMPLEMENTED_HANDLE = 1014,
473    LOOKUP_FAILED = 1017,
474    TYPE_MISMATCH = 1020,
475    MISSING_DEPENDENCY = 1021,
476    POP_RESOURCE_ERROR = 1023,
477    BR_TYPE_MISMATCH_ERROR = 1025,
478    ABORT_TYPE_MISMATCH_ERROR = 1026,
479    STLOC_TYPE_MISMATCH_ERROR = 1027,
480    STLOC_UNSAFE_TO_DESTROY_ERROR = 1028,
481    UNSAFE_RET_LOCAL_OR_RESOURCE_STILL_BORROWED = 1029,
482    RET_TYPE_MISMATCH_ERROR = 1030,
483    RET_BORROWED_MUTABLE_REFERENCE_ERROR = 1031,
484    FREEZEREF_TYPE_MISMATCH_ERROR = 1032,
485    FREEZEREF_EXISTS_MUTABLE_BORROW_ERROR = 1033,
486    BORROWFIELD_TYPE_MISMATCH_ERROR = 1034,
487    BORROWFIELD_BAD_FIELD_ERROR = 1035,
488    BORROWFIELD_EXISTS_MUTABLE_BORROW_ERROR = 1036,
489    COPYLOC_UNAVAILABLE_ERROR = 1037,
490    COPYLOC_RESOURCE_ERROR = 1038,
491    COPYLOC_EXISTS_BORROW_ERROR = 1039,
492    MOVELOC_UNAVAILABLE_ERROR = 1040,
493    MOVELOC_EXISTS_BORROW_ERROR = 1041,
494    BORROWLOC_REFERENCE_ERROR = 1042,
495    BORROWLOC_UNAVAILABLE_ERROR = 1043,
496    BORROWLOC_EXISTS_BORROW_ERROR = 1044,
497    CALL_TYPE_MISMATCH_ERROR = 1045,
498    CALL_BORROWED_MUTABLE_REFERENCE_ERROR = 1046,
499    PACK_TYPE_MISMATCH_ERROR = 1047,
500    UNPACK_TYPE_MISMATCH_ERROR = 1048,
501    READREF_TYPE_MISMATCH_ERROR = 1049,
502    READREF_RESOURCE_ERROR = 1050,
503    READREF_EXISTS_MUTABLE_BORROW_ERROR = 1051,
504    WRITEREF_TYPE_MISMATCH_ERROR = 1052,
505    WRITEREF_RESOURCE_ERROR = 1053,
506    WRITEREF_EXISTS_BORROW_ERROR = 1054,
507    WRITEREF_NO_MUTABLE_REFERENCE_ERROR = 1055,
508    INTEGER_OP_TYPE_MISMATCH_ERROR = 1056,
509    BOOLEAN_OP_TYPE_MISMATCH_ERROR = 1057,
510    EQUALITY_OP_TYPE_MISMATCH_ERROR = 1058,
511    EXISTS_RESOURCE_TYPE_MISMATCH_ERROR = 1059,
512    BORROWGLOBAL_TYPE_MISMATCH_ERROR = 1060,
513    BORROWGLOBAL_NO_RESOURCE_ERROR = 1061,
514    MOVEFROM_TYPE_MISMATCH_ERROR = 1062,
515    MOVEFROM_NO_RESOURCE_ERROR = 1063,
516    MOVETO_TYPE_MISMATCH_ERROR = 1064,
517    MOVETO_NO_RESOURCE_ERROR = 1065,
518    // The self address of a module the transaction is publishing is not the sender address
519    MODULE_ADDRESS_DOES_NOT_MATCH_SENDER = 1067,
520    // The module does not have any module handles. Each module or script must have at least one
521    // module handle.
522    NO_MODULE_HANDLES = 1068,
523    POSITIVE_STACK_SIZE_AT_BLOCK_END = 1069,
524    MISSING_ACQUIRES_RESOURCE_ANNOTATION_ERROR = 1070,
525    EXTRANEOUS_ACQUIRES_RESOURCE_ANNOTATION_ERROR = 1071,
526    DUPLICATE_ACQUIRES_RESOURCE_ANNOTATION_ERROR = 1072,
527    INVALID_ACQUIRES_RESOURCE_ANNOTATION_ERROR = 1073,
528    GLOBAL_REFERENCE_ERROR = 1074,
529    CONSTRAINT_KIND_MISMATCH = 1075,
530    NUMBER_OF_TYPE_ARGUMENTS_MISMATCH = 1076,
531    LOOP_IN_INSTANTIATION_GRAPH = 1077,
532    // Reported when a struct has zero fields
533    ZERO_SIZED_STRUCT = 1080,
534    LINKER_ERROR = 1081,
535    INVALID_CONSTANT_TYPE = 1082,
536    MALFORMED_CONSTANT_DATA = 1083,
537    EMPTY_CODE_UNIT = 1084,
538    INVALID_LOOP_SPLIT = 1085,
539    INVALID_LOOP_BREAK = 1086,
540    INVALID_LOOP_CONTINUE = 1087,
541    UNSAFE_RET_UNUSED_RESOURCES = 1088,
542    TOO_MANY_LOCALS = 1089,
543    GENERIC_MEMBER_OPCODE_MISMATCH = 1090,
544    FUNCTION_RESOLUTION_FAILURE = 1091,
545    INVALID_OPERATION_IN_SCRIPT = 1094,
546    // The sender is trying to publish a module named `M`, but the sender's account already
547    // contains a module with this name.
548    DUPLICATE_MODULE_NAME = 1095,
549    // The sender is trying to publish a module that breaks the compatibility checks
550    BACKWARD_INCOMPATIBLE_MODULE_UPDATE = 1096,
551    // The updated module introduces a cyclic dependency (i.e., A uses B and B also uses A)
552    CYCLIC_MODULE_DEPENDENCY = 1097,
553    NUMBER_OF_ARGUMENTS_MISMATCH = 1098,
554    INVALID_PARAM_TYPE_FOR_DESERIALIZATION = 1099,
555    FAILED_TO_DESERIALIZE_ARGUMENT = 1100,
556    NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH = 1101,
557    CALLED_SCRIPT_VISIBLE_FROM_NON_SCRIPT_VISIBLE = 1102,
558    EXECUTE_SCRIPT_FUNCTION_CALLED_ON_NON_SCRIPT_VISIBLE = 1103,
559
560    // These are errors that the VM might raise if a violation of internal
561    // invariants takes place.
562    // Invariant Violation Errors: 2000-2999
563    UNKNOWN_INVARIANT_VIOLATION_ERROR = 2000,
564    EMPTY_VALUE_STACK = 2003,
565    PC_OVERFLOW = 2005,
566    VERIFICATION_ERROR = 2006,
567    STORAGE_ERROR = 2008,
568    INTERNAL_TYPE_ERROR = 2009,
569    EVENT_KEY_MISMATCH = 2010,
570    UNREACHABLE = 2011,
571    VM_STARTUP_FAILURE = 2012,
572    UNEXPECTED_ERROR_FROM_KNOWN_MOVE_FUNCTION = 2015,
573    VERIFIER_INVARIANT_VIOLATION = 2016,
574    UNEXPECTED_VERIFIER_ERROR = 2017,
575    UNEXPECTED_DESERIALIZATION_ERROR = 2018,
576    FAILED_TO_SERIALIZE_WRITE_SET_CHANGES = 2019,
577    FAILED_TO_DESERIALIZE_RESOURCE = 2020,
578    // Failed to resolve type due to linking being broken after verification
579    TYPE_RESOLUTION_FAILURE = 2021,
580
581    // Errors that can arise from binary decoding (deserialization)
582    // Deserializtion Errors: 3000-3999
583    UNKNOWN_BINARY_ERROR = 3000,
584    MALFORMED = 3001,
585    BAD_MAGIC = 3002,
586    UNKNOWN_VERSION = 3003,
587    UNKNOWN_TABLE_TYPE = 3004,
588    UNKNOWN_SIGNATURE_TYPE = 3005,
589    UNKNOWN_SERIALIZED_TYPE = 3006,
590    UNKNOWN_OPCODE = 3007,
591    BAD_HEADER_TABLE = 3008,
592    UNEXPECTED_SIGNATURE_TYPE = 3009,
593    DUPLICATE_TABLE = 3010,
594    UNKNOWN_NOMINAL_RESOURCE = 3012,
595    UNKNOWN_KIND = 3013,
596    UNKNOWN_NATIVE_STRUCT_FLAG = 3014,
597    BAD_U64 = 3019,
598    BAD_U128 = 3020,
599    VALUE_SERIALIZATION_ERROR = 3022,
600    VALUE_DESERIALIZATION_ERROR = 3023,
601    CODE_DESERIALIZATION_ERROR = 3024,
602    INVALID_FLAG_BITS = 3025,
603
604    // Errors that can arise at runtime
605    // Runtime Errors: 4000-4999
606    UNKNOWN_RUNTIME_STATUS = 4000,
607    EXECUTED = 4001,
608    OUT_OF_GAS = 4002,
609    // We tried to access a resource that does not exist under the account.
610    RESOURCE_DOES_NOT_EXIST = 4003,
611    // We tried to create a resource under an account where that resource
612    // already exists.
613    RESOURCE_ALREADY_EXISTS = 4004,
614    MISSING_DATA = 4008,
615    DATA_FORMAT_ERROR = 4009,
616    ABORTED = 4016,
617    ARITHMETIC_ERROR = 4017,
618    EXECUTION_STACK_OVERFLOW = 4020,
619    CALL_STACK_OVERFLOW = 4021,
620    VM_MAX_TYPE_DEPTH_REACHED = 4024,
621    VM_MAX_VALUE_DEPTH_REACHED = 4025,
622
623    // A reserved status to represent an unknown vm status.
624    // this is std::u64::MAX, but we can't pattern match on that, so put the hardcoded value in
625    UNKNOWN_STATUS = 18446744073709551615,
626}
627}
628
629impl StatusCode {
630    /// Return the status type for this status code
631    pub fn status_type(self) -> StatusType {
632        let major_status_number: u64 = self.into();
633        if major_status_number >= VALIDATION_STATUS_MIN_CODE
634            && major_status_number <= VALIDATION_STATUS_MAX_CODE
635        {
636            return StatusType::Validation;
637        }
638
639        if major_status_number >= VERIFICATION_STATUS_MIN_CODE
640            && major_status_number <= VERIFICATION_STATUS_MAX_CODE
641        {
642            return StatusType::Verification;
643        }
644
645        if major_status_number >= INVARIANT_VIOLATION_STATUS_MIN_CODE
646            && major_status_number <= INVARIANT_VIOLATION_STATUS_MAX_CODE
647        {
648            return StatusType::InvariantViolation;
649        }
650
651        if major_status_number >= DESERIALIZATION_STATUS_MIN_CODE
652            && major_status_number <= DESERIALIZATION_STATUS_MAX_CODE
653        {
654            return StatusType::Deserialization;
655        }
656
657        if major_status_number >= EXECUTION_STATUS_MIN_CODE
658            && major_status_number <= EXECUTION_STATUS_MAX_CODE
659        {
660            return StatusType::Execution;
661        }
662
663        StatusType::Unknown
664    }
665}
666
667// TODO(#1307)
668impl ser::Serialize for StatusCode {
669    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
670    where
671        S: ser::Serializer,
672    {
673        serializer.serialize_u64((*self).into())
674    }
675}
676
677impl<'de> de::Deserialize<'de> for StatusCode {
678    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
679    where
680        D: de::Deserializer<'de>,
681    {
682        struct StatusCodeVisitor;
683        impl<'de> de::Visitor<'de> for StatusCodeVisitor {
684            type Value = StatusCode;
685
686            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
687                formatter.write_str("StatusCode as u64")
688            }
689
690            fn visit_u64<E>(self, v: u64) -> std::result::Result<StatusCode, E>
691            where
692                E: de::Error,
693            {
694                Ok(StatusCode::try_from(v).unwrap_or(StatusCode::UNKNOWN_STATUS))
695            }
696        }
697
698        deserializer.deserialize_u64(StatusCodeVisitor)
699    }
700}
701
702impl From<StatusCode> for u64 {
703    fn from(status: StatusCode) -> u64 {
704        status as u64
705    }
706}
707
708pub mod sub_status {
709    // Native Function Error sub-codes
710    pub const NFE_VECTOR_ERROR_BASE: u64 = 0;
711    // Failure in BCS deserialization
712    pub const NFE_BCS_SERIALIZATION_FAILURE: u64 = 0x1C5;
713}
714
715/// The `Arbitrary` impl only generates validation statuses since the full enum is too large.
716#[cfg(any(test, feature = "fuzzing"))]
717impl Arbitrary for StatusCode {
718    type Parameters = ();
719    type Strategy = BoxedStrategy<Self>;
720
721    fn arbitrary_with(_args: ()) -> Self::Strategy {
722        (any::<usize>())
723            .prop_map(|index| {
724                let status_code_value = STATUS_CODE_VALUES[index % STATUS_CODE_VALUES.len()];
725                StatusCode::try_from(status_code_value).unwrap()
726            })
727            .boxed()
728    }
729}
730
731#[test]
732fn test_status_codes() {
733    use std::collections::HashSet;
734    // Make sure that within the 0-EXECUTION_STATUS_MAX_CODE that all of the status codes succeed
735    // when they should, and fail when they should.
736    for possible_major_status_code in 0..=EXECUTION_STATUS_MAX_CODE {
737        if STATUS_CODE_VALUES.contains(&possible_major_status_code) {
738            let status = StatusCode::try_from(possible_major_status_code);
739            assert!(status.is_ok());
740            let to_major_status_code = u64::from(status.unwrap());
741            assert_eq!(possible_major_status_code, to_major_status_code);
742        } else {
743            assert!(StatusCode::try_from(possible_major_status_code).is_err())
744        }
745    }
746
747    let mut seen_statuses = HashSet::new();
748    let mut seen_codes = HashSet::new();
749    // Now make sure that all of the error codes (including any that may be out-of-range) succeed.
750    // Make sure there aren't any duplicate mappings
751    for major_status_code in STATUS_CODE_VALUES.iter() {
752        assert!(
753            !seen_codes.contains(major_status_code),
754            "Duplicate major_status_code found"
755        );
756        seen_codes.insert(*major_status_code);
757        let status = StatusCode::try_from(*major_status_code);
758        assert!(status.is_ok());
759        let unwrapped_status = status.unwrap();
760        assert!(
761            !seen_statuses.contains(&unwrapped_status),
762            "Found duplicate u64 -> Status mapping"
763        );
764        seen_statuses.insert(unwrapped_status);
765        let to_major_status_code = u64::from(unwrapped_status);
766        assert_eq!(*major_status_code, to_major_status_code);
767    }
768}