fuel_core_interfaces/
db.rs

1use crate::{
2    common::{
3        fuel_storage::Mappable,
4        fuel_tx::{
5            Receipt,
6            Transaction,
7            UtxoId,
8        },
9        fuel_types::{
10            Bytes32,
11            ContractId,
12            MessageId,
13        },
14        fuel_vm::prelude::InterpreterError,
15    },
16    model::{
17        Coin,
18        FuelBlockConsensus,
19        FuelBlockDb,
20        Message,
21    },
22};
23use std::io::ErrorKind;
24use thiserror::Error;
25
26pub use crate::common::fuel_vm::storage::{
27    ContractsAssets,
28    ContractsInfo,
29    ContractsRawCode,
30    ContractsState,
31};
32
33pub trait Transactional {
34    /// Commits the pending state changes into the database.
35    fn commit(self) -> Result<(), Error>;
36
37    // TODO: It is work around for `Box<dyn DatabaseTransaction`>.
38    //  with `DatabaseTransaction` from `fuel-core-storage` crate we can remove it.
39    /// The same as `commit` but for case if the value is boxed.
40    fn commit_box(self: Box<Self>) -> Result<(), Error>;
41}
42
43// TODO: Replace this trait with `Transaction<Database>` from `fuel-core-storage`.
44//  `core::fmt::Debug` bound added here to be able to see a proper error in the tests without
45//  updating them=)
46/// The database transaction for the `Database` type.
47pub trait DatabaseTransaction<Database>:
48    Transactional + core::fmt::Debug + Send + Sync
49{
50    // TODO: After removing of `Box<dyn DatabaseTransaction>` replace this method with
51    //  `AsRef<Database>` trait.
52    /// Returns the reference to the `Database` instance.
53    fn database(&self) -> &Database;
54    // TODO: After removing of `Box<dyn DatabaseTransaction>` replace this method with
55    //  `AsMut<Database>` trait.
56    /// Returns the mutable reference to the `Database` instance.
57    fn database_mut(&mut self) -> &mut Database;
58}
59
60/// The table of blocks generated by Fuels validators.
61/// Right now, we have only that type of block, but we will support others in the future.
62pub struct FuelBlocks;
63
64impl Mappable for FuelBlocks {
65    /// Unique identifier of the fuel block.
66    type Key = Bytes32;
67    type SetValue = FuelBlockDb;
68    type GetValue = Self::SetValue;
69}
70
71/// The latest UTXO id of the contract. The contract's UTXO represents the unique id of the state.
72/// After each transaction, old UTXO is consumed, and new UTXO is produced. UTXO is used as an
73/// input to the next transaction related to the `ContractId` smart contract.
74pub struct ContractsLatestUtxo;
75
76impl Mappable for ContractsLatestUtxo {
77    type Key = ContractId;
78    /// The latest UTXO id.
79    type SetValue = UtxoId;
80    type GetValue = Self::SetValue;
81}
82
83/// Receipts of different hidden internal operations.
84pub struct Receipts;
85
86impl Mappable for Receipts {
87    /// Unique identifier of the transaction.
88    type Key = Bytes32;
89    type SetValue = [Receipt];
90    type GetValue = Vec<Receipt>;
91}
92
93/// The table of consensus metadata associated with sealed (finalized) blocks
94pub struct SealedBlockConsensus;
95
96impl Mappable for SealedBlockConsensus {
97    type Key = Bytes32;
98    type SetValue = FuelBlockConsensus;
99    type GetValue = Self::SetValue;
100}
101
102/// The storage table of coins. Each [`Coin`](crate::model::Coin) is represented by unique `UtxoId`.
103pub struct Coins;
104
105impl Mappable for Coins {
106    type Key = UtxoId;
107    type SetValue = Coin;
108    type GetValue = Self::SetValue;
109}
110
111/// The storage table of bridged Ethereum [`Message`](crate::model::Message)s.
112pub struct Messages;
113
114impl Mappable for Messages {
115    type Key = MessageId;
116    type SetValue = Message;
117    type GetValue = Self::SetValue;
118}
119
120/// The storage table of confirmed transactions.
121pub struct Transactions;
122
123impl Mappable for Transactions {
124    type Key = Bytes32;
125    type SetValue = Transaction;
126    type GetValue = Self::SetValue;
127}
128
129// TODO: Add macro to define all common tables to avoid copy/paste of the code.
130// TODO: Add macro to define common unit tests.
131
132#[derive(Error, Debug)]
133#[non_exhaustive]
134pub enum Error {
135    #[error("error performing binary serialization")]
136    Codec,
137    #[error("Failed to initialize chain")]
138    ChainAlreadyInitialized,
139    #[error("Chain is not yet initialized")]
140    ChainUninitialized,
141    #[error("Invalid database version")]
142    InvalidDatabaseVersion,
143    #[error("error occurred in the underlying datastore `{0}`")]
144    DatabaseError(Box<dyn std::error::Error + Send + Sync>),
145    #[error(transparent)]
146    Other(#[from] anyhow::Error),
147}
148
149impl From<Error> for std::io::Error {
150    fn from(e: Error) -> Self {
151        std::io::Error::new(ErrorKind::Other, e)
152    }
153}
154
155#[derive(Debug, Error)]
156pub enum KvStoreError {
157    #[error("generic error occurred")]
158    Error(Box<dyn std::error::Error + Send + Sync>),
159    /// This error should be created with `not_found` macro.
160    #[error("resource of type `{0}` was not found at the: {1}")]
161    NotFound(&'static str, &'static str),
162}
163
164/// Creates `KvStoreError::NotFound` error with file and line information inside.
165///
166/// # Examples
167///
168/// ```
169/// use fuel_core_interfaces::not_found;
170/// use fuel_core_interfaces::db::Messages;
171///
172/// let string_type = not_found!("BlockId");
173/// let mappable_type = not_found!(Messages);
174/// let mappable_path = not_found!(fuel_core_interfaces::db::Coins);
175/// ```
176#[macro_export]
177macro_rules! not_found {
178    ($name: literal) => {
179        $crate::db::KvStoreError::NotFound($name, concat!(file!(), ":", line!()))
180    };
181    ($ty: path) => {
182        $crate::db::KvStoreError::NotFound(
183            ::core::any::type_name::<
184                <$ty as $crate::common::fuel_storage::Mappable>::GetValue,
185            >(),
186            concat!(file!(), ":", line!()),
187        )
188    };
189}
190
191impl From<Error> for KvStoreError {
192    fn from(e: Error) -> Self {
193        KvStoreError::Error(Box::new(e))
194    }
195}
196
197impl From<KvStoreError> for Error {
198    fn from(e: KvStoreError) -> Self {
199        Error::DatabaseError(Box::new(e))
200    }
201}
202
203impl From<KvStoreError> for std::io::Error {
204    fn from(e: KvStoreError) -> Self {
205        std::io::Error::new(ErrorKind::Other, e)
206    }
207}
208
209impl From<Error> for InterpreterError {
210    fn from(e: Error) -> Self {
211        InterpreterError::Io(std::io::Error::new(std::io::ErrorKind::Other, e))
212    }
213}
214
215impl From<KvStoreError> for InterpreterError {
216    fn from(e: KvStoreError) -> Self {
217        InterpreterError::Io(std::io::Error::new(std::io::ErrorKind::Other, e))
218    }
219}
220
221#[cfg(test)]
222mod test {
223    use super::*;
224
225    #[test]
226    fn not_found_output() {
227        #[rustfmt::skip]
228        assert_eq!(
229            format!("{}", not_found!("BlockId")),
230            format!("resource of type `BlockId` was not found at the: {}:{}", file!(), line!() - 1)
231        );
232        #[rustfmt::skip]
233        assert_eq!(
234            format!("{}", not_found!(Coins)),
235            format!("resource of type `fuel_core_interfaces::model::coin::Coin` was not found at the: {}:{}", file!(), line!() - 1)
236        );
237    }
238}