use std::sync::{Arc, Mutex};
use pchain_types::cryptography::PublicAddress;
use pchain_world_state::storage::WorldStateStorage;
use wasmer::Store;
use crate::{
contract::{
self, ContractBinaryFunctions, ContractValidateError, MethodCallError, ModuleBuildError,
SmartContractContext,
},
cost::CostChange,
read_write_set::ReadWriteSet,
transition::TransitionContext,
types::CallTx,
wasmer::{wasmer_env, wasmer_store},
BlockchainParams, Cache,
};
pub(crate) struct ContractModule {
store: Store,
module: contract::Module,
pub gas_cost: CostChange,
}
impl ContractModule {
pub(crate) fn new(
contract_code: &Vec<u8>,
memory_limit: Option<usize>,
) -> Result<Self, ModuleBuildError> {
let wasmer_store = wasmer_store::instantiate_store(u64::MAX, memory_limit);
let module = contract::Module::from_wasm_bytecode(
contract::CBI_VERSION,
contract_code,
&wasmer_store,
)?;
Ok(Self {
store: wasmer_store,
module,
gas_cost: CostChange::default(),
})
}
pub(crate) fn build_contract<S>(
contract_address: PublicAddress,
sc_ctx: &SmartContractContext,
rw_set: &ReadWriteSet<S>,
) -> Result<Self, ()>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
let (module, store, gas_cost) = {
let (result, gas_cost) = rw_set.code_from_sc_cache(contract_address, sc_ctx);
match result {
Some((module, store)) => (module, store, gas_cost),
None => return Err(()),
}
};
Ok(Self {
store,
module,
gas_cost,
})
}
pub(crate) fn validate(&self) -> Result<(), ContractValidateError> {
self.module.validate_contract(&self.store)
}
pub(crate) fn cache(&self, contract_address: PublicAddress, cache: &mut Cache) {
self.module.cache_to(contract_address, cache)
}
pub(crate) fn instantiate<S>(
self,
ctx: Arc<Mutex<TransitionContext<S>>>,
call_counter: u32,
is_view: bool,
tx: CallTx,
bd: BlockchainParams,
) -> Result<ContractInstance<S>, ()>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
let gas_limit = tx.gas_limit;
let environment = wasmer_env::Env::new(ctx, call_counter, is_view, tx, bd);
let importable = if is_view {
contract::create_importable_view::<wasmer_env::Env<S>, ContractBinaryFunctions>(
&self.store,
&environment,
)
} else {
contract::create_importable::<wasmer_env::Env<S>, ContractBinaryFunctions>(
&self.store,
&environment,
)
};
let instance = self
.module
.instantiate(&importable, gas_limit)
.map_err(|_| ())?;
Ok(ContractInstance {
environment,
instance,
})
}
}
pub(crate) struct ContractInstance<S>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
environment: wasmer_env::Env<S>,
instance: contract::Instance,
}
impl<S> ContractInstance<S>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
pub(crate) fn call(self) -> (TransitionContext<S>, u64, Option<MethodCallError>) {
self.environment
.init_wasmer_remaining_points(self.instance.remaining_points());
let call_result = unsafe { self.instance.call_method() };
let non_wasmer_gas_amount = self.environment.get_non_wasm_gas_amount();
self.environment.drop_wasmer_remaining_points();
let (remaining_gas, call_error) = match call_result {
Ok(remaining_gas) => (remaining_gas, None),
Err((remaining_gas, call_error)) => (remaining_gas, Some(call_error)),
};
let total_gas = self
.environment
.call_tx
.gas_limit
.saturating_sub(remaining_gas)
.saturating_sub(non_wasmer_gas_amount);
let ctx = self.environment.context.lock().unwrap().clone();
(ctx, total_gas, call_error)
}
}