fuel_vm/storage/
interpreter.rs

1//! Trait definitions for storage backend
2
3use fuel_storage::{
4    StorageAsRef,
5    StorageInspect,
6    StorageMutate,
7    StorageRead,
8    StorageSize,
9    StorageWrite,
10};
11use fuel_tx::{
12    ConsensusParameters,
13    Contract,
14    StorageSlot,
15};
16use fuel_types::{
17    AssetId,
18    BlockHeight,
19    Bytes32,
20    ContractId,
21    Word,
22};
23
24use crate::{
25    prelude::{
26        InterpreterError,
27        RuntimeError,
28    },
29    storage::{
30        ContractsAssets,
31        ContractsRawCode,
32        ContractsState,
33        ContractsStateData,
34        UploadedBytecode,
35        UploadedBytecodes,
36    },
37};
38use alloc::{
39    borrow::Cow,
40    vec::Vec,
41};
42use core::ops::{
43    Deref,
44    DerefMut,
45};
46
47use super::blob_data::BlobData;
48
49/// When this trait is implemented, the underlying interpreter is guaranteed to
50/// have full functionality
51pub trait InterpreterStorage:
52    StorageWrite<ContractsRawCode, Error = Self::DataError>
53    + StorageSize<ContractsRawCode, Error = Self::DataError>
54    + StorageRead<ContractsRawCode, Error = Self::DataError>
55    + StorageWrite<ContractsState, Error = Self::DataError>
56    + StorageSize<ContractsState, Error = Self::DataError>
57    + StorageRead<ContractsState, Error = Self::DataError>
58    + StorageMutate<UploadedBytecodes, Error = Self::DataError>
59    + StorageWrite<BlobData, Error = Self::DataError>
60    + StorageSize<BlobData, Error = Self::DataError>
61    + StorageRead<BlobData, Error = Self::DataError>
62    + ContractsAssetsStorage<Error = Self::DataError>
63{
64    /// Error implementation for reasons unspecified in the protocol.
65    type DataError: Into<InterpreterError<Self::DataError>>
66        + Into<RuntimeError<Self::DataError>>
67        + core::fmt::Debug;
68
69    /// Provide the current block height in which the transactions should be
70    /// executed.
71    fn block_height(&self) -> Result<BlockHeight, Self::DataError>;
72
73    /// Provide the current version of consensus parameters used to execute transaction.
74    fn consensus_parameters_version(&self) -> Result<u32, Self::DataError>;
75
76    /// Provide the current version of state transition function used to execute
77    /// transaction.
78    fn state_transition_version(&self) -> Result<u32, Self::DataError>;
79
80    /// Return the timestamp of a given block
81    ///
82    /// This isn't optional because the VM is expected to panic if an invalid block height
83    /// is passed - under the assumption that the block height is consistent, the
84    /// storage should necessarily have the timestamp for the block, unless some I/O
85    /// error prevents it from fetching it.
86    fn timestamp(&self, height: BlockHeight) -> Result<Word, Self::DataError>;
87
88    /// Provide the block hash from a given height.
89    fn block_hash(&self, block_height: BlockHeight) -> Result<Bytes32, Self::DataError>;
90
91    /// Provide the coinbase address for the VM instructions implementation.
92    fn coinbase(&self) -> Result<ContractId, Self::DataError>;
93
94    /// Set the consensus parameters in the storage under the `version`.
95    ///
96    /// Returns the previous consensus parameters if they were set.
97    fn set_consensus_parameters(
98        &mut self,
99        version: u32,
100        consensus_parameters: &ConsensusParameters,
101    ) -> Result<Option<ConsensusParameters>, Self::DataError>;
102
103    /// Returns `true` if the fully uploaded state transition bytecode is present in the
104    /// storage.
105    fn contains_state_transition_bytecode_root(
106        &self,
107        root: &Bytes32,
108    ) -> Result<bool, Self::DataError> {
109        let bytecode = self.storage::<UploadedBytecodes>().get(root)?;
110
111        if let Some(cow) = bytecode {
112            if let UploadedBytecode::Completed(_) = cow.as_ref() {
113                Ok(true)
114            } else {
115                Ok(false)
116            }
117        } else {
118            Ok(false)
119        }
120    }
121
122    /// Set the state transition bytecode in the storage under the `version`.
123    ///
124    /// Returns the previous bytecode if it was set.
125    fn set_state_transition_bytecode(
126        &mut self,
127        version: u32,
128        hash: &Bytes32,
129    ) -> Result<Option<Bytes32>, Self::DataError>;
130
131    /// Deploy a contract into the storage with contract id
132    fn deploy_contract_with_id(
133        &mut self,
134        slots: &[StorageSlot],
135        contract: &Contract,
136        id: &ContractId,
137    ) -> Result<(), Self::DataError> {
138        self.storage_contract_insert(id, contract)?;
139
140        // On the `fuel-core` side it is done in more optimal way
141        slots.iter().try_for_each(|s| {
142            self.contract_state_insert(id, s.key(), s.value().as_ref())?;
143            Ok(())
144        })?;
145        Ok(())
146    }
147
148    /// Fetch a previously inserted contract code from the chain state for a
149    /// given contract.
150    fn storage_contract(
151        &self,
152        id: &ContractId,
153    ) -> Result<Option<Cow<'_, Contract>>, Self::DataError> {
154        StorageInspect::<ContractsRawCode>::get(self, id)
155    }
156
157    /// Fetch the size of a previously inserted contract code from the chain state for a
158    /// given contract.
159    fn storage_contract_size(
160        &self,
161        id: &ContractId,
162    ) -> Result<Option<usize>, Self::DataError> {
163        StorageSize::<ContractsRawCode>::size_of_value(self, id)
164    }
165
166    /// Append a contract to the chain, provided its identifier.
167    ///
168    /// Canonically, the identifier should be [`Contract::id`].
169    fn storage_contract_insert(
170        &mut self,
171        id: &ContractId,
172        contract: &Contract,
173    ) -> Result<(), Self::DataError> {
174        StorageMutate::<ContractsRawCode>::insert(self, id, contract.as_ref())
175    }
176
177    /// Check if a provided contract exists in the chain.
178    fn storage_contract_exists(&self, id: &ContractId) -> Result<bool, Self::DataError> {
179        self.storage::<ContractsRawCode>().contains_key(id)
180    }
181
182    /// Fetch the value form a key-value mapping in a contract storage.
183    fn contract_state(
184        &self,
185        id: &ContractId,
186        key: &Bytes32,
187    ) -> Result<Option<Cow<'_, ContractsStateData>>, Self::DataError> {
188        StorageInspect::<ContractsState>::get(self, &(id, key).into())
189    }
190
191    /// Insert a key-value mapping in a contract storage.
192    fn contract_state_insert(
193        &mut self,
194        contract: &ContractId,
195        key: &Bytes32,
196        value: &[u8],
197    ) -> Result<(), Self::DataError> {
198        StorageWrite::<ContractsState>::write_bytes(
199            self,
200            &(contract, key).into(),
201            value,
202        )?;
203        Ok(())
204    }
205
206    /// Insert a key-value mapping into a contract storage.
207    fn contract_state_replace(
208        &mut self,
209        contract: &ContractId,
210        key: &Bytes32,
211        value: &[u8],
212    ) -> Result<Option<Vec<u8>>, Self::DataError> {
213        let (_, prev) = StorageWrite::<ContractsState>::replace_bytes(
214            self,
215            &(contract, key).into(),
216            value,
217        )?;
218        Ok(prev)
219    }
220
221    /// Fetch a range of values from a key-value mapping in a contract storage.
222    /// Returns the full range requested using optional values in case
223    /// a requested slot is unset.  
224    fn contract_state_range(
225        &self,
226        id: &ContractId,
227        start_key: &Bytes32,
228        range: usize,
229    ) -> Result<Vec<Option<Cow<ContractsStateData>>>, Self::DataError>;
230
231    /// Insert a range of key-value mappings into contract storage.
232    /// Returns the number of keys that were previously unset but are now set.
233    fn contract_state_insert_range<'a, I>(
234        &mut self,
235        contract: &ContractId,
236        start_key: &Bytes32,
237        values: I,
238    ) -> Result<usize, Self::DataError>
239    where
240        I: Iterator<Item = &'a [u8]>;
241
242    /// Remove a range of key-values from contract storage.
243    /// Returns None if any of the keys in the range were already unset.
244    fn contract_state_remove_range(
245        &mut self,
246        contract: &ContractId,
247        start_key: &Bytes32,
248        range: usize,
249    ) -> Result<Option<()>, Self::DataError>;
250}
251
252/// Storage operations for contract assets.
253pub trait ContractsAssetsStorage: StorageMutate<ContractsAssets> {
254    /// Fetch the balance of an asset ID in a contract storage.
255    fn contract_asset_id_balance(
256        &self,
257        id: &ContractId,
258        asset_id: &AssetId,
259    ) -> Result<Option<Word>, Self::Error> {
260        let balance = self
261            .storage::<ContractsAssets>()
262            .get(&(id, asset_id).into())?
263            .map(Cow::into_owned);
264
265        Ok(balance)
266    }
267
268    /// Update the balance of an asset ID in a contract storage.
269    fn contract_asset_id_balance_insert(
270        &mut self,
271        contract: &ContractId,
272        asset_id: &AssetId,
273        value: Word,
274    ) -> Result<(), Self::Error> {
275        StorageMutate::<ContractsAssets>::insert(
276            self,
277            &(contract, asset_id).into(),
278            &value,
279        )
280    }
281
282    /// Update the balance of an asset ID in a contract storage.
283    /// Returns the old balance, if any.
284    fn contract_asset_id_balance_replace(
285        &mut self,
286        contract: &ContractId,
287        asset_id: &AssetId,
288        value: Word,
289    ) -> Result<Option<Word>, Self::Error> {
290        StorageMutate::<ContractsAssets>::replace(
291            self,
292            &(contract, asset_id).into(),
293            &value,
294        )
295    }
296}
297
298impl<S> ContractsAssetsStorage for &mut S where S: ContractsAssetsStorage {}
299
300impl<S> InterpreterStorage for &mut S
301where
302    S: InterpreterStorage,
303{
304    type DataError = <S as InterpreterStorage>::DataError;
305
306    fn block_height(&self) -> Result<BlockHeight, Self::DataError> {
307        <S as InterpreterStorage>::block_height(self.deref())
308    }
309
310    fn consensus_parameters_version(&self) -> Result<u32, Self::DataError> {
311        <S as InterpreterStorage>::consensus_parameters_version(self.deref())
312    }
313
314    fn state_transition_version(&self) -> Result<u32, Self::DataError> {
315        <S as InterpreterStorage>::state_transition_version(self.deref())
316    }
317
318    fn timestamp(&self, height: BlockHeight) -> Result<Word, Self::DataError> {
319        <S as InterpreterStorage>::timestamp(self.deref(), height)
320    }
321
322    fn block_hash(&self, block_height: BlockHeight) -> Result<Bytes32, Self::DataError> {
323        <S as InterpreterStorage>::block_hash(self.deref(), block_height)
324    }
325
326    fn coinbase(&self) -> Result<ContractId, Self::DataError> {
327        <S as InterpreterStorage>::coinbase(self.deref())
328    }
329
330    fn set_consensus_parameters(
331        &mut self,
332        version: u32,
333        consensus_parameters: &ConsensusParameters,
334    ) -> Result<Option<ConsensusParameters>, Self::DataError> {
335        <S as InterpreterStorage>::set_consensus_parameters(
336            self.deref_mut(),
337            version,
338            consensus_parameters,
339        )
340    }
341
342    fn set_state_transition_bytecode(
343        &mut self,
344        version: u32,
345        hash: &Bytes32,
346    ) -> Result<Option<Bytes32>, Self::DataError> {
347        <S as InterpreterStorage>::set_state_transition_bytecode(
348            self.deref_mut(),
349            version,
350            hash,
351        )
352    }
353
354    fn storage_contract_size(
355        &self,
356        id: &ContractId,
357    ) -> Result<Option<usize>, Self::DataError> {
358        <S as InterpreterStorage>::storage_contract_size(self.deref(), id)
359    }
360
361    fn contract_state_range(
362        &self,
363        id: &ContractId,
364        start_key: &Bytes32,
365        range: usize,
366    ) -> Result<Vec<Option<Cow<ContractsStateData>>>, Self::DataError> {
367        <S as InterpreterStorage>::contract_state_range(
368            self.deref(),
369            id,
370            start_key,
371            range,
372        )
373    }
374
375    fn contract_state_insert_range<'a, I>(
376        &mut self,
377        contract: &ContractId,
378        start_key: &Bytes32,
379        values: I,
380    ) -> Result<usize, Self::DataError>
381    where
382        I: Iterator<Item = &'a [u8]>,
383    {
384        <S as InterpreterStorage>::contract_state_insert_range(
385            self.deref_mut(),
386            contract,
387            start_key,
388            values,
389        )
390    }
391
392    fn contract_state_remove_range(
393        &mut self,
394        contract: &ContractId,
395        start_key: &Bytes32,
396        range: usize,
397    ) -> Result<Option<()>, Self::DataError> {
398        <S as InterpreterStorage>::contract_state_remove_range(
399            self.deref_mut(),
400            contract,
401            start_key,
402            range,
403        )
404    }
405}