1#![no_std]
22#![warn(missing_docs)]
23#![doc(html_logo_url = "https://docs.gear.rs/logo.svg")]
24#![doc(html_favicon_url = "https://gear-tech.io/favicons/favicon.ico")]
25
26extern crate alloc;
27
28mod simple;
29
30use core::fmt::Debug;
31use enum_iterator::Sequence;
32#[cfg(feature = "codec")]
33use {
34 alloc::vec::Vec,
35 scale_info::scale::{Decode, Encode, Error, Input},
36};
37
38pub use simple::*;
39
40#[derive(
42 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Sequence, derive_more::Display,
43)]
44#[non_exhaustive]
45#[repr(u32)]
46pub enum ExecutionError {
47 #[display(fmt = "Not enough gas for operation")]
49 NotEnoughGas = 100,
50
51 #[display(fmt = "Not enough value for operation")]
53 NotEnoughValue = 101,
54
55 #[display(fmt = "Length is overflowed to read payload")]
57 TooBigReadLen = 103,
58
59 #[display(fmt = "Cannot take data in payload range from message with size")]
61 ReadWrongRange = 104,
62
63 #[display(fmt = "Not running in reply context")]
65 NoReplyContext = 105,
66
67 #[display(fmt = "Not running in signal context")]
69 NoSignalContext = 106,
70
71 #[display(fmt = "No status code in reply/signal context")]
73 NoStatusCodeContext = 107,
74
75 #[display(fmt = "Reply sending is only allowed in `init` and `handle` functions")]
77 IncorrectEntryForReply = 108,
78}
79
80#[derive(
82 Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Sequence, derive_more::Display,
83)]
84#[non_exhaustive]
85#[repr(u32)]
86pub enum MemoryError {
87 #[display(fmt = "Trying to allocate more memory in block-chain runtime than allowed")]
89 RuntimeAllocOutOfBounds = 200,
90 #[display(fmt = "Trying to access memory outside wasm program memory")]
92 AccessOutOfBounds = 201,
93}
94
95#[derive(
97 Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Sequence, derive_more::Display,
98)]
99#[non_exhaustive]
100#[repr(u32)]
101pub enum MessageError {
102 #[display(fmt = "Max message size exceed")]
104 MaxMessageSizeExceed = 300,
105
106 #[display(fmt = "Message limit exceeded")]
110 OutgoingMessagesAmountLimitExceeded = 301,
111
112 #[display(fmt = "Duplicate reply message")]
114 DuplicateReply = 302,
115
116 #[display(fmt = "Duplicate waking message")]
119 DuplicateWaking = 303,
120
121 #[display(fmt = "An attempt to commit or push a payload into an already formed message")]
123 LateAccess = 304,
124
125 #[display(fmt = "Message with given handle is not found")]
127 OutOfBounds = 305,
128
129 #[display(fmt = "Duplicated program initialization message")]
132 DuplicateInit = 306,
133
134 #[display(fmt = "In case of non-zero message value must be greater than existential deposit")]
137 InsufficientValue = 307,
138
139 #[display(
144 fmt = "In case of non-zero message gas limit must be greater than mailbox threshold"
145 )]
146 InsufficientGasLimit = 308,
147
148 #[display(fmt = "Reply deposit already exists for given message")]
151 DuplicateReplyDeposit = 309,
152
153 #[display(
156 fmt = "Reply deposit could be only created for init or handle message sent within the execution"
157 )]
158 IncorrectMessageForReplyDeposit = 310,
159
160 #[display(fmt = "Outgoing messages bytes limit exceeded")]
163 OutgoingMessagesBytesLimitExceeded = 311,
164
165 #[display(fmt = "Offset value for the input payload is out of it's size bounds")]
168 OutOfBoundsInputSliceOffset = 312,
169
170 #[display(fmt = "Too big length value is set to form a slice (range) of the input buffer")]
173 OutOfBoundsInputSliceLength = 313,
174
175 #[display(fmt = "Not enough gas to hold dispatch message")]
178 InsufficientGasForDelayedSending = 399,
179}
180
181#[derive(
183 Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Sequence, derive_more::Display,
184)]
185#[non_exhaustive]
186#[repr(u32)]
187pub enum ReservationError {
189 #[display(fmt = "Invalid reservation ID")]
191 InvalidReservationId = 500,
192 #[display(fmt = "Reservation limit has reached")]
194 ReservationsLimitReached = 501,
195 #[display(fmt = "Reservation duration cannot be zero")]
197 ZeroReservationDuration = 502,
198 #[display(fmt = "Reservation amount cannot be zero")]
200 ZeroReservationAmount = 503,
201 #[display(fmt = "Reservation amount cannot be below mailbox threshold")]
203 ReservationBelowMailboxThreshold = 504,
204}
205
206#[derive(
208 Debug,
209 Clone,
210 Copy,
211 Eq,
212 PartialEq,
213 Hash,
214 PartialOrd,
215 Ord,
216 Sequence,
217 derive_more::Display,
218 derive_more::From,
219)]
220#[non_exhaustive]
221pub enum ExtError {
222 #[display(fmt = "Execution error: {_0}")]
224 Execution(ExecutionError),
225
226 #[display(fmt = "Memory error: {_0}")]
228 Memory(MemoryError),
229
230 #[display(fmt = "Message error: {_0}")]
232 Message(MessageError),
233
234 #[display(fmt = "Reservation error: {_0}")]
236 Reservation(ReservationError),
237
238 Unsupported,
240}
241
242impl ExtError {
243 pub fn to_u32(self) -> u32 {
245 match self {
246 ExtError::Execution(err) => err as u32,
247 ExtError::Memory(err) => err as u32,
248 ExtError::Message(err) => err as u32,
249 ExtError::Reservation(err) => err as u32,
250 ExtError::Unsupported => u32::MAX,
251 }
252 }
253
254 pub fn from_u32(code: u32) -> Option<Self> {
256 match code {
257 100 => Some(ExecutionError::NotEnoughGas.into()),
258 101 => Some(ExecutionError::NotEnoughValue.into()),
259 103 => Some(ExecutionError::TooBigReadLen.into()),
260 104 => Some(ExecutionError::ReadWrongRange.into()),
261 105 => Some(ExecutionError::NoReplyContext.into()),
262 106 => Some(ExecutionError::NoSignalContext.into()),
263 107 => Some(ExecutionError::NoStatusCodeContext.into()),
264 108 => Some(ExecutionError::IncorrectEntryForReply.into()),
265 200 => Some(MemoryError::RuntimeAllocOutOfBounds.into()),
267 201 => Some(MemoryError::AccessOutOfBounds.into()),
268 300 => Some(MessageError::MaxMessageSizeExceed.into()),
270 301 => Some(MessageError::OutgoingMessagesAmountLimitExceeded.into()),
271 302 => Some(MessageError::DuplicateReply.into()),
272 303 => Some(MessageError::DuplicateWaking.into()),
273 304 => Some(MessageError::LateAccess.into()),
274 305 => Some(MessageError::OutOfBounds.into()),
275 306 => Some(MessageError::DuplicateInit.into()),
276 307 => Some(MessageError::InsufficientValue.into()),
277 308 => Some(MessageError::InsufficientGasLimit.into()),
278 309 => Some(MessageError::DuplicateReplyDeposit.into()),
279 310 => Some(MessageError::IncorrectMessageForReplyDeposit.into()),
280 311 => Some(MessageError::OutgoingMessagesBytesLimitExceeded.into()),
281 312 => Some(MessageError::OutOfBoundsInputSliceOffset.into()),
282 313 => Some(MessageError::OutOfBoundsInputSliceLength.into()),
283 399 => Some(MessageError::InsufficientGasForDelayedSending.into()),
284 500 => Some(ReservationError::InvalidReservationId.into()),
286 501 => Some(ReservationError::ReservationsLimitReached.into()),
287 502 => Some(ReservationError::ZeroReservationDuration.into()),
288 503 => Some(ReservationError::ZeroReservationAmount.into()),
289 504 => Some(ReservationError::ReservationBelowMailboxThreshold.into()),
290 0xffff |
292 600 |
293 u32::MAX => Some(ExtError::Unsupported),
294 _ => None,
295 }
296 }
297}
298
299#[cfg(feature = "codec")]
300impl Encode for ExtError {
301 fn encode(&self) -> Vec<u8> {
302 ExtError::to_u32(*self).to_le_bytes().to_vec()
303 }
304}
305
306#[cfg(feature = "codec")]
307impl Decode for ExtError {
308 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
309 let mut code = [0; 4];
310 input.read(&mut code)?;
311 let err =
312 ExtError::from_u32(u32::from_le_bytes(code)).ok_or("Failed to decode error code")?;
313 Ok(err)
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use alloc::collections::BTreeMap;
321
322 #[test]
323 fn error_codes_are_unique() {
324 let mut codes = BTreeMap::new();
325
326 for err in enum_iterator::all::<ExtError>() {
327 let code = err.to_u32();
328 if let Some(same_code_err) = codes.insert(code, err) {
329 panic!("{:?} has same code {:?} as {:?}", same_code_err, code, err);
330 }
331 }
332 }
333
334 #[test]
335 fn encode_decode() {
336 for err in enum_iterator::all::<ExtError>() {
337 let code = err.to_u32();
338 let decoded = ExtError::from_u32(code)
339 .unwrap_or_else(|| unreachable!("failed to decode error code: {}", code));
340 assert_eq!(err, decoded);
341 }
342 }
343
344 #[test]
345 fn error_code_no_specific_value() {
346 for err in enum_iterator::all::<ExtError>() {
347 let code = err.to_u32();
348 assert_ne!(code, 0); }
350 }
351
352 #[test]
362 fn error_codes_forbidden() {
363 let codes = [
364 0xffff, 600, ];
367
368 for code in codes {
370 let err = ExtError::from_u32(code);
371 assert_eq!(err, Some(ExtError::Unsupported));
372 }
373
374 for err in enum_iterator::all::<ExtError>() {
376 let code = err.to_u32();
377 assert!(!codes.contains(&code));
378 }
379 }
380}