diff --git a/Cargo.lock b/Cargo.lock index 9865f0192..5476418d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -694,12 +694,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "intx" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f38a50a899dc47a6d0ed5508e7f601a2e34c3a85303514b5d137f3c10a0c75" - [[package]] name = "itertools" version = "0.10.5" @@ -1412,9 +1406,8 @@ version = "0.0.17" [[package]] name = "soroban-wasmi" version = "0.30.0-soroban" -source = "git+https://github.com/stellar/wasmi?rev=3dc639fde3bebf0bf364a9fa4ac2f0efb7ee9995#3dc639fde3bebf0bf364a9fa4ac2f0efb7ee9995" +source = "git+https://github.com/stellar/wasmi?rev=284c963ba080703061797e2a3cba0853edee0dd4#284c963ba080703061797e2a3cba0853edee0dd4" dependencies = [ - "intx", "smallvec", "spin", "wasmi_arena", @@ -1840,12 +1833,12 @@ dependencies = [ [[package]] name = "wasmi_arena" version = "0.4.0" -source = "git+https://github.com/stellar/wasmi?rev=3dc639fde3bebf0bf364a9fa4ac2f0efb7ee9995#3dc639fde3bebf0bf364a9fa4ac2f0efb7ee9995" +source = "git+https://github.com/stellar/wasmi?rev=284c963ba080703061797e2a3cba0853edee0dd4#284c963ba080703061797e2a3cba0853edee0dd4" [[package]] name = "wasmi_core" version = "0.12.0" -source = "git+https://github.com/stellar/wasmi?rev=3dc639fde3bebf0bf364a9fa4ac2f0efb7ee9995#3dc639fde3bebf0bf364a9fa4ac2f0efb7ee9995" +source = "git+https://github.com/stellar/wasmi?rev=284c963ba080703061797e2a3cba0853edee0dd4#284c963ba080703061797e2a3cba0853edee0dd4" dependencies = [ "downcast-rs", "libm", diff --git a/Cargo.toml b/Cargo.toml index 49a279687..67594e7e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ default-features = false package = "soroban-wasmi" version = "0.30.0-soroban" git = "https://github.com/stellar/wasmi" -rev = "3dc639fde3bebf0bf364a9fa4ac2f0efb7ee9995" +rev = "284c963ba080703061797e2a3cba0853edee0dd4" [workspace.dependencies.stellar-strkey] version = "0.0.7" diff --git a/soroban-env-common/src/error.rs b/soroban-env-common/src/error.rs index efeded5d0..6d43941cf 100644 --- a/soroban-env-common/src/error.rs +++ b/soroban-env-common/src/error.rs @@ -164,7 +164,9 @@ impl From for Error { wasmi::core::TrapCode::BadSignature => ScErrorCode::UnexpectedType, - wasmi::core::TrapCode::StackOverflow | wasmi::core::TrapCode::OutOfFuel => { + wasmi::core::TrapCode::StackOverflow + | wasmi::core::TrapCode::OutOfFuel + | wasmi::core::TrapCode::GrowthOperationLimited => { return Error::from_type_and_code(ScErrorType::Budget, ScErrorCode::ExceededLimit) } }; diff --git a/soroban-env-host/benches/common/measure.rs b/soroban-env-host/benches/common/measure.rs index 43d6cdafb..e05f0846f 100644 --- a/soroban-env-host/benches/common/measure.rs +++ b/soroban-env-host/benches/common/measure.rs @@ -220,7 +220,7 @@ impl Measurements { /// /// f(x) = N_x * (a + b * Option) [1] /// -/// The `N_x` here is batch size if the host is doing `batched_charge` for the +/// The `N_x` here is batch size if the host is doing `bulk_charge` for the /// corresponding `x`. The goal of the HCM is to record the relation of x and /// f(x) in order for a, b -- the constant and linear cost parameters -- to be /// extracted. In the ideal setup, we pass in an array of samples with various diff --git a/soroban-env-host/src/budget.rs b/soroban-env-host/src/budget.rs index f7272277a..cd24843d6 100644 --- a/soroban-env-host/src/budget.rs +++ b/soroban-env-host/src/budget.rs @@ -13,7 +13,7 @@ use crate::{ Error, Host, HostError, DEFAULT_HOST_DEPTH_LIMIT, }; -use wasmi::FuelCosts; +use wasmi::{errors, FuelCosts, ResourceLimiter}; /// We provide a "cost model" object that evaluates a linear expression: /// @@ -69,7 +69,7 @@ impl HostCostModel for ContractCostParamEntry { } } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone)] pub struct BudgetDimension { /// A set of cost models that map input values (eg. event counts, object /// sizes) from some CostType to whatever concrete resource type is being @@ -205,7 +205,7 @@ impl BudgetDimension { /// doesn't derive all the traits we want. These fields (coarsely) define the /// relative costs of different wasm instruction types and are for wasmi internal /// fuel metering use only. Units are in "fuels". -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone)] pub(crate) struct FuelConfig { /// The base fuel costs for all instructions. pub base: u64, @@ -250,7 +250,21 @@ impl FuelConfig { } } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) struct WasmiLimits { + pub table_elements: u32, + pub instances: usize, + pub tables: usize, + pub memories: usize, +} + +pub(crate) const WASMI_LIMITS_CONFIG: WasmiLimits = WasmiLimits { + table_elements: 1000, + instances: 1, + tables: 1, + memories: 1, +}; + +#[derive(Clone)] pub(crate) struct BudgetImpl { pub cpu_insns: BudgetDimension, pub mem_bytes: BudgetDimension, @@ -435,7 +449,7 @@ impl DepthLimiter for BudgetImpl { } } -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Default, Clone)] pub struct Budget(pub(crate) Rc>); impl Debug for Budget { @@ -508,7 +522,13 @@ impl Budget { f(self.0.try_borrow_mut_or_err()?) } - fn charge_in_bulk( + /// Performs a bulk charge to the budget under the specified [`CostType`]. + /// The `iterations` is the batch size. The caller needs to ensure: + /// 1. the batched charges have identical costs (having the same + /// [`CostType`] and `input`) + /// 2. The input passed in (Some/None) is consistent with the [`CostModel`] + /// underneath the [`CostType`] (linear/constant). + pub fn bulk_charge( &self, ty: ContractCostType, iterations: u64, @@ -559,27 +579,7 @@ impl Budget { /// Otherwise it is a linear model. The caller needs to ensure the input /// passed is consistent with the inherent model underneath. pub fn charge(&self, ty: ContractCostType, input: Option) -> Result<(), HostError> { - self.charge_in_bulk(ty, 1, input) - } - - pub fn apply_wasmi_fuels(&self, cpu_fuel: u64, mem_fuel: u64) -> Result<(), HostError> { - self.charge_in_bulk(ContractCostType::WasmInsnExec, cpu_fuel, None)?; - self.charge_in_bulk(ContractCostType::WasmMemAlloc, mem_fuel, None) - } - - /// Performs a bulk charge to the budget under the specified [`CostType`]. - /// The `iterations` is the batch size. The caller needs to ensure: - /// 1. the batched charges have identical costs (having the same - /// [`CostType`] and `input`) - /// 2. The input passed in (Some/None) is consistent with the [`CostModel`] - /// underneath the [`CostType`] (linear/constant). - pub fn batched_charge( - &self, - ty: ContractCostType, - iterations: u64, - input: Option, - ) -> Result<(), HostError> { - self.charge_in_bulk(ty, iterations, input) + self.bulk_charge(ty, 1, input) } pub fn with_free_budget(&self, f: F) -> Result @@ -688,7 +688,7 @@ impl Budget { Ok(()) } - fn get_cpu_insns_remaining_as_fuel(&self) -> Result { + pub(crate) fn get_cpu_insns_remaining_as_fuel(&self) -> Result { let cpu_remaining = self.get_cpu_insns_remaining()?; let cpu_per_fuel = self .0 @@ -714,29 +714,6 @@ impl Budget { Ok(cpu_remaining / cpu_per_fuel) } - fn get_mem_bytes_remaining_as_fuel(&self) -> Result { - let bytes_remaining = self.get_mem_bytes_remaining()?; - let bytes_per_fuel = self - .0 - .try_borrow_or_err()? - .mem_bytes - .get_cost_model(ContractCostType::WasmMemAlloc) - .linear_term; - - if bytes_per_fuel < 0 { - return Err((ScErrorType::Context, ScErrorCode::InvalidInput).into()); - } - let bytes_per_fuel = (bytes_per_fuel as u64).max(1); - // See comment about rounding above. - Ok(bytes_remaining / bytes_per_fuel) - } - - pub fn get_fuels_budget(&self) -> Result<(u64, u64), HostError> { - let cpu_fuel = self.get_cpu_insns_remaining_as_fuel()?; - let mem_fuel = self.get_mem_bytes_remaining_as_fuel()?; - Ok((cpu_fuel, mem_fuel)) - } - // generate a wasmi fuel cost schedule based on our calibration pub fn wasmi_fuel_costs(&self) -> Result { let config = &self.0.try_borrow_or_err()?.fuel_config; @@ -1054,3 +1031,72 @@ impl Default for BudgetImpl { b } } + +impl ResourceLimiter for Host { + fn memory_growing( + &mut self, + _current: usize, + desired: usize, + maximum: Option, + ) -> Result { + let host_limit = self + .as_budget() + .get_mem_bytes_remaining() + .map_err(|_| errors::MemoryError::OutOfBoundsGrowth)?; + + let allow = if desired as u64 > host_limit { + false + } else { + match maximum { + Some(max) => desired <= max, + None => true, + } + }; + + if allow { + self.as_budget() + .bulk_charge(ContractCostType::WasmMemAlloc, desired as u64, None) + .map(|_| true) + .map_err(|_| errors::MemoryError::OutOfBoundsGrowth) + } else { + Err(errors::MemoryError::OutOfBoundsGrowth) + } + } + + fn table_growing( + &mut self, + current: u32, + desired: u32, + maximum: Option, + ) -> Result { + let allow = if desired > WASMI_LIMITS_CONFIG.table_elements { + false + } else { + match maximum { + Some(max) => desired <= max, + None => true, + } + }; + if allow { + Ok(allow) + } else { + Err(errors::TableError::GrowOutOfBounds { + maximum: maximum.unwrap_or(u32::MAX), + current, + delta: desired - current, + }) + } + } + + fn instances(&self) -> usize { + WASMI_LIMITS_CONFIG.instances + } + + fn tables(&self) -> usize { + WASMI_LIMITS_CONFIG.tables + } + + fn memories(&self) -> usize { + WASMI_LIMITS_CONFIG.memories + } +} diff --git a/soroban-env-host/src/host/metered_map.rs b/soroban-env-host/src/host/metered_map.rs index 223bcb902..ce7f60906 100644 --- a/soroban-env-host/src/host/metered_map.rs +++ b/soroban-env-host/src/host/metered_map.rs @@ -37,18 +37,18 @@ where { fn charge_access(&self, count: usize, b: &B) -> Result<(), HostError> { b.as_budget() - .batched_charge(ContractCostType::MapEntry, count as u64, None) + .bulk_charge(ContractCostType::MapEntry, count as u64, None) } fn charge_scan(&self, b: &B) -> Result<(), HostError> { b.as_budget() - .batched_charge(ContractCostType::MapEntry, self.map.len() as u64, None) + .bulk_charge(ContractCostType::MapEntry, self.map.len() as u64, None) } fn charge_binsearch(&self, b: &B) -> Result<(), HostError> { let mag = 64 - (self.map.len() as u64).leading_zeros(); b.as_budget() - .batched_charge(ContractCostType::MapEntry, 1 + mag as u64, None) + .bulk_charge(ContractCostType::MapEntry, 1 + mag as u64, None) } } @@ -337,7 +337,7 @@ where a: &MeteredOrdMap, b: &MeteredOrdMap, ) -> Result { - self.as_budget().batched_charge( + self.as_budget().bulk_charge( ContractCostType::MapEntry, a.map.len().min(b.map.len()) as u64, None, @@ -357,7 +357,7 @@ where a: &MeteredOrdMap, b: &MeteredOrdMap, ) -> Result { - self.batched_charge( + self.bulk_charge( ContractCostType::MapEntry, a.map.len().min(b.map.len()) as u64, None, diff --git a/soroban-env-host/src/host/metered_vector.rs b/soroban-env-host/src/host/metered_vector.rs index 057196351..c9416d260 100644 --- a/soroban-env-host/src/host/metered_vector.rs +++ b/soroban-env-host/src/host/metered_vector.rs @@ -31,16 +31,16 @@ where A: DeclaredSizeForMetering, { fn charge_access(&self, count: usize, budget: &Budget) -> Result<(), HostError> { - budget.batched_charge(ContractCostType::VecEntry, count as u64, None) + budget.bulk_charge(ContractCostType::VecEntry, count as u64, None) } fn charge_scan(&self, budget: &Budget) -> Result<(), HostError> { - budget.batched_charge(ContractCostType::VecEntry, self.vec.len() as u64, None) + budget.bulk_charge(ContractCostType::VecEntry, self.vec.len() as u64, None) } fn charge_binsearch(&self, budget: &Budget) -> Result<(), HostError> { let mag = 64 - (self.vec.len() as u64).leading_zeros(); - budget.batched_charge(ContractCostType::VecEntry, 1 + mag as u64, None) + budget.bulk_charge(ContractCostType::VecEntry, 1 + mag as u64, None) } } @@ -335,7 +335,7 @@ where a: &MeteredVector, b: &MeteredVector, ) -> Result { - self.as_budget().batched_charge( + self.as_budget().bulk_charge( ContractCostType::VecEntry, a.vec.len().min(b.vec.len()) as u64, None, @@ -355,7 +355,7 @@ where a: &MeteredVector, b: &MeteredVector, ) -> Result { - self.as_budget().batched_charge( + self.as_budget().bulk_charge( ContractCostType::VecEntry, a.vec.len().min(b.vec.len()) as u64, None, diff --git a/soroban-env-host/src/test/budget_metering.rs b/soroban-env-host/src/test/budget_metering.rs index 2364f7561..208da5dde 100644 --- a/soroban-env-host/src/test/budget_metering.rs +++ b/soroban-env-host/src/test/budget_metering.rs @@ -46,8 +46,9 @@ fn xdr_object_conversion() -> Result<(), HostError> { fn vm_hostfn_invocation() -> Result<(), HostError> { let host = Host::test_host_with_recording_footprint(); let id_obj = host.register_test_contract_wasm(VEC); + // this contract requests initial pages = 16 worth of linear memory, not sure why let host = host - .test_budget(100_000, 100_000) + .test_budget(100_000, 1_048_576) .enable_model(ContractCostType::InvokeVmFunction, 10, 0, 1, 0) .enable_model(ContractCostType::InvokeHostFunction, 10, 0, 1, 0); @@ -262,7 +263,7 @@ fn total_amount_charged_from_random_inputs() -> Result<(), HostError> { ]; for ty in ContractCostType::variants() { - host.with_budget(|b| b.batched_charge(ty, tracker[ty as usize].0, tracker[ty as usize].1))?; + host.with_budget(|b| b.bulk_charge(ty, tracker[ty as usize].0, tracker[ty as usize].1))?; } let actual = format!("{:?}", host.as_budget()); expect![[r#" diff --git a/soroban-env-host/src/test/hostile.rs b/soroban-env-host/src/test/hostile.rs index 53082be70..25e79dbe0 100644 --- a/soroban-env-host/src/test/hostile.rs +++ b/soroban-env-host/src/test/hostile.rs @@ -1,10 +1,11 @@ use soroban_env_common::{ - xdr::{ScErrorCode, ScErrorType}, + xdr::{ContractCostType, ScErrorCode, ScErrorType}, Env, EnvBase, Symbol, Val, VecObject, }; +use soroban_synth_wasm::{Arity, ModEmitter, Operand}; use soroban_test_wasms::HOSTILE; -use crate::{host_object::HostVec, Host, HostError}; +use crate::{budget::AsBudget, host_object::HostVec, Host, HostError}; #[test] fn hostile_iloop_traps() -> Result<(), HostError> { @@ -190,3 +191,57 @@ fn hostile_forged_objects_trap() -> Result<(), HostError> { Ok(()) } + +fn wasm_module_with_mem_grow(n_pages: usize) -> Vec { + let mut fe = ModEmitter::new().func(Arity(0), 0); + fe.push(Operand::Const32(n_pages as i32)); + fe.memory_grow(); + fe.drop(); + fe.push(Symbol::try_from_small_str("pass").unwrap()); + fe.finish_and_export("test").finish() +} + +#[test] +fn excessive_memory_growth() -> Result<(), HostError> { + // not sure why calling `memory_grow(32)`, wasmi requests 33 pages of memory + let wasm = wasm_module_with_mem_grow(32); + let host = Host::test_host_with_recording_footprint() + .test_budget(0, 0) + .enable_model(ContractCostType::WasmMemAlloc, 0, 0, 1, 0); + let contract_id_obj = host.register_test_contract_wasm(wasm.as_slice()); + host.set_diagnostic_level(crate::DiagnosticLevel::Debug)?; + + // This one should just run out of memory + { + let mem_budget = 33 * 0x10_000; + host.as_budget().reset_limits(40_000, mem_budget)?; + let res = host.call( + contract_id_obj, + Symbol::try_from_small_str("test")?, + host.add_host_object(HostVec::new())?, + ); + assert!(HostError::result_matches_err( + res, + (ScErrorType::Budget, ScErrorCode::ExceededLimit) + )); + } + + // allowing one extra page should be okay + { + let mem_budget = 34 * 0x10_000; + // Note on requiring non-zero cpu limit: even though no cpu instruction + // is consumed, wasmi does require checking internally there is enough fuel + // to finish the task. memory_grow is a special instruction that requires extra + // fuel (64 bytes per fuel), so we would roughly require `requested memory size / 64` + // cpu instructions to satisfy wasmi. + host.as_budget().reset_limits(40_000, mem_budget)?; + let res = host.call( + contract_id_obj, + Symbol::try_from_small_str("test")?, + host.add_host_object(HostVec::new())?, + ); + assert!(res.is_ok()); + } + + Ok(()) +} diff --git a/soroban-env-host/src/vm.rs b/soroban-env-host/src/vm.rs index 0915cef82..c4441d6f8 100644 --- a/soroban-env-host/src/vm.rs +++ b/soroban-env-host/src/vm.rs @@ -165,6 +165,8 @@ impl Vm { Self::check_meta_section(host, &module)?; let mut store = Store::new(&engine, host.clone()); + store.limiter(|host| host); + let mut linker = >::new(&engine); for hf in HOST_FUNCTIONS { @@ -225,7 +227,7 @@ impl Vm { inputs: &[Value], ) -> Result { let mut wasm_ret: [Value; 1] = [Value::I64(0)]; - self.store.try_borrow_mut_or_err()?.fill_fuels(host)?; + self.store.try_borrow_mut_or_err()?.add_fuel_to_vm(host)?; let res = func.call( &mut *self.store.try_borrow_mut_or_err()?, inputs, @@ -236,7 +238,9 @@ impl Vm { // wasmi instruction) remaining when the `OutOfFuel` trap occurs. This is only observable // if the contract traps with `OutOfFuel`, which may appear confusing if they look closely // at the budget amount consumed. So it should be fine. - self.store.try_borrow_mut_or_err()?.return_fuels(host)?; + self.store + .try_borrow_mut_or_err()? + .return_fuel_to_host(host)?; if let Err(e) = res { // When a call fails with a wasmi::Error::Trap that carries a HostError diff --git a/soroban-env-host/src/vm/dispatch.rs b/soroban-env-host/src/vm/dispatch.rs index 4cd959e12..e62d61327 100644 --- a/soroban-env-host/src/vm/dispatch.rs +++ b/soroban-env-host/src/vm/dispatch.rs @@ -147,7 +147,7 @@ macro_rules! generate_dispatch_functions { // This is where the VM -> Host boundary is crossed. // We first return all fuels from the VM back to the host such that // the host maintains control of the budget. - FuelRefillable::return_fuels(&mut caller, &host).map_err(|he| Trap::from(he))?; + FuelRefillable::return_fuel_to_host(&mut caller, &host).map_err(|he| Trap::from(he))?; host.charge_budget(ContractCostType::InvokeHostFunction, None)?; let mut vmcaller = VmCaller(Some(caller)); @@ -185,7 +185,7 @@ macro_rules! generate_dispatch_functions { // This is where the Host->VM boundary is crossed. // We supply the remaining host budget as fuel to the VM. let caller = vmcaller.try_mut().map_err(|e| Trap::from(HostError::from(e)))?; - FuelRefillable::fill_fuels(caller, &host).map_err(|he| Trap::from(he))?; + FuelRefillable::add_fuel_to_vm(caller, &host).map_err(|he| Trap::from(he))?; res } diff --git a/soroban-env-host/src/vm/fuel_refillable.rs b/soroban-env-host/src/vm/fuel_refillable.rs index ccf98837c..9b57a8d85 100644 --- a/soroban-env-host/src/vm/fuel_refillable.rs +++ b/soroban-env-host/src/vm/fuel_refillable.rs @@ -1,25 +1,25 @@ use crate::{ budget::AsBudget, - xdr::{ScErrorCode, ScErrorType}, + xdr::{ContractCostType, ScErrorCode, ScErrorType}, Host, HostError, }; use wasmi::{errors::FuelError, Caller, Store}; pub(crate) trait FuelRefillable { - fn fuels_consumed(&self) -> Result<(u64, u64), HostError>; + fn fuel_consumed(&self) -> Result; - fn fuels_total(&self) -> Result<(u64, u64), HostError>; + fn fuel_total(&self) -> Result; - fn add_fuels(&mut self, cpu: u64, mem: u64) -> Result<(), HostError>; + fn add_fuel(&mut self, fuel: u64) -> Result<(), HostError>; - fn reset_fuels(&mut self) -> Result<(), HostError>; + fn reset_fuel(&mut self) -> Result<(), HostError>; fn is_clean(&self) -> Result { - Ok(self.fuels_consumed()? == (0, 0) && self.fuels_total()? == (0, 0)) + Ok(self.fuel_consumed()? == 0 && self.fuel_total()? == 0) } - fn fill_fuels(&mut self, host: &Host) -> Result<(), HostError> { + fn add_fuel_to_vm(&mut self, host: &Host) -> Result<(), HostError> { if !self.is_clean()? { return Err(host.err( ScErrorType::WasmVm, @@ -28,54 +28,41 @@ pub(crate) trait FuelRefillable { &[], )); } - let (cpu, mem) = host.as_budget().get_fuels_budget()?; - self.add_fuels(cpu, mem) + let fuel = host.as_budget().get_cpu_insns_remaining_as_fuel()?; + self.add_fuel(fuel) } - fn return_fuels(&mut self, host: &Host) -> Result<(), HostError> { - let (cpu, mem) = self.fuels_consumed()?; - host.as_budget().apply_wasmi_fuels(cpu, mem)?; - self.reset_fuels() + fn return_fuel_to_host(&mut self, host: &Host) -> Result<(), HostError> { + let fuel = self.fuel_consumed()?; + host.as_budget() + .bulk_charge(ContractCostType::WasmInsnExec, fuel, None)?; + self.reset_fuel() } } -//wasmi::Store + macro_rules! impl_refillable_for_store { ($store: ty) => { impl<'a> FuelRefillable for $store { - fn fuels_consumed(&self) -> Result<(u64, u64), HostError> { - let cpu = self.fuel_consumed().ok_or_else(|| { - HostError::from(wasmi::Error::Store(FuelError::FuelMeteringDisabled)) - })?; - let mem = self.mem_fuel_consumed().ok_or_else(|| { + fn fuel_consumed(&self) -> Result { + self.fuel_consumed().ok_or_else(|| { HostError::from(wasmi::Error::Store(FuelError::FuelMeteringDisabled)) - })?; - Ok((cpu, mem)) + }) } - fn fuels_total(&self) -> Result<(u64, u64), HostError> { - let cpu = self.fuel_total().ok_or_else(|| { - HostError::from(wasmi::Error::Store(FuelError::FuelMeteringDisabled)) - })?; - let mem = self.mem_fuel_total().ok_or_else(|| { + fn fuel_total(&self) -> Result { + self.fuel_total().ok_or_else(|| { HostError::from(wasmi::Error::Store(FuelError::FuelMeteringDisabled)) - })?; - Ok((cpu, mem)) + }) } - fn add_fuels(&mut self, cpu: u64, mem: u64) -> Result<(), HostError> { - self.add_fuel(cpu) - .map_err(|fe| HostError::from(wasmi::Error::Store(fe)))?; - self.add_mem_fuel(mem) - .map_err(|fe| HostError::from(wasmi::Error::Store(fe)))?; - Ok(()) + fn add_fuel(&mut self, fuel: u64) -> Result<(), HostError> { + self.add_fuel(fuel) + .map_err(|fe| HostError::from(wasmi::Error::Store(fe))) } - fn reset_fuels(&mut self) -> Result<(), HostError> { + fn reset_fuel(&mut self) -> Result<(), HostError> { self.reset_fuel() - .map_err(|fe| HostError::from(wasmi::Error::Store(fe)))?; - self.reset_mem_fuel() - .map_err(|fe| HostError::from(wasmi::Error::Store(fe)))?; - Ok(()) + .map_err(|fe| HostError::from(wasmi::Error::Store(fe))) } } };