use fuel_storage::{
StorageAsRef,
StorageInspect,
StorageMutate,
StorageRead,
StorageSize,
StorageWrite,
};
use fuel_tx::{
ConsensusParameters,
Contract,
StorageSlot,
};
use fuel_types::{
AssetId,
BlockHeight,
Bytes32,
ContractId,
Word,
};
use crate::{
prelude::{
InterpreterError,
RuntimeError,
},
storage::{
ContractsAssets,
ContractsRawCode,
ContractsState,
ContractsStateData,
UploadedBytecode,
UploadedBytecodes,
},
};
use alloc::{
borrow::Cow,
vec::Vec,
};
use core::ops::{
Deref,
DerefMut,
};
pub trait InterpreterStorage:
StorageWrite<ContractsRawCode, Error = Self::DataError>
+ StorageSize<ContractsRawCode, Error = Self::DataError>
+ StorageRead<ContractsRawCode, Error = Self::DataError>
+ StorageWrite<ContractsState, Error = Self::DataError>
+ StorageSize<ContractsState, Error = Self::DataError>
+ StorageRead<ContractsState, Error = Self::DataError>
+ StorageMutate<UploadedBytecodes, Error = Self::DataError>
+ ContractsAssetsStorage<Error = Self::DataError>
{
type DataError: Into<InterpreterError<Self::DataError>>
+ Into<RuntimeError<Self::DataError>>
+ core::fmt::Debug;
fn block_height(&self) -> Result<BlockHeight, Self::DataError>;
fn consensus_parameters_version(&self) -> Result<u32, Self::DataError>;
fn state_transition_version(&self) -> Result<u32, Self::DataError>;
fn timestamp(&self, height: BlockHeight) -> Result<Word, Self::DataError>;
fn block_hash(&self, block_height: BlockHeight) -> Result<Bytes32, Self::DataError>;
fn coinbase(&self) -> Result<ContractId, Self::DataError>;
fn set_consensus_parameters(
&mut self,
version: u32,
consensus_parameters: &ConsensusParameters,
) -> Result<Option<ConsensusParameters>, Self::DataError>;
fn contains_state_transition_bytecode_root(
&self,
root: &Bytes32,
) -> Result<bool, Self::DataError> {
let bytecode = self.storage::<UploadedBytecodes>().get(root)?;
if let Some(cow) = bytecode {
if let UploadedBytecode::Completed(_) = cow.as_ref() {
Ok(true)
} else {
Ok(false)
}
} else {
Ok(false)
}
}
fn set_state_transition_bytecode(
&mut self,
version: u32,
hash: &Bytes32,
) -> Result<Option<Bytes32>, Self::DataError>;
fn deploy_contract_with_id(
&mut self,
slots: &[StorageSlot],
contract: &Contract,
id: &ContractId,
) -> Result<(), Self::DataError> {
self.storage_contract_insert(id, contract)?;
slots.iter().try_for_each(|s| {
self.contract_state_insert(id, s.key(), s.value().as_ref())?;
Ok(())
})?;
Ok(())
}
fn storage_contract(
&self,
id: &ContractId,
) -> Result<Option<Cow<'_, Contract>>, Self::DataError> {
StorageInspect::<ContractsRawCode>::get(self, id)
}
fn storage_contract_size(
&self,
id: &ContractId,
) -> Result<Option<usize>, Self::DataError> {
StorageSize::<ContractsRawCode>::size_of_value(self, id)
}
fn read_contract(
&self,
id: &ContractId,
writer: &mut [u8],
) -> Result<Option<Word>, Self::DataError> {
Ok(StorageRead::<ContractsRawCode>::read(self, id, writer)?.map(|r| r as Word))
}
fn storage_contract_insert(
&mut self,
id: &ContractId,
contract: &Contract,
) -> Result<Option<Contract>, Self::DataError> {
StorageMutate::<ContractsRawCode>::insert(self, id, contract.as_ref())
}
fn storage_contract_exists(&self, id: &ContractId) -> Result<bool, Self::DataError> {
self.storage::<ContractsRawCode>().contains_key(id)
}
fn contract_state(
&self,
id: &ContractId,
key: &Bytes32,
) -> Result<Option<Cow<'_, ContractsStateData>>, Self::DataError> {
StorageInspect::<ContractsState>::get(self, &(id, key).into())
}
fn contract_state_insert(
&mut self,
contract: &ContractId,
key: &Bytes32,
value: &[u8],
) -> Result<(usize, Option<Vec<u8>>), Self::DataError> {
let result = StorageWrite::<ContractsState>::replace(
self,
&(contract, key).into(),
value,
)?;
Ok(result)
}
fn contract_state_remove(
&mut self,
contract: &ContractId,
key: &Bytes32,
) -> Result<Option<ContractsStateData>, Self::DataError> {
let result = StorageWrite::<ContractsState>::take(self, &(contract, key).into())?
.map(Into::into);
Ok(result)
}
fn contract_state_range(
&self,
id: &ContractId,
start_key: &Bytes32,
range: usize,
) -> Result<Vec<Option<Cow<ContractsStateData>>>, Self::DataError>;
fn contract_state_insert_range<'a, I>(
&mut self,
contract: &ContractId,
start_key: &Bytes32,
values: I,
) -> Result<usize, Self::DataError>
where
I: Iterator<Item = &'a [u8]>;
fn contract_state_remove_range(
&mut self,
contract: &ContractId,
start_key: &Bytes32,
range: usize,
) -> Result<Option<()>, Self::DataError>;
}
pub trait ContractsAssetsStorage: StorageMutate<ContractsAssets> {
fn contract_asset_id_balance(
&self,
id: &ContractId,
asset_id: &AssetId,
) -> Result<Option<Word>, Self::Error> {
let balance = self
.storage::<ContractsAssets>()
.get(&(id, asset_id).into())?
.map(Cow::into_owned);
Ok(balance)
}
fn contract_asset_id_balance_insert(
&mut self,
contract: &ContractId,
asset_id: &AssetId,
value: Word,
) -> Result<Option<Word>, Self::Error> {
StorageMutate::<ContractsAssets>::insert(
self,
&(contract, asset_id).into(),
&value,
)
}
}
impl<S> ContractsAssetsStorage for &mut S where S: ContractsAssetsStorage {}
impl<S> InterpreterStorage for &mut S
where
S: InterpreterStorage,
{
type DataError = <S as InterpreterStorage>::DataError;
fn block_height(&self) -> Result<BlockHeight, Self::DataError> {
<S as InterpreterStorage>::block_height(self.deref())
}
fn consensus_parameters_version(&self) -> Result<u32, Self::DataError> {
<S as InterpreterStorage>::consensus_parameters_version(self.deref())
}
fn state_transition_version(&self) -> Result<u32, Self::DataError> {
<S as InterpreterStorage>::state_transition_version(self.deref())
}
fn timestamp(&self, height: BlockHeight) -> Result<Word, Self::DataError> {
<S as InterpreterStorage>::timestamp(self.deref(), height)
}
fn block_hash(&self, block_height: BlockHeight) -> Result<Bytes32, Self::DataError> {
<S as InterpreterStorage>::block_hash(self.deref(), block_height)
}
fn coinbase(&self) -> Result<ContractId, Self::DataError> {
<S as InterpreterStorage>::coinbase(self.deref())
}
fn set_consensus_parameters(
&mut self,
version: u32,
consensus_parameters: &ConsensusParameters,
) -> Result<Option<ConsensusParameters>, Self::DataError> {
<S as InterpreterStorage>::set_consensus_parameters(
self.deref_mut(),
version,
consensus_parameters,
)
}
fn set_state_transition_bytecode(
&mut self,
version: u32,
hash: &Bytes32,
) -> Result<Option<Bytes32>, Self::DataError> {
<S as InterpreterStorage>::set_state_transition_bytecode(
self.deref_mut(),
version,
hash,
)
}
fn storage_contract_size(
&self,
id: &ContractId,
) -> Result<Option<usize>, Self::DataError> {
<S as InterpreterStorage>::storage_contract_size(self.deref(), id)
}
fn read_contract(
&self,
id: &ContractId,
writer: &mut [u8],
) -> Result<Option<Word>, Self::DataError> {
<S as InterpreterStorage>::read_contract(self.deref(), id, writer)
}
fn contract_state_range(
&self,
id: &ContractId,
start_key: &Bytes32,
range: usize,
) -> Result<Vec<Option<Cow<ContractsStateData>>>, Self::DataError> {
<S as InterpreterStorage>::contract_state_range(
self.deref(),
id,
start_key,
range,
)
}
fn contract_state_insert_range<'a, I>(
&mut self,
contract: &ContractId,
start_key: &Bytes32,
values: I,
) -> Result<usize, Self::DataError>
where
I: Iterator<Item = &'a [u8]>,
{
<S as InterpreterStorage>::contract_state_insert_range(
self.deref_mut(),
contract,
start_key,
values,
)
}
fn contract_state_remove_range(
&mut self,
contract: &ContractId,
start_key: &Bytes32,
range: usize,
) -> Result<Option<()>, Self::DataError> {
<S as InterpreterStorage>::contract_state_remove_range(
self.deref_mut(),
contract,
start_key,
range,
)
}
}