From 3bb82d8b6bddd493006202638805713e692e5bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Gaspard?= Date: Tue, 3 Sep 2024 09:00:00 +0200 Subject: [PATCH] feat: update gas costs, to better reflect the costs post-memtrie --- core/parameters/res/runtime_configs/61.yaml | 13 +++--- core/parameters/res/runtime_configs/72.yaml | 9 +++- .../res/runtime_configs/parameters.yaml | 3 ++ .../runtime_configs/parameters_testnet.yaml | 3 ++ core/parameters/src/cost.rs | 9 ++++ core/parameters/src/parameter.rs | 3 ++ core/parameters/src/view.rs | 13 ++++++ .../near-vm-runner/src/logic/gas_counter.rs | 25 ++++++++--- runtime/near-vm-runner/src/logic/logic.rs | 44 +++++++++++++++++-- 9 files changed, 104 insertions(+), 18 deletions(-) diff --git a/core/parameters/res/runtime_configs/61.yaml b/core/parameters/res/runtime_configs/61.yaml index a7224cca644..c5ea6782e0f 100644 --- a/core/parameters/res/runtime_configs/61.yaml +++ b/core/parameters/res/runtime_configs/61.yaml @@ -1,8 +1,9 @@ # Compute costs to allow for flat storage read-only MVP. # See https://github.com/near/nearcore/issues/8006 -wasm_touching_trie_node: { old: 16_101_955_926, new: { gas: 16_101_955_926, compute: 110_000_000_000 } } -wasm_storage_write_base: { old: 64_196_736_000, new: { gas: 64_196_736_000, compute: 200_000_000_000 } } -wasm_storage_remove_base: { old: 53_473_030_500, new: { gas: 53_473_030_500, compute: 200_000_000_000 } } -wasm_storage_read_base: { old: 56_356_845_750, new: { gas: 56_356_845_750, compute: 200_000_000_000 } } -wasm_storage_has_key_base: { old: 54_039_896_625, new: { gas: 54_039_896_625, compute: 200_000_000_000 } } -flat_storage_reads: { old: false, new: true } +wasm_touching_trie_node: { old: 16_101_955_926, new: { gas: 16_101_955_926, compute: 110_000_000_000 } } +wasm_storage_write_base: { old: 64_196_736_000, new: { gas: 64_196_736_000, compute: 200_000_000_000 } } +wasm_storage_remove_base: { old: 53_473_030_500, new: { gas: 53_473_030_500, compute: 200_000_000_000 } } +wasm_storage_read_base: { old: 56_356_845_750, new: { gas: 56_356_845_750, compute: 200_000_000_000 } } +wasm_storage_small_read_base: { old: 56_356_845_750, new: { gas: 56_356_845_750, compute: 200_000_000_000 } } +wasm_storage_has_key_base: { old: 54_039_896_625, new: { gas: 54_039_896_625, compute: 200_000_000_000 } } +flat_storage_reads: { old: false, new: true } diff --git a/core/parameters/res/runtime_configs/72.yaml b/core/parameters/res/runtime_configs/72.yaml index faac9c28577..91a50d5e19a 100644 --- a/core/parameters/res/runtime_configs/72.yaml +++ b/core/parameters/res/runtime_configs/72.yaml @@ -1 +1,8 @@ -main_storage_proof_size_soft_limit: {old: 3_000_000, new: 4_000_000} \ No newline at end of file +main_storage_proof_size_soft_limit: {old: 3_000_000, new: 4_000_000} +wasm_storage_has_key_base: { old: { gas: 54_039_896_625, compute: 200_000_000_000 }, new: { gas: 54_039_896_625, compute: 8_000_000_000 } } +wasm_storage_has_key_byte: { old: 30_790_845, new: { gas: 30_790_845, compute: 9_000_000 } } +wasm_storage_small_read_base: { old: { gas: 56_356_845_750, compute: 200_000_000_000 }, new: { gas: 56_356_845_750, compute: 9_000_000_000 } } +wasm_storage_small_read_key_byte: { old: 30_952_533, new: { gas: 30_952_533, compute: 10_000_000 } } +wasm_storage_small_read_value_byte: { old: 5_611_005, new: { gas: 5_611_005, compute: 2_500_000 } } +wasm_touching_trie_node: { old: { gas: 16_101_955_926, compute: 110_000_000_000 }, new: { gas: 16_101_955_926, compute: 20_000_000_000 } } +wasm_read_cached_trie_node: { old: 2_280_000_000, new: { gas: 2_280_000_000, compute: 1_400_000_000 } } diff --git a/core/parameters/res/runtime_configs/parameters.yaml b/core/parameters/res/runtime_configs/parameters.yaml index 0f005066eaf..4da71cd7a3d 100644 --- a/core/parameters/res/runtime_configs/parameters.yaml +++ b/core/parameters/res/runtime_configs/parameters.yaml @@ -152,6 +152,9 @@ wasm_storage_write_evicted_byte: 32_117_307 wasm_storage_read_base: 56_356_845_750 wasm_storage_read_key_byte: 30_952_533 wasm_storage_read_value_byte: 5_611_005 +wasm_storage_small_read_base: 56_356_845_750 +wasm_storage_small_read_key_byte: 30_952_533 +wasm_storage_small_read_value_byte: 5_611_005 wasm_storage_remove_base: 53_473_030_500 wasm_storage_remove_key_byte: 38_220_384 wasm_storage_remove_ret_value_byte: 11_531_556 diff --git a/core/parameters/res/runtime_configs/parameters_testnet.yaml b/core/parameters/res/runtime_configs/parameters_testnet.yaml index 7ff426ac24d..dc5a2418eda 100644 --- a/core/parameters/res/runtime_configs/parameters_testnet.yaml +++ b/core/parameters/res/runtime_configs/parameters_testnet.yaml @@ -149,6 +149,9 @@ wasm_storage_write_evicted_byte: 32_117_307 wasm_storage_read_base: 56_356_845_750 wasm_storage_read_key_byte: 30_952_533 wasm_storage_read_value_byte: 5_611_005 +wasm_storage_small_read_base: 56_356_845_750 +wasm_storage_small_read_key_byte: 30_952_533 +wasm_storage_small_read_value_byte: 5_611_005 wasm_storage_remove_base: 53_473_030_500 wasm_storage_remove_key_byte: 38_220_384 wasm_storage_remove_ret_value_byte: 11_531_556 diff --git a/core/parameters/src/cost.rs b/core/parameters/src/cost.rs index d978b8aa239..5ead3c51bf0 100644 --- a/core/parameters/src/cost.rs +++ b/core/parameters/src/cost.rs @@ -103,6 +103,9 @@ impl ExtCostsConfig { ExtCosts::storage_read_base => SAFETY_MULTIPLIER * 18785615250, ExtCosts::storage_read_key_byte => SAFETY_MULTIPLIER * 10317511, ExtCosts::storage_read_value_byte => SAFETY_MULTIPLIER * 1870335, + ExtCosts::storage_small_read_base => SAFETY_MULTIPLIER * 18785615250, + ExtCosts::storage_small_read_key_byte => SAFETY_MULTIPLIER * 10317511, + ExtCosts::storage_small_read_value_byte => SAFETY_MULTIPLIER * 1870335, ExtCosts::storage_remove_base => SAFETY_MULTIPLIER * 17824343500, ExtCosts::storage_remove_key_byte => SAFETY_MULTIPLIER * 12740128, ExtCosts::storage_remove_ret_value_byte => SAFETY_MULTIPLIER * 3843852, @@ -268,6 +271,9 @@ pub enum ExtCosts { bls12381_p1_decompress_element = 80, bls12381_p2_decompress_base = 81, bls12381_p2_decompress_element = 82, + storage_small_read_base = 83, + storage_small_read_key_byte = 84, + storage_small_read_value_byte = 85, } // Type of an action, used in fees logic. @@ -351,6 +357,9 @@ impl ExtCosts { ExtCosts::storage_read_base => Parameter::WasmStorageReadBase, ExtCosts::storage_read_key_byte => Parameter::WasmStorageReadKeyByte, ExtCosts::storage_read_value_byte => Parameter::WasmStorageReadValueByte, + ExtCosts::storage_small_read_base => Parameter::WasmStorageSmallReadBase, + ExtCosts::storage_small_read_key_byte => Parameter::WasmStorageSmallReadKeyByte, + ExtCosts::storage_small_read_value_byte => Parameter::WasmStorageSmallReadValueByte, ExtCosts::storage_remove_base => Parameter::WasmStorageRemoveBase, ExtCosts::storage_remove_key_byte => Parameter::WasmStorageRemoveKeyByte, ExtCosts::storage_remove_ret_value_byte => Parameter::WasmStorageRemoveRetValueByte, diff --git a/core/parameters/src/parameter.rs b/core/parameters/src/parameter.rs index ee8e75f5e69..7a866df2bd7 100644 --- a/core/parameters/src/parameter.rs +++ b/core/parameters/src/parameter.rs @@ -110,6 +110,9 @@ pub enum Parameter { WasmStorageReadBase, WasmStorageReadKeyByte, WasmStorageReadValueByte, + WasmStorageSmallReadBase, + WasmStorageSmallReadKeyByte, + WasmStorageSmallReadValueByte, WasmStorageRemoveBase, WasmStorageRemoveKeyByte, WasmStorageRemoveRetValueByte, diff --git a/core/parameters/src/view.rs b/core/parameters/src/view.rs index 3c1ffd812a2..0b4db0966fa 100644 --- a/core/parameters/src/view.rs +++ b/core/parameters/src/view.rs @@ -383,6 +383,13 @@ pub struct ExtCostsConfigView { /// Storage trie read value cost per byte cost pub storage_read_value_byte: Gas, + /// Storage trie read key base cost, when doing small reads + pub storage_small_read_base: Gas, + /// Storage trie read key per byte cost, when doing small reads + pub storage_small_read_key_byte: Gas, + /// Storage trie read value cost per byte cost, when doing small reads + pub storage_small_read_value_byte: Gas, + /// Remove key from trie base cost pub storage_remove_base: Gas, /// Remove key from trie per byte cost @@ -522,6 +529,9 @@ impl From for ExtCostsConfigView { storage_read_base: config.gas_cost(ExtCosts::storage_read_base), storage_read_key_byte: config.gas_cost(ExtCosts::storage_read_key_byte), storage_read_value_byte: config.gas_cost(ExtCosts::storage_read_value_byte), + storage_small_read_base: config.gas_cost(ExtCosts::storage_small_read_base), + storage_small_read_key_byte: config.gas_cost(ExtCosts::storage_small_read_key_byte), + storage_small_read_value_byte: config.gas_cost(ExtCosts::storage_small_read_value_byte), storage_remove_base: config.gas_cost(ExtCosts::storage_remove_base), storage_remove_key_byte: config.gas_cost(ExtCosts::storage_remove_key_byte), storage_remove_ret_value_byte: config.gas_cost(ExtCosts::storage_remove_ret_value_byte), @@ -622,6 +632,9 @@ impl From for crate::ExtCostsConfig { ExtCosts::storage_read_base => view.storage_read_base, ExtCosts::storage_read_key_byte => view.storage_read_key_byte, ExtCosts::storage_read_value_byte => view.storage_read_value_byte, + ExtCosts::storage_small_read_base => view.storage_small_read_base, + ExtCosts::storage_small_read_key_byte => view.storage_small_read_key_byte, + ExtCosts::storage_small_read_value_byte => view.storage_small_read_value_byte, ExtCosts::storage_remove_base => view.storage_remove_base, ExtCosts::storage_remove_key_byte => view.storage_remove_key_byte, ExtCosts::storage_remove_ret_value_byte => view.storage_remove_ret_value_byte, diff --git a/runtime/near-vm-runner/src/logic/gas_counter.rs b/runtime/near-vm-runner/src/logic/gas_counter.rs index f11ef3b859e..b16d676afea 100644 --- a/runtime/near-vm-runner/src/logic/gas_counter.rs +++ b/runtime/near-vm-runner/src/logic/gas_counter.rs @@ -116,15 +116,18 @@ impl GasCounter { } } - /// Simpler version of `deduct_gas()` for when no promises are involved. + /// Checks whether the current contract execution is allowed to burn this much new gas. /// - /// Return an error if there are arithmetic overflows. - pub(crate) fn burn_gas(&mut self, gas_burnt: Gas) -> Result<()> { - let new_burnt_gas = - self.fast_counter.burnt_gas.checked_add(gas_burnt).ok_or(HostError::IntegerOverflow)?; + /// If yes, returns the total amount of gas that would have been burnt if this amount of gas + /// were burnt. + pub(crate) fn can_burn_gas(&mut self, gas_to_be_burnt: Gas) -> Result { + let new_burnt_gas = self + .fast_counter + .burnt_gas + .checked_add(gas_to_be_burnt) + .ok_or(HostError::IntegerOverflow)?; if new_burnt_gas <= self.fast_counter.gas_limit { - self.fast_counter.burnt_gas = new_burnt_gas; - Ok(()) + Ok(new_burnt_gas) } else { // In the past `new_used_gas` would be computed using an implicit wrapping addition, // which would then give an opportunity for the `assert` (now `debug_assert`) in the @@ -138,6 +141,14 @@ impl GasCounter { } } + /// Simpler version of `deduct_gas()` for when no promises are involved. + /// + /// Return an error if there are arithmetic overflows. + pub(crate) fn burn_gas(&mut self, gas_burnt: Gas) -> Result<()> { + self.fast_counter.burnt_gas = self.can_burn_gas(gas_burnt)?; + Ok(()) + } + pub(crate) fn process_gas_limit(&mut self, new_burnt_gas: Gas, new_used_gas: Gas) -> HostError { use std::cmp::min; // Never burn more gas than what was paid for. diff --git a/runtime/near-vm-runner/src/logic/logic.rs b/runtime/near-vm-runner/src/logic/logic.rs index 09ddffa74e4..36f9d8262a6 100644 --- a/runtime/near-vm-runner/src/logic/logic.rs +++ b/runtime/near-vm-runner/src/logic/logic.rs @@ -3157,7 +3157,17 @@ bls12381_p2_decompress_base + bls12381_p2_decompress_element * num_elements` /// cost to read key from register + cost to write value into register`. pub fn storage_read(&mut self, key_len: u64, key_ptr: u64, register_id: u64) -> Result { self.result_state.gas_counter.pay_base(base)?; - self.result_state.gas_counter.pay_base(storage_read_base)?; + // Note: this host function charges the costs only after performing the work. In order to + // not be vulnerable to the various issues that could arise from that, this function does + // check that there is at least enough gas available for the "small" reads before performing + // the reads. And if there is enough gas for small reads but not large reads, the read + // should be interrupted before going down to disk, so there would not be undercharging. + // + // Make sure that we do have enough gas for the "small" reads at least, at the same place + // as we originally did. We will charge the actual gas at the end when we know to which + // cost type we should charge it. + let gas_to_be_burnt = storage_small_read_base.gas(&self.result_state.config.ext_costs); + self.result_state.gas_counter.can_burn_gas(gas_to_be_burnt)?; let key = get_memory_or_register!(self, key_ptr, key_len)?; if key.len() as u64 > self.config.limit_config.max_length_storage_key { return Err(HostError::KeyLengthExceeded { @@ -3166,7 +3176,12 @@ bls12381_p2_decompress_base + bls12381_p2_decompress_element * num_elements` } .into()); } - self.result_state.gas_counter.pay_per(storage_read_key_byte, key.len() as u64)?; + let gas_to_be_burnt = storage_small_read_key_byte + .gas(&self.result_state.config.ext_costs) + .checked_mul(key.len() as u64) + .and_then(|g| g.checked_add(gas_to_be_burnt)) + .ok_or(HostError::IntegerOverflow)?; + self.result_state.gas_counter.can_burn_gas(gas_to_be_burnt)?; let nodes_before = self.ext.get_trie_nodes_count(); let read = self.ext.storage_get(&key, self.config.storage_get_mode); let nodes_delta = self @@ -3175,8 +3190,29 @@ bls12381_p2_decompress_base + bls12381_p2_decompress_element * num_elements` .checked_sub(&nodes_before) .ok_or(InconsistentStateError::IntegerOverflow)?; self.result_state.gas_counter.add_trie_fees(&nodes_delta)?; - let read = - Self::deref_value(&mut self.result_state.gas_counter, storage_read_value_byte, read?)?; + let read = match read? { + Some(read) => { + let read_len = read.len() as u64; + // TODO BEFORE UNDRAFTING: REPLACE WITH THE RIGHT CONSTANT USAGE + if read_len < 4096 { + self.result_state.gas_counter.pay_base(storage_small_read_base)?; + self.result_state + .gas_counter + .pay_per(storage_small_read_key_byte, key.len() as u64)?; + self.result_state + .gas_counter + .pay_per(storage_small_read_value_byte, read_len)?; + } else { + self.result_state.gas_counter.pay_base(storage_read_base)?; + self.result_state + .gas_counter + .pay_per(storage_read_key_byte, key.len() as u64)?; + self.result_state.gas_counter.pay_per(storage_read_value_byte, read_len)?; + } + Some(read.deref()?) + } + None => None, + }; #[cfg(feature = "io_trace")] tracing::trace!(