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