diff --git a/packages/vm/src/calls.rs b/packages/vm/src/calls.rs index 32c7eff3eb..fb60521045 100644 --- a/packages/vm/src/calls.rs +++ b/packages/vm/src/calls.rs @@ -607,6 +607,7 @@ mod tests { call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg) .unwrap() .unwrap(); + assert_eq!(instance.get_gas_left(), 494235049729); } #[test] @@ -626,6 +627,7 @@ mod tests { call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg) .unwrap() .unwrap(); + assert_eq!(instance.get_gas_left(), 485686146123); } #[test] @@ -644,6 +646,7 @@ mod tests { let err = call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg).unwrap_err(); assert!(matches!(err, VmError::GasDepletion {})); + assert_eq!(instance.get_gas_left(), 0); } #[test] @@ -668,6 +671,7 @@ mod tests { } err => panic!("Unexpected error: {:?}", err), } + assert_eq!(instance.get_gas_left(), 493100600000); } #[test] @@ -690,6 +694,7 @@ mod tests { } err => panic!("Unexpected error: {:?}", err), } + assert_eq!(instance.get_gas_left(), 493655750000); } #[test] @@ -715,6 +720,7 @@ mod tests { query_response.as_slice(), b"{\"verifier\":\"someone else\"}" ); + assert_eq!(instance.get_gas_left(), 485028949541); } #[test] @@ -733,6 +739,7 @@ mod tests { let contract_result = call_query(&mut instance, &mock_env(), msg).unwrap(); let query_response = contract_result.unwrap(); assert_eq!(query_response.as_slice(), b"{\"verifier\":\"verifies\"}"); + assert_eq!(instance.get_gas_left(), 489741349723); } #[cfg(feature = "stargate")] diff --git a/packages/vm/src/environment.rs b/packages/vm/src/environment.rs index 02270726ec..df66e19e9e 100644 --- a/packages/vm/src/environment.rs +++ b/packages/vm/src/environment.rs @@ -3,7 +3,7 @@ use std::borrow::{Borrow, BorrowMut}; use std::ptr::NonNull; use std::sync::{Arc, RwLock}; -use wasmer::{HostEnvInitError, Instance as WasmerInstance, Memory, Val, WasmerEnv}; +use wasmer::{Global, HostEnvInitError, Instance as WasmerInstance, Memory, Val, WasmerEnv}; use wasmer_middlewares::metering::{get_remaining_points, set_remaining_points, MeteringPoints}; use crate::backend::{BackendApi, GasInfo, Querier, Storage}; @@ -82,6 +82,8 @@ impl GasState { /// A environment that provides access to the ContextData. /// The environment is clonable but clones access the same underlying data. pub struct Environment { + pub global_remaining_points: Option, + pub global_points_exhausted: Option, pub api: A, pub print_debug: bool, pub gas_config: GasConfig, @@ -95,6 +97,8 @@ unsafe impl Sync for Environment impl Clone for Environment { fn clone(&self) -> Self { Environment { + global_remaining_points: self.global_remaining_points.clone(), + global_points_exhausted: self.global_points_exhausted.clone(), api: self.api, print_debug: self.print_debug, gas_config: self.gas_config.clone(), @@ -112,6 +116,8 @@ impl WasmerEnv for Environment { impl Environment { pub fn new(api: A, gas_limit: u64, print_debug: bool) -> Self { Environment { + global_remaining_points: None, + global_points_exhausted: None, api, print_debug, gas_config: GasConfig::default(), @@ -272,6 +278,12 @@ impl Environment { }) } + pub fn get_gas_left_ex(&self, remain: &Global, exhausted: &Global) -> u64 { + match exhausted.get() { + value if value.unwrap_i32() > 0 => 0, + _ => u64::try_from(remain.get()).unwrap(), + } + } pub fn get_gas_left(&self) -> u64 { self.with_wasmer_instance(|instance| { Ok(match get_remaining_points(instance) { @@ -282,6 +294,11 @@ impl Environment { .expect("Wasmer instance is not set. This is a bug in the lifecycle.") } + pub fn set_global(&mut self, remain: Global, exhausted: Global) { + self.global_remaining_points = Some(remain); + self.global_points_exhausted = Some(exhausted) + } + pub fn set_gas_left(&self, new_value: u64) { self.with_wasmer_instance(|instance| { set_remaining_points(instance, new_value); @@ -290,6 +307,11 @@ impl Environment { .expect("Wasmer instance is not set. This is a bug in the lifecycle.") } + pub fn set_gas_left_ex(&self, a: &Global, new_limit: u64) { + a.set(new_limit.into()) + .expect("Can't set `wasmer_metering_remaining_points` in Instance"); + } + /// Decreases gas left by the given amount. /// If the amount exceeds the available gas, the remaining gas is set to 0 and /// an VmError::GasDepletion error is returned. @@ -374,7 +396,9 @@ pub fn process_gas_info( env: &Environment, info: GasInfo, ) -> VmResult<()> { - let gas_left = env.get_gas_left(); + let remain_points = env.global_remaining_points.as_ref().unwrap(); + let exhausted_points = env.global_points_exhausted.as_ref().unwrap(); + let gas_left = env.get_gas_left_ex(remain_points, exhausted_points); let new_limit = env.with_gas_state_mut(|gas_state| { gas_state.externally_used_gas += info.externally_used; @@ -386,7 +410,7 @@ pub fn process_gas_info( }); // This tells wasmer how much more gas it can consume from this point in time. - env.set_gas_left(new_limit); + env.set_gas_left_ex(remain_points, new_limit); if info.externally_used + info.cost > gas_left { Err(VmError::gas_depletion()) @@ -429,7 +453,7 @@ mod tests { Environment, Box, ) { - let env = Environment::new(MockApi::default(), gas_limit, false); + let mut env = Environment::new(MockApi::default(), gas_limit, false); let module = compile(CONTRACT, TESTING_MEMORY_LIMIT, &[]).unwrap(); let store = module.store(); @@ -458,6 +482,16 @@ mod tests { let instance_ptr = NonNull::from(instance.as_ref()); env.set_wasmer_instance(Some(instance_ptr)); env.set_gas_left(gas_limit); + let remaining_points = instance + .exports + .get_global("wasmer_metering_remaining_points"); + let points_exhausted = instance + .exports + .get_global("wasmer_metering_points_exhausted"); + env.set_global( + remaining_points.unwrap().clone(), + points_exhausted.unwrap().clone(), + ); (env, instance) } diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs index bae11f403c..376708a870 100644 --- a/packages/vm/src/imports.rs +++ b/packages/vm/src/imports.rs @@ -540,7 +540,7 @@ mod tests { Box, ) { let gas_limit = TESTING_GAS_LIMIT; - let env = Environment::new(api, gas_limit, false); + let mut env = Environment::new(api, gas_limit, false); let module = compile(CONTRACT, TESTING_MEMORY_LIMIT, &[]).unwrap(); let store = module.store(); @@ -569,6 +569,16 @@ mod tests { let instance_ptr = NonNull::from(instance.as_ref()); env.set_wasmer_instance(Some(instance_ptr)); env.set_gas_left(gas_limit); + let remaining_points = wasmer_instance + .exports + .get_global("wasmer_metering_remaining_points"); + let points_exhausted = wasmer_instance + .exports + .get_global("wasmer_metering_points_exhausted"); + env.set_global( + remaining_points.unwrap().clone(), + points_exhausted.unwrap().clone(), + ); env.set_storage_readonly(false); (env, instance) diff --git a/packages/vm/src/instance.rs b/packages/vm/src/instance.rs index dd33e90b2c..3c55b72d46 100644 --- a/packages/vm/src/instance.rs +++ b/packages/vm/src/instance.rs @@ -85,7 +85,7 @@ where ) -> VmResult { let store = module.store(); - let env = Environment::new(backend.api, gas_limit, print_debug); + let mut env = Environment::new(backend.api, gas_limit, print_debug); let mut import_obj = ImportObject::new(); let mut env_imports = Exports::new(); @@ -238,6 +238,16 @@ where let instance_ptr = NonNull::from(wasmer_instance.as_ref()); env.set_wasmer_instance(Some(instance_ptr)); env.set_gas_left(gas_limit); + let remaining_points = wasmer_instance + .exports + .get_global("wasmer_metering_remaining_points"); + let points_exhausted = wasmer_instance + .exports + .get_global("wasmer_metering_points_exhausted"); + env.set_global( + remaining_points.unwrap().clone(), + points_exhausted.unwrap().clone(), + ); env.move_in(backend.storage, backend.querier); let instance = Instance { _inner: wasmer_instance,