fuel_core_executor/refs/
contract.rs1use core::fmt;
2use fuel_core_storage::{
3 Error as StorageError,
4 Mappable,
5 MerkleRoot,
6 StorageAsRef,
7 StorageInspect,
8 iter::{
9 IterableStore,
10 IteratorOverTable,
11 },
12 not_found,
13 tables::{
14 ContractsAssets,
15 ContractsLatestUtxo,
16 ContractsState,
17 },
18};
19use fuel_core_types::{
20 fuel_crypto::Hasher,
21 fuel_types::{
22 Bytes32,
23 ContractId,
24 },
25 services::executor::{
26 Error as ExecutorError,
27 Result as ExecutorResult,
28 },
29};
30#[cfg(feature = "std")]
31use std::borrow::Cow;
32
33#[cfg(not(feature = "std"))]
34use alloc::borrow::Cow;
35
36use crate::contract_state_hash::{
37 compute_balances_hash,
38 compute_state_hash,
39};
40
41pub struct ContractRef<Database> {
43 database: Database,
44 contract_id: ContractId,
45}
46
47impl<Database> ContractRef<Database> {
48 pub fn new(database: Database, contract_id: ContractId) -> Self {
49 Self {
50 database,
51 contract_id,
52 }
53 }
54
55 pub fn contract_id(&self) -> &ContractId {
56 &self.contract_id
57 }
58
59 pub fn database(&self) -> &Database {
60 &self.database
61 }
62
63 pub fn database_mut(&mut self) -> &mut Database {
64 &mut self.database
65 }
66}
67
68impl<Database> ContractRef<Database>
69where
70 Database: StorageInspect<ContractsLatestUtxo>,
71 ExecutorError: From<Database::Error>,
72{
73 pub fn utxo(
74 &self,
75 ) -> Result<
76 Option<Cow<'_, <ContractsLatestUtxo as Mappable>::OwnedValue>>,
77 Database::Error,
78 > {
79 self.database.storage().get(&self.contract_id)
80 }
81}
82
83impl<Database> ContractRef<Database>
84where
85 Database: StorageInspect<ContractsLatestUtxo>,
86 ExecutorError: From<Database::Error>,
87{
88 pub fn validated_utxo(
89 &self,
90 utxo_validation: bool,
91 ) -> ExecutorResult<<ContractsLatestUtxo as Mappable>::OwnedValue> {
92 let maybe_utxo_id = self.utxo()?.map(|utxo| utxo.into_owned());
93 let expected_utxo_id = if utxo_validation {
94 maybe_utxo_id.ok_or(ExecutorError::ContractUtxoMissing(self.contract_id))?
95 } else {
96 maybe_utxo_id.unwrap_or_default()
97 };
98 Ok(expected_utxo_id)
99 }
100}
101
102impl<Database> ContractRef<Database>
103where
104 Database: IterableStore<Column = fuel_core_storage::column::Column>,
105{
106 pub fn balance_root(&self) -> Result<Bytes32, StorageError> {
107 Ok(compute_balances_hash(
108 &self
109 .database
110 .iter_all_by_prefix::<ContractsAssets, _>(Some(self.contract_id))
111 .map(|res| res.map(|(key, value)| (*key.asset_id(), Some(value))))
112 .collect::<Result<_, _>>()?,
113 ))
114 }
115}
116
117impl<Database> ContractRef<Database>
118where
119 Database: IterableStore<Column = fuel_core_storage::column::Column>,
120{
121 pub fn state_root(&self) -> Result<Bytes32, StorageError> {
122 Ok(compute_state_hash(
123 &self
124 .database
125 .iter_all_by_prefix::<ContractsState, _>(Some(self.contract_id))
126 .map(|res| res.map(|(key, value)| (*key.state_key(), Some(value.into()))))
127 .collect::<Result<_, _>>()?,
128 ))
129 }
130}
131
132pub trait ContractStorageTrait:
133 StorageInspect<ContractsLatestUtxo, Error = Self::InnerError>
134{
135 type InnerError: fmt::Debug + fmt::Display + Send + Sync + 'static;
136}
137
138impl<D, E> ContractStorageTrait for D
139where
140 D: StorageInspect<ContractsLatestUtxo, Error = E>,
141 E: fmt::Debug + fmt::Display + Send + Sync + 'static,
142{
143 type InnerError = E;
144}
145
146impl<'a, Database> ContractRef<&'a Database>
147where
148 &'a Database: ContractStorageTrait<InnerError = StorageError>,
149 &'a Database: IterableStore<Column = fuel_core_storage::column::Column>,
150{
151 pub fn root(&self) -> anyhow::Result<MerkleRoot> {
153 let contract_id = *self.contract_id();
154 let utxo = *self
155 .database()
156 .storage::<ContractsLatestUtxo>()
157 .get(&contract_id)?
158 .ok_or(not_found!(ContractsLatestUtxo))?
159 .into_owned()
160 .utxo_id();
161
162 let state_root = self.state_root()?;
163
164 let balance_root = self.balance_root()?;
165
166 let contract_hash = *Hasher::default()
167 .chain(contract_id.as_ref())
169 .chain(utxo.tx_id().as_ref())
170 .chain(utxo.output_index().to_be_bytes())
171 .chain(state_root.as_slice())
172 .chain(balance_root.as_slice())
173 .finalize();
174
175 Ok(contract_hash)
176 }
177}