spacejam_service/service/
result.rs

1//! Work result types
2
3use crate::{Gas, OpaqueHash, ServiceId, Vec, service::RefineLoad};
4
5use serde::{Deserialize, Serialize};
6
7#[cfg(feature = "json")]
8use {crate::String, crate::service::RefineLoadJson, spacejson::Json};
9
10#[cfg(feature = "json")]
11pub use json::WorkExecResultJson;
12
13/// (11.6) Represents the result of a work item.
14#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
15#[cfg_attr(feature = "json", derive(Json))]
16pub struct WorkDigest {
17    /// (s) The service id
18    pub service_id: ServiceId,
19
20    /// (c) The code hash
21    #[cfg_attr(feature = "json", json(hex))]
22    pub code_hash: OpaqueHash,
23
24    /// (y) The payload hash
25    #[cfg_attr(feature = "json", json(hex))]
26    pub payload_hash: OpaqueHash,
27
28    /// (g) The accumulate gas
29    pub accumulate_gas: Gas,
30
31    /// (l) The result of the work item
32    #[cfg_attr(feature = "json", json(nested))]
33    pub result: WorkExecResult,
34
35    /// (u, i, x, z, e) The refine load
36    #[cfg_attr(feature = "json", json(nested))]
37    pub refine_load: RefineLoad,
38}
39
40/// Represents the result of a work execution. (11.7)
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
42#[repr(C)]
43pub enum WorkExecResult {
44    /// The ok result
45    Ok(Vec<u8>),
46    /// ∞ denoting an out-of-gas error
47    OutOfGas,
48    /// ☇ denoting an unexpected program termination
49    Panic,
50    /// ⊥ the number of exports made was invalidly reported
51    InvalidExports,
52    /// the size of the digest (refinement output) would
53    /// cross the acceptable limit
54    InvalidDigest,
55    /// (BAD) the third indicates that the service’s code
56    /// was not available for lookup in state at the posterior state
57    /// of the lookup-anchor block
58    BadCode,
59    /// (BIG) the code was available but was beyond the maximum size
60    CodeOversize,
61}
62
63/// The result of is-authorized invocation (ΨI)
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
65pub struct Executed {
66    /// The output
67    pub data: Vec<u8>,
68
69    /// The reason
70    pub exec: WorkExecResult,
71
72    /// The gas used
73    pub gas: Gas,
74}
75
76impl Executed {
77    /// Create a new executed result
78    pub fn new(data: Vec<u8>, exec: WorkExecResult, gas: Gas) -> Self {
79        Self { data, exec, gas }
80    }
81
82    /// Check if the execution is successful
83    pub fn is_ok(&self) -> bool {
84        matches!(self.exec, WorkExecResult::Ok(_))
85    }
86}
87
88/// The result of refine invocation (ΨR)
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
90pub struct Refined {
91    /// The executed result
92    pub executed: Executed,
93
94    /// The imports
95    pub segments: Vec<Segment>,
96}
97
98impl Refined {
99    /// Create a new refined result
100    pub fn new(executed: Executed, segments: Vec<[u8; crate::SEGMENT_SIZE]>) -> Self {
101        Self {
102            executed,
103            segments: segments.iter().map(|s| Segment(*s)).collect(),
104        }
105    }
106}
107
108/// A segment of the import segments
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
110pub struct Segment(#[serde(with = "codec::bytes")] pub [u8; crate::SEGMENT_SIZE]);
111
112#[cfg(feature = "json")]
113mod json {
114    use super::WorkExecResult;
115    use crate::String;
116    use serde::{Deserialize, Serialize};
117    use spacejson::Json;
118
119    /// TODO: support enum in Json macro
120    #[derive(Debug, Clone, Serialize, Deserialize, Default)]
121    pub struct WorkExecResultJson {
122        /// The ok result
123        pub ok: Option<String>,
124        /// The out of gas result
125        #[serde(default = "default_some_unit")]
126        pub out_of_gas: Option<()>,
127        /// The panic result
128        #[serde(default = "default_some_unit")]
129        pub panic: Option<()>,
130        /// The invalid exports result
131        #[serde(default = "default_some_unit")]
132        pub invalid_exports: Option<()>,
133        /// The invalid digest result
134        #[serde(default = "default_some_unit")]
135        pub invalid_digest: Option<()>,
136        /// The bad code result
137        #[serde(default = "default_some_unit")]
138        pub bad_code: Option<()>,
139        /// The code oversize result
140        #[serde(default = "default_some_unit")]
141        pub code_oversize: Option<()>,
142    }
143
144    fn default_some_unit() -> Option<()> {
145        Some(())
146    }
147
148    impl Json<WorkExecResultJson> for WorkExecResult {
149        fn to_json(self) -> WorkExecResultJson {
150            match self {
151                WorkExecResult::Ok(v) => WorkExecResultJson {
152                    ok: Some(hex::encode(v)),
153                    ..Default::default()
154                },
155                WorkExecResult::OutOfGas => WorkExecResultJson {
156                    out_of_gas: Some(()),
157                    ..Default::default()
158                },
159                WorkExecResult::Panic => WorkExecResultJson {
160                    panic: Some(()),
161                    ..Default::default()
162                },
163                WorkExecResult::InvalidExports => WorkExecResultJson {
164                    invalid_exports: Some(()),
165                    ..Default::default()
166                },
167                WorkExecResult::InvalidDigest => WorkExecResultJson {
168                    invalid_digest: Some(()),
169                    ..Default::default()
170                },
171                WorkExecResult::CodeOversize => WorkExecResultJson {
172                    code_oversize: Some(()),
173                    ..Default::default()
174                },
175                WorkExecResult::BadCode => WorkExecResultJson {
176                    bad_code: Some(()),
177                    ..Default::default()
178                },
179            }
180        }
181
182        fn from_json(json: WorkExecResultJson) -> anyhow::Result<Self> {
183            if let Some(ok) = json.ok {
184                Ok(WorkExecResult::Ok(
185                    hex::decode(ok.trim_start_matches("0x"))
186                        .map_err(|e| anyhow::anyhow!("{e:?}"))?,
187                ))
188            } else if json.out_of_gas.is_none() {
189                Ok(WorkExecResult::OutOfGas)
190            } else if json.panic.is_none() {
191                Ok(WorkExecResult::Panic)
192            } else if json.bad_code.is_none() {
193                Ok(WorkExecResult::BadCode)
194            } else {
195                Ok(WorkExecResult::CodeOversize)
196            }
197        }
198    }
199}