#![no_std]
#![warn(missing_docs)]
#![doc(html_logo_url = "https://gear-tech.io/logo.png")]
#![doc(html_favicon_url = "https://gear-tech.io/favicon.ico")]
#![cfg_attr(docsrs, feature(doc_cfg))]
extern crate alloc;
use core::fmt::Debug;
use enum_iterator::Sequence;
#[cfg(feature = "codec")]
use {
alloc::vec::Vec,
parity_scale_codec::{Decode, Encode, Error, Input},
};
pub use simple::*;
mod simple;
#[repr(u32)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
pub enum ExecutionError {
#[error("Not enough gas for operation")]
NotEnoughGas = 100,
#[error("Not enough value for operation")]
NotEnoughValue = 101,
#[error("Length is overflowed to read payload")]
TooBigReadLen = 103,
#[error("Cannot take data in payload range from message with size")]
ReadWrongRange = 104,
#[error("Not running in reply context")]
NoReplyContext = 105,
#[error("Not running in signal context")]
NoSignalContext = 106,
#[error("No status code in reply/signal context")]
NoStatusCodeContext = 107,
#[error("Reply sending is only allowed in `init` and `handle` functions")]
IncorrectEntryForReply = 108,
}
#[repr(u32)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
pub enum MemoryError {
#[error("Trying to allocate more memory in block-chain runtime than allowed")]
RuntimeAllocOutOfBounds = 200,
#[error("Trying to access memory outside wasm program memory")]
AccessOutOfBounds = 201,
}
#[repr(u32)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
pub enum MessageError {
#[error("Max message size exceed")]
MaxMessageSizeExceed = 300,
#[error("Message limit exceeded")]
OutgoingMessagesAmountLimitExceeded = 301,
#[error("Duplicate reply message")]
DuplicateReply = 302,
#[error("Duplicate waking message")]
DuplicateWaking = 303,
#[error("An attempt to commit or push a payload into an already formed message")]
LateAccess = 304,
#[error("Message with given handle is not found")]
OutOfBounds = 305,
#[error("Duplicated program initialization message")]
DuplicateInit = 306,
#[error("In case of non-zero message value must be greater than existential deposit")]
InsufficientValue = 307,
#[error("In case of non-zero message gas limit must be greater than mailbox threshold")]
InsufficientGasLimit = 308,
#[error("Reply deposit already exists for given message")]
DuplicateReplyDeposit = 309,
#[error(
"Reply deposit could be only created for init or handle message sent within the execution"
)]
IncorrectMessageForReplyDeposit = 310,
#[error("Outgoing messages bytes limit exceeded")]
OutgoingMessagesBytesLimitExceeded = 311,
#[error("Offset value for the input payload is out of it's size bounds")]
OutOfBoundsInputSliceOffset = 312,
#[error("Too big length value is set to form a slice (range) of the input buffer")]
OutOfBoundsInputSliceLength = 313,
#[error("Not enough gas to hold dispatch message")]
InsufficientGasForDelayedSending = 399,
}
#[repr(u32)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
pub enum ReservationError {
#[error("Invalid reservation ID")]
InvalidReservationId = 500,
#[error("Reservation limit has reached")]
ReservationsLimitReached = 501,
#[error("Reservation duration cannot be zero")]
ZeroReservationDuration = 502,
#[error("Reservation amount cannot be zero")]
ZeroReservationAmount = 503,
#[error("Reservation amount cannot be below mailbox threshold")]
ReservationBelowMailboxThreshold = 504,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
#[non_exhaustive]
pub enum ExtError {
#[error("Execution error: {0}")]
Execution(#[from] ExecutionError),
#[error("Memory error: {0}")]
Memory(#[from] MemoryError),
#[error("Message error: {0}")]
Message(#[from] MessageError),
#[error("Reservation error: {0}")]
Reservation(#[from] ReservationError),
#[error("Unsupported error")]
Unsupported,
}
impl ExtError {
pub fn to_u32(self) -> u32 {
match self {
ExtError::Execution(err) => err as u32,
ExtError::Memory(err) => err as u32,
ExtError::Message(err) => err as u32,
ExtError::Reservation(err) => err as u32,
ExtError::Unsupported => u32::MAX,
}
}
pub fn from_u32(code: u32) -> Option<Self> {
match code {
100 => Some(ExecutionError::NotEnoughGas.into()),
101 => Some(ExecutionError::NotEnoughValue.into()),
103 => Some(ExecutionError::TooBigReadLen.into()),
104 => Some(ExecutionError::ReadWrongRange.into()),
105 => Some(ExecutionError::NoReplyContext.into()),
106 => Some(ExecutionError::NoSignalContext.into()),
107 => Some(ExecutionError::NoStatusCodeContext.into()),
108 => Some(ExecutionError::IncorrectEntryForReply.into()),
200 => Some(MemoryError::RuntimeAllocOutOfBounds.into()),
201 => Some(MemoryError::AccessOutOfBounds.into()),
300 => Some(MessageError::MaxMessageSizeExceed.into()),
301 => Some(MessageError::OutgoingMessagesAmountLimitExceeded.into()),
302 => Some(MessageError::DuplicateReply.into()),
303 => Some(MessageError::DuplicateWaking.into()),
304 => Some(MessageError::LateAccess.into()),
305 => Some(MessageError::OutOfBounds.into()),
306 => Some(MessageError::DuplicateInit.into()),
307 => Some(MessageError::InsufficientValue.into()),
308 => Some(MessageError::InsufficientGasLimit.into()),
309 => Some(MessageError::DuplicateReplyDeposit.into()),
310 => Some(MessageError::IncorrectMessageForReplyDeposit.into()),
311 => Some(MessageError::OutgoingMessagesBytesLimitExceeded.into()),
312 => Some(MessageError::OutOfBoundsInputSliceOffset.into()),
313 => Some(MessageError::OutOfBoundsInputSliceLength.into()),
399 => Some(MessageError::InsufficientGasForDelayedSending.into()),
500 => Some(ReservationError::InvalidReservationId.into()),
501 => Some(ReservationError::ReservationsLimitReached.into()),
502 => Some(ReservationError::ZeroReservationDuration.into()),
503 => Some(ReservationError::ZeroReservationAmount.into()),
504 => Some(ReservationError::ReservationBelowMailboxThreshold.into()),
0xffff |
600 |
u32::MAX => Some(ExtError::Unsupported),
_ => None,
}
}
}
#[cfg(feature = "codec")]
impl Encode for ExtError {
fn encode(&self) -> Vec<u8> {
ExtError::to_u32(*self).to_le_bytes().to_vec()
}
}
#[cfg(feature = "codec")]
impl Decode for ExtError {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let mut code = [0; 4];
input.read(&mut code)?;
let err =
ExtError::from_u32(u32::from_le_bytes(code)).ok_or("Failed to decode error code")?;
Ok(err)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::collections::BTreeMap;
#[test]
fn error_codes_are_unique() {
let mut codes = BTreeMap::new();
for err in enum_iterator::all::<ExtError>() {
let code = err.to_u32();
if let Some(same_code_err) = codes.insert(code, err) {
panic!("{:?} has same code {:?} as {:?}", same_code_err, code, err);
}
}
}
#[test]
fn encode_decode() {
for err in enum_iterator::all::<ExtError>() {
let code = err.to_u32();
let decoded = ExtError::from_u32(code)
.unwrap_or_else(|| unreachable!("failed to decode error code: {}", code));
assert_eq!(err, decoded);
}
}
#[test]
fn error_code_no_specific_value() {
for err in enum_iterator::all::<ExtError>() {
let code = err.to_u32();
assert_ne!(code, 0); }
}
#[test]
fn error_codes_forbidden() {
let codes = [
0xffff,
600,
];
for code in codes {
let err = ExtError::from_u32(code);
assert_eq!(err, Some(ExtError::Unsupported));
}
for err in enum_iterator::all::<ExtError>() {
let code = err.to_u32();
assert!(!codes.contains(&code));
}
}
}