1use crate::state::{HostState, NextInput};
12use crate::MockHost;
13use core::{
14 cell::RefCell,
15 ptr,
16 slice::{from_raw_parts, from_raw_parts_mut},
17};
18use tezos_smart_rollup_core::smart_rollup_core::{ReadInputMessageInfo, SmartRollupCore};
19use tezos_smart_rollup_core::PREIMAGE_HASH_SIZE;
20use tezos_smart_rollup_host::metadata::METADATA_SIZE;
21use tezos_smart_rollup_host::Error;
22
23impl From<HostState> for MockHost {
24 fn from(state: HostState) -> Self {
25 Self {
26 info: super::info_for_level(state.curr_level as i32),
27 state: RefCell::new(state),
28 }
29 }
30}
31
32impl AsMut<HostState> for MockHost {
33 fn as_mut(&mut self) -> &mut HostState {
34 self.state.get_mut()
35 }
36}
37
38unsafe impl SmartRollupCore for MockHost {
39 unsafe fn read_input(
40 &self,
41 message_info: *mut ReadInputMessageInfo,
42 dst: *mut u8,
43 max_bytes: usize,
44 ) -> i32 {
45 if let Some(NextInput { level, id, payload }) =
46 self.state.borrow_mut().handle_read_input(max_bytes)
47 {
48 let input_message_info = ReadInputMessageInfo {
49 level: level as i32,
50 id: id as i32,
51 };
52 ptr::write(message_info, input_message_info);
53
54 let slice = from_raw_parts_mut(dst, payload.len());
56 slice.copy_from_slice(payload.as_slice());
57
58 payload.len().try_into().unwrap()
59 } else {
60 0_i32
61 }
62 }
63
64 unsafe fn write_debug(&self, src: *const u8, num_bytes: usize) {
65 let debug_out = from_raw_parts(src, num_bytes).to_vec();
66
67 let debug = String::from_utf8(debug_out).expect("unexpected non-utf8 debug log");
68
69 eprint!("{}", &debug);
70 }
71
72 unsafe fn write_output(&self, src: *const u8, num_bytes: usize) -> i32 {
73 let output = from_raw_parts(src, num_bytes).to_vec();
74
75 self.state
76 .borrow_mut()
77 .handle_write_output(output)
78 .map(|_| 0)
79 .unwrap_or_else(Error::code)
80 }
81
82 unsafe fn store_has(&self, path: *const u8, len: usize) -> i32 {
83 let path = from_raw_parts(path, len);
84 self.state
85 .borrow()
86 .handle_store_has(path)
87 .unwrap_or_else(Error::code)
88 }
89
90 unsafe fn store_read(
91 &self,
92 path: *const u8,
93 len: usize,
94 offset: usize,
95 dst: *mut u8,
96 max_bytes: usize,
97 ) -> i32 {
98 let path = from_raw_parts(path, len);
99
100 let bytes = self
101 .state
102 .borrow()
103 .handle_store_read(path, offset, max_bytes);
104
105 match bytes {
106 Ok(bytes) => {
107 assert!(bytes.len() <= max_bytes);
108
109 let slice = from_raw_parts_mut(dst, bytes.len());
110 slice.copy_from_slice(bytes.as_slice());
111
112 bytes.len().try_into().unwrap()
113 }
114 Err(e) => e.code(),
115 }
116 }
117
118 unsafe fn store_write(
119 &self,
120 path: *const u8,
121 len: usize,
122 offset: usize,
123 src: *const u8,
124 num_bytes: usize,
125 ) -> i32 {
126 let path = from_raw_parts(path, len);
127 let bytes = from_raw_parts(src, num_bytes);
128
129 self.state
130 .borrow_mut()
131 .handle_store_write(path, offset, bytes)
132 .map(|_| 0)
133 .unwrap_or_else(Error::code)
134 }
135
136 unsafe fn store_delete(&self, path: *const u8, len: usize) -> i32 {
137 let path = from_raw_parts(path, len);
138
139 self.state
140 .borrow_mut()
141 .handle_store_delete(path)
142 .map(|_| 0)
143 .unwrap_or_else(Error::code)
144 }
145
146 unsafe fn store_delete_value(&self, path: *const u8, len: usize) -> i32 {
147 let path = from_raw_parts(path, len);
148
149 self.state
150 .borrow_mut()
151 .handle_store_delete_value(path)
152 .map(|_| 0)
153 .unwrap_or_else(Error::code)
154 }
155
156 unsafe fn store_list_size(&self, path: *const u8, len: usize) -> i64 {
157 let path = from_raw_parts(path, len);
158
159 self.state
160 .borrow()
161 .handle_store_list_size(path)
162 .unwrap_or_else(|e| e.code() as i64)
163 }
164
165 unsafe fn store_move(
166 &self,
167 from_path: *const u8,
168 from_path_len: usize,
169 to_path: *const u8,
170 to_path_len: usize,
171 ) -> i32 {
172 let from_path = from_raw_parts(from_path, from_path_len);
173 let to_path = from_raw_parts(to_path, to_path_len);
174
175 self.state
176 .borrow_mut()
177 .handle_store_move(from_path, to_path)
178 .map(|_| 0)
179 .unwrap_or_else(Error::code)
180 }
181
182 unsafe fn store_copy(
183 &self,
184 from_path: *const u8,
185 from_path_len: usize,
186 to_path: *const u8,
187 to_path_len: usize,
188 ) -> i32 {
189 let from_path = from_raw_parts(from_path, from_path_len);
190 let to_path = from_raw_parts(to_path, to_path_len);
191
192 self.state
193 .borrow_mut()
194 .handle_store_copy(from_path, to_path)
195 .map(|_| 0)
196 .unwrap_or_else(Error::code)
197 }
198
199 unsafe fn reveal_preimage(
200 &self,
201 hash_addr: *const u8,
202 hash_len: usize,
203 destination_addr: *mut u8,
204 max_bytes: usize,
205 ) -> i32 {
206 let hash = from_raw_parts(hash_addr, hash_len)
208 .try_into()
209 .unwrap_or_else(|_| panic!("Hash is not {} bytes", PREIMAGE_HASH_SIZE));
210
211 let bytes = self
212 .state
213 .borrow()
214 .handle_reveal_preimage(&hash, max_bytes)
215 .to_vec();
216
217 assert!(bytes.len() <= max_bytes);
218
219 let slice = from_raw_parts_mut(destination_addr, bytes.len());
220 slice.copy_from_slice(bytes.as_slice());
221
222 bytes.len().try_into().unwrap()
223 }
224
225 unsafe fn store_value_size(&self, path: *const u8, path_len: usize) -> i32 {
226 let path = from_raw_parts(path, path_len);
227 self.state
228 .borrow()
229 .handle_store_value_size(path)
230 .unwrap_or_else(Error::code)
231 }
232
233 unsafe fn reveal_metadata(&self, destination_addr: *mut u8, max_bytes: usize) -> i32 {
234 assert!(METADATA_SIZE <= max_bytes);
235 let metadata: [u8; METADATA_SIZE] =
236 self.state.borrow().get_metadata().clone().into();
237 let slice = from_raw_parts_mut(destination_addr, metadata.len());
238 slice.copy_from_slice(metadata.as_slice());
239 metadata.len().try_into().unwrap()
240 }
241
242 #[cfg(feature = "proto-alpha")]
243 unsafe fn reveal(
244 &self,
245 _payload_addr: *const u8,
246 _payload_len: usize,
247 _destination_addr: *mut u8,
248 _max_bytes: usize,
249 ) -> i32 {
250 unimplemented!("The `reveal` host function is not yet mocked.")
252 }
253}
254
255#[cfg(test)]
256mod tests {
257
258 use super::MockHost;
259
260 use crate::state::HostState;
261 use tezos_smart_rollup_core::{MAX_FILE_CHUNK_SIZE, MAX_INPUT_MESSAGE_SIZE};
262 use tezos_smart_rollup_host::input::Message;
263 use tezos_smart_rollup_host::{
264 metadata::RollupMetadata,
265 path::RefPath,
266 runtime::{Runtime, RuntimeError},
267 };
268
269 #[test]
270 fn test_read_input_message() {
271 let mut mock_host = MockHost::default();
273
274 mock_host
275 .as_mut()
276 .add_input(vec![5; MAX_INPUT_MESSAGE_SIZE / 2]);
277
278 let result = mock_host.read_input();
280
281 let expected = Ok(Some(Message::new(
283 mock_host.level(),
284 0,
285 vec![5; MAX_INPUT_MESSAGE_SIZE / 2],
286 )));
287
288 assert_eq!(expected, result);
289 }
290
291 #[test]
292 fn test_reveal_preimage() {
293 let mut state = HostState::default();
295
296 let data = vec![b'a'; 3 * 1024];
297
298 let hash = state.set_preimage(data);
299
300 let mock_host = MockHost::from(state);
301
302 let mut buffer = [0; 300];
303 let _result = mock_host.reveal_preimage(&hash, &mut buffer);
305
306 assert_eq!(buffer, [b'a'; 300]);
309 }
310
311 #[test]
312 fn test_reveal_metadata() {
313 let metadata_bytes = [
316 b'M', 165, 28, b']', 231, 161, 205, 212, 148, 193, b'[', b'S', 129, b'^', 31,
318 170, b'L', 26, 150, 202, 0, 0, 0, 42,
320 ];
321
322 let expected_metadata = RollupMetadata::from(metadata_bytes);
323 let state = HostState::default_with_metadata(expected_metadata.clone());
324 let mock_host = MockHost::from(state); let result = mock_host.reveal_metadata();
328
329 assert_eq!(expected_metadata, result);
331 }
332
333 #[test]
334 fn read_value_slice_not_found() {
335 let mock = MockHost::default();
336 const PATH: RefPath<'static> = RefPath::assert_from(b"/some/path");
337 let mut buffer = [0_u8; 16];
338
339 assert_eq!(
340 mock.store_read_slice(&PATH, 0, &mut buffer),
341 Err(RuntimeError::HostErr(
342 tezos_smart_rollup_host::Error::StoreNotAValue
343 ))
344 );
345 }
346
347 #[test]
348 fn read_value_slice_partial_buffer_fill() {
349 let mut mock = MockHost::default();
350 const PATH: RefPath<'static> = RefPath::assert_from(b"/some/path");
351 let value = [1_u8; 8];
352 let mut buffer = [0_u8; 16];
353
354 mock.store_write(&PATH, &value, 0)
355 .expect("Could not write value to store");
356
357 assert_eq!(mock.store_read_slice(&PATH, 0, &mut buffer), Ok(8_usize));
358
359 assert_eq!(buffer, [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
360 }
361
362 #[test]
363 fn read_value_slice_complete_buffer_fill() {
364 let mut mock = MockHost::default();
365 const PATH: RefPath<'static> = RefPath::assert_from(b"/some/path");
366 let value = [1_u8; 16];
367 let mut buffer = [0_u8; 16];
368
369 mock.store_write(&PATH, &value, 0)
370 .expect("Could not write value to store");
371
372 assert_eq!(mock.store_read_slice(&PATH, 0, &mut buffer), Ok(16_usize));
373
374 assert_eq!(buffer, [1_u8; 16]);
375 }
376
377 #[test]
378 fn test_store_write() {
379 let mut state = HostState::default();
380 let size = 256_i32;
381 let data = vec![b'a'; size as usize];
382 let path = b"/a/b";
383
384 state.handle_store_write(path, 0, &data).unwrap();
385 let value_size = state.handle_store_value_size(path).unwrap();
386
387 assert_eq!(size, value_size)
388 }
389
390 #[test]
391 fn store_read_and_write_all() {
392 let mut mock = MockHost::default();
393 const PATH: RefPath = RefPath::assert_from(b"/path/value");
394
395 let value: Vec<u8> = (0..MAX_FILE_CHUNK_SIZE * 2 + 100)
396 .map(|v| (v % 100).try_into().unwrap())
397 .collect();
398
399 Runtime::store_write_all(&mut mock, &PATH, &value)
400 .expect("Could not write value to store");
401
402 let value_in_durable = Runtime::store_read_all(&mock, &PATH)
403 .expect("Could not read the value from the store");
404
405 let size = mock.store_value_size(&PATH);
406
407 assert_eq!(Ok(value.len()), size);
408 assert_eq!(value_in_durable, value);
409 }
410
411 #[test]
412 fn store_write_all_delete_previous_value() {
413 let mut mock = MockHost::default();
414 const PATH: RefPath = RefPath::assert_from(b"/path/value");
415
416 let initial_value: Vec<u8> = (0..MAX_FILE_CHUNK_SIZE * 2 + 100)
418 .map(|v| (v % 100).try_into().unwrap())
419 .collect();
420
421 Runtime::store_write_all(&mut mock, &PATH, &initial_value)
422 .expect("Could not write value to store");
423 let initial_size = mock.store_value_size(&PATH).expect("Could not read size");
424 let initial_value_in_store = Runtime::store_read_all(&mock, &PATH)
425 .expect("Could not read the value from the store");
426
427 let smaller_value: Vec<u8> = (0..MAX_FILE_CHUNK_SIZE + 100)
429 .map(|v| (v % 50).try_into().unwrap())
430 .collect();
431
432 Runtime::store_write_all(&mut mock, &PATH, &smaller_value)
433 .expect("Could not write value to store");
434 let new_size = mock.store_value_size(&PATH).expect("Could not read size");
435 let new_value_in_store = Runtime::store_read_all(&mock, &PATH)
436 .expect("Could not read the value from the store");
437
438 assert!(new_size < initial_size);
442 assert_ne!(new_value_in_store, initial_value_in_store);
443 assert_eq!(new_value_in_store, smaller_value);
444 }
445}