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        StorageWrite::<ContractsState>::replace_bytes(
214            self,
215            &(contract, key).into(),
216            value,
217        )
218    }
219
220    /// Fetch a range of values from a key-value mapping in a contract storage.
221    /// Returns the full range requested using optional values in case
222    /// a requested slot is unset.  
223    fn contract_state_range(
224        &self,
225        id: &ContractId,
226        start_key: &Bytes32,
227        range: usize,
228    ) -> Result<Vec<Option<Cow<ContractsStateData>>>, Self::DataError>;
229
230    /// Insert a range of key-value mappings into contract storage.
231    /// Returns the number of keys that were previously unset but are now set.
232    fn contract_state_insert_range<'a, I>(
233        &mut self,
234        contract: &ContractId,
235        start_key: &Bytes32,
236        values: I,
237    ) -> Result<usize, Self::DataError>
238    where
239        I: Iterator<Item = &'a [u8]>;
240
241    /// Remove a range of key-values from contract storage.
242    /// Returns None if any of the keys in the range were already unset.
243    fn contract_state_remove_range(
244        &mut self,
245        contract: &ContractId,
246        start_key: &Bytes32,
247        range: usize,
248    ) -> Result<Option<()>, Self::DataError>;
249}
250
251/// Storage operations for contract assets.
252pub trait ContractsAssetsStorage: StorageMutate<ContractsAssets> {
253    /// Fetch the balance of an asset ID in a contract storage.
254    fn contract_asset_id_balance(
255        &self,
256        id: &ContractId,
257        asset_id: &AssetId,
258    ) -> Result<Option<Word>, Self::Error> {
259        let balance = self
260            .storage::<ContractsAssets>()
261            .get(&(id, asset_id).into())?
262            .map(Cow::into_owned);
263
264        Ok(balance)
265    }
266
267    /// Update the balance of an asset ID in a contract storage.
268    fn contract_asset_id_balance_insert(
269        &mut self,
270        contract: &ContractId,
271        asset_id: &AssetId,
272        value: Word,
273    ) -> Result<(), Self::Error> {
274        StorageMutate::<ContractsAssets>::insert(
275            self,
276            &(contract, asset_id).into(),
277            &value,
278        )
279    }
280
281    /// Update the balance of an asset ID in a contract storage.
282    /// Returns the old balance, if any.
283    fn contract_asset_id_balance_replace(
284        &mut self,
285        contract: &ContractId,
286        asset_id: &AssetId,
287        value: Word,
288    ) -> Result<Option<Word>, Self::Error> {
289        StorageMutate::<ContractsAssets>::replace(
290            self,
291            &(contract, asset_id).into(),
292            &value,
293        )
294    }
295}
296
297impl<S> ContractsAssetsStorage for &mut S where S: ContractsAssetsStorage {}
298
299impl<S> InterpreterStorage for &mut S
300where
301    S: InterpreterStorage,
302{
303    type DataError = <S as InterpreterStorage>::DataError;
304
305    fn block_height(&self) -> Result<BlockHeight, Self::DataError> {
306        <S as InterpreterStorage>::block_height(self.deref())
307    }
308
309    fn consensus_parameters_version(&self) -> Result<u32, Self::DataError> {
310        <S as InterpreterStorage>::consensus_parameters_version(self.deref())
311    }
312
313    fn state_transition_version(&self) -> Result<u32, Self::DataError> {
314        <S as InterpreterStorage>::state_transition_version(self.deref())
315    }
316
317    fn timestamp(&self, height: BlockHeight) -> Result<Word, Self::DataError> {
318        <S as InterpreterStorage>::timestamp(self.deref(), height)
319    }
320
321    fn block_hash(&self, block_height: BlockHeight) -> Result<Bytes32, Self::DataError> {
322        <S as InterpreterStorage>::block_hash(self.deref(), block_height)
323    }
324
325    fn coinbase(&self) -> Result<ContractId, Self::DataError> {
326        <S as InterpreterStorage>::coinbase(self.deref())
327    }
328
329    fn set_consensus_parameters(
330        &mut self,
331        version: u32,
332        consensus_parameters: &ConsensusParameters,
333    ) -> Result<Option<ConsensusParameters>, Self::DataError> {
334        <S as InterpreterStorage>::set_consensus_parameters(
335            self.deref_mut(),
336            version,
337            consensus_parameters,
338        )
339    }
340
341    fn set_state_transition_bytecode(
342        &mut self,
343        version: u32,
344        hash: &Bytes32,
345    ) -> Result<Option<Bytes32>, Self::DataError> {
346        <S as InterpreterStorage>::set_state_transition_bytecode(
347            self.deref_mut(),
348            version,
349            hash,
350        )
351    }
352
353    fn storage_contract_size(
354        &self,
355        id: &ContractId,
356    ) -> Result<Option<usize>, Self::DataError> {
357        <S as InterpreterStorage>::storage_contract_size(self.deref(), id)
358    }
359
360    fn contract_state_range(
361        &self,
362        id: &ContractId,
363        start_key: &Bytes32,
364        range: usize,
365    ) -> Result<Vec<Option<Cow<ContractsStateData>>>, Self::DataError> {
366        <S as InterpreterStorage>::contract_state_range(
367            self.deref(),
368            id,
369            start_key,
370            range,
371        )
372    }
373
374    fn contract_state_insert_range<'a, I>(
375        &mut self,
376        contract: &ContractId,
377        start_key: &Bytes32,
378        values: I,
379    ) -> Result<usize, Self::DataError>
380    where
381        I: Iterator<Item = &'a [u8]>,
382    {
383        <S as InterpreterStorage>::contract_state_insert_range(
384            self.deref_mut(),
385            contract,
386            start_key,
387            values,
388        )
389    }
390
391    fn contract_state_remove_range(
392        &mut self,
393        contract: &ContractId,
394        start_key: &Bytes32,
395        range: usize,
396    ) -> Result<Option<()>, Self::DataError> {
397        <S as InterpreterStorage>::contract_state_remove_range(
398            self.deref_mut(),
399            contract,
400            start_key,
401            range,
402        )
403    }
404}