1#![forbid(unsafe_code)]
18#![warn(rust_2018_idioms)]
19#![deny(
20 dead_code,
21 nonstandard_style,
22 unused_imports,
23 unused_mut,
24 unused_variables,
25 unused_unsafe,
26 unreachable_patterns
27)]
28
29use avm_interface::raw_outcome::RawAVMOutcome;
30
31use serde::Deserialize;
32use serde::Serialize;
33use std::borrow::Cow;
34use std::time::Duration;
35
36pub trait DataStore {
40 type Error;
41
42 fn initialize(&mut self) -> Result<(), Self::Error>;
43
44 fn store_data(
45 &mut self,
46 data: &[u8],
47 particle_id: &str,
48 current_peer_id: &str,
49 ) -> Result<(), Self::Error>;
50
51 fn read_data(
52 &mut self,
53 particle_id: &str,
54 current_peer_id: &str,
55 ) -> Result<Vec<u8>, Self::Error>;
56
57 fn cleanup_data(&mut self, particle_id: &str, current_peer_id: &str)
59 -> Result<(), Self::Error>;
60
61 fn detect_anomaly(
68 &self,
69 execution_time: Duration,
70 memory_delta: usize,
71 outcome: &RawAVMOutcome,
72 ) -> bool;
73
74 fn collect_anomaly_data(
75 &mut self,
76 particle_id: &str,
77 current_peer_id: &str,
78 anomaly_data: AnomalyData<'_>,
79 ) -> Result<(), Self::Error>;
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
83pub struct AnomalyData<'data> {
84 #[serde(borrow)]
85 pub air_script: Cow<'data, str>,
86 #[serde(borrow, with = "serde_bytes")]
87 pub particle: Cow<'data, [u8]>, #[serde(borrow, with = "serde_bytes")]
89 pub prev_data: Cow<'data, [u8]>,
90 #[serde(borrow, with = "serde_bytes")]
91 pub current_data: Cow<'data, [u8]>,
92 #[serde(borrow, with = "serde_bytes")]
93 pub call_results: Cow<'data, [u8]>,
94 #[serde(borrow, with = "serde_bytes")]
95 pub avm_outcome: Cow<'data, [u8]>,
96 pub execution_time: Duration,
97 pub memory_delta: usize,
98}
99
100impl<'data> AnomalyData<'data> {
101 #[allow(clippy::too_many_arguments)]
102 pub fn new(
103 air_script: &'data str,
104 particle: &'data [u8],
105 prev_data: &'data [u8],
106 current_data: &'data [u8],
107 call_results: &'data [u8],
108 avm_outcome: &'data [u8],
109 execution_time: Duration,
110 memory_delta: usize,
111 ) -> Self {
112 Self {
113 air_script: air_script.into(),
114 particle: particle.into(),
115 prev_data: prev_data.into(),
116 current_data: current_data.into(),
117 call_results: call_results.into(),
118 avm_outcome: avm_outcome.into(),
119 execution_time,
120 memory_delta,
121 }
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 fn anomaly_json(
130 air_script: &str,
131 particle: &[u8],
132 prev_data: &[u8],
133 current_data: &[u8],
134 call_results: &[u8],
135 avm_outcome: &[u8],
136 ) -> String {
137 format!(
138 concat!(
139 r#"{{"air_script":{air_script},"#,
140 r#""particle":{particle},"#,
141 r#""prev_data":{prev_data},"#,
142 r#""current_data":{current_data},"#,
143 r#""call_results":{call_results},"#,
144 r#""avm_outcome":{avm_outcome},"#,
145 r#""execution_time":{{"secs":42,"nanos":0}},"#,
146 r#""memory_delta":123"#,
147 r#"}}"#
148 ),
149 air_script = serde_json::to_string(air_script).unwrap(),
150 particle = serde_json::to_string(particle).unwrap(),
151 prev_data = serde_json::to_string(prev_data).unwrap(),
152 current_data = serde_json::to_string(current_data).unwrap(),
153 call_results = serde_json::to_string(call_results).unwrap(),
154 avm_outcome = serde_json::to_string(avm_outcome).unwrap(),
155 )
156 }
157 #[test]
158 fn anomaly_data_se() {
159 let anomaly = AnomalyData::new(
160 "(null)",
161 br#"{"data":"value"}"#, br#"{"trace":[]}"#, br#"{"trace":[1,2,3]}"#, b"{}", b"{}",
166 Duration::from_secs(42),
167 123,
168 );
169
170 let json_data = serde_json::to_string(&anomaly).expect("JSON serialize anomaly data");
171 let expected = anomaly_json(
172 &anomaly.air_script,
173 &anomaly.particle,
174 &anomaly.prev_data,
175 &anomaly.current_data,
176 &anomaly.call_results,
177 &anomaly.avm_outcome,
178 );
179 assert_eq!(json_data, expected);
180 }
181
182 #[test]
183 fn anomaly_data_de() {
184 let particle = br#"{"particle":"data"}"#;
185 let current_data = br#"{"data":"current"}"#;
186 let prev_data = br#"{"data":"prev"}"#;
187 let avm_outcome = br#"{"avm":[1,2,3]}"#;
188 let call_results = br#"{"call_results": "excellent result"}"#;
189 let json_data = anomaly_json(
190 "(null)",
191 &particle[..],
192 &prev_data[..],
193 ¤t_data[..],
194 &call_results[..],
195 &avm_outcome[..],
196 );
197
198 let anomaly: AnomalyData<'_> =
199 serde_json::from_str(&json_data).expect("deserialize JSON anomaly data");
200
201 assert_eq!(
202 anomaly,
203 AnomalyData::new(
204 "(null)",
205 &particle[..],
206 &prev_data[..],
207 ¤t_data[..],
208 &call_results[..],
209 &avm_outcome[..],
210 Duration::from_secs(42),
211 123,
212 )
213 )
214 }
215}