use core::fmt; use fuel_core_storage::{ not_found, tables::{ ContractsAssets, ContractsLatestUtxo, ContractsState, }, Error as StorageError, Mappable, MerkleRoot, MerkleRootStorage, StorageAsRef, StorageInspect, }; use fuel_core_types::{ fuel_crypto::Hasher, fuel_types::{ Bytes32, ContractId, }, services::executor::{ Error as ExecutorError, Result as ExecutorResult, }, }; use std::borrow::Cow; /// The wrapper around `contract_id` to simplify work with `Contract` in the database. pub struct ContractRef { database: Database, contract_id: ContractId, } impl ContractRef { pub fn new(database: Database, contract_id: ContractId) -> Self { Self { database, contract_id, } } pub fn contract_id(&self) -> &ContractId { &self.contract_id } pub fn database(&self) -> &Database { &self.database } pub fn database_mut(&mut self) -> &mut Database { &mut self.database } } impl ContractRef where Database: StorageInspect, ExecutorError: From, { pub fn utxo( &self, ) -> Result< Option::OwnedValue>>, Database::Error, > { self.database.storage().get(&self.contract_id) } } impl ContractRef where Database: StorageInspect, ExecutorError: From, { pub fn validated_utxo( &self, utxo_validation: bool, ) -> ExecutorResult<::OwnedValue> { let maybe_utxo_id = self.utxo()?.map(|utxo| utxo.into_owned()); let expected_utxo_id = if utxo_validation { maybe_utxo_id.ok_or(ExecutorError::ContractUtxoMissing(self.contract_id))? } else { maybe_utxo_id.unwrap_or_default() }; Ok(expected_utxo_id) } } impl ContractRef where Database: MerkleRootStorage, { pub fn balance_root( &self, ) -> Result>::Error> { self.database.root(&self.contract_id).map(Into::into) } } impl ContractRef where Database: MerkleRootStorage, { pub fn state_root( &self, ) -> Result>::Error> { self.database.root(&self.contract_id).map(Into::into) } } pub trait ContractStorageTrait: StorageInspect + MerkleRootStorage + MerkleRootStorage { type InnerError: fmt::Debug + fmt::Display + Send + Sync + 'static; } impl ContractStorageTrait for D where D: StorageInspect + MerkleRootStorage + MerkleRootStorage, { type InnerError = StorageError; } impl<'a, Database> ContractRef<&'a Database> where Database: ContractStorageTrait, anyhow::Error: From, { /// Returns the state root of the whole contract. pub fn root(&self) -> anyhow::Result { let contract_id = *self.contract_id(); let utxo = *self .database() .storage::() .get(&contract_id)? .ok_or(not_found!(ContractsLatestUtxo))? .into_owned() .utxo_id(); let state_root = self .database .storage::() .root(&contract_id)?; let balance_root = self .database .storage::() .root(&contract_id)?; let contract_hash = *Hasher::default() // `ContractId` already is based on contract's code and salt so we don't need it. .chain(contract_id.as_ref()) .chain(utxo.tx_id().as_ref()) .chain(utxo.output_index().to_be_bytes()) .chain(state_root.as_slice()) .chain(balance_root.as_slice()) .finalize(); Ok(contract_hash) } }