1#![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
11pub static VALIDATION_STATUS_MIN_CODE: u64 = 0;
13
14pub static VALIDATION_STATUS_MAX_CODE: u64 = 999;
16
17pub static VERIFICATION_STATUS_MIN_CODE: u64 = 1000;
19
20pub static VERIFICATION_STATUS_MAX_CODE: u64 = 1999;
22
23pub static INVARIANT_VIOLATION_STATUS_MIN_CODE: u64 = 2000;
25
26pub static INVARIANT_VIOLATION_STATUS_MAX_CODE: u64 = 2999;
28
29pub static DESERIALIZATION_STATUS_MIN_CODE: u64 = 3000;
31
32pub static DESERIALIZATION_STATUS_MAX_CODE: u64 = 3999;
34
35pub static EXECUTION_STATUS_MIN_CODE: u64 = 4000;
37
38pub static EXECUTION_STATUS_MAX_CODE: u64 = 4999;
40
41#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
46pub enum VMStatus {
47 Executed,
49
50 Error(StatusCode),
54
55 MoveAbort(AbortLocation, u64),
57
58 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, u64),
73 ExecutionFailure {
74 location: AbortLocation,
75 function: u16,
76 code_offset: u16,
77 },
78 MiscellaneousError,
79}
80
81pub type DiscardedVMStatus = StatusCode;
82
83#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
86pub enum AbortLocation {
87 Module(ModuleId),
89 Script,
91}
92
93#[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 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 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 pub fn status_type(&self) -> StatusType {
132 self.status_code().status_type()
133 }
134
135 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 StatusType::Unknown => Err(code),
160 StatusType::Validation => Err(code),
163 StatusType::InvariantViolation => Err(code),
165 StatusType::Verification => Ok(KeptVMStatus::MiscellaneousError),
167 StatusType::Deserialization => Ok(KeptVMStatus::MiscellaneousError),
172 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 pub const ACCOUNT_MODULE_NAME: &str = "DiemAccount";
312 pub static ACCOUNT_MODULE_IDENTIFIER: Lazy<Identifier> =
314 Lazy::new(|| Identifier::new(ACCOUNT_MODULE_NAME).unwrap());
315 pub static ACCOUNT_MODULE: Lazy<ModuleId> =
317 Lazy::new(|| ModuleId::new(CORE_CODE_ADDRESS, ACCOUNT_MODULE_IDENTIFIER.clone()));
318 pub fn account_module_abort() -> AbortLocation {
320 AbortLocation::Module(ACCOUNT_MODULE.clone())
321 }
322
323 pub const DIEM_MODULE_NAME: &str = "Diem";
325 pub static DIEM_MODULE_IDENTIFIER: Lazy<Identifier> =
327 Lazy::new(|| Identifier::new(DIEM_MODULE_NAME).unwrap());
328 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 pub const DESIGNATED_DEALER_MODULE_NAME: &str = "DesignatedDealer";
337 pub static DESIGNATED_DEALER_MODULE_IDENTIFIER: Lazy<Identifier> =
339 Lazy::new(|| Identifier::new(DESIGNATED_DEALER_MODULE_NAME).unwrap());
340 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)]
394pub enum StatusCode {
398 UNKNOWN_VALIDATION_STATUS = 0,
402 INVALID_SIGNATURE = 1,
404 INVALID_AUTH_KEY = 2,
406 SEQUENCE_NUMBER_TOO_OLD = 3,
408 SEQUENCE_NUMBER_TOO_NEW = 4,
410 INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE = 5,
412 TRANSACTION_EXPIRED = 6,
414 SENDING_ACCOUNT_DOES_NOT_EXIST = 7,
416 REJECTED_WRITE_SET = 8,
419 INVALID_WRITE_SET = 9,
421 EXCEEDED_MAX_TRANSACTION_SIZE = 10,
423 UNKNOWN_SCRIPT = 11,
425 UNKNOWN_MODULE = 12,
427 MAX_GAS_UNITS_EXCEEDS_MAX_GAS_UNITS_BOUND = 13,
430 MAX_GAS_UNITS_BELOW_MIN_TRANSACTION_GAS_UNITS = 14,
433 GAS_UNIT_PRICE_BELOW_MIN_BOUND = 15,
436 GAS_UNIT_PRICE_ABOVE_MAX_BOUND = 16,
439 INVALID_GAS_SPECIFIER = 17,
442 SENDING_ACCOUNT_FROZEN = 18,
444 UNABLE_TO_DESERIALIZE_ACCOUNT = 19,
446 CURRENCY_INFO_DOES_NOT_EXIST = 20,
448 INVALID_MODULE_PUBLISHER = 21,
450 NO_ACCOUNT_ROLE = 22,
452 BAD_CHAIN_ID = 23,
454 SEQUENCE_NUMBER_TOO_BIG = 24,
456 BAD_TRANSACTION_FEE_CURRENCY = 25,
458
459 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 MODULE_ADDRESS_DOES_NOT_MATCH_SENDER = 1067,
520 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 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 DUPLICATE_MODULE_NAME = 1095,
549 BACKWARD_INCOMPATIBLE_MODULE_UPDATE = 1096,
551 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 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 TYPE_RESOLUTION_FAILURE = 2021,
580
581 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 UNKNOWN_RUNTIME_STATUS = 4000,
607 EXECUTED = 4001,
608 OUT_OF_GAS = 4002,
609 RESOURCE_DOES_NOT_EXIST = 4003,
611 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 UNKNOWN_STATUS = 18446744073709551615,
626}
627}
628
629impl StatusCode {
630 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
667impl 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 pub const NFE_VECTOR_ERROR_BASE: u64 = 0;
711 pub const NFE_BCS_SERIALIZATION_FAILURE: u64 = 0x1C5;
713}
714
715#[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 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 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}