1use enum_iterator::Sequence;
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9#[cfg(feature = "codec")]
10use {
11 parity_scale_codec::{Decode, Encode},
12 scale_decode::DecodeAsType,
13 scale_encode::EncodeAsType,
14 scale_info::TypeInfo,
15};
16
17#[repr(u8)]
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
20#[cfg_attr(
21 feature = "codec",
22 derive(Encode, EncodeAsType, Decode, DecodeAsType, TypeInfo)
23)]
24#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
25pub enum ReplyCode {
26 #[error("Success reply sent due to {0}")]
28 Success(#[from] SuccessReplyReason) = 0,
29
30 #[error("Error reply sent due to {0}")]
32 Error(#[from] ErrorReplyReason) = 1,
33
34 #[error("<unsupported reply code>")]
37 Unsupported = 255,
38}
39
40impl ReplyCode {
41 fn discriminant(&self) -> u8 {
42 unsafe { *<*const _>::from(self).cast::<u8>() }
46 }
47
48 pub fn to_bytes(self) -> [u8; 4] {
50 let mut bytes = [self.discriminant(), 0, 0, 0];
51
52 match self {
53 Self::Success(reason) => bytes[1..].copy_from_slice(&reason.to_bytes()),
54 Self::Error(reason) => bytes[1..].copy_from_slice(&reason.to_bytes()),
55 Self::Unsupported => {}
56 }
57
58 bytes
59 }
60
61 pub fn from_bytes(bytes: [u8; 4]) -> Self {
63 match bytes[0] {
64 b if Self::Success(SuccessReplyReason::Unsupported).discriminant() == b => {
65 let reason_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
66 Self::Success(SuccessReplyReason::from_bytes(reason_bytes))
67 }
68 b if Self::Error(ErrorReplyReason::Unsupported).discriminant() == b => {
69 let reason_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
70 Self::Error(ErrorReplyReason::from_bytes(reason_bytes))
71 }
72 _ => Self::Unsupported,
73 }
74 }
75
76 pub fn error(reason: impl Into<ErrorReplyReason>) -> Self {
78 Self::Error(reason.into())
79 }
80
81 pub fn is_success(&self) -> bool {
83 matches!(self, Self::Success(_))
84 }
85
86 pub fn is_error(&self) -> bool {
88 matches!(self, Self::Error(_))
89 }
90
91 pub fn is_unsupported(&self) -> bool {
93 matches!(self, Self::Unsupported)
94 }
95}
96
97#[repr(u8)]
99#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
100#[cfg_attr(
101 feature = "codec",
102 derive(Encode, EncodeAsType, Decode, DecodeAsType, TypeInfo)
103)]
104#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
105pub enum SuccessReplyReason {
106 #[error("automatic sending")]
108 Auto = 0,
109
110 #[error("manual sending")]
112 Manual = 1,
113
114 #[error("<unsupported reason>")]
117 Unsupported = 255,
118}
119
120impl SuccessReplyReason {
121 fn to_bytes(self) -> [u8; 3] {
122 [self as u8, 0, 0]
123 }
124
125 fn from_bytes(bytes: [u8; 3]) -> Self {
126 match bytes[0] {
127 b if Self::Auto as u8 == b => Self::Auto,
128 b if Self::Manual as u8 == b => Self::Manual,
129 _ => Self::Unsupported,
130 }
131 }
132}
133
134#[repr(u8)]
138#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
139#[cfg_attr(
140 feature = "codec",
141 derive(Encode, EncodeAsType, Decode, DecodeAsType, TypeInfo)
142)]
143#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
144pub enum ErrorReplyReason {
145 #[error("execution error ({0})")]
147 Execution(#[from] SimpleExecutionError) = 0,
148
149 #[error("destination actor is unavailable ({0})")]
151 UnavailableActor(#[from] SimpleUnavailableActorError) = 2,
152
153 #[error("removal from waitlist")]
155 RemovedFromWaitlist = 3,
156
157 #[error("<unsupported reason>")]
160 Unsupported = 255,
161}
162
163impl ErrorReplyReason {
164 pub fn is_exited(&self) -> bool {
166 matches!(
167 self,
168 Self::UnavailableActor(SimpleUnavailableActorError::ProgramExited)
169 )
170 }
171
172 pub fn is_userspace_panic(&self) -> bool {
174 matches!(self, Self::Execution(SimpleExecutionError::UserspacePanic))
175 }
176
177 fn discriminant(&self) -> u8 {
178 unsafe { *<*const _>::from(self).cast::<u8>() }
182 }
183
184 fn to_bytes(self) -> [u8; 3] {
185 let mut bytes = [self.discriminant(), 0, 0];
186
187 match self {
188 Self::Execution(error) => bytes[1..].copy_from_slice(&error.to_bytes()),
189 Self::UnavailableActor(error) => bytes[1..].copy_from_slice(&error.to_bytes()),
190 Self::RemovedFromWaitlist | Self::Unsupported => {}
191 }
192
193 bytes
194 }
195
196 fn from_bytes(bytes: [u8; 3]) -> Self {
197 match bytes[0] {
198 1 |
199 4 => Self::Unsupported,
200 b if Self::Execution(SimpleExecutionError::Unsupported).discriminant() == b => {
201 let err_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
202 Self::Execution(SimpleExecutionError::from_bytes(err_bytes))
203 }
204 b if Self::UnavailableActor(SimpleUnavailableActorError::Unsupported).discriminant() == b => {
205 let err_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
206 Self::UnavailableActor(SimpleUnavailableActorError::from_bytes(err_bytes))
207 }
208 b if Self::RemovedFromWaitlist.discriminant() == b => Self::RemovedFromWaitlist,
209 _ => Self::Unsupported,
210 }
211 }
212}
213
214#[repr(u8)]
216#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
217#[cfg_attr(
218 feature = "codec",
219 derive(Encode, EncodeAsType, Decode, DecodeAsType, TypeInfo)
220)]
221#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
222pub enum SimpleExecutionError {
223 #[error("Message ran out of gas")]
225 RanOutOfGas = 0,
226
227 #[error("Program reached memory limit")]
229 MemoryOverflow = 1,
230
231 #[error("Message ran into uncatchable error")]
233 BackendError = 2,
234
235 #[error("Message panicked")]
239 UserspacePanic = 3,
240
241 #[error("Program called WASM `unreachable` instruction")]
243 UnreachableInstruction = 4,
244
245 #[error("Program reached stack limit")]
247 StackLimitExceeded = 5,
248
249 #[error("<unsupported error>")]
252 Unsupported = 255,
253}
254
255impl SimpleExecutionError {
256 fn to_bytes(self) -> [u8; 2] {
257 [self as u8, 0]
258 }
259
260 fn from_bytes(bytes: [u8; 2]) -> Self {
261 match bytes[0] {
262 b if Self::RanOutOfGas as u8 == b => Self::RanOutOfGas,
263 b if Self::MemoryOverflow as u8 == b => Self::MemoryOverflow,
264 b if Self::BackendError as u8 == b => Self::BackendError,
265 b if Self::UserspacePanic as u8 == b => Self::UserspacePanic,
266 b if Self::UnreachableInstruction as u8 == b => Self::UnreachableInstruction,
267 b if Self::StackLimitExceeded as u8 == b => Self::StackLimitExceeded,
268 _ => Self::Unsupported,
269 }
270 }
271}
272
273#[repr(u8)]
275#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
276#[cfg_attr(
277 feature = "codec",
278 derive(Encode, EncodeAsType, Decode, DecodeAsType, TypeInfo)
279)]
280#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
281pub enum SimpleUnavailableActorError {
282 #[error("Program exited")]
286 ProgramExited = 0,
287
288 #[error("Program was terminated due failed initialization")]
290 InitializationFailure = 1,
291
292 #[error("Program is not initialized yet")]
294 Uninitialized = 2,
295
296 #[error("Program was not created")]
298 ProgramNotCreated = 3,
299
300 #[error("Program re-instrumentation failed")]
302 ReinstrumentationFailure = 4,
303
304 #[error("<unsupported error>")]
307 Unsupported = 255,
308}
309
310impl SimpleUnavailableActorError {
311 fn to_bytes(self) -> [u8; 2] {
312 [self as u8, 0]
313 }
314
315 fn from_bytes(bytes: [u8; 2]) -> Self {
316 match bytes[0] {
317 b if Self::ProgramExited as u8 == b => Self::ProgramExited,
318 b if Self::InitializationFailure as u8 == b => Self::InitializationFailure,
319 b if Self::Uninitialized as u8 == b => Self::Uninitialized,
320 b if Self::ProgramNotCreated as u8 == b => Self::ProgramNotCreated,
321 b if Self::ReinstrumentationFailure as u8 == b => Self::ReinstrumentationFailure,
322 _ => Self::Unsupported,
323 }
324 }
325}
326
327#[repr(u8)]
332#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Sequence, thiserror::Error)]
333#[cfg_attr(
334 feature = "codec",
335 derive(Encode, EncodeAsType, Decode, DecodeAsType, TypeInfo)
336)]
337#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
338pub enum SignalCode {
339 #[error("Signal message sent due to execution error ({0})")]
341 Execution(#[from] SimpleExecutionError),
342
343 #[error("Signal message sent due to removal from waitlist")]
345 RemovedFromWaitlist,
346}
347
348impl SignalCode {
349 pub const fn to_u32(self) -> u32 {
351 match self {
352 Self::Execution(SimpleExecutionError::UserspacePanic) => 100,
353 Self::Execution(SimpleExecutionError::RanOutOfGas) => 101,
354 Self::Execution(SimpleExecutionError::BackendError) => 102,
355 Self::Execution(SimpleExecutionError::MemoryOverflow) => 103,
356 Self::Execution(SimpleExecutionError::UnreachableInstruction) => 104,
357 Self::Execution(SimpleExecutionError::StackLimitExceeded) => 105,
358 Self::RemovedFromWaitlist => 200,
359 Self::Execution(SimpleExecutionError::Unsupported) => u32::MAX,
361 }
362 }
363
364 pub const fn from_u32(num: u32) -> Option<Self> {
366 let res = match num {
367 v if Self::Execution(SimpleExecutionError::UserspacePanic).to_u32() == v => {
368 Self::Execution(SimpleExecutionError::UserspacePanic)
369 }
370 v if Self::Execution(SimpleExecutionError::RanOutOfGas).to_u32() == v => {
371 Self::Execution(SimpleExecutionError::RanOutOfGas)
372 }
373 v if Self::Execution(SimpleExecutionError::BackendError).to_u32() == v => {
374 Self::Execution(SimpleExecutionError::BackendError)
375 }
376 v if Self::Execution(SimpleExecutionError::MemoryOverflow).to_u32() == v => {
377 Self::Execution(SimpleExecutionError::MemoryOverflow)
378 }
379 v if Self::Execution(SimpleExecutionError::UnreachableInstruction).to_u32() == v => {
380 Self::Execution(SimpleExecutionError::UnreachableInstruction)
381 }
382 v if Self::Execution(SimpleExecutionError::StackLimitExceeded).to_u32() == v => {
383 Self::Execution(SimpleExecutionError::StackLimitExceeded)
384 }
385 v if Self::Execution(SimpleExecutionError::Unsupported).to_u32() == v => {
386 Self::Execution(SimpleExecutionError::Unsupported)
387 }
388 v if Self::RemovedFromWaitlist.to_u32() == v => Self::RemovedFromWaitlist,
389 _ => return None,
390 };
391
392 Some(res)
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399
400 #[test]
401 fn test_forbidden_codes() {
402 let codes = [
403 1, 4, ];
406
407 for code in codes {
409 let err = ErrorReplyReason::from_bytes([code, 0, 0]);
410 assert_eq!(err, ErrorReplyReason::Unsupported);
411 }
412
413 for code in enum_iterator::all::<ErrorReplyReason>() {
415 let bytes = code.to_bytes();
416 assert!(!codes.contains(&bytes[0]));
417 }
418 }
419
420 #[test]
421 fn test_reply_code_encode_decode() {
422 for code in enum_iterator::all::<ReplyCode>() {
423 let bytes = code.to_bytes();
424 assert_eq!(code, ReplyCode::from_bytes(bytes));
425 }
426 }
427
428 #[test]
429 fn test_signal_code_encode_decode() {
430 for signal in enum_iterator::all::<SignalCode>() {
431 let code = signal.to_u32();
432 assert_eq!(signal, SignalCode::from_u32(code).unwrap());
433 }
434 }
435}