use anyhow::Result;
use pchain_world_state::storage::WorldStateStorage;
use std::{
convert::TryInto,
mem::MaybeUninit,
sync::{Arc, Mutex},
};
use wasmer::{Global, LazyInit, Memory, NativeFunc};
use crate::{
contract::FuncError, cost::CostChange, gas, transition::TransitionContext, types::CallTx,
wasmer::wasmer_memory::MemoryContext, BlockchainParams,
};
#[derive(wasmer::WasmerEnv, Clone)]
pub(crate) struct Env<S>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
pub context: Arc<Mutex<TransitionContext<S>>>,
pub call_counter: u32,
pub gas_meter: Arc<Mutex<MaybeUninit<GasMeter>>>,
pub call_tx: CallTx,
pub params_from_blockchain: BlockchainParams,
pub is_view: bool,
#[wasmer(export)]
pub memory: LazyInit<Memory>,
#[wasmer(export(name = "alloc"))]
pub alloc: LazyInit<NativeFunc<u32, wasmer::WasmPtr<u8, wasmer::Array>>>,
}
impl<S> Env<S>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
pub fn new(
context: Arc<Mutex<TransitionContext<S>>>,
call_counter: u32,
is_view: bool,
call_tx: CallTx,
params_from_blockchain: BlockchainParams,
) -> Env<S> {
Env {
context,
call_counter,
gas_meter: Arc::new(Mutex::new(MaybeUninit::uninit())),
memory: LazyInit::default(),
alloc: LazyInit::default(),
call_tx,
params_from_blockchain,
is_view,
}
}
pub fn init_wasmer_remaining_points(&self, global: Global) {
self.gas_meter.lock().unwrap().write(GasMeter {
wasmer_gas: global,
non_wasmer_gas_amount: 0,
});
}
pub fn drop_wasmer_remaining_points(&self) {
unsafe { self.gas_meter.lock().unwrap().assume_init_drop() };
}
pub fn get_wasmer_remaining_points(&self) -> u64 {
unsafe {
self.gas_meter
.lock()
.unwrap()
.assume_init_ref()
.wasmer_gas
.get()
.try_into()
.unwrap()
}
}
pub fn get_non_wasm_gas_amount(&self) -> u64 {
unsafe {
self.gas_meter
.lock()
.unwrap()
.assume_init_ref()
.non_wasmer_gas_amount
}
}
pub fn consume_non_wasm_gas(&self, change: CostChange) {
let (deduct, _) = change.values();
if deduct > 0 {
unsafe {
self.gas_meter
.lock()
.unwrap()
.assume_init_mut()
.substract_non_wasmer_gas(deduct);
}
}
}
pub fn consume_wasm_gas(&self, gas_consumed: u64) -> u64 {
let gas_meter_lock = self.gas_meter.lock().unwrap();
unsafe { gas_meter_lock.assume_init_ref().substract(gas_consumed) }
}
pub fn write_bytes(&self, value: Vec<u8>, val_ptr_ptr: u32) -> Result<u32, FuncError> {
self.consume_wasm_gas(gas::wasm_memory_write_cost(value.len()));
MemoryContext::write_bytes_to_memory(self, value, val_ptr_ptr).map_err(FuncError::Runtime)
}
pub fn read_bytes(&self, offset: u32, len: u32) -> Result<Vec<u8>, FuncError> {
self.consume_wasm_gas(gas::wasm_memory_read_cost(len as usize));
MemoryContext::read_bytes_from_memory(self, offset, len).map_err(FuncError::Runtime)
}
}
impl<S> MemoryContext for Env<S>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
fn get_memory(&self) -> &Memory {
self.memory_ref().unwrap()
}
fn get_alloc(&self) -> &NativeFunc<u32, wasmer::WasmPtr<u8, wasmer::Array>> {
self.alloc_ref().unwrap()
}
}
pub(crate) struct GasMeter {
wasmer_gas: wasmer::Global,
non_wasmer_gas_amount: u64,
}
impl GasMeter {
fn substract(&self, amount: u64) -> u64 {
let current_remaining_points: u64 = self.wasmer_gas.get().try_into().unwrap();
let new_remaining_points = current_remaining_points.saturating_sub(amount);
self.wasmer_gas
.set(new_remaining_points.into())
.expect("Can't subtract `wasmer_metering_remaining_points` in Env");
new_remaining_points
}
fn substract_non_wasmer_gas(&mut self, amount: u64) -> u64 {
self.non_wasmer_gas_amount = self.non_wasmer_gas_amount.saturating_add(amount);
self.substract(amount)
}
}