diff --git a/src/runtime/import/util.rs b/src/runtime/import/util.rs index ac3f0d3f1..b803286c0 100644 --- a/src/runtime/import/util.rs +++ b/src/runtime/import/util.rs @@ -23,11 +23,13 @@ use super::acl::acl_allow; use crate::runtime::vm_runtime::{ContractSection, Env}; /// Host function for logging strings. -pub(crate) fn drk_log(ctx: FunctionEnvMut, ptr: WasmPtr, len: u32) { - let env = ctx.data(); - let cid = &env.contract_id; - let memory_view = env.memory_view(&ctx); +pub(crate) fn drk_log(mut ctx: FunctionEnvMut, ptr: WasmPtr, len: u32) { + let (env, mut store) = ctx.data_and_store_mut(); + // Subtract used gas. Here we count the length of the string. + env.subtract_gas(&mut store, len as u64); + + let memory_view = env.memory_view(&store); match ptr.read_utf8_string(&memory_view, len) { Ok(msg) => { let mut logs = env.logs.borrow_mut(); @@ -37,7 +39,8 @@ pub(crate) fn drk_log(ctx: FunctionEnvMut, ptr: WasmPtr, len: u32) { Err(_) => { error!( target: "runtime::util::drk_log", - "[WASM] [{}] drk_log(): Failed to read UTF-8 string from VM memory", cid, + "[WASM] [{}] drk_log(): Failed to read UTF-8 string from VM memory", + env.contract_id, ); } } @@ -48,8 +51,8 @@ pub(crate) fn drk_log(ctx: FunctionEnvMut, ptr: WasmPtr, len: u32) { /// /// Returns `SUCCESS` on success, otherwise returns an error code corresponding /// to a [`ContractError`]. -pub(crate) fn set_return_data(ctx: FunctionEnvMut, ptr: WasmPtr, len: u32) -> i64 { - let env = ctx.data(); +pub(crate) fn set_return_data(mut ctx: FunctionEnvMut, ptr: WasmPtr, len: u32) -> i64 { + let (env, mut store) = ctx.data_and_store_mut(); let cid = &env.contract_id; // Enforce function ACL @@ -61,7 +64,10 @@ pub(crate) fn set_return_data(ctx: FunctionEnvMut, ptr: WasmPtr, len: u return darkfi_sdk::error::CALLER_ACCESS_DENIED } - let memory_view = env.memory_view(&ctx); + // Subtract used gas. Here we count the length read from the memory slice. + env.subtract_gas(&mut store, len as u64); + + let memory_view = env.memory_view(&store); let Ok(slice) = ptr.slice(&memory_view, len) else { return darkfi_sdk::error::INTERNAL_ERROR }; let Ok(return_data) = slice.read_to_vec() else { return darkfi_sdk::error::INTERNAL_ERROR }; @@ -79,11 +85,14 @@ pub(crate) fn set_return_data(ctx: FunctionEnvMut, ptr: WasmPtr, len: u /// /// Returns an index corresponding to the new object's index in the objects /// store. (This index is equal to the last index in the store.) -pub(crate) fn put_object_bytes(ctx: FunctionEnvMut, ptr: WasmPtr, len: u32) -> i64 { - let env = ctx.data(); - let cid = &env.contract_id; - let memory_view = env.memory_view(&ctx); +pub(crate) fn put_object_bytes(mut ctx: FunctionEnvMut, ptr: WasmPtr, len: u32) -> i64 { + let (env, mut store) = ctx.data_and_store_mut(); + let cid = env.contract_id; + // Subtract used gas. Here we count the length read from the memory slice. + env.subtract_gas(&mut store, len as u64); + + let memory_view = env.memory_view(&store); //debug!(target: "runtime::util", "diagnostic:"); let pages = memory_view.size().0; //debug!(target: "runtime::util", " pages: {}", pages); @@ -128,11 +137,10 @@ pub(crate) fn put_object_bytes(ctx: FunctionEnvMut, ptr: WasmPtr, len: /// The object's data is written to `ptr`. /// /// Returns `SUCCESS` on success and an error code otherwise. -pub(crate) fn get_object_bytes(ctx: FunctionEnvMut, ptr: WasmPtr, idx: u32) -> i64 { +pub(crate) fn get_object_bytes(mut ctx: FunctionEnvMut, ptr: WasmPtr, idx: u32) -> i64 { // Get the slice, where we will read the size of the buffer - let env = ctx.data(); - let cid = &env.contract_id; - let memory_view = env.memory_view(&ctx); + let (env, mut store) = ctx.data_and_store_mut(); + let cid = env.contract_id; // Get the object from env let objects = env.objects.borrow(); @@ -143,12 +151,18 @@ pub(crate) fn get_object_bytes(ctx: FunctionEnvMut, ptr: WasmPtr, idx: ); return darkfi_sdk::error::DATA_TOO_LARGE } - let obj = &objects[idx as usize]; + let obj = objects[idx as usize].clone(); + drop(objects); + if obj.len() > u32::MAX as usize { return darkfi_sdk::error::DATA_TOO_LARGE } + // Subtract used gas. Here we count the bytes written to the memory slice + env.subtract_gas(&mut store, obj.len() as u64); + // Read N bytes from the object and write onto the ptr. + let memory_view = env.memory_view(&store); let Ok(slice) = ptr.slice(&memory_view, obj.len() as u32) else { error!( target: "runtime::util::get_object_bytes", @@ -158,7 +172,7 @@ pub(crate) fn get_object_bytes(ctx: FunctionEnvMut, ptr: WasmPtr, idx: }; // Put the result in the VM - if let Err(e) = slice.write_slice(obj) { + if let Err(e) = slice.write_slice(&obj) { error!( target: "runtime::util::get_object_bytes", "[WASM] [{}] get_object_bytes(): Failed to write to memory slice: {}", cid, e, @@ -171,10 +185,10 @@ pub(crate) fn get_object_bytes(ctx: FunctionEnvMut, ptr: WasmPtr, idx: /// Returns the size (number of bytes) of an object in the object store /// specified by index `idx`. -pub(crate) fn get_object_size(ctx: FunctionEnvMut, idx: u32) -> i64 { +pub(crate) fn get_object_size(mut ctx: FunctionEnvMut, idx: u32) -> i64 { // Get the slice, where we will read the size of the buffer - let env = ctx.data(); - let cid = &env.contract_id; + let (env, mut store) = ctx.data_and_store_mut(); + let cid = env.contract_id; // Get the object from env let objects = env.objects.borrow(); @@ -187,48 +201,62 @@ pub(crate) fn get_object_size(ctx: FunctionEnvMut, idx: u32) -> i64 { } let obj = &objects[idx as usize]; - if obj.len() > u32::MAX as usize { + let obj_len = obj.len(); + drop(objects); + + if obj_len > u32::MAX as usize { return darkfi_sdk::error::DATA_TOO_LARGE } - obj.len() as i64 + // Subtract used gas. Here we count the size of the object. + // TODO: This could probably be fixed-cost + env.subtract_gas(&mut store, obj_len as u64); + + obj_len as i64 } /// Will return current epoch number. pub(crate) fn get_current_epoch(ctx: FunctionEnvMut) -> u64 { + // TODO: Gas cost ctx.data().time_keeper.current_epoch() } /// Will return current block height number, which is equivalent /// to current slot number. pub(crate) fn get_current_block_height(ctx: FunctionEnvMut) -> u64 { + // TODO: Gas cost ctx.data().time_keeper.current_slot() } /// Will return current slot number. pub(crate) fn get_current_slot(ctx: FunctionEnvMut) -> u64 { + // TODO: Gas cost ctx.data().time_keeper.current_slot() } /// Will return current runtime configured verifying block height number, /// which is equivalent to verifying slot number. pub(crate) fn get_verifying_block_height(ctx: FunctionEnvMut) -> u64 { + // TODO: Gas cost ctx.data().time_keeper.verifying_slot } /// Will return current runtime configured verifying slot number. pub(crate) fn get_verifying_slot(ctx: FunctionEnvMut) -> u64 { + // TODO: Gas cost ctx.data().time_keeper.verifying_slot } /// Will return current runtime configured verifying block height epoch number, /// which is equivalent to verifying slot epoch number. pub(crate) fn get_verifying_block_height_epoch(ctx: FunctionEnvMut) -> u64 { + // TODO: Gas cost ctx.data().time_keeper.verifying_slot_epoch() } /// Will return current runtime configured verifying slot epoch number. pub(crate) fn get_verifying_slot_epoch(ctx: FunctionEnvMut) -> u64 { + // TODO: Gas cost ctx.data().time_keeper.verifying_slot_epoch() } @@ -252,6 +280,7 @@ pub(crate) fn get_slot(ctx: FunctionEnvMut, slot: u64) -> i64 { return darkfi_sdk::error::CALLER_ACCESS_DENIED } + // TODO: Gas cost let ret = match env.blockchain.lock().unwrap().slots.get_by_id(slot) { Ok(v) => v, Err(e) => { @@ -275,5 +304,6 @@ pub(crate) fn get_slot(ctx: FunctionEnvMut, slot: u64) -> i64 { /// Will return current blockchain timestamp. pub(crate) fn get_blockchain_time(ctx: FunctionEnvMut) -> u64 { + // TODO: Gas cost ctx.data().time_keeper.blockchain_timestamp() } diff --git a/src/runtime/vm_runtime.rs b/src/runtime/vm_runtime.rs index 7d006ff2b..d4b391830 100644 --- a/src/runtime/vm_runtime.rs +++ b/src/runtime/vm_runtime.rs @@ -25,12 +25,12 @@ use darkfi_sdk::{crypto::ContractId, entrypoint}; use darkfi_serial::serialize; use log::{debug, error, info}; use wasmer::{ - imports, wasmparser::Operator, AsStoreRef, CompilerConfig, Function, FunctionEnv, Instance, - Memory, MemoryView, Module, Pages, Store, Value, WASM_PAGE_SIZE, + imports, wasmparser::Operator, AsStoreMut, AsStoreRef, CompilerConfig, Function, FunctionEnv, + Instance, Memory, MemoryView, Module, Pages, Store, Value, WASM_PAGE_SIZE, }; use wasmer_compiler_singlepass::Singlepass; use wasmer_middlewares::{ - metering::{get_remaining_points, MeteringPoints}, + metering::{get_remaining_points, set_remaining_points, MeteringPoints}, Metering, }; @@ -97,6 +97,8 @@ pub struct Env { pub objects: RefCell>>, /// Helper structure to calculate time related operations pub time_keeper: TimeKeeper, + /// Parent `Instance` + pub instance: Option>, } impl Env { @@ -115,12 +117,28 @@ impl Env { pub fn memory(&self) -> &Memory { self.memory.as_ref().unwrap() } + + /// Subtract given gas cost from remaining gas in the current runtime + pub fn subtract_gas(&mut self, ctx: &mut impl AsStoreMut, gas: u64) { + match get_remaining_points(ctx, self.instance.as_ref().unwrap()) { + MeteringPoints::Remaining(rem) => { + if gas > rem { + set_remaining_points(ctx, self.instance.as_ref().unwrap(), 0); + } else { + set_remaining_points(ctx, self.instance.as_ref().unwrap(), rem - gas); + } + } + MeteringPoints::Exhausted => { + set_remaining_points(ctx, self.instance.as_ref().unwrap(), 0); + } + } + } } /// Define a wasm runtime. pub struct Runtime { /// A wasm instance - pub instance: Instance, + pub instance: Arc, /// A wasm store (global state) pub store: Store, // Wrapper for [`Env`], defined above. @@ -175,6 +193,7 @@ impl Runtime { memory: None, objects: RefCell::new(vec![]), time_keeper, + instance: None, }, ); @@ -315,10 +334,11 @@ impl Runtime { }; debug!(target: "runtime::vm_runtime", "Instantiating module"); - let instance = Instance::new(&mut store, &module, &imports)?; + let instance = Arc::new(Instance::new(&mut store, &module, &imports)?); let env_mut = ctx.as_mut(&mut store); env_mut.memory = Some(instance.exports.get_with_generics(MEMORY)?); + env_mut.instance = Some(Arc::clone(&instance)); Ok(Self { instance, store, ctx }) }