calimero_runtime/logic/host_functions/storage.rs
1use crate::{
2 errors::HostError,
3 logic::{sys, VMHostFunctions, VMLogicResult},
4};
5
6impl VMHostFunctions<'_> {
7 /// Reads a value from the persistent storage.
8 ///
9 /// If the key exists, the corresponding value is copied into the specified register.
10 ///
11 /// # Arguments
12 ///
13 /// * `src_key_ptr` - A pointer to the key source-buffer in guest memory.
14 /// * `dest_register_id` - The ID of the destination register in host memory where
15 /// to place the value (if found).
16 ///
17 /// # Returns
18 ///
19 /// * Returns `1` if the key was found and the value was recorded into the register.
20 /// * Returns `0` if the key was not found.
21 ///
22 /// # Errors
23 ///
24 /// * `HostError::KeyLengthOverflow` if the key size exceeds the configured limit.
25 /// * `HostError::InvalidMemoryAccess` if memory access fails for a descriptor buffer.
26 pub fn storage_read(&mut self, src_key_ptr: u64, dest_register_id: u64) -> VMLogicResult<u32> {
27 let key = unsafe { self.read_guest_memory_typed::<sys::Buffer<'_>>(src_key_ptr)? };
28
29 let logic = self.borrow_logic();
30
31 if key.len() > logic.limits.max_storage_key_size.get() {
32 return Err(HostError::KeyLengthOverflow.into());
33 }
34 let key = self.read_guest_memory_slice(&key).to_vec();
35
36 if let Some(value) = logic.storage.get(&key) {
37 self.with_logic_mut(|logic| {
38 logic.registers.set(logic.limits, dest_register_id, value)
39 })?;
40
41 return Ok(1);
42 }
43
44 Ok(0)
45 }
46
47 /// Removes a key-value pair from persistent storage.
48 ///
49 /// If the key exists, the value is copied into the specified host register before removal.
50 ///
51 /// # Arguments
52 ///
53 /// * `src_key_ptr` - A pointer to the key source-buffer in guest memory.
54 /// * `dest_register_id` - The ID of the destination register in host memory where to place
55 /// the value (if found).
56 ///
57 /// # Returns
58 ///
59 /// * Returns `1` if the key was found and removed.
60 /// * Returns `0` if the key was not found.
61 ///
62 /// # Errors
63 ///
64 /// * `HostError::KeyLengthOverflow` if the key size exceeds the configured limit.
65 /// * `HostError::InvalidMemoryAccess` if memory access fails for a descriptor buffer.
66 pub fn storage_remove(
67 &mut self,
68 src_key_ptr: u64,
69 dest_register_id: u64,
70 ) -> VMLogicResult<u32> {
71 let key = unsafe { self.read_guest_memory_typed::<sys::Buffer<'_>>(src_key_ptr)? };
72
73 let logic = self.borrow_logic();
74
75 if key.len() > logic.limits.max_storage_key_size.get() {
76 return Err(HostError::KeyLengthOverflow.into());
77 }
78
79 let key = self.read_guest_memory_slice(&key).to_vec();
80
81 if let Some(value) = logic.storage.get(&key) {
82 self.with_logic_mut(|logic| {
83 let _ignored = logic.storage.remove(&key);
84 logic.registers.set(logic.limits, dest_register_id, value)
85 })?;
86
87 return Ok(1);
88 }
89
90 Ok(0)
91 }
92
93 /// Writes a key-value pair to persistent storage.
94 ///
95 /// If the key already exists, its value is overwritten. The old value is placed
96 /// into the specified host register.
97 ///
98 /// # Arguments
99 ///
100 /// * `src_key_ptr` - A pointer to the key source-buffer in guest memory.
101 /// * `src_value_ptr` - A pointer to the value source-buffer in guest memory.
102 /// * `dest_register_id` - The ID of the destination register in host memory where to place
103 /// the old value (if found).
104 ///
105 /// # Returns
106 ///
107 /// * Returns `1` if a value was evicted (i.e., the key already existed).
108 /// * Returns `0` if a new key was inserted.
109 ///
110 /// # Errors
111 ///
112 /// * `HostError::KeyLengthOverflow` if the key size exceeds the limit.
113 /// * `HostError::ValueLengthOverflow` if the value size exceeds the limit.
114 /// * `HostError::InvalidMemoryAccess` if memory access fails for descriptor buffers.
115 pub fn storage_write(
116 &mut self,
117 src_key_ptr: u64,
118 src_value_ptr: u64,
119 dest_register_id: u64,
120 ) -> VMLogicResult<u32> {
121 let key = unsafe { self.read_guest_memory_typed::<sys::Buffer<'_>>(src_key_ptr)? };
122 let value = unsafe { self.read_guest_memory_typed::<sys::Buffer<'_>>(src_value_ptr)? };
123
124 let logic = self.borrow_logic();
125
126 if key.len() > logic.limits.max_storage_key_size.get() {
127 return Err(HostError::KeyLengthOverflow.into());
128 }
129
130 if value.len() > logic.limits.max_storage_value_size.get() {
131 return Err(HostError::ValueLengthOverflow.into());
132 }
133
134 let key = self.read_guest_memory_slice(&key).to_vec();
135 let value = self.read_guest_memory_slice(&value).to_vec();
136
137 let evicted = self.with_logic_mut(|logic| logic.storage.set(key, value));
138
139 if let Some(evicted) = evicted {
140 self.with_logic_mut(|logic| {
141 logic.registers.set(logic.limits, dest_register_id, evicted)
142 })?;
143
144 return Ok(1);
145 }
146
147 Ok(0)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use crate::logic::{
154 tests::{prepare_guest_buf_descriptor, setup_vm, write_str, SimpleMockStorage},
155 Cow, VMContext, VMLimits, VMLogic, DIGEST_SIZE,
156 };
157 use wasmer::{AsStoreMut, Store};
158
159 /// Tests the basic `storage_write` and `storage_read` host functions.
160 #[test]
161 fn test_storage_write_read() {
162 let mut storage = SimpleMockStorage::new();
163 let limits = VMLimits::default();
164 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
165 let mut host = logic.host_functions(store.as_store_mut());
166
167 let key = "key";
168 let key_ptr = 200u64;
169 // Guest: write `key` to its memory.
170 write_str(&host, key_ptr, key);
171 let key_buf_ptr = 10u64;
172 // Guest: prepare the descriptor for the destination buffer so host can write there.
173 prepare_guest_buf_descriptor(&host, key_buf_ptr, key_ptr, key.len() as u64);
174
175 let value = "value";
176 let value_ptr = 300u64;
177 // Guest: write `value` to its memory.
178 write_str(&host, value_ptr, value);
179 let value_buf_ptr = 32u64;
180 // Guest: prepare the descriptor for the destination buffer so host can write there.
181 prepare_guest_buf_descriptor(&host, value_buf_ptr, value_ptr, value.len() as u64);
182
183 let register_id = 1u64;
184 // Guest: as host to write key-value pair to host storage.
185 let res = host
186 .storage_write(key_buf_ptr, value_buf_ptr, register_id)
187 .unwrap();
188 // Guest: verify the storage writing was successful.
189 assert_eq!(res, 0);
190
191 // Guest: ask the host to read from it's storage with a key located at `key_buf_ptr` and
192 // put the result into `register_id`.
193 let res = host.storage_read(key_buf_ptr, register_id).unwrap();
194 // Ensure, the storage read was successful
195 assert_eq!(res, 1);
196 // Verify that the register length has the proper size
197 assert_eq!(host.register_len(register_id).unwrap(), value.len() as u64);
198
199 // Guest: ask the host to read the register and verify that the register has the proper
200 // content after the `storage_read()` successfully exectued.
201 let buf_ptr = 400u64;
202 let data_output_ptr = 500u64;
203 // Guest: prepare the descriptor for the destination buffer so host can write there.
204 prepare_guest_buf_descriptor(&host, buf_ptr, data_output_ptr, value.len() as u64);
205
206 // Guest: read the register from the host into `buf_ptr`.
207 let res = host.read_register(register_id, buf_ptr).unwrap();
208 // Guest: assert the host successfully wrote the data from its register to our `buf_ptr`.
209 assert_eq!(res, 1);
210
211 let mut mem_buffer = vec![0u8; value.len()];
212 // Host: perform a priveleged read of the contents of guest's memory to verify it
213 // matches the `value`.
214 host.borrow_memory()
215 .read(data_output_ptr, &mut mem_buffer)
216 .unwrap();
217 let mem_buffer_str = std::str::from_utf8(&mem_buffer).unwrap();
218 // Verify that the value from the register, after the successfull `storage_read()`
219 // operation matches the same `value` when we initially wrote to the storage.
220 assert_eq!(mem_buffer_str, value);
221 }
222
223 /// Tests the `storage_remove()` host function.
224 #[test]
225 fn test_storage_remove() {
226 let mut storage = SimpleMockStorage::new();
227 let limits = VMLimits::default();
228 let (mut logic, mut store) = setup_vm!(&mut storage, &limits, vec![]);
229 let mut host = logic.host_functions(store.as_store_mut());
230
231 let key = "to_remove";
232 let value = "old_value";
233 // Manually write into host storage for simplicity reasons.
234 let _unused = host.with_logic_mut(|logic| {
235 logic
236 .storage
237 .set(key.as_bytes().to_vec(), value.as_bytes().to_vec())
238 });
239
240 let key_ptr = 100u64;
241 // Guest: write key to its memory
242 write_str(&host, key_ptr, key);
243 let key_buf_ptr = 16u64;
244 // Guest: prepare the descriptor for the destination buffer so host can access it.
245 prepare_guest_buf_descriptor(&host, key_buf_ptr, key_ptr, key.len() as u64);
246
247 let register_id = 1u64;
248 // Guest: ask host to remove from storage the value with the given key.
249 let res = host.storage_remove(key_buf_ptr, register_id).unwrap();
250 // Verify the storage removal was successful.
251 assert_eq!(res, 1);
252 // Verify the storage doesn't have a specified key anymore.
253 assert_eq!(
254 host.borrow_logic().storage.has(&key.as_bytes().to_vec()),
255 false
256 );
257 // Verify the removed value was put into the host register.
258 assert_eq!(
259 host.borrow_logic().registers.get(register_id).unwrap(),
260 value.as_bytes()
261 );
262
263 // Verify the host register contains the removed value.
264 let buf_ptr = 200u64;
265 let data_output_ptr = 300u64;
266 // Guest: prepare the descriptor for the destination buffer so host can write there.
267 prepare_guest_buf_descriptor(&host, buf_ptr, data_output_ptr, value.len() as u64);
268
269 // Guest: read the register from the host into `buf_ptr`.
270 let res = host.read_register(register_id, buf_ptr).unwrap();
271 // Guest: assert the host successfully wrote the data from its register to our `buf_ptr`.
272 assert_eq!(res, 1);
273
274 let mut mem_buffer = vec![0u8; value.len()];
275 // Host: perform a priveleged read of the contents of guest's memory to verify it
276 // matches the `value`.
277 host.borrow_memory()
278 .read(data_output_ptr, &mut mem_buffer)
279 .unwrap();
280 assert_eq!(std::str::from_utf8(&mem_buffer).unwrap(), value);
281 }
282}