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