#![allow(clippy::unit_arg)]
use super::language_storage::ModuleId;
use anyhow::Result;
use serde::{de, ser, Deserialize, Serialize};
use std::{convert::TryFrom, fmt};
pub static VALIDATION_STATUS_MIN_CODE: u64 = 0;
pub static VALIDATION_STATUS_MAX_CODE: u64 = 999;
pub static VERIFICATION_STATUS_MIN_CODE: u64 = 1000;
pub static VERIFICATION_STATUS_MAX_CODE: u64 = 1999;
pub static INVARIANT_VIOLATION_STATUS_MIN_CODE: u64 = 2000;
pub static INVARIANT_VIOLATION_STATUS_MAX_CODE: u64 = 2999;
pub static DESERIALIZATION_STATUS_MIN_CODE: u64 = 3000;
pub static DESERIALIZATION_STATUS_MAX_CODE: u64 = 3999;
pub static EXECUTION_STATUS_MIN_CODE: u64 = 4000;
pub static EXECUTION_STATUS_MAX_CODE: u64 = 4999;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum VMStatus {
Executed,
Error(StatusCode),
MoveAbort(AbortLocation, u64),
ExecutionFailure {
status_code: StatusCode,
location: AbortLocation,
function: u16,
code_offset: u16,
},
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum KeptVMStatus {
Executed,
OutOfGas,
MoveAbort(AbortLocation, u64),
ExecutionFailure {
location: AbortLocation,
function: u16,
code_offset: u16,
},
MiscellaneousError,
}
pub type DiscardedVMStatus = StatusCode;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum AbortLocation {
Module(ModuleId),
Script,
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum StatusType {
Validation,
Verification,
InvariantViolation,
Deserialization,
Execution,
Unknown,
}
impl VMStatus {
pub fn status_code(&self) -> StatusCode {
match self {
Self::Executed => StatusCode::EXECUTED,
Self::MoveAbort(_, _) => StatusCode::ABORTED,
Self::ExecutionFailure { status_code, .. } => *status_code,
Self::Error(code) => {
let code = *code;
debug_assert!(code != StatusCode::EXECUTED);
debug_assert!(code != StatusCode::ABORTED);
debug_assert!(code.status_type() != StatusType::Execution);
code
}
}
}
pub fn move_abort_code(&self) -> Option<u64> {
match self {
Self::MoveAbort(_, code) => Some(*code),
Self::Error(_) | Self::ExecutionFailure { .. } | Self::Executed => None,
}
}
pub fn status_type(&self) -> StatusType {
self.status_code().status_type()
}
pub fn keep_or_discard(self) -> Result<KeptVMStatus, DiscardedVMStatus> {
match self {
VMStatus::Executed => Ok(KeptVMStatus::Executed),
VMStatus::MoveAbort(location, code) => Ok(KeptVMStatus::MoveAbort(location, code)),
VMStatus::ExecutionFailure {
status_code: StatusCode::OUT_OF_GAS,
..
}
| VMStatus::Error(StatusCode::OUT_OF_GAS) => Ok(KeptVMStatus::OutOfGas),
VMStatus::ExecutionFailure {
status_code: _status_code,
location,
function,
code_offset,
} => Ok(KeptVMStatus::ExecutionFailure {
location,
function,
code_offset,
}),
VMStatus::Error(code) => {
match code.status_type() {
StatusType::Unknown => Err(code),
StatusType::Validation => Err(code),
StatusType::InvariantViolation => Err(code),
StatusType::Verification => Ok(KeptVMStatus::MiscellaneousError),
StatusType::Deserialization => Ok(KeptVMStatus::MiscellaneousError),
StatusType::Execution => Ok(KeptVMStatus::ExecutionFailure {
location: AbortLocation::Script,
function: 0,
code_offset: 0,
}),
}
}
}
}
}
impl fmt::Display for StatusType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let string = match self {
StatusType::Validation => "Validation",
StatusType::Verification => "Verification",
StatusType::InvariantViolation => "Invariant violation",
StatusType::Deserialization => "Deserialization",
StatusType::Execution => "Execution",
StatusType::Unknown => "Unknown",
};
write!(f, "{}", string)
}
}
impl fmt::Display for VMStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let status_type = self.status_type();
let mut status = format!("status {:#?} of type {}", self.status_code(), status_type);
if let VMStatus::MoveAbort(_, code) = self {
status = format!("{} with sub status {}", status, code);
}
write!(f, "{}", status)
}
}
impl fmt::Display for KeptVMStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "status ")?;
match self {
KeptVMStatus::Executed => write!(f, "EXECUTED"),
KeptVMStatus::OutOfGas => write!(f, "OUT_OF_GAS"),
KeptVMStatus::MiscellaneousError => write!(f, "MISCELLANEOUS_ERROR"),
KeptVMStatus::MoveAbort(location, code) => {
write!(f, "ABORTED with code {} in {}", code, location)
}
KeptVMStatus::ExecutionFailure {
location,
function,
code_offset,
} => write!(
f,
"EXECUTION_FAILURE at bytecode offset {} in function index {} in {}",
code_offset, function, location
),
}
}
}
impl fmt::Debug for VMStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
VMStatus::Executed => write!(f, "EXECUTED"),
VMStatus::Error(code) => f.debug_struct("ERROR").field("status_code", code).finish(),
VMStatus::MoveAbort(location, code) => f
.debug_struct("ABORTED")
.field("code", code)
.field("location", location)
.finish(),
VMStatus::ExecutionFailure {
status_code,
location,
function,
code_offset,
} => f
.debug_struct("EXECUTION_FAILURE")
.field("status_code", status_code)
.field("location", location)
.field("function_definition", function)
.field("code_offset", code_offset)
.finish(),
}
}
}
impl fmt::Debug for KeptVMStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
KeptVMStatus::Executed => write!(f, "EXECUTED"),
KeptVMStatus::OutOfGas => write!(f, "OUT_OF_GAS"),
KeptVMStatus::MoveAbort(location, code) => f
.debug_struct("ABORTED")
.field("code", code)
.field("location", location)
.finish(),
KeptVMStatus::ExecutionFailure {
location,
function,
code_offset,
} => f
.debug_struct("EXECUTION_FAILURE")
.field("location", location)
.field("function_definition", function)
.field("code_offset", code_offset)
.finish(),
KeptVMStatus::MiscellaneousError => write!(f, "MISCELLANEOUS_ERROR"),
}
}
}
impl fmt::Display for AbortLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AbortLocation::Script => write!(f, "Script"),
AbortLocation::Module(id) => write!(f, "{}", id),
}
}
}
impl fmt::Debug for AbortLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self)
}
}
impl std::error::Error for VMStatus {}
pub mod known_locations {
use crate::types::{
identifier::Identifier,
language_storage::{ModuleId, CORE_CODE_ADDRESS},
vm_status::AbortLocation,
};
use once_cell::sync::Lazy;
pub const ACCOUNT_MODULE_NAME: &str = "DiemAccount";
pub static ACCOUNT_MODULE_IDENTIFIER: Lazy<Identifier> =
Lazy::new(|| Identifier::new(ACCOUNT_MODULE_NAME).unwrap());
pub static ACCOUNT_MODULE: Lazy<ModuleId> =
Lazy::new(|| ModuleId::new(CORE_CODE_ADDRESS, ACCOUNT_MODULE_IDENTIFIER.clone()));
pub fn account_module_abort() -> AbortLocation {
AbortLocation::Module(ACCOUNT_MODULE.clone())
}
pub const DIEM_MODULE_NAME: &str = "Diem";
pub static DIEM_MODULE_IDENTIFIER: Lazy<Identifier> =
Lazy::new(|| Identifier::new(DIEM_MODULE_NAME).unwrap());
pub static DIEM_MODULE: Lazy<ModuleId> =
Lazy::new(|| ModuleId::new(CORE_CODE_ADDRESS, DIEM_MODULE_IDENTIFIER.clone()));
pub fn diem_module_abort() -> AbortLocation {
AbortLocation::Module(DIEM_MODULE.clone())
}
pub const DESIGNATED_DEALER_MODULE_NAME: &str = "DesignatedDealer";
pub static DESIGNATED_DEALER_MODULE_IDENTIFIER: Lazy<Identifier> =
Lazy::new(|| Identifier::new(DESIGNATED_DEALER_MODULE_NAME).unwrap());
pub static DESIGNATED_DEALER_MODULE: Lazy<ModuleId> = Lazy::new(|| {
ModuleId::new(
CORE_CODE_ADDRESS,
DESIGNATED_DEALER_MODULE_IDENTIFIER.clone(),
)
});
pub fn designated_dealer_module_abort() -> AbortLocation {
AbortLocation::Module(DESIGNATED_DEALER_MODULE.clone())
}
}
macro_rules! derive_status_try_from_repr {
(
#[repr($repr_ty:ident)]
$( #[$metas:meta] )*
$vis:vis enum $enum_name:ident {
$(
$variant:ident = $value: expr
),*
$( , )?
}
) => {
#[repr($repr_ty)]
$( #[$metas] )*
$vis enum $enum_name {
$(
$variant = $value
),*
}
impl std::convert::TryFrom<$repr_ty> for $enum_name {
type Error = &'static str;
fn try_from(value: $repr_ty) -> Result<Self, Self::Error> {
match value {
$(
$value => Ok($enum_name::$variant),
)*
_ => Err("invalid StatusCode"),
}
}
}
#[cfg(any(test, feature = "fuzzing"))]
const STATUS_CODE_VALUES: &'static [$repr_ty] = &[
$($value),*
];
};
}
derive_status_try_from_repr! {
#[repr(u64)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum StatusCode {
UNKNOWN_VALIDATION_STATUS = 0,
INVALID_SIGNATURE = 1,
INVALID_AUTH_KEY = 2,
SEQUENCE_NUMBER_TOO_OLD = 3,
SEQUENCE_NUMBER_TOO_NEW = 4,
INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE = 5,
TRANSACTION_EXPIRED = 6,
SENDING_ACCOUNT_DOES_NOT_EXIST = 7,
REJECTED_WRITE_SET = 8,
INVALID_WRITE_SET = 9,
EXCEEDED_MAX_TRANSACTION_SIZE = 10,
UNKNOWN_SCRIPT = 11,
UNKNOWN_MODULE = 12,
MAX_GAS_UNITS_EXCEEDS_MAX_GAS_UNITS_BOUND = 13,
MAX_GAS_UNITS_BELOW_MIN_TRANSACTION_GAS_UNITS = 14,
GAS_UNIT_PRICE_BELOW_MIN_BOUND = 15,
GAS_UNIT_PRICE_ABOVE_MAX_BOUND = 16,
INVALID_GAS_SPECIFIER = 17,
SENDING_ACCOUNT_FROZEN = 18,
UNABLE_TO_DESERIALIZE_ACCOUNT = 19,
CURRENCY_INFO_DOES_NOT_EXIST = 20,
INVALID_MODULE_PUBLISHER = 21,
NO_ACCOUNT_ROLE = 22,
BAD_CHAIN_ID = 23,
SEQUENCE_NUMBER_TOO_BIG = 24,
BAD_TRANSACTION_FEE_CURRENCY = 25,
UNKNOWN_VERIFICATION_ERROR = 1000,
INDEX_OUT_OF_BOUNDS = 1001,
INVALID_SIGNATURE_TOKEN = 1003,
RECURSIVE_STRUCT_DEFINITION = 1005,
INVALID_RESOURCE_FIELD = 1006,
INVALID_FALL_THROUGH = 1007,
NEGATIVE_STACK_SIZE_WITHIN_BLOCK = 1009,
INVALID_MAIN_FUNCTION_SIGNATURE = 1011,
DUPLICATE_ELEMENT = 1012,
INVALID_MODULE_HANDLE = 1013,
UNIMPLEMENTED_HANDLE = 1014,
LOOKUP_FAILED = 1017,
TYPE_MISMATCH = 1020,
MISSING_DEPENDENCY = 1021,
POP_RESOURCE_ERROR = 1023,
BR_TYPE_MISMATCH_ERROR = 1025,
ABORT_TYPE_MISMATCH_ERROR = 1026,
STLOC_TYPE_MISMATCH_ERROR = 1027,
STLOC_UNSAFE_TO_DESTROY_ERROR = 1028,
UNSAFE_RET_LOCAL_OR_RESOURCE_STILL_BORROWED = 1029,
RET_TYPE_MISMATCH_ERROR = 1030,
RET_BORROWED_MUTABLE_REFERENCE_ERROR = 1031,
FREEZEREF_TYPE_MISMATCH_ERROR = 1032,
FREEZEREF_EXISTS_MUTABLE_BORROW_ERROR = 1033,
BORROWFIELD_TYPE_MISMATCH_ERROR = 1034,
BORROWFIELD_BAD_FIELD_ERROR = 1035,
BORROWFIELD_EXISTS_MUTABLE_BORROW_ERROR = 1036,
COPYLOC_UNAVAILABLE_ERROR = 1037,
COPYLOC_RESOURCE_ERROR = 1038,
COPYLOC_EXISTS_BORROW_ERROR = 1039,
MOVELOC_UNAVAILABLE_ERROR = 1040,
MOVELOC_EXISTS_BORROW_ERROR = 1041,
BORROWLOC_REFERENCE_ERROR = 1042,
BORROWLOC_UNAVAILABLE_ERROR = 1043,
BORROWLOC_EXISTS_BORROW_ERROR = 1044,
CALL_TYPE_MISMATCH_ERROR = 1045,
CALL_BORROWED_MUTABLE_REFERENCE_ERROR = 1046,
PACK_TYPE_MISMATCH_ERROR = 1047,
UNPACK_TYPE_MISMATCH_ERROR = 1048,
READREF_TYPE_MISMATCH_ERROR = 1049,
READREF_RESOURCE_ERROR = 1050,
READREF_EXISTS_MUTABLE_BORROW_ERROR = 1051,
WRITEREF_TYPE_MISMATCH_ERROR = 1052,
WRITEREF_RESOURCE_ERROR = 1053,
WRITEREF_EXISTS_BORROW_ERROR = 1054,
WRITEREF_NO_MUTABLE_REFERENCE_ERROR = 1055,
INTEGER_OP_TYPE_MISMATCH_ERROR = 1056,
BOOLEAN_OP_TYPE_MISMATCH_ERROR = 1057,
EQUALITY_OP_TYPE_MISMATCH_ERROR = 1058,
EXISTS_RESOURCE_TYPE_MISMATCH_ERROR = 1059,
BORROWGLOBAL_TYPE_MISMATCH_ERROR = 1060,
BORROWGLOBAL_NO_RESOURCE_ERROR = 1061,
MOVEFROM_TYPE_MISMATCH_ERROR = 1062,
MOVEFROM_NO_RESOURCE_ERROR = 1063,
MOVETO_TYPE_MISMATCH_ERROR = 1064,
MOVETO_NO_RESOURCE_ERROR = 1065,
MODULE_ADDRESS_DOES_NOT_MATCH_SENDER = 1067,
NO_MODULE_HANDLES = 1068,
POSITIVE_STACK_SIZE_AT_BLOCK_END = 1069,
MISSING_ACQUIRES_RESOURCE_ANNOTATION_ERROR = 1070,
EXTRANEOUS_ACQUIRES_RESOURCE_ANNOTATION_ERROR = 1071,
DUPLICATE_ACQUIRES_RESOURCE_ANNOTATION_ERROR = 1072,
INVALID_ACQUIRES_RESOURCE_ANNOTATION_ERROR = 1073,
GLOBAL_REFERENCE_ERROR = 1074,
CONSTRAINT_KIND_MISMATCH = 1075,
NUMBER_OF_TYPE_ARGUMENTS_MISMATCH = 1076,
LOOP_IN_INSTANTIATION_GRAPH = 1077,
ZERO_SIZED_STRUCT = 1080,
LINKER_ERROR = 1081,
INVALID_CONSTANT_TYPE = 1082,
MALFORMED_CONSTANT_DATA = 1083,
EMPTY_CODE_UNIT = 1084,
INVALID_LOOP_SPLIT = 1085,
INVALID_LOOP_BREAK = 1086,
INVALID_LOOP_CONTINUE = 1087,
UNSAFE_RET_UNUSED_RESOURCES = 1088,
TOO_MANY_LOCALS = 1089,
GENERIC_MEMBER_OPCODE_MISMATCH = 1090,
FUNCTION_RESOLUTION_FAILURE = 1091,
INVALID_OPERATION_IN_SCRIPT = 1094,
DUPLICATE_MODULE_NAME = 1095,
BACKWARD_INCOMPATIBLE_MODULE_UPDATE = 1096,
CYCLIC_MODULE_DEPENDENCY = 1097,
NUMBER_OF_ARGUMENTS_MISMATCH = 1098,
INVALID_PARAM_TYPE_FOR_DESERIALIZATION = 1099,
FAILED_TO_DESERIALIZE_ARGUMENT = 1100,
NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH = 1101,
CALLED_SCRIPT_VISIBLE_FROM_NON_SCRIPT_VISIBLE = 1102,
EXECUTE_SCRIPT_FUNCTION_CALLED_ON_NON_SCRIPT_VISIBLE = 1103,
UNKNOWN_INVARIANT_VIOLATION_ERROR = 2000,
EMPTY_VALUE_STACK = 2003,
PC_OVERFLOW = 2005,
VERIFICATION_ERROR = 2006,
STORAGE_ERROR = 2008,
INTERNAL_TYPE_ERROR = 2009,
EVENT_KEY_MISMATCH = 2010,
UNREACHABLE = 2011,
VM_STARTUP_FAILURE = 2012,
UNEXPECTED_ERROR_FROM_KNOWN_MOVE_FUNCTION = 2015,
VERIFIER_INVARIANT_VIOLATION = 2016,
UNEXPECTED_VERIFIER_ERROR = 2017,
UNEXPECTED_DESERIALIZATION_ERROR = 2018,
FAILED_TO_SERIALIZE_WRITE_SET_CHANGES = 2019,
FAILED_TO_DESERIALIZE_RESOURCE = 2020,
TYPE_RESOLUTION_FAILURE = 2021,
UNKNOWN_BINARY_ERROR = 3000,
MALFORMED = 3001,
BAD_MAGIC = 3002,
UNKNOWN_VERSION = 3003,
UNKNOWN_TABLE_TYPE = 3004,
UNKNOWN_SIGNATURE_TYPE = 3005,
UNKNOWN_SERIALIZED_TYPE = 3006,
UNKNOWN_OPCODE = 3007,
BAD_HEADER_TABLE = 3008,
UNEXPECTED_SIGNATURE_TYPE = 3009,
DUPLICATE_TABLE = 3010,
UNKNOWN_NOMINAL_RESOURCE = 3012,
UNKNOWN_KIND = 3013,
UNKNOWN_NATIVE_STRUCT_FLAG = 3014,
BAD_U64 = 3019,
BAD_U128 = 3020,
VALUE_SERIALIZATION_ERROR = 3022,
VALUE_DESERIALIZATION_ERROR = 3023,
CODE_DESERIALIZATION_ERROR = 3024,
INVALID_FLAG_BITS = 3025,
UNKNOWN_RUNTIME_STATUS = 4000,
EXECUTED = 4001,
OUT_OF_GAS = 4002,
RESOURCE_DOES_NOT_EXIST = 4003,
RESOURCE_ALREADY_EXISTS = 4004,
MISSING_DATA = 4008,
DATA_FORMAT_ERROR = 4009,
ABORTED = 4016,
ARITHMETIC_ERROR = 4017,
EXECUTION_STACK_OVERFLOW = 4020,
CALL_STACK_OVERFLOW = 4021,
VM_MAX_TYPE_DEPTH_REACHED = 4024,
VM_MAX_VALUE_DEPTH_REACHED = 4025,
UNKNOWN_STATUS = 18446744073709551615,
}
}
impl StatusCode {
pub fn status_type(self) -> StatusType {
let major_status_number: u64 = self.into();
if major_status_number >= VALIDATION_STATUS_MIN_CODE
&& major_status_number <= VALIDATION_STATUS_MAX_CODE
{
return StatusType::Validation;
}
if major_status_number >= VERIFICATION_STATUS_MIN_CODE
&& major_status_number <= VERIFICATION_STATUS_MAX_CODE
{
return StatusType::Verification;
}
if major_status_number >= INVARIANT_VIOLATION_STATUS_MIN_CODE
&& major_status_number <= INVARIANT_VIOLATION_STATUS_MAX_CODE
{
return StatusType::InvariantViolation;
}
if major_status_number >= DESERIALIZATION_STATUS_MIN_CODE
&& major_status_number <= DESERIALIZATION_STATUS_MAX_CODE
{
return StatusType::Deserialization;
}
if major_status_number >= EXECUTION_STATUS_MIN_CODE
&& major_status_number <= EXECUTION_STATUS_MAX_CODE
{
return StatusType::Execution;
}
StatusType::Unknown
}
}
impl ser::Serialize for StatusCode {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serializer.serialize_u64((*self).into())
}
}
impl<'de> de::Deserialize<'de> for StatusCode {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct StatusCodeVisitor;
impl<'de> de::Visitor<'de> for StatusCodeVisitor {
type Value = StatusCode;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("StatusCode as u64")
}
fn visit_u64<E>(self, v: u64) -> std::result::Result<StatusCode, E>
where
E: de::Error,
{
Ok(StatusCode::try_from(v).unwrap_or(StatusCode::UNKNOWN_STATUS))
}
}
deserializer.deserialize_u64(StatusCodeVisitor)
}
}
impl From<StatusCode> for u64 {
fn from(status: StatusCode) -> u64 {
status as u64
}
}
pub mod sub_status {
pub const NFE_VECTOR_ERROR_BASE: u64 = 0;
pub const NFE_BCS_SERIALIZATION_FAILURE: u64 = 0x1C5;
}
#[cfg(any(test, feature = "fuzzing"))]
impl Arbitrary for StatusCode {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: ()) -> Self::Strategy {
(any::<usize>())
.prop_map(|index| {
let status_code_value = STATUS_CODE_VALUES[index % STATUS_CODE_VALUES.len()];
StatusCode::try_from(status_code_value).unwrap()
})
.boxed()
}
}
#[test]
fn test_status_codes() {
use std::collections::HashSet;
for possible_major_status_code in 0..=EXECUTION_STATUS_MAX_CODE {
if STATUS_CODE_VALUES.contains(&possible_major_status_code) {
let status = StatusCode::try_from(possible_major_status_code);
assert!(status.is_ok());
let to_major_status_code = u64::from(status.unwrap());
assert_eq!(possible_major_status_code, to_major_status_code);
} else {
assert!(StatusCode::try_from(possible_major_status_code).is_err())
}
}
let mut seen_statuses = HashSet::new();
let mut seen_codes = HashSet::new();
for major_status_code in STATUS_CODE_VALUES.iter() {
assert!(
!seen_codes.contains(major_status_code),
"Duplicate major_status_code found"
);
seen_codes.insert(*major_status_code);
let status = StatusCode::try_from(*major_status_code);
assert!(status.is_ok());
let unwrapped_status = status.unwrap();
assert!(
!seen_statuses.contains(&unwrapped_status),
"Found duplicate u64 -> Status mapping"
);
seen_statuses.insert(unwrapped_status);
let to_major_status_code = u64::from(unwrapped_status);
assert_eq!(*major_status_code, to_major_status_code);
}
}