1use enum_iterator::Sequence;
20#[cfg(feature = "codec")]
21use scale_info::{
22 scale::{self, Decode, Encode},
23 TypeInfo,
24};
25
26#[repr(u8)]
27#[derive(
28 Clone,
29 Copy,
30 Debug,
31 PartialEq,
32 Eq,
33 PartialOrd,
34 Ord,
35 Hash,
36 Default,
37 derive_more::Display,
38 derive_more::From,
39 Sequence,
40)]
41#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo), codec(crate = scale), allow(clippy::unnecessary_cast))]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43pub enum ReplyCode {
45 #[display("Success reply sent due to {_0}")]
47 Success(SuccessReplyReason) = 0,
48
49 #[display("Error reply sent due to {_0}")]
51 Error(ErrorReplyReason) = 1,
52
53 #[default]
56 #[display("<unsupported reply code>")]
57 Unsupported = 255,
58}
59
60impl ReplyCode {
61 fn discriminant(&self) -> u8 {
62 unsafe { *<*const _>::from(self).cast::<u8>() }
66 }
67
68 pub fn to_bytes(self) -> [u8; 4] {
70 let mut bytes = [self.discriminant(), 0, 0, 0];
71
72 match self {
73 Self::Success(reason) => bytes[1..].copy_from_slice(&reason.to_bytes()),
74 Self::Error(reason) => bytes[1..].copy_from_slice(&reason.to_bytes()),
75 Self::Unsupported => {}
76 }
77
78 bytes
79 }
80
81 pub fn from_bytes(bytes: [u8; 4]) -> Self {
83 match bytes[0] {
84 b if Self::Success(Default::default()).discriminant() == b => {
85 let reason_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
86 Self::Success(SuccessReplyReason::from_bytes(reason_bytes))
87 }
88 b if Self::Error(Default::default()).discriminant() == b => {
89 let reason_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
90 Self::Error(ErrorReplyReason::from_bytes(reason_bytes))
91 }
92 _ => Self::Unsupported,
93 }
94 }
95
96 pub fn error(reason: impl Into<ErrorReplyReason>) -> Self {
98 Self::Error(reason.into())
99 }
100
101 pub fn is_success(&self) -> bool {
103 matches!(self, Self::Success(_))
104 }
105
106 pub fn is_error(&self) -> bool {
108 matches!(self, Self::Error(_))
109 }
110
111 pub fn is_unsupported(&self) -> bool {
113 matches!(self, Self::Unsupported)
114 }
115}
116
117#[repr(u8)]
118#[derive(
119 Clone,
120 Copy,
121 Debug,
122 PartialEq,
123 Eq,
124 PartialOrd,
125 Ord,
126 Hash,
127 Default,
128 derive_more::Display,
129 Sequence,
130)]
131#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo), codec(crate = scale), allow(clippy::unnecessary_cast))]
132#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
133pub enum SuccessReplyReason {
135 #[display("automatic sending")]
137 Auto = 0,
138
139 #[display("manual sending")]
141 Manual = 1,
142
143 #[default]
146 #[display("<unsupported reason>")]
147 Unsupported = 255,
148}
149
150impl SuccessReplyReason {
151 fn to_bytes(self) -> [u8; 3] {
152 [self as u8, 0, 0]
153 }
154
155 fn from_bytes(bytes: [u8; 3]) -> Self {
156 match bytes[0] {
157 b if Self::Auto as u8 == b => Self::Auto,
158 b if Self::Manual as u8 == b => Self::Manual,
159 _ => Self::Unsupported,
160 }
161 }
162}
163
164#[repr(u8)]
165#[derive(
166 Clone,
167 Copy,
168 Debug,
169 PartialEq,
170 Eq,
171 PartialOrd,
172 Ord,
173 Hash,
174 Default,
175 derive_more::Display,
176 derive_more::From,
177 Sequence,
178)]
179#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo), codec(crate = scale), allow(clippy::unnecessary_cast))]
180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181pub enum ErrorReplyReason {
185 #[display("execution error ({_0})")]
187 Execution(SimpleExecutionError) = 0,
188
189 #[display("destination actor is unavailable ({_0})")]
191 UnavailableActor(SimpleUnavailableActorError) = 2,
192
193 #[display("removal from waitlist")]
195 RemovedFromWaitlist = 3,
196
197 #[default]
200 #[display("<unsupported reason>")]
201 Unsupported = 255,
202}
203
204impl ErrorReplyReason {
205 pub fn is_exited(&self) -> bool {
207 matches!(
208 self,
209 Self::UnavailableActor(SimpleUnavailableActorError::ProgramExited)
210 )
211 }
212
213 pub fn is_userspace_panic(&self) -> bool {
215 matches!(self, Self::Execution(SimpleExecutionError::UserspacePanic))
216 }
217
218 fn discriminant(&self) -> u8 {
219 unsafe { *<*const _>::from(self).cast::<u8>() }
223 }
224
225 fn to_bytes(self) -> [u8; 3] {
226 let mut bytes = [self.discriminant(), 0, 0];
227
228 match self {
229 Self::Execution(error) => bytes[1..].copy_from_slice(&error.to_bytes()),
230 Self::UnavailableActor(error) => bytes[1..].copy_from_slice(&error.to_bytes()),
231 Self::RemovedFromWaitlist | Self::Unsupported => {}
232 }
233
234 bytes
235 }
236
237 fn from_bytes(bytes: [u8; 3]) -> Self {
238 match bytes[0] {
239 1 |
240 4 => Self::Unsupported,
241 b if Self::Execution(Default::default()).discriminant() == b => {
242 let err_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
243 Self::Execution(SimpleExecutionError::from_bytes(err_bytes))
244 }
245 b if Self::UnavailableActor(Default::default()).discriminant() == b => {
246 let err_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
247 Self::UnavailableActor(SimpleUnavailableActorError::from_bytes(err_bytes))
248 }
249 b if Self::RemovedFromWaitlist.discriminant() == b => Self::RemovedFromWaitlist,
250 _ => Self::Unsupported,
251 }
252 }
253}
254
255#[repr(u8)]
256#[derive(
257 Clone,
258 Copy,
259 Debug,
260 PartialEq,
261 Eq,
262 PartialOrd,
263 Ord,
264 Hash,
265 Default,
266 derive_more::Display,
267 Sequence,
268)]
269#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo), codec(crate = scale), allow(clippy::unnecessary_cast))]
270#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
271pub enum SimpleExecutionError {
273 #[display("Message ran out of gas")]
275 RanOutOfGas = 0,
276
277 #[display("Program reached memory limit")]
279 MemoryOverflow = 1,
280
281 #[display("Message ran into uncatchable error")]
283 BackendError = 2,
284
285 #[display("Message panicked")]
289 UserspacePanic = 3,
290
291 #[display("Program called WASM `unreachable` instruction")]
293 UnreachableInstruction = 4,
294
295 #[display("Program reached stack limit")]
297 StackLimitExceeded = 5,
298
299 #[default]
302 #[display("<unsupported error>")]
303 Unsupported = 255,
304}
305
306impl SimpleExecutionError {
307 fn to_bytes(self) -> [u8; 2] {
308 [self as u8, 0]
309 }
310
311 fn from_bytes(bytes: [u8; 2]) -> Self {
312 match bytes[0] {
313 b if Self::RanOutOfGas as u8 == b => Self::RanOutOfGas,
314 b if Self::MemoryOverflow as u8 == b => Self::MemoryOverflow,
315 b if Self::BackendError as u8 == b => Self::BackendError,
316 b if Self::UserspacePanic as u8 == b => Self::UserspacePanic,
317 b if Self::UnreachableInstruction as u8 == b => Self::UnreachableInstruction,
318 b if Self::StackLimitExceeded as u8 == b => Self::StackLimitExceeded,
319 _ => Self::Unsupported,
320 }
321 }
322}
323
324#[repr(u8)]
325#[derive(
326 Clone,
327 Copy,
328 Debug,
329 PartialEq,
330 Eq,
331 PartialOrd,
332 Ord,
333 Hash,
334 Default,
335 derive_more::Display,
336 Sequence,
337)]
338#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo), codec(crate = scale), allow(clippy::unnecessary_cast))]
339#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
340pub enum SimpleUnavailableActorError {
342 #[display("Program exited")]
346 ProgramExited = 0,
347
348 #[display("Program was terminated due failed initialization")]
350 InitializationFailure = 1,
351
352 #[display("Program is not initialized yet")]
354 Uninitialized = 2,
355
356 #[display("Program was not created")]
358 ProgramNotCreated = 3,
359
360 #[display("Program re-instrumentation failed")]
362 ReinstrumentationFailure = 4,
363
364 #[default]
367 #[display("<unsupported error>")]
368 Unsupported = 255,
369}
370
371impl SimpleUnavailableActorError {
372 fn to_bytes(self) -> [u8; 2] {
373 [self as u8, 0]
374 }
375
376 fn from_bytes(bytes: [u8; 2]) -> Self {
377 match bytes[0] {
378 b if Self::ProgramExited as u8 == b => Self::ProgramExited,
379 b if Self::InitializationFailure as u8 == b => Self::InitializationFailure,
380 b if Self::Uninitialized as u8 == b => Self::Uninitialized,
381 b if Self::ProgramNotCreated as u8 == b => Self::ProgramNotCreated,
382 b if Self::ReinstrumentationFailure as u8 == b => Self::ReinstrumentationFailure,
383 _ => Self::Unsupported,
384 }
385 }
386}
387
388#[derive(
389 Clone,
390 Copy,
391 Debug,
392 PartialEq,
393 Eq,
394 PartialOrd,
395 Ord,
396 Hash,
397 Default,
398 derive_more::Display,
399 derive_more::From,
400 Sequence,
401)]
402#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo), codec(crate = scale), allow(clippy::unnecessary_cast))]
403#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
404pub enum SignalCode {
409 #[display("Signal message sent due to execution error ({_0})")]
411 Execution(SimpleExecutionError),
412
413 #[default]
415 #[display("Signal message sent due to removal from waitlist")]
416 RemovedFromWaitlist,
417}
418
419impl SignalCode {
420 pub const fn to_u32(self) -> u32 {
422 match self {
423 Self::Execution(SimpleExecutionError::UserspacePanic) => 100,
424 Self::Execution(SimpleExecutionError::RanOutOfGas) => 101,
425 Self::Execution(SimpleExecutionError::BackendError) => 102,
426 Self::Execution(SimpleExecutionError::MemoryOverflow) => 103,
427 Self::Execution(SimpleExecutionError::UnreachableInstruction) => 104,
428 Self::Execution(SimpleExecutionError::StackLimitExceeded) => 105,
429 Self::RemovedFromWaitlist => 200,
430 Self::Execution(SimpleExecutionError::Unsupported) => u32::MAX,
432 }
433 }
434
435 pub const fn from_u32(num: u32) -> Option<Self> {
437 let res = match num {
438 v if Self::Execution(SimpleExecutionError::UserspacePanic).to_u32() == v => {
439 Self::Execution(SimpleExecutionError::UserspacePanic)
440 }
441 v if Self::Execution(SimpleExecutionError::RanOutOfGas).to_u32() == v => {
442 Self::Execution(SimpleExecutionError::RanOutOfGas)
443 }
444 v if Self::Execution(SimpleExecutionError::BackendError).to_u32() == v => {
445 Self::Execution(SimpleExecutionError::BackendError)
446 }
447 v if Self::Execution(SimpleExecutionError::MemoryOverflow).to_u32() == v => {
448 Self::Execution(SimpleExecutionError::MemoryOverflow)
449 }
450 v if Self::Execution(SimpleExecutionError::UnreachableInstruction).to_u32() == v => {
451 Self::Execution(SimpleExecutionError::UnreachableInstruction)
452 }
453 v if Self::Execution(SimpleExecutionError::StackLimitExceeded).to_u32() == v => {
454 Self::Execution(SimpleExecutionError::StackLimitExceeded)
455 }
456 v if Self::Execution(SimpleExecutionError::Unsupported).to_u32() == v => {
457 Self::Execution(SimpleExecutionError::Unsupported)
458 }
459 v if Self::RemovedFromWaitlist.to_u32() == v => Self::RemovedFromWaitlist,
460 _ => return None,
461 };
462
463 Some(res)
464 }
465}
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470
471 #[test]
472 fn test_forbidden_codes() {
473 let codes = [
474 1, 4, ];
477
478 for code in codes {
480 let err = ErrorReplyReason::from_bytes([code, 0, 0]);
481 assert_eq!(err, ErrorReplyReason::Unsupported);
482 }
483
484 for code in enum_iterator::all::<ErrorReplyReason>() {
486 let bytes = code.to_bytes();
487 assert!(!codes.contains(&bytes[0]));
488 }
489 }
490
491 #[test]
492 fn test_reply_code_encode_decode() {
493 for code in enum_iterator::all::<ReplyCode>() {
494 let bytes = code.to_bytes();
495 assert_eq!(code, ReplyCode::from_bytes(bytes));
496 }
497 }
498
499 #[test]
500 fn test_signal_code_encode_decode() {
501 for signal in enum_iterator::all::<SignalCode>() {
502 let code = signal.to_u32();
503 assert_eq!(signal, SignalCode::from_u32(code).unwrap());
504 }
505 }
506}