diff --git a/packages/vm/src/calls.rs b/packages/vm/src/calls.rs index dc24a11a6f..3cbea37278 100644 --- a/packages/vm/src/calls.rs +++ b/packages/vm/src/calls.rs @@ -606,6 +606,7 @@ mod tests { call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg) .unwrap() .unwrap(); + assert_eq!(instance.get_gas_left(), 494235049729); } #[test] @@ -625,6 +626,7 @@ mod tests { call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg) .unwrap() .unwrap(); + assert_eq!(instance.get_gas_left(), 485686146123); } #[test] @@ -643,6 +645,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] @@ -667,6 +670,7 @@ mod tests { } err => panic!("Unexpected error: {err:?}"), } + assert_eq!(instance.get_gas_left(), 493100600000); } #[test] @@ -689,6 +693,7 @@ mod tests { } err => panic!("Unexpected error: {err:?}"), } + assert_eq!(instance.get_gas_left(), 493655750000); } #[test] @@ -714,6 +719,7 @@ mod tests { query_response.as_slice(), b"{\"verifier\":\"someone else\"}" ); + assert_eq!(instance.get_gas_left(), 485028949541); } #[test] @@ -732,6 +738,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 202cedd99a..a32317c83e 100644 --- a/packages/vm/src/environment.rs +++ b/packages/vm/src/environment.rs @@ -7,7 +7,7 @@ use std::rc::Rc; use std::sync::{Arc, RwLock}; use derivative::Derivative; -use wasmer::{AsStoreMut, Instance as WasmerInstance, Memory, MemoryView, Value}; +use wasmer::{AsStoreMut, Global, Instance as WasmerInstance, Memory, MemoryView, Value}; use wasmer_middlewares::metering::{get_remaining_points, set_remaining_points, MeteringPoints}; use crate::backend::{BackendApi, GasInfo, Querier, Storage}; @@ -111,6 +111,8 @@ pub type DebugHandlerFn = dyn for<'a, 'b> FnMut(/* msg */ &'a str, DebugInfo<'b> /// The environment is clonable but clones access the same underlying data. pub struct Environment { pub memory: Option, + pub global_remaining_points: Option, + pub global_points_exhausted: Option, pub api: A, pub gas_config: GasConfig, data: Arc>>, @@ -124,6 +126,8 @@ impl Clone for Environment { fn clone(&self) -> Self { Environment { memory: None, + global_remaining_points: self.global_remaining_points.clone(), + global_points_exhausted: self.global_points_exhausted.clone(), api: self.api, gas_config: self.gas_config.clone(), data: self.data.clone(), @@ -135,6 +139,8 @@ impl Environment { pub fn new(api: A, gas_limit: u64) -> Self { Environment { memory: None, + global_remaining_points: None, + global_points_exhausted: None, api, gas_config: GasConfig::default(), data: Arc::new(RwLock::new(ContextData::new(gas_limit))), @@ -147,6 +153,11 @@ impl Environment { }) } + pub fn set_global(&mut self, remain: Global, exhausted: Global) { + self.global_remaining_points = Some(remain); + self.global_points_exhausted = Some(exhausted) + } + pub fn debug_handler(&self) -> Option>> { self.with_context_data(|context_data| { // This clone here requires us to wrap the function in Rc instead of Box @@ -322,6 +333,18 @@ impl Environment { }) } + pub fn get_gas_left_ex( + &self, + remain: &Global, + exhausted: &Global, + store: &mut impl AsStoreMut, + ) -> u64 { + match exhausted.get(store) { + value if value.unwrap_i32() > 0 => 0, + _ => u64::try_from(remain.get(store)).unwrap(), + } + } + pub fn get_gas_left(&self, store: &mut impl AsStoreMut) -> u64 { self.with_wasmer_instance(|instance| { Ok(match get_remaining_points(store, instance) { @@ -332,6 +355,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, store: &mut impl AsStoreMut, new_limit: u64) { + a.set(store, new_limit.into()) + .expect("Can't set `wasmer_metering_remaining_points` in Instance"); + } + pub fn set_gas_left(&self, store: &mut impl AsStoreMut, new_value: u64) { self.with_wasmer_instance(|instance| { set_remaining_points(store, instance, new_value); @@ -417,7 +445,9 @@ pub fn process_gas_info( store: &mut impl AsStoreMut, info: GasInfo, ) -> VmResult<()> { - let gas_left = env.get_gas_left(store); + 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, store); let new_limit = env.with_gas_state_mut(|gas_state| { gas_state.externally_used_gas += info.externally_used; @@ -429,7 +459,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(store, new_limit); + env.set_gas_left_ex(remain_points, store, new_limit); if info.externally_used + info.cost > gas_left { Err(VmError::gas_depletion()) @@ -473,7 +503,7 @@ mod tests { Store, Box, ) { - let env = Environment::new(MockApi::default(), gas_limit); + let mut env = Environment::new(MockApi::default(), gas_limit); let (engine, module) = compile(CONTRACT, &[]).unwrap(); let mut store = make_store_with_engine(engine, TESTING_MEMORY_LIMIT); @@ -503,6 +533,16 @@ mod tests { let instance_ptr = NonNull::from(instance.as_ref()); env.set_wasmer_instance(Some(instance_ptr)); env.set_gas_left(&mut store, 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, store, instance) } diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs index d9769d7861..68d9981c8f 100644 --- a/packages/vm/src/imports.rs +++ b/packages/vm/src/imports.rs @@ -663,6 +663,16 @@ mod tests { env.set_wasmer_instance(Some(instance_ptr)); env.set_gas_left(&mut store, 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); } diff --git a/packages/vm/src/instance.rs b/packages/vm/src/instance.rs index 47f991a746..2fff387b8c 100644 --- a/packages/vm/src/instance.rs +++ b/packages/vm/src/instance.rs @@ -271,6 +271,16 @@ where env.memory = Some(memory); env.set_wasmer_instance(Some(instance_ptr)); env.set_gas_left(&mut store, 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); }