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}