fuel_block_executor/refs/
contract.rs

1use fuel_chain_config::GenesisCommitment;
2use fuel_core_interfaces::{
3    common::{
4        fuel_storage::{
5            Mappable,
6            MerkleRootStorage,
7            StorageAsMut,
8            StorageAsRef,
9            StorageInspect,
10        },
11        fuel_tx::{
12            Bytes32,
13            ContractId,
14        },
15        prelude::{
16            Hasher,
17            MerkleRoot,
18        },
19    },
20    db::{
21        ContractsAssets,
22        ContractsLatestUtxo,
23        ContractsState,
24    },
25    executor::Error,
26    not_found,
27};
28use std::{
29    borrow::Cow,
30    error::Error as StdError,
31};
32
33/// The wrapper around `contract_id` to simplify work with `Contract` in the database.
34pub struct ContractRef<Database> {
35    database: Database,
36    contract_id: ContractId,
37}
38
39impl<Database> ContractRef<Database> {
40    pub fn new(database: Database, contract_id: ContractId) -> Self {
41        Self {
42            database,
43            contract_id,
44        }
45    }
46
47    pub fn contract_id(&self) -> &ContractId {
48        &self.contract_id
49    }
50
51    pub fn database(&self) -> &Database {
52        &self.database
53    }
54
55    pub fn database_mut(&mut self) -> &mut Database {
56        &mut self.database
57    }
58}
59
60impl<Database> ContractRef<Database>
61where
62    Database: StorageInspect<ContractsLatestUtxo>,
63    Error: From<Database::Error>,
64{
65    pub fn utxo(
66        &self,
67    ) -> Result<
68        Option<Cow<'_, <ContractsLatestUtxo as Mappable>::GetValue>>,
69        Database::Error,
70    > {
71        self.database.storage().get(&self.contract_id)
72    }
73}
74
75impl<Database> ContractRef<Database>
76where
77    Database: StorageInspect<ContractsLatestUtxo>,
78    Error: From<Database::Error>,
79{
80    pub fn validated_utxo(
81        &self,
82        utxo_validation: bool,
83    ) -> Result<<ContractsLatestUtxo as Mappable>::GetValue, Error> {
84        let maybe_utxo_id = self.utxo()?.map(|utxo| utxo.into_owned());
85        let expected_utxo_id = if utxo_validation {
86            maybe_utxo_id.ok_or(Error::ContractUtxoMissing(self.contract_id))?
87        } else {
88            maybe_utxo_id.unwrap_or_default()
89        };
90        Result::<_, Error>::Ok(expected_utxo_id)
91    }
92}
93
94impl<Database> ContractRef<Database>
95where
96    for<'b> Database: MerkleRootStorage<ContractId, ContractsAssets<'b>>,
97{
98    pub fn balance_root(
99        &mut self,
100    ) -> Result<Bytes32, <Database as StorageInspect<ContractsAssets<'_>>>::Error> {
101        self.database.root(&self.contract_id).map(Into::into)
102    }
103}
104
105impl<Database> ContractRef<Database>
106where
107    for<'b> Database: MerkleRootStorage<ContractId, ContractsState<'b>>,
108{
109    pub fn state_root(
110        &mut self,
111    ) -> Result<Bytes32, <Database as StorageInspect<ContractsState<'_>>>::Error> {
112        self.database.root(&self.contract_id).map(Into::into)
113    }
114}
115
116pub trait ContractStorageTrait<'a>:
117    StorageInspect<ContractsLatestUtxo, Error = Self::InnerError>
118    + MerkleRootStorage<ContractId, ContractsState<'a>, Error = Self::InnerError>
119    + MerkleRootStorage<ContractId, ContractsAssets<'a>, Error = Self::InnerError>
120{
121    type InnerError: StdError + Send + Sync + 'static;
122}
123
124impl<'a, Database> GenesisCommitment for ContractRef<&'a mut Database>
125where
126    for<'b> Database: ContractStorageTrait<'a>,
127{
128    fn root(&mut self) -> anyhow::Result<MerkleRoot> {
129        let contract_id = *self.contract_id();
130        let utxo = self
131            .database()
132            .storage::<ContractsLatestUtxo>()
133            .get(&contract_id)?
134            .ok_or(not_found!(ContractsLatestUtxo))?
135            .into_owned();
136
137        let state_root = self
138            .database_mut()
139            .storage::<ContractsState>()
140            .root(&contract_id)?;
141
142        let balance_root = self
143            .database_mut()
144            .storage::<ContractsAssets>()
145            .root(&contract_id)?;
146
147        let contract_hash = *Hasher::default()
148            // `ContractId` already is based on contract's code and salt so we don't need it.
149            .chain(contract_id.as_ref())
150            .chain(utxo.tx_id().as_ref())
151            .chain([utxo.output_index()])
152            .chain(state_root.as_slice())
153            .chain(balance_root.as_slice())
154            .finalize();
155
156        Ok(contract_hash)
157    }
158}