1#![no_std]
22#![warn(missing_docs)]
23#![doc(html_logo_url = "https://gear-tech.io/logo.png")]
24#![doc(html_favicon_url = "https://gear-tech.io/favicon.ico")]
25#![cfg_attr(docsrs, feature(doc_auto_cfg))]
26
27extern crate alloc;
28
29use core::fmt::Debug;
30use enum_iterator::Sequence;
31#[cfg(feature = "codec")]
32use {
33 alloc::vec::Vec,
34 parity_scale_codec::{Decode, Encode, Error, Input},
35};
36
37pub use simple::*;
38
39mod simple;
40
41#[repr(u32)]
43#[non_exhaustive]
44#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
45pub enum ExecutionError {
46 #[error("Not enough gas for operation")]
48 NotEnoughGas = 100,
49
50 #[error("Not enough value for operation")]
52 NotEnoughValue = 101,
53
54 #[error("Length is overflowed to read payload")]
56 TooBigReadLen = 103,
57
58 #[error("Cannot take data in payload range from message with size")]
60 ReadWrongRange = 104,
61
62 #[error("Not running in reply context")]
64 NoReplyContext = 105,
65
66 #[error("Not running in signal context")]
68 NoSignalContext = 106,
69
70 #[error("No status code in reply/signal context")]
72 NoStatusCodeContext = 107,
73
74 #[error("Reply sending is only allowed in `init` and `handle` functions")]
76 IncorrectEntryForReply = 108,
77}
78
79#[repr(u32)]
81#[non_exhaustive]
82#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
83pub enum MemoryError {
84 #[error("Trying to allocate more memory in block-chain runtime than allowed")]
86 RuntimeAllocOutOfBounds = 200,
87 #[error("Trying to access memory outside wasm program memory")]
89 AccessOutOfBounds = 201,
90}
91
92#[repr(u32)]
94#[non_exhaustive]
95#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
96pub enum MessageError {
97 #[error("Max message size exceed")]
99 MaxMessageSizeExceed = 300,
100
101 #[error("Message limit exceeded")]
105 OutgoingMessagesAmountLimitExceeded = 301,
106
107 #[error("Duplicate reply message")]
109 DuplicateReply = 302,
110
111 #[error("Duplicate waking message")]
114 DuplicateWaking = 303,
115
116 #[error("An attempt to commit or push a payload into an already formed message")]
118 LateAccess = 304,
119
120 #[error("Message with given handle is not found")]
122 OutOfBounds = 305,
123
124 #[error("Duplicated program initialization message")]
127 DuplicateInit = 306,
128
129 #[error("In case of non-zero message value must be greater than existential deposit")]
132 InsufficientValue = 307,
133
134 #[error("In case of non-zero message gas limit must be greater than mailbox threshold")]
139 InsufficientGasLimit = 308,
140
141 #[error("Reply deposit already exists for given message")]
144 DuplicateReplyDeposit = 309,
145
146 #[error(
149 "Reply deposit could be only created for init or handle message sent within the execution"
150 )]
151 IncorrectMessageForReplyDeposit = 310,
152
153 #[error("Outgoing messages bytes limit exceeded")]
156 OutgoingMessagesBytesLimitExceeded = 311,
157
158 #[error("Offset value for the input payload is out of it's size bounds")]
161 OutOfBoundsInputSliceOffset = 312,
162
163 #[error("Too big length value is set to form a slice (range) of the input buffer")]
166 OutOfBoundsInputSliceLength = 313,
167
168 #[error("Not enough gas to hold dispatch message")]
171 InsufficientGasForDelayedSending = 399,
172}
173
174#[repr(u32)]
177#[non_exhaustive]
178#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
179pub enum ReservationError {
180 #[error("Invalid reservation ID")]
182 InvalidReservationId = 500,
183 #[error("Reservation limit has reached")]
185 ReservationsLimitReached = 501,
186 #[error("Reservation duration cannot be zero")]
188 ZeroReservationDuration = 502,
189 #[error("Reservation amount cannot be zero")]
191 ZeroReservationAmount = 503,
192 #[error("Reservation amount cannot be below mailbox threshold")]
194 ReservationBelowMailboxThreshold = 504,
195}
196
197#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
199#[non_exhaustive]
200pub enum ExtError {
201 #[error("Execution error: {0}")]
203 Execution(#[from] ExecutionError),
204
205 #[error("Memory error: {0}")]
207 Memory(#[from] MemoryError),
208
209 #[error("Message error: {0}")]
211 Message(#[from] MessageError),
212
213 #[error("Reservation error: {0}")]
215 Reservation(#[from] ReservationError),
216
217 #[error("Unsupported error")]
219 Unsupported,
220}
221
222impl ExtError {
223 pub fn to_u32(self) -> u32 {
225 match self {
226 ExtError::Execution(err) => err as u32,
227 ExtError::Memory(err) => err as u32,
228 ExtError::Message(err) => err as u32,
229 ExtError::Reservation(err) => err as u32,
230 ExtError::Unsupported => u32::MAX,
231 }
232 }
233
234 pub fn from_u32(code: u32) -> Option<Self> {
236 match code {
237 100 => Some(ExecutionError::NotEnoughGas.into()),
238 101 => Some(ExecutionError::NotEnoughValue.into()),
239 103 => Some(ExecutionError::TooBigReadLen.into()),
240 104 => Some(ExecutionError::ReadWrongRange.into()),
241 105 => Some(ExecutionError::NoReplyContext.into()),
242 106 => Some(ExecutionError::NoSignalContext.into()),
243 107 => Some(ExecutionError::NoStatusCodeContext.into()),
244 108 => Some(ExecutionError::IncorrectEntryForReply.into()),
245 200 => Some(MemoryError::RuntimeAllocOutOfBounds.into()),
247 201 => Some(MemoryError::AccessOutOfBounds.into()),
248 300 => Some(MessageError::MaxMessageSizeExceed.into()),
250 301 => Some(MessageError::OutgoingMessagesAmountLimitExceeded.into()),
251 302 => Some(MessageError::DuplicateReply.into()),
252 303 => Some(MessageError::DuplicateWaking.into()),
253 304 => Some(MessageError::LateAccess.into()),
254 305 => Some(MessageError::OutOfBounds.into()),
255 306 => Some(MessageError::DuplicateInit.into()),
256 307 => Some(MessageError::InsufficientValue.into()),
257 308 => Some(MessageError::InsufficientGasLimit.into()),
258 309 => Some(MessageError::DuplicateReplyDeposit.into()),
259 310 => Some(MessageError::IncorrectMessageForReplyDeposit.into()),
260 311 => Some(MessageError::OutgoingMessagesBytesLimitExceeded.into()),
261 312 => Some(MessageError::OutOfBoundsInputSliceOffset.into()),
262 313 => Some(MessageError::OutOfBoundsInputSliceLength.into()),
263 399 => Some(MessageError::InsufficientGasForDelayedSending.into()),
264 500 => Some(ReservationError::InvalidReservationId.into()),
266 501 => Some(ReservationError::ReservationsLimitReached.into()),
267 502 => Some(ReservationError::ZeroReservationDuration.into()),
268 503 => Some(ReservationError::ZeroReservationAmount.into()),
269 504 => Some(ReservationError::ReservationBelowMailboxThreshold.into()),
270 0xffff |
272 600 |
273 u32::MAX => Some(ExtError::Unsupported),
274 _ => None,
275 }
276 }
277}
278
279#[cfg(feature = "codec")]
280impl Encode for ExtError {
281 fn encode(&self) -> Vec<u8> {
282 ExtError::to_u32(*self).to_le_bytes().to_vec()
283 }
284}
285
286#[cfg(feature = "codec")]
287impl Decode for ExtError {
288 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
289 let mut code = [0; 4];
290 input.read(&mut code)?;
291 let err =
292 ExtError::from_u32(u32::from_le_bytes(code)).ok_or("Failed to decode error code")?;
293 Ok(err)
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300 use alloc::collections::BTreeMap;
301
302 #[test]
303 fn error_codes_are_unique() {
304 let mut codes = BTreeMap::new();
305
306 for err in enum_iterator::all::<ExtError>() {
307 let code = err.to_u32();
308 if let Some(same_code_err) = codes.insert(code, err) {
309 panic!("{:?} has same code {:?} as {:?}", same_code_err, code, err);
310 }
311 }
312 }
313
314 #[test]
315 fn encode_decode() {
316 for err in enum_iterator::all::<ExtError>() {
317 let code = err.to_u32();
318 let decoded = ExtError::from_u32(code)
319 .unwrap_or_else(|| unreachable!("failed to decode error code: {}", code));
320 assert_eq!(err, decoded);
321 }
322 }
323
324 #[test]
325 fn error_code_no_specific_value() {
326 for err in enum_iterator::all::<ExtError>() {
327 let code = err.to_u32();
328 assert_ne!(code, 0); }
330 }
331
332 #[test]
342 fn error_codes_forbidden() {
343 let codes = [
344 0xffff, 600, ];
347
348 for code in codes {
350 let err = ExtError::from_u32(code);
351 assert_eq!(err, Some(ExtError::Unsupported));
352 }
353
354 for err in enum_iterator::all::<ExtError>() {
356 let code = err.to_u32();
357 assert!(!codes.contains(&code));
358 }
359 }
360}