Skip to main content

lsp_max/runtime/typestate/
machine.rs

1//! TypestateKernel implementations for each AccessAdmissionLaw phase transition.
2
3use super::*;
4impl Machine<AccessAdmissionLaw, Uninitialized, EmptyData> {
5    /// Transitions from Uninitialized to Initializing using the client parameters.
6    pub fn admit_initialize(
7        self,
8        client_caps: serde_json::Value,
9    ) -> Machine<AccessAdmissionLaw, Initializing, InitializingData> {
10        Machine::new(
11            Initializing,
12            InitializingData {
13                client_capabilities: client_caps,
14            },
15        )
16    }
17}
18
19impl Machine<AccessAdmissionLaw, Initializing, InitializingData> {
20    /// Transitions from Initializing to Initialized once server capabilities are registered.
21    pub fn admit_initialized(
22        self,
23        server_caps: serde_json::Value,
24    ) -> Machine<AccessAdmissionLaw, Initialized, InitializedData> {
25        Machine::new(
26            Initialized,
27            InitializedData {
28                client_capabilities: self.data.client_capabilities,
29                server_capabilities: server_caps,
30            },
31        )
32    }
33}
34
35impl Machine<AccessAdmissionLaw, Initialized, InitializedData> {
36    /// Transitions from Initialized to ShutDown.
37    pub fn admit_shutdown(self) -> Machine<AccessAdmissionLaw, ShutDown, EmptyData> {
38        Machine::new(
39            ShutDown,
40            EmptyData {
41                client_capabilities: Some(self.data.client_capabilities),
42                server_capabilities: Some(self.data.server_capabilities),
43            },
44        )
45    }
46}
47
48impl Machine<AccessAdmissionLaw, ShutDown, EmptyData> {
49    /// Transitions from ShutDown to Exited.
50    pub fn admit_exit(self) -> Machine<AccessAdmissionLaw, Exited, EmptyData> {
51        Machine::new(
52            Exited,
53            EmptyData {
54                client_capabilities: self.data.client_capabilities,
55                server_capabilities: self.data.server_capabilities,
56            },
57        )
58    }
59}
60
61// ==========================================
62// TypestateKernel Implementations
63// ==========================================
64
65impl TypestateKernel<AccessAdmissionLaw, Uninitialized, EmptyData>
66    for Machine<AccessAdmissionLaw, Uninitialized, EmptyData>
67{
68    type Input = serde_json::Value;
69    type OutputPhase = Initializing;
70    type OutputData = InitializingData;
71    type Receipt = lsp_max_protocol::Receipt;
72
73    fn validate(&self, _input: &Self::Input) -> Result<(), <AccessAdmissionLaw as Law>::Error> {
74        Ok(())
75    }
76
77    fn select(&self, _input: &Self::Input) -> Self::OutputPhase {
78        Initializing
79    }
80
81    fn admit(
82        self,
83        input: Self::Input,
84    ) -> Result<
85        Machine<AccessAdmissionLaw, Self::OutputPhase, Self::OutputData>,
86        <AccessAdmissionLaw as Law>::Error,
87    > {
88        self.validate(&input)?;
89        Ok(self.admit_initialize(input))
90    }
91
92    fn receipt(&self) -> Self::Receipt {
93        let receipt_id = "rcpt-uninitialized".to_string();
94        let hash = sha256(receipt_id.as_bytes());
95        lsp_max_protocol::Receipt {
96            receipt_id,
97            hash,
98            prev_receipt_hash: None,
99        }
100    }
101
102    fn exit(self) -> EmptyData {
103        self.data
104    }
105
106    fn replay(history: Vec<Self::Receipt>) -> Result<Self, ChainError> {
107        validate_and_reconstruct_chain_checked(&history).map_err(chain_err_from_str)?;
108        Ok(Machine::new(Uninitialized, EmptyData::default()))
109    }
110}
111
112impl TypestateKernel<AccessAdmissionLaw, Initializing, InitializingData>
113    for Machine<AccessAdmissionLaw, Initializing, InitializingData>
114{
115    type Input = serde_json::Value;
116    type OutputPhase = Initialized;
117    type OutputData = InitializedData;
118    type Receipt = lsp_max_protocol::Receipt;
119
120    fn validate(&self, _input: &Self::Input) -> Result<(), <AccessAdmissionLaw as Law>::Error> {
121        Ok(())
122    }
123
124    fn select(&self, _input: &Self::Input) -> Self::OutputPhase {
125        Initialized
126    }
127
128    fn admit(
129        self,
130        input: Self::Input,
131    ) -> Result<
132        Machine<AccessAdmissionLaw, Self::OutputPhase, Self::OutputData>,
133        <AccessAdmissionLaw as Law>::Error,
134    > {
135        self.validate(&input)?;
136        Ok(self.admit_initialized(input))
137    }
138
139    fn receipt(&self) -> Self::Receipt {
140        let client_caps_json = serde_json::to_string(&self.data.client_capabilities)
141            .unwrap_or_else(|_| "<unserializable>".to_string());
142        let receipt_id = format!("rcpt-uninitialized-to-initializing:{}", client_caps_json);
143        let prev_hash = sha256(b"rcpt-uninitialized");
144        let hash = sha256(format!("{}:{}", prev_hash, receipt_id).as_bytes());
145        lsp_max_protocol::Receipt {
146            receipt_id,
147            hash,
148            prev_receipt_hash: Some(prev_hash),
149        }
150    }
151
152    fn exit(self) -> InitializingData {
153        self.data
154    }
155
156    fn replay(history: Vec<Self::Receipt>) -> Result<Self, ChainError> {
157        if history.len() < 2 {
158            return Err(ChainError::InsufficientHistory {
159                required: 2,
160                got: history.len(),
161            });
162        }
163        let (client_caps, _) =
164            validate_and_reconstruct_chain_checked(&history).map_err(chain_err_from_str)?;
165        Ok(Machine::new(
166            Initializing,
167            InitializingData {
168                client_capabilities: client_caps,
169            },
170        ))
171    }
172}
173
174impl TypestateKernel<AccessAdmissionLaw, Initialized, InitializedData>
175    for Machine<AccessAdmissionLaw, Initialized, InitializedData>
176{
177    type Input = ();
178    type OutputPhase = ShutDown;
179    type OutputData = EmptyData;
180    type Receipt = lsp_max_protocol::Receipt;
181
182    fn validate(&self, _input: &Self::Input) -> Result<(), <AccessAdmissionLaw as Law>::Error> {
183        Ok(())
184    }
185
186    fn select(&self, _input: &Self::Input) -> Self::OutputPhase {
187        ShutDown
188    }
189
190    fn admit(
191        self,
192        input: Self::Input,
193    ) -> Result<
194        Machine<AccessAdmissionLaw, Self::OutputPhase, Self::OutputData>,
195        <AccessAdmissionLaw as Law>::Error,
196    > {
197        self.validate(&input)?;
198        Ok(self.admit_shutdown())
199    }
200
201    fn receipt(&self) -> Self::Receipt {
202        let client_caps_json = serde_json::to_string(&self.data.client_capabilities)
203            .unwrap_or_else(|_| "<unserializable>".to_string());
204        let server_caps_json = serde_json::to_string(&self.data.server_capabilities)
205            .unwrap_or_else(|_| "<unserializable>".to_string());
206        let hash_0 = sha256(b"rcpt-uninitialized");
207        let rcpt_1 = format!("rcpt-uninitialized-to-initializing:{}", client_caps_json);
208        let prev_hash = sha256(format!("{}:{}", hash_0, rcpt_1).as_bytes());
209        let receipt_id = format!("rcpt-initializing-to-initialized:{}", server_caps_json);
210        let hash = sha256(format!("{}:{}", prev_hash, receipt_id).as_bytes());
211        lsp_max_protocol::Receipt {
212            receipt_id,
213            hash,
214            prev_receipt_hash: Some(prev_hash),
215        }
216    }
217
218    fn exit(self) -> InitializedData {
219        self.data
220    }
221
222    fn replay(history: Vec<Self::Receipt>) -> Result<Self, ChainError> {
223        if history.len() < 3 {
224            return Err(ChainError::InsufficientHistory {
225                required: 3,
226                got: history.len(),
227            });
228        }
229        let (client_caps, server_caps) =
230            validate_and_reconstruct_chain_checked(&history).map_err(chain_err_from_str)?;
231        Ok(Machine::new(
232            Initialized,
233            InitializedData {
234                client_capabilities: client_caps,
235                server_capabilities: server_caps,
236            },
237        ))
238    }
239}
240
241impl TypestateKernel<AccessAdmissionLaw, ShutDown, EmptyData>
242    for Machine<AccessAdmissionLaw, ShutDown, EmptyData>
243{
244    type Input = ();
245    type OutputPhase = Exited;
246    type OutputData = EmptyData;
247    type Receipt = lsp_max_protocol::Receipt;
248
249    fn validate(&self, _input: &Self::Input) -> Result<(), <AccessAdmissionLaw as Law>::Error> {
250        Ok(())
251    }
252
253    fn select(&self, _input: &Self::Input) -> Self::OutputPhase {
254        Exited
255    }
256
257    fn admit(
258        self,
259        input: Self::Input,
260    ) -> Result<
261        Machine<AccessAdmissionLaw, Self::OutputPhase, Self::OutputData>,
262        <AccessAdmissionLaw as Law>::Error,
263    > {
264        self.validate(&input)?;
265        Ok(self.admit_exit())
266    }
267
268    fn receipt(&self) -> Self::Receipt {
269        let client_caps_json = self
270            .data
271            .client_capabilities
272            .as_ref()
273            .map(|c| serde_json::to_string(c).unwrap_or_else(|_| "<unserializable>".to_string()))
274            .unwrap_or_else(|| "null".to_string());
275        let server_caps_json = self
276            .data
277            .server_capabilities
278            .as_ref()
279            .map(|s| serde_json::to_string(s).unwrap_or_else(|_| "<unserializable>".to_string()))
280            .unwrap_or_else(|| "null".to_string());
281        let hash_0 = sha256(b"rcpt-uninitialized");
282        let rcpt_1 = format!("rcpt-uninitialized-to-initializing:{}", client_caps_json);
283        let hash_1 = sha256(format!("{}:{}", hash_0, rcpt_1).as_bytes());
284        let rcpt_2 = format!("rcpt-initializing-to-initialized:{}", server_caps_json);
285        let prev_hash = sha256(format!("{}:{}", hash_1, rcpt_2).as_bytes());
286        let receipt_id = "rcpt-initialized-to-shutdown".to_string();
287        let hash = sha256(format!("{}:{}", prev_hash, receipt_id).as_bytes());
288        lsp_max_protocol::Receipt {
289            receipt_id,
290            hash,
291            prev_receipt_hash: Some(prev_hash),
292        }
293    }
294
295    fn exit(self) -> EmptyData {
296        self.data
297    }
298
299    fn replay(history: Vec<Self::Receipt>) -> Result<Self, ChainError> {
300        if history.len() < 4 {
301            return Err(ChainError::InsufficientHistory {
302                required: 4,
303                got: history.len(),
304            });
305        }
306        let (client_caps, server_caps) =
307            validate_and_reconstruct_chain_checked(&history).map_err(chain_err_from_str)?;
308        Ok(Machine::new(
309            ShutDown,
310            EmptyData {
311                client_capabilities: Some(client_caps),
312                server_capabilities: Some(server_caps),
313            },
314        ))
315    }
316}
317
318impl TypestateKernel<AccessAdmissionLaw, Exited, EmptyData>
319    for Machine<AccessAdmissionLaw, Exited, EmptyData>
320{
321    type Input = ();
322    type OutputPhase = Exited;
323    type OutputData = EmptyData;
324    type Receipt = lsp_max_protocol::Receipt;
325
326    fn validate(&self, _input: &Self::Input) -> Result<(), <AccessAdmissionLaw as Law>::Error> {
327        Err("Already exited")
328    }
329
330    fn select(&self, _input: &Self::Input) -> Self::OutputPhase {
331        Exited
332    }
333
334    fn admit(
335        self,
336        _input: Self::Input,
337    ) -> Result<
338        Machine<AccessAdmissionLaw, Self::OutputPhase, Self::OutputData>,
339        <AccessAdmissionLaw as Law>::Error,
340    > {
341        self.validate(&_input)?;
342        Ok(self)
343    }
344
345    fn receipt(&self) -> Self::Receipt {
346        let client_caps_json = self
347            .data
348            .client_capabilities
349            .as_ref()
350            .map(|c| serde_json::to_string(c).unwrap_or_else(|_| "<unserializable>".to_string()))
351            .unwrap_or_else(|| "null".to_string());
352        let server_caps_json = self
353            .data
354            .server_capabilities
355            .as_ref()
356            .map(|s| serde_json::to_string(s).unwrap_or_else(|_| "<unserializable>".to_string()))
357            .unwrap_or_else(|| "null".to_string());
358        let hash_0 = sha256(b"rcpt-uninitialized");
359        let rcpt_1 = format!("rcpt-uninitialized-to-initializing:{}", client_caps_json);
360        let hash_1 = sha256(format!("{}:{}", hash_0, rcpt_1).as_bytes());
361        let rcpt_2 = format!("rcpt-initializing-to-initialized:{}", server_caps_json);
362        let hash_2 = sha256(format!("{}:{}", hash_1, rcpt_2).as_bytes());
363        let rcpt_3 = "rcpt-initialized-to-shutdown".to_string();
364        let prev_hash = sha256(format!("{}:{}", hash_2, rcpt_3).as_bytes());
365        let receipt_id = "rcpt-shutdown-to-exited".to_string();
366        let hash = sha256(format!("{}:{}", prev_hash, receipt_id).as_bytes());
367        lsp_max_protocol::Receipt {
368            receipt_id,
369            hash,
370            prev_receipt_hash: Some(prev_hash),
371        }
372    }
373
374    fn exit(self) -> EmptyData {
375        self.data
376    }
377
378    fn replay(history: Vec<Self::Receipt>) -> Result<Self, ChainError> {
379        if history.len() < 5 {
380            return Err(ChainError::InsufficientHistory {
381                required: 5,
382                got: history.len(),
383            });
384        }
385        let (client_caps, server_caps) =
386            validate_and_reconstruct_chain_checked(&history).map_err(chain_err_from_str)?;
387        Ok(Machine::new(
388            Exited,
389            EmptyData {
390                client_capabilities: Some(client_caps),
391                server_capabilities: Some(server_caps),
392            },
393        ))
394    }
395}