use core::{
fmt::{self, Debug, Formatter},
u16, u8,
};
use crate::{
account::{
AddKeyFailure, RemoveKeyFailure, SetThresholdFailure, TryFromIntError,
TryFromSliceForPublicKeyError, UpdateKeyFailure,
},
bytesrepr,
system_contract_errors::{mint, pos},
CLValueError,
};
const RESERVED_ERROR_MAX: u32 = u16::MAX as u32;
const POS_ERROR_OFFSET: u32 = RESERVED_ERROR_MAX - u8::MAX as u32;
const MINT_ERROR_OFFSET: u32 = (POS_ERROR_OFFSET - 1) - u8::MAX as u32;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum ApiError {
None,
MissingArgument,
InvalidArgument,
Deserialize,
Read,
ValueNotFound,
ContractNotFound,
GetKey,
UnexpectedKeyVariant,
UnexpectedContractRefVariant,
InvalidPurseName,
InvalidPurse,
UpgradeContractAtURef,
Transfer,
NoAccessRights,
CLTypeMismatch,
EarlyEndOfStream,
Formatting,
LeftOverBytes,
OutOfMemory,
MaxKeysLimit,
DuplicateKey,
PermissionDenied,
MissingKey,
ThresholdViolation,
KeyManagementThreshold,
DeploymentThreshold,
InsufficientTotalWeight,
InvalidSystemContract,
PurseNotCreated,
Unhandled,
BufferTooSmall,
HostBufferEmpty,
HostBufferFull,
AllocLayout,
Mint(u8),
ProofOfStake(u8),
User(u16),
}
impl From<bytesrepr::Error> for ApiError {
fn from(error: bytesrepr::Error) -> Self {
match error {
bytesrepr::Error::EarlyEndOfStream => ApiError::EarlyEndOfStream,
bytesrepr::Error::Formatting => ApiError::Formatting,
bytesrepr::Error::LeftOverBytes => ApiError::LeftOverBytes,
bytesrepr::Error::OutOfMemory => ApiError::OutOfMemory,
}
}
}
impl From<AddKeyFailure> for ApiError {
fn from(error: AddKeyFailure) -> Self {
match error {
AddKeyFailure::MaxKeysLimit => ApiError::MaxKeysLimit,
AddKeyFailure::DuplicateKey => ApiError::DuplicateKey,
AddKeyFailure::PermissionDenied => ApiError::PermissionDenied,
}
}
}
impl From<UpdateKeyFailure> for ApiError {
fn from(error: UpdateKeyFailure) -> Self {
match error {
UpdateKeyFailure::MissingKey => ApiError::MissingKey,
UpdateKeyFailure::PermissionDenied => ApiError::PermissionDenied,
UpdateKeyFailure::ThresholdViolation => ApiError::ThresholdViolation,
}
}
}
impl From<RemoveKeyFailure> for ApiError {
fn from(error: RemoveKeyFailure) -> Self {
match error {
RemoveKeyFailure::MissingKey => ApiError::MissingKey,
RemoveKeyFailure::PermissionDenied => ApiError::PermissionDenied,
RemoveKeyFailure::ThresholdViolation => ApiError::ThresholdViolation,
}
}
}
impl From<SetThresholdFailure> for ApiError {
fn from(error: SetThresholdFailure) -> Self {
match error {
SetThresholdFailure::KeyManagementThreshold => ApiError::KeyManagementThreshold,
SetThresholdFailure::DeploymentThreshold => ApiError::DeploymentThreshold,
SetThresholdFailure::PermissionDeniedError => ApiError::PermissionDenied,
SetThresholdFailure::InsufficientTotalWeight => ApiError::InsufficientTotalWeight,
}
}
}
impl From<CLValueError> for ApiError {
fn from(error: CLValueError) -> Self {
match error {
CLValueError::Serialization(bytesrepr_error) => bytesrepr_error.into(),
CLValueError::Type(_) => ApiError::CLTypeMismatch,
}
}
}
#[doc(hidden)]
impl From<TryFromIntError> for ApiError {
fn from(_error: TryFromIntError) -> Self {
ApiError::Unhandled
}
}
impl From<TryFromSliceForPublicKeyError> for ApiError {
fn from(_error: TryFromSliceForPublicKeyError) -> Self {
ApiError::Deserialize
}
}
impl From<mint::Error> for ApiError {
fn from(error: mint::Error) -> Self {
ApiError::Mint(error as u8)
}
}
impl From<pos::Error> for ApiError {
fn from(error: pos::Error) -> Self {
ApiError::ProofOfStake(error as u8)
}
}
impl From<ApiError> for u32 {
fn from(error: ApiError) -> Self {
match error {
ApiError::None => 1,
ApiError::MissingArgument => 2,
ApiError::InvalidArgument => 3,
ApiError::Deserialize => 4,
ApiError::Read => 5,
ApiError::ValueNotFound => 6,
ApiError::ContractNotFound => 7,
ApiError::GetKey => 8,
ApiError::UnexpectedKeyVariant => 9,
ApiError::UnexpectedContractRefVariant => 10,
ApiError::InvalidPurseName => 11,
ApiError::InvalidPurse => 12,
ApiError::UpgradeContractAtURef => 13,
ApiError::Transfer => 14,
ApiError::NoAccessRights => 15,
ApiError::CLTypeMismatch => 16,
ApiError::EarlyEndOfStream => 17,
ApiError::Formatting => 18,
ApiError::LeftOverBytes => 19,
ApiError::OutOfMemory => 20,
ApiError::MaxKeysLimit => 21,
ApiError::DuplicateKey => 22,
ApiError::PermissionDenied => 23,
ApiError::MissingKey => 24,
ApiError::ThresholdViolation => 25,
ApiError::KeyManagementThreshold => 26,
ApiError::DeploymentThreshold => 27,
ApiError::InsufficientTotalWeight => 28,
ApiError::InvalidSystemContract => 29,
ApiError::PurseNotCreated => 30,
ApiError::Unhandled => 31,
ApiError::BufferTooSmall => 32,
ApiError::HostBufferEmpty => 33,
ApiError::HostBufferFull => 34,
ApiError::AllocLayout => 35,
ApiError::Mint(value) => MINT_ERROR_OFFSET + u32::from(value),
ApiError::ProofOfStake(value) => POS_ERROR_OFFSET + u32::from(value),
ApiError::User(value) => RESERVED_ERROR_MAX + 1 + u32::from(value),
}
}
}
impl Debug for ApiError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ApiError::None => write!(f, "ApiError::None")?,
ApiError::MissingArgument => write!(f, "ApiError::MissingArgument")?,
ApiError::InvalidArgument => write!(f, "ApiError::InvalidArgument")?,
ApiError::Deserialize => write!(f, "ApiError::Deserialize")?,
ApiError::Read => write!(f, "ApiError::Read")?,
ApiError::ValueNotFound => write!(f, "ApiError::ValueNotFound")?,
ApiError::ContractNotFound => write!(f, "ApiError::ContractNotFound")?,
ApiError::GetKey => write!(f, "ApiError::GetKey")?,
ApiError::UnexpectedKeyVariant => write!(f, "ApiError::UnexpectedKeyVariant")?,
ApiError::UnexpectedContractRefVariant => {
write!(f, "ApiError::UnexpectedContractRefVariant")?
}
ApiError::InvalidPurseName => write!(f, "ApiError::InvalidPurseName")?,
ApiError::InvalidPurse => write!(f, "ApiError::InvalidPurse")?,
ApiError::UpgradeContractAtURef => write!(f, "ApiError::UpgradeContractAtURef")?,
ApiError::Transfer => write!(f, "ApiError::Transfer")?,
ApiError::NoAccessRights => write!(f, "ApiError::NoAccessRights")?,
ApiError::CLTypeMismatch => write!(f, "ApiError::CLTypeMismatch")?,
ApiError::EarlyEndOfStream => write!(f, "ApiError::EarlyEndOfStream")?,
ApiError::Formatting => write!(f, "ApiError::Formatting")?,
ApiError::LeftOverBytes => write!(f, "ApiError::LeftOverBytes")?,
ApiError::OutOfMemory => write!(f, "ApiError::OutOfMemory")?,
ApiError::MaxKeysLimit => write!(f, "ApiError::MaxKeysLimit")?,
ApiError::DuplicateKey => write!(f, "ApiError::DuplicateKey")?,
ApiError::PermissionDenied => write!(f, "ApiError::PermissionDenied")?,
ApiError::MissingKey => write!(f, "ApiError::MissingKey")?,
ApiError::ThresholdViolation => write!(f, "ApiError::ThresholdViolation")?,
ApiError::KeyManagementThreshold => write!(f, "ApiError::KeyManagementThreshold")?,
ApiError::DeploymentThreshold => write!(f, "ApiError::DeploymentThreshold")?,
ApiError::InsufficientTotalWeight => write!(f, "ApiError::InsufficientTotalWeight")?,
ApiError::InvalidSystemContract => write!(f, "ApiError::InvalidSystemContract")?,
ApiError::PurseNotCreated => write!(f, "ApiError::PurseNotCreated")?,
ApiError::Unhandled => write!(f, "ApiError::Unhandled")?,
ApiError::BufferTooSmall => write!(f, "ApiError::BufferTooSmall")?,
ApiError::HostBufferEmpty => write!(f, "ApiError::HostBufferEmpty")?,
ApiError::HostBufferFull => write!(f, "ApiError::HostBufferFull")?,
ApiError::AllocLayout => write!(f, "ApiError::AllocLayout")?,
ApiError::Mint(value) => write!(f, "ApiError::Mint({})", value)?,
ApiError::ProofOfStake(value) => write!(f, "ApiError::ProofOfStake({})", value)?,
ApiError::User(value) => write!(f, "ApiError::User({})", value)?,
}
write!(f, " [{}]", u32::from(*self))
}
}
#[doc(hidden)]
pub fn i32_from(result: Result<(), ApiError>) -> i32 {
match result {
Ok(()) => 0,
Err(error) => u32::from(error) as i32,
}
}
pub fn result_from(value: i32) -> Result<(), ApiError> {
match value {
0 => Ok(()),
1 => Err(ApiError::None),
2 => Err(ApiError::MissingArgument),
3 => Err(ApiError::InvalidArgument),
4 => Err(ApiError::Deserialize),
5 => Err(ApiError::Read),
6 => Err(ApiError::ValueNotFound),
7 => Err(ApiError::ContractNotFound),
8 => Err(ApiError::GetKey),
9 => Err(ApiError::UnexpectedKeyVariant),
10 => Err(ApiError::UnexpectedContractRefVariant),
11 => Err(ApiError::InvalidPurseName),
12 => Err(ApiError::InvalidPurse),
13 => Err(ApiError::UpgradeContractAtURef),
14 => Err(ApiError::Transfer),
15 => Err(ApiError::NoAccessRights),
16 => Err(ApiError::CLTypeMismatch),
17 => Err(ApiError::EarlyEndOfStream),
18 => Err(ApiError::Formatting),
19 => Err(ApiError::LeftOverBytes),
20 => Err(ApiError::OutOfMemory),
21 => Err(ApiError::MaxKeysLimit),
22 => Err(ApiError::DuplicateKey),
23 => Err(ApiError::PermissionDenied),
24 => Err(ApiError::MissingKey),
25 => Err(ApiError::ThresholdViolation),
26 => Err(ApiError::KeyManagementThreshold),
27 => Err(ApiError::DeploymentThreshold),
28 => Err(ApiError::InsufficientTotalWeight),
29 => Err(ApiError::InvalidSystemContract),
30 => Err(ApiError::PurseNotCreated),
31 => Err(ApiError::Unhandled),
32 => Err(ApiError::BufferTooSmall),
33 => Err(ApiError::HostBufferEmpty),
34 => Err(ApiError::HostBufferFull),
35 => Err(ApiError::AllocLayout),
_ => {
if value > RESERVED_ERROR_MAX as i32 && value <= (2 * RESERVED_ERROR_MAX + 1) as i32 {
Err(ApiError::User(value as u16))
} else if value >= POS_ERROR_OFFSET as i32 && value <= RESERVED_ERROR_MAX as i32 {
Err(ApiError::ProofOfStake(value as u8))
} else if value >= MINT_ERROR_OFFSET as i32 && value < POS_ERROR_OFFSET as i32 {
Err(ApiError::Mint(value as u8))
} else {
Err(ApiError::Unhandled)
}
}
}
}
#[cfg(test)]
mod tests {
use std::{i32, u16, u8};
use super::*;
fn round_trip(result: Result<(), ApiError>) {
let code = i32_from(result);
assert_eq!(result, result_from(code));
}
#[test]
fn error() {
assert_eq!(65_024_u32, ApiError::Mint(0).into()); assert_eq!(65_279_u32, ApiError::Mint(u8::MAX).into());
assert_eq!(65_280_u32, ApiError::ProofOfStake(0).into()); assert_eq!(65_535_u32, ApiError::ProofOfStake(u8::MAX).into());
assert_eq!(65_536_u32, ApiError::User(0).into()); assert_eq!(131_071_u32, ApiError::User(u16::MAX).into());
assert_eq!("ApiError::GetKey [8]", &format!("{:?}", ApiError::GetKey));
assert_eq!(
"ApiError::Mint(0) [65024]",
&format!("{:?}", ApiError::Mint(0))
);
assert_eq!(
"ApiError::Mint(255) [65279]",
&format!("{:?}", ApiError::Mint(u8::MAX))
);
assert_eq!(
"ApiError::ProofOfStake(0) [65280]",
&format!("{:?}", ApiError::ProofOfStake(0))
);
assert_eq!(
"ApiError::ProofOfStake(255) [65535]",
&format!("{:?}", ApiError::ProofOfStake(u8::MAX))
);
assert_eq!(
"ApiError::User(0) [65536]",
&format!("{:?}", ApiError::User(0))
);
assert_eq!(
"ApiError::User(65535) [131071]",
&format!("{:?}", ApiError::User(u16::MAX))
);
assert_eq!(Err(ApiError::Unhandled), result_from(i32::MAX));
assert_eq!(
Err(ApiError::Unhandled),
result_from(MINT_ERROR_OFFSET as i32 - 1)
);
assert_eq!(Err(ApiError::Unhandled), result_from(-1));
assert_eq!(Err(ApiError::Unhandled), result_from(i32::MIN));
round_trip(Ok(()));
round_trip(Err(ApiError::None));
round_trip(Err(ApiError::MissingArgument));
round_trip(Err(ApiError::InvalidArgument));
round_trip(Err(ApiError::Deserialize));
round_trip(Err(ApiError::Read));
round_trip(Err(ApiError::ValueNotFound));
round_trip(Err(ApiError::ContractNotFound));
round_trip(Err(ApiError::GetKey));
round_trip(Err(ApiError::UnexpectedKeyVariant));
round_trip(Err(ApiError::UnexpectedContractRefVariant));
round_trip(Err(ApiError::InvalidPurseName));
round_trip(Err(ApiError::InvalidPurse));
round_trip(Err(ApiError::UpgradeContractAtURef));
round_trip(Err(ApiError::Transfer));
round_trip(Err(ApiError::NoAccessRights));
round_trip(Err(ApiError::CLTypeMismatch));
round_trip(Err(ApiError::EarlyEndOfStream));
round_trip(Err(ApiError::Formatting));
round_trip(Err(ApiError::LeftOverBytes));
round_trip(Err(ApiError::OutOfMemory));
round_trip(Err(ApiError::MaxKeysLimit));
round_trip(Err(ApiError::DuplicateKey));
round_trip(Err(ApiError::PermissionDenied));
round_trip(Err(ApiError::MissingKey));
round_trip(Err(ApiError::ThresholdViolation));
round_trip(Err(ApiError::KeyManagementThreshold));
round_trip(Err(ApiError::DeploymentThreshold));
round_trip(Err(ApiError::InsufficientTotalWeight));
round_trip(Err(ApiError::InvalidSystemContract));
round_trip(Err(ApiError::PurseNotCreated));
round_trip(Err(ApiError::Unhandled));
round_trip(Err(ApiError::BufferTooSmall));
round_trip(Err(ApiError::HostBufferEmpty));
round_trip(Err(ApiError::HostBufferFull));
round_trip(Err(ApiError::AllocLayout));
round_trip(Err(ApiError::Mint(0)));
round_trip(Err(ApiError::Mint(u8::MAX)));
round_trip(Err(ApiError::ProofOfStake(0)));
round_trip(Err(ApiError::ProofOfStake(u8::MAX)));
round_trip(Err(ApiError::User(0)));
round_trip(Err(ApiError::User(u16::MAX)));
}
}