fuel_core_executor/refs/
contract.rs

1use core::fmt;
2use fuel_core_storage::{
3    not_found,
4    tables::ContractsLatestUtxo,
5    Error as StorageError,
6    Mappable,
7    MerkleRoot,
8    StorageAsRef,
9    StorageInspect,
10};
11use fuel_core_types::{
12    fuel_crypto::Hasher,
13    fuel_types::{
14        Bytes32,
15        ContractId,
16    },
17    services::executor::{
18        Error as ExecutorError,
19        Result as ExecutorResult,
20    },
21};
22
23#[cfg(feature = "std")]
24use std::borrow::Cow;
25
26#[cfg(not(feature = "std"))]
27use alloc::borrow::Cow;
28
29#[cfg(feature = "smt")]
30pub use smt::*;
31
32#[cfg(not(feature = "smt"))]
33pub use not_smt::*;
34
35/// The wrapper around `contract_id` to simplify work with `Contract` in the database.
36pub struct ContractRef<Database> {
37    database: Database,
38    contract_id: ContractId,
39}
40
41impl<Database> ContractRef<Database> {
42    pub fn new(database: Database, contract_id: ContractId) -> Self {
43        Self {
44            database,
45            contract_id,
46        }
47    }
48
49    pub fn contract_id(&self) -> &ContractId {
50        &self.contract_id
51    }
52
53    pub fn database(&self) -> &Database {
54        &self.database
55    }
56
57    pub fn database_mut(&mut self) -> &mut Database {
58        &mut self.database
59    }
60}
61
62impl<Database> ContractRef<Database>
63where
64    Database: StorageInspect<ContractsLatestUtxo>,
65    ExecutorError: From<Database::Error>,
66{
67    pub fn utxo(
68        &self,
69    ) -> Result<
70        Option<Cow<'_, <ContractsLatestUtxo as Mappable>::OwnedValue>>,
71        Database::Error,
72    > {
73        self.database.storage().get(&self.contract_id)
74    }
75}
76
77impl<Database> ContractRef<Database>
78where
79    Database: StorageInspect<ContractsLatestUtxo>,
80    ExecutorError: From<Database::Error>,
81{
82    pub fn validated_utxo(
83        &self,
84        utxo_validation: bool,
85    ) -> ExecutorResult<<ContractsLatestUtxo as Mappable>::OwnedValue> {
86        let maybe_utxo_id = self.utxo()?.map(|utxo| utxo.into_owned());
87        let expected_utxo_id = if utxo_validation {
88            maybe_utxo_id.ok_or(ExecutorError::ContractUtxoMissing(self.contract_id))?
89        } else {
90            maybe_utxo_id.unwrap_or_default()
91        };
92        Ok(expected_utxo_id)
93    }
94}
95
96#[cfg(feature = "smt")]
97mod smt {
98    use super::*;
99    use fuel_core_storage::{
100        tables::{
101            ContractsAssets,
102            ContractsState,
103        },
104        MerkleRootStorage,
105    };
106
107    impl<Database> ContractRef<Database>
108    where
109        Database: ContractStorageTrait,
110    {
111        pub fn balance_root(
112            &self,
113        ) -> Result<Bytes32, <Database as StorageInspect<ContractsAssets>>::Error>
114        {
115            <Database as MerkleRootStorage<ContractId, ContractsAssets>>::root(
116                &self.database,
117                &self.contract_id,
118            )
119            .map(Into::into)
120        }
121
122        pub fn state_root(
123            &self,
124        ) -> Result<Bytes32, <Database as StorageInspect<ContractsState>>::Error>
125        {
126            <Database as MerkleRootStorage<ContractId, ContractsState>>::root(
127                &self.database,
128                &self.contract_id,
129            )
130            .map(Into::into)
131        }
132    }
133
134    pub trait ContractStorageTrait:
135        StorageInspect<ContractsLatestUtxo, Error = Self::InnerError>
136        + MerkleRootStorage<ContractId, ContractsState, Error = Self::InnerError>
137        + MerkleRootStorage<ContractId, ContractsAssets, Error = Self::InnerError>
138    {
139        type InnerError: fmt::Debug + fmt::Display + Send + Sync + 'static;
140    }
141
142    impl<D, E> ContractStorageTrait for D
143    where
144        D: StorageInspect<ContractsLatestUtxo, Error = E>
145            + MerkleRootStorage<ContractId, ContractsState, Error = E>
146            + MerkleRootStorage<ContractId, ContractsAssets, Error = E>,
147        E: fmt::Debug + fmt::Display + Send + Sync + 'static,
148    {
149        type InnerError = E;
150    }
151}
152
153#[cfg(not(feature = "smt"))]
154mod not_smt {
155    use super::*;
156    use fuel_core_storage::Error as StorageError;
157
158    impl<Database> ContractRef<Database> {
159        pub fn balance_root(&self) -> Result<Bytes32, StorageError> {
160            Ok(Bytes32::zeroed())
161        }
162    }
163
164    impl<Database> ContractRef<Database> {
165        pub fn state_root(&self) -> Result<Bytes32, StorageError> {
166            Ok(Bytes32::zeroed())
167        }
168    }
169
170    pub trait ContractStorageTrait:
171        StorageInspect<ContractsLatestUtxo, Error = Self::InnerError>
172    {
173        type InnerError: fmt::Debug + fmt::Display + Send + Sync + 'static;
174    }
175
176    impl<D, E> ContractStorageTrait for D
177    where
178        D: StorageInspect<ContractsLatestUtxo, Error = E>,
179        E: fmt::Debug + fmt::Display + Send + Sync + 'static,
180    {
181        type InnerError = E;
182    }
183}
184
185impl<'a, Database> ContractRef<&'a Database>
186where
187    &'a Database: ContractStorageTrait<InnerError = StorageError>,
188{
189    /// Returns the state root of the whole contract.
190    pub fn root(&self) -> anyhow::Result<MerkleRoot> {
191        let contract_id = *self.contract_id();
192        let utxo = *self
193            .database()
194            .storage::<ContractsLatestUtxo>()
195            .get(&contract_id)?
196            .ok_or(not_found!(ContractsLatestUtxo))?
197            .into_owned()
198            .utxo_id();
199
200        let state_root = self.state_root()?;
201
202        let balance_root = self.balance_root()?;
203
204        let contract_hash = *Hasher::default()
205            // `ContractId` already is based on contract's code and salt so we don't need it.
206            .chain(contract_id.as_ref())
207            .chain(utxo.tx_id().as_ref())
208            .chain(utxo.output_index().to_be_bytes())
209            .chain(state_root.as_slice())
210            .chain(balance_root.as_slice())
211            .finalize();
212
213        Ok(contract_hash)
214    }
215}