Skip to main content

fuel_core_storage/
lib.rs

1//! The crate `fuel-core-storage` contains storage types, primitives, tables used by `fuel-core`.
2//! This crate doesn't contain the actual implementation of the storage. It works around the
3//! `Database` and is used by services to provide a default implementation. Primitives
4//! defined here are used by services but are flexible enough to customize the
5//! logic when the `Database` is known.
6
7#![cfg_attr(not(feature = "std"), no_std)]
8#![deny(clippy::arithmetic_side_effects)]
9#![deny(clippy::cast_possible_truncation)]
10#![deny(unused_crate_dependencies)]
11#![deny(missing_docs)]
12#![deny(warnings)]
13
14#[cfg(feature = "alloc")]
15extern crate alloc;
16
17#[cfg(test)]
18use fuel_core_storage as _;
19
20use anyhow::anyhow;
21use core::array::TryFromSliceError;
22use fuel_core_types::services::executor::Error as ExecutorError;
23
24#[cfg(feature = "alloc")]
25use alloc::{
26    boxed::Box,
27    string::ToString,
28};
29
30pub use fuel_vm_private::{
31    fuel_storage::*,
32    storage::{
33        ContractsAssetsStorage,
34        InterpreterStorage,
35        predicate::PredicateStorageRequirements,
36    },
37};
38
39pub mod blueprint;
40pub mod codec;
41pub mod column;
42pub mod iter;
43pub mod kv_store;
44pub mod merkle;
45pub mod structured_storage;
46pub mod tables;
47#[cfg(feature = "test-helpers")]
48pub mod test_helpers;
49pub mod transactional;
50pub mod vm_storage;
51
52use fuel_core_types::fuel_merkle::binary::MerkleTreeError;
53pub use fuel_vm_private::storage::{
54    ContractsAssetKey,
55    ContractsStateData,
56    ContractsStateKey,
57};
58#[doc(hidden)]
59pub use paste;
60#[cfg(feature = "test-helpers")]
61#[doc(hidden)]
62pub use rand;
63
64/// The storage result alias.
65pub type Result<T> = core::result::Result<T, Error>;
66
67#[derive(Debug, derive_more::Display, derive_more::From)]
68#[non_exhaustive]
69/// Error occurring during interaction with storage
70pub enum Error {
71    /// Error occurred during serialization or deserialization of the entity.
72    #[display("error performing serialization or deserialization `{_0}`")]
73    Codec(anyhow::Error),
74    /// Error occurred during interaction with database.
75    #[display("error occurred in the underlying datastore `{_0:?}`")]
76    DatabaseError(Box<dyn core::fmt::Debug + Send + Sync>),
77    /// This error should be created with `not_found` macro.
78    #[display("resource was not found in table `{_0}` at the: {_1}")]
79    NotFound(&'static str, &'static str),
80    // TODO: Do we need this type at all?
81    /// Unknown or not expected(by architecture) error.
82    #[from]
83    Other(anyhow::Error),
84}
85
86#[cfg(feature = "test-helpers")]
87impl PartialEq for Error {
88    fn eq(&self, other: &Self) -> bool {
89        self.to_string().eq(&other.to_string())
90    }
91}
92
93impl From<Error> for anyhow::Error {
94    fn from(error: Error) -> Self {
95        anyhow::Error::msg(error)
96    }
97}
98
99impl From<TryFromSliceError> for Error {
100    fn from(e: TryFromSliceError) -> Self {
101        Self::Other(anyhow::anyhow!(e))
102    }
103}
104
105impl From<Error> for ExecutorError {
106    fn from(e: Error) -> Self {
107        ExecutorError::StorageError(e.to_string())
108    }
109}
110
111impl From<Error> for fuel_vm_private::prelude::InterpreterError<Error> {
112    fn from(e: Error) -> Self {
113        fuel_vm_private::prelude::InterpreterError::Storage(e)
114    }
115}
116
117impl From<Error> for fuel_vm_private::prelude::RuntimeError<Error> {
118    fn from(e: Error) -> Self {
119        fuel_vm_private::prelude::RuntimeError::Storage(e)
120    }
121}
122
123impl From<MerkleTreeError<Error>> for Error {
124    fn from(e: MerkleTreeError<Error>) -> Self {
125        match e {
126            MerkleTreeError::StorageError(s) => s,
127            e => Error::Other(anyhow!(e)),
128        }
129    }
130}
131
132/// The helper trait to work with storage errors.
133pub trait IsNotFound {
134    /// Return `true` if the error is [`Error::NotFound`].
135    fn is_not_found(&self) -> bool;
136}
137
138impl IsNotFound for Error {
139    fn is_not_found(&self) -> bool {
140        matches!(self, Error::NotFound(_, _))
141    }
142}
143
144impl<T> IsNotFound for Result<T> {
145    fn is_not_found(&self) -> bool {
146        match self {
147            Err(err) => err.is_not_found(),
148            _ => false,
149        }
150    }
151}
152
153/// The traits allow work with the storage in batches.
154/// Some implementations can perform batch operations faster than one by one.
155#[impl_tools::autoimpl(for<T: trait> &mut T)]
156pub trait StorageBatchMutate<Type: Mappable>: StorageMutate<Type> {
157    /// Initialize the storage with batch insertion. This method is more performant than
158    /// [`Self::insert_batch`] in some cases.
159    ///
160    /// # Errors
161    ///
162    /// Returns an error if the storage is already initialized.
163    fn init_storage<'a, Iter>(&mut self, set: Iter) -> Result<()>
164    where
165        Iter: 'a + Iterator<Item = (&'a Type::Key, &'a Type::Value)>,
166        Type::Key: 'a,
167        Type::Value: 'a;
168
169    /// Inserts the key-value pair into the storage in batch.
170    fn insert_batch<'a, Iter>(&mut self, set: Iter) -> Result<()>
171    where
172        Iter: 'a + Iterator<Item = (&'a Type::Key, &'a Type::Value)>,
173        Type::Key: 'a,
174        Type::Value: 'a;
175
176    /// Removes the key-value pairs from the storage in batch.
177    fn remove_batch<'a, Iter>(&mut self, set: Iter) -> Result<()>
178    where
179        Iter: 'a + Iterator<Item = &'a Type::Key>,
180        Type::Key: 'a;
181}
182
183/// Creates `StorageError::NotFound` error with file and line information inside.
184///
185/// # Examples
186///
187/// ```
188/// use fuel_core_storage::not_found;
189/// use fuel_core_storage::tables::Messages;
190///
191/// let string_type = not_found!("BlockId");
192/// let mappable_type = not_found!(Messages);
193/// let mappable_path = not_found!(fuel_core_storage::tables::Messages);
194/// ```
195#[macro_export]
196macro_rules! not_found {
197    ($name: literal) => {
198        $crate::Error::NotFound($name, concat!(file!(), ":", line!()))
199    };
200    ($ty: path) => {
201        $crate::Error::NotFound(
202            ::core::any::type_name::<$ty>(),
203            concat!(file!(), ":", line!()),
204        )
205    };
206}
207
208#[cfg(test)]
209mod test {
210    use crate::tables::Coins;
211
212    #[test]
213    fn not_found_output() {
214        #[rustfmt::skip]
215        assert_eq!(
216            format!("{}", not_found!("BlockId")),
217            format!("resource was not found in table `BlockId` at the: {}:{}", file!(), line!() - 1)
218        );
219        #[rustfmt::skip]
220        assert_eq!(
221            format!("{}", not_found!(Coins)),
222            format!("resource was not found in table `fuel_core_storage::tables::Coins` at the: {}:{}", file!(), line!() - 1)
223        );
224    }
225}