diff --git a/radix-engine/src/system/system_modules/costing/fee_table.rs b/radix-engine/src/system/system_modules/costing/fee_table.rs index 41760ea80b..dc3cd867d2 100644 --- a/radix-engine/src/system/system_modules/costing/fee_table.rs +++ b/radix-engine/src/system/system_modules/costing/fee_table.rs @@ -176,9 +176,25 @@ impl FeeTable { _export_name: &str, wasm_execution_units: u32, ) -> u32 { - // From `costing::spin_loop`, it takes 5.5391 ms for 1918122691 wasm execution units. - // Therefore, cost for single unit: 5.5391 * 1000 / 1918122691 * 100 = 0.00028877714 - + // W - WASM execution units + // C - cost units + // c - single cost unit + // T - execution time (1 µs = 100 c => 1 ms = 100,000 c) + // + // Cost units might be expressed as + // C = T * c + // + // To convert W to C, we need a d. + // C = W / divider + // divider = W / C + // divider = W / (T * c) + // + // From `costing::spin_loop_v2`, it takes T=960 ms to consume W=274,517,401,207 wasm execution + // units (measured at GH benchmark, git rev 1fd85c47ef, EC2 instance type c6a.4xlarge). + // T = 960 ms = 960 * 100,000 + // W = 274,517,401,207 + // Therefore + // divider = 274,517,401,207 / (960 * 100,000) = 2859.556 ~= 3000 wasm_execution_units / 3000 } diff --git a/radix-engine/src/vm/wasm_runtime/scrypto_runtime.rs b/radix-engine/src/vm/wasm_runtime/scrypto_runtime.rs index af4d953a47..da80c29f9c 100644 --- a/radix-engine/src/vm/wasm_runtime/scrypto_runtime.rs +++ b/radix-engine/src/vm/wasm_runtime/scrypto_runtime.rs @@ -1,3 +1,5 @@ +use core::u32; + use crate::errors::InvokeError; use crate::errors::RuntimeError; use crate::internal_prelude::*; @@ -21,6 +23,7 @@ pub struct ScryptoRuntime<'y, Y: SystemApi> { export_name: String, wasm_execution_units_buffer: u32, scrypto_vm_version: ScryptoVmVersion, + wasm_execution_units_base: u32, } impl<'y, Y: SystemApi> ScryptoRuntime<'y, Y> { @@ -30,6 +33,16 @@ impl<'y, Y: SystemApi> ScryptoRuntime<'y, Y> { export_name: String, scrypto_vm_version: ScryptoVmVersion, ) -> Self { + let wasm_execution_units_base = if scrypto_vm_version < ScryptoVmVersion::cuttlefish() { + 0 + } else { + // Add 7,000 base units to make sure the we do not undercharge for WASM execution, + // which might lead to system exploitation. + // This is especially important in corner-cases such as `costing::spin_loop_v2` benchmark. + // less frequently. + 7000 + }; + ScryptoRuntime { api, buffers: index_map_new(), @@ -38,9 +51,9 @@ impl<'y, Y: SystemApi> ScryptoRuntime<'y, Y> { export_name, wasm_execution_units_buffer: 0, scrypto_vm_version, + wasm_execution_units_base, } } - pub fn parse_blueprint_id( package_address: Vec, blueprint_name: Vec, @@ -59,9 +72,9 @@ impl<'y, Y: SystemApi> ScryptoRuntime<'y, Y> { ) -> Result<(), InvokeError> { assert!(n > self.wasm_execution_units_buffer); let n_remaining_after_buffer_used = n - self.wasm_execution_units_buffer; - let amount_to_request = n_remaining_after_buffer_used - .checked_add(WASM_EXECUTION_COST_UNITS_BUFFER) - .unwrap_or(u32::MAX); + let amount_to_request = + n_remaining_after_buffer_used.saturating_add(WASM_EXECUTION_COST_UNITS_BUFFER); + self.api .consume_cost_units(ClientCostingEntry::RunWasmCode { package_address: &self.package_address, @@ -401,6 +414,8 @@ impl<'y, Y: SystemApi> WasmRuntime for ScryptoRuntime<'y, Y> { &mut self, n: u32, ) -> Result<(), InvokeError> { + let n = n.saturating_add(self.wasm_execution_units_base); + if n <= self.wasm_execution_units_buffer { self.wasm_execution_units_buffer -= n; Ok(())