fuel_core_executor/refs/
contract.rs1use 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
35pub 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 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 .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}