Skip to content

Commit

Permalink
feature: Support compute costs for ExtCosts (#8426)
Browse files Browse the repository at this point in the history
**UPDATE:**
This PR has been updated to match the description of Compute Costs NEP: near/NEPs#455

Concretely, now we specify compute costs directly without weights indirection.

--------- 

This PR addresses #8264, in particular allowing to add parameter weights for Storage Costs.

Some open questions and potential improvements:
- [X] Do we need to surface weights in the `views.rs` (RPC interface of the node?)
- [X] Ideally we would avoid specifying the old base value in the diff config if we only want to change the weight. For now we need to do:
```yaml
wasm_storage_read_base: {
    old: { base: 50_000_000_000, weight: 3 },
    new: { base: 50_000_000_000, weight: 7 }
}
```
  • Loading branch information
aborg-dev authored Mar 21, 2023
1 parent 5d0c50a commit 6082633
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 84 deletions.
25 changes: 18 additions & 7 deletions core/primitives-core/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::parameter::Parameter;
use crate::types::Gas;
use crate::types::{Compute, Gas};
use enum_map::{enum_map, EnumMap};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -260,18 +260,28 @@ pub struct ViewConfig {
pub max_gas_burnt: Gas,
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct ParameterCost {
pub gas: Gas,
pub compute: Compute,
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct ExtCostsConfig {
pub costs: EnumMap<ExtCosts, Gas>,
pub costs: EnumMap<ExtCosts, ParameterCost>,
}

// We multiply the actual computed costs by the fixed factor to ensure we
// have certain reserve for further gas price variation.
const SAFETY_MULTIPLIER: u64 = 3;

impl ExtCostsConfig {
pub fn cost(&self, param: ExtCosts) -> Gas {
self.costs[param]
pub fn gas_cost(&self, param: ExtCosts) -> Gas {
self.costs[param].gas
}

pub fn compute_cost(&self, param: ExtCosts) -> Compute {
self.costs[param].compute
}

/// Convenience constructor to use in tests where the exact gas cost does
Expand Down Expand Up @@ -341,14 +351,15 @@ impl ExtCostsConfig {
ExtCosts::alt_bn128_pairing_check_element => 5_102_000_000_000,
ExtCosts::alt_bn128_g1_sum_base => 3_000_000_000,
ExtCosts::alt_bn128_g1_sum_element => 5_000_000_000,
};
}
.map(|_, value| ParameterCost { gas: value, compute: value });
ExtCostsConfig { costs }
}

fn free() -> ExtCostsConfig {
ExtCostsConfig {
costs: enum_map! {
_ => 0
_ => ParameterCost { gas: 0, compute: 0 }
},
}
}
Expand Down Expand Up @@ -472,7 +483,7 @@ pub enum ActionCosts {

impl ExtCosts {
pub fn value(self, config: &ExtCostsConfig) -> Gas {
config.cost(self)
config.gas_cost(self)
}

pub fn param(&self) -> Parameter {
Expand Down
2 changes: 2 additions & 0 deletions core/primitives-core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub type ShardId = u64;
pub type Balance = u128;
/// Gas is a type for storing amount of gas.
pub type Gas = u64;
/// Compute is a type for storing compute time. Measured in femtoseconds (10^-15 seconds).
pub type Compute = u64;

/// Weight of unused gas to distribute to scheduled function call actions.
/// Used in `promise_batch_action_function_call_weight` host function.
Expand Down
4 changes: 2 additions & 2 deletions core/primitives/src/runtime/config_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ mod tests {
let base_cfg = store.get_config(LowerStorageCost.protocol_version());
let new_cfg = store.get_config(LowerDataReceiptAndEcrecoverBaseCost.protocol_version());
assert!(
base_cfg.wasm_config.ext_costs.cost(ExtCosts::ecrecover_base)
> new_cfg.wasm_config.ext_costs.cost(ExtCosts::ecrecover_base)
base_cfg.wasm_config.ext_costs.gas_cost(ExtCosts::ecrecover_base)
> new_cfg.wasm_config.ext_costs.gas_cost(ExtCosts::ecrecover_base)
);
}

Expand Down
46 changes: 45 additions & 1 deletion core/primitives/src/runtime/parameter_table.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::config::{AccountCreationConfig, RuntimeConfig};
use near_primitives_core::account::id::ParseAccountError;
use near_primitives_core::config::{ExtCostsConfig, VMConfig};
use near_primitives_core::config::{ExtCostsConfig, ParameterCost, VMConfig};
use near_primitives_core::parameter::{FeeParameter, Parameter};
use near_primitives_core::runtime::fees::{Fee, RuntimeFeesConfig, StorageUsageConfig};
use near_primitives_core::types::AccountId;
Expand All @@ -13,6 +13,7 @@ use std::collections::BTreeMap;
pub(crate) enum ParameterValue {
U64(u64),
Rational { numerator: i32, denominator: i32 },
ParameterCost { gas: u64, compute: u64 },
Fee { send_sir: u64, send_not_sir: u64, execution: u64 },
// Can be used to store either a string or u128. Ideally, we would use a dedicated enum member
// for u128, but this is currently impossible to express in YAML (see
Expand Down Expand Up @@ -89,6 +90,24 @@ impl TryFrom<&ParameterValue> for Rational32 {
}
}

impl TryFrom<&ParameterValue> for ParameterCost {
type Error = ValueConversionError;

fn try_from(value: &ParameterValue) -> Result<Self, Self::Error> {
match value {
ParameterValue::ParameterCost { gas, compute } => {
Ok(ParameterCost { gas: *gas, compute: *compute })
}
// If not specified, compute costs default to gas costs.
&ParameterValue::U64(v) => Ok(ParameterCost { gas: v, compute: v }),
_ => Err(ValueConversionError::ParseType(
std::any::type_name::<ParameterCost>(),
value.clone(),
)),
}
}
}

impl TryFrom<&ParameterValue> for Fee {
type Error = ValueConversionError;

Expand Down Expand Up @@ -142,6 +161,9 @@ impl core::fmt::Display for ParameterValue {
ParameterValue::Rational { numerator, denominator } => {
write!(f, "{numerator} / {denominator}")
}
ParameterValue::ParameterCost { gas, compute } => {
write!(f, "{:>20}, compute: {:>20}", format_number(*gas), format_number(*compute))
}
ParameterValue::Fee { send_sir, send_not_sir, execution } => {
write!(
f,
Expand Down Expand Up @@ -470,6 +492,7 @@ burnt_gas_reward: {
numerator: 1_000_000,
denominator: 300,
}
wasm_storage_read_base: { gas: 50_000_000_000, compute: 100_000_000_000 }
"#;

static BASE_1: &str = r#"
Expand All @@ -496,6 +519,10 @@ burnt_gas_reward: {
old: { numerator: 1_000_000, denominator: 300 },
new: { numerator: 2_000_000, denominator: 500 },
}
wasm_storage_read_base: {
old: { gas: 50_000_000_000, compute: 100_000_000_000 },
new: { gas: 50_000_000_000, compute: 200_000_000_000 },
}
"#;

static DIFF_1: &str = r#"
Expand Down Expand Up @@ -533,6 +560,10 @@ burnt_gas_reward: {
(Parameter::StorageNumBytesAccount, "100"),
(Parameter::StorageNumExtraBytesRecord, "40"),
(Parameter::BurntGasReward, "{ numerator: 1_000_000, denominator: 300 }"),
(
Parameter::WasmStorageReadBase,
"{ gas: 50_000_000_000, compute: 100_000_000_000 }",
),
],
);
}
Expand Down Expand Up @@ -567,6 +598,10 @@ burnt_gas_reward: {
(Parameter::StorageNumExtraBytesRecord, "40"),
(Parameter::WasmRegularOpCost, "3856371"),
(Parameter::BurntGasReward, "{ numerator: 2_000_000, denominator: 500 }"),
(
Parameter::WasmStorageReadBase,
"{ gas: 50_000_000_000, compute: 200_000_000_000 }",
),
],
);
}
Expand All @@ -586,6 +621,10 @@ burnt_gas_reward: {
(Parameter::WasmRegularOpCost, "0"),
(Parameter::MaxMemoryPages, "512"),
(Parameter::BurntGasReward, "{ numerator: 3_000_000, denominator: 800 }"),
(
Parameter::WasmStorageReadBase,
"{ gas: 50_000_000_000, compute: 200_000_000_000 }",
),
],
);
}
Expand All @@ -602,6 +641,10 @@ burnt_gas_reward: {
(Parameter::StorageNumBytesAccount, "100"),
(Parameter::StorageNumExtraBytesRecord, "40"),
(Parameter::BurntGasReward, "{ numerator: 1_000_000, denominator: 300 }"),
(
Parameter::WasmStorageReadBase,
"{ gas: 50_000_000_000, compute: 100_000_000_000 }",
),
],
);
}
Expand Down Expand Up @@ -716,6 +759,7 @@ burnt_gas_reward: {
Parameter::StorageNumBytesAccount,
Parameter::StorageNumExtraBytesRecord,
Parameter::BurntGasReward,
Parameter::WasmStorageReadBase,
]
.iter(),
);
Expand Down
131 changes: 68 additions & 63 deletions core/primitives/src/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use borsh::{BorshDeserialize, BorshSerialize};
use chrono::DateTime;
use near_crypto::{PublicKey, Signature};
use near_o11y::pretty;
use near_primitives_core::config::{ActionCosts, ExtCosts, VMConfig};
use near_primitives_core::config::{ActionCosts, ExtCosts, ParameterCost, VMConfig};
use near_primitives_core::runtime::fees::Fee;
use num_rational::Rational32;
use std::collections::HashMap;
Expand Down Expand Up @@ -2682,67 +2682,71 @@ pub struct ExtCostsConfigView {
impl From<near_primitives_core::config::ExtCostsConfig> for ExtCostsConfigView {
fn from(config: near_primitives_core::config::ExtCostsConfig) -> Self {
Self {
base: config.cost(ExtCosts::base),
contract_loading_base: config.cost(ExtCosts::contract_loading_base),
contract_loading_bytes: config.cost(ExtCosts::contract_loading_bytes),
read_memory_base: config.cost(ExtCosts::read_memory_base),
read_memory_byte: config.cost(ExtCosts::read_memory_byte),
write_memory_base: config.cost(ExtCosts::write_memory_base),
write_memory_byte: config.cost(ExtCosts::write_memory_byte),
read_register_base: config.cost(ExtCosts::read_register_base),
read_register_byte: config.cost(ExtCosts::read_register_byte),
write_register_base: config.cost(ExtCosts::write_register_base),
write_register_byte: config.cost(ExtCosts::write_register_byte),
utf8_decoding_base: config.cost(ExtCosts::utf8_decoding_base),
utf8_decoding_byte: config.cost(ExtCosts::utf8_decoding_byte),
utf16_decoding_base: config.cost(ExtCosts::utf16_decoding_base),
utf16_decoding_byte: config.cost(ExtCosts::utf16_decoding_byte),
sha256_base: config.cost(ExtCosts::sha256_base),
sha256_byte: config.cost(ExtCosts::sha256_byte),
keccak256_base: config.cost(ExtCosts::keccak256_base),
keccak256_byte: config.cost(ExtCosts::keccak256_byte),
keccak512_base: config.cost(ExtCosts::keccak512_base),
keccak512_byte: config.cost(ExtCosts::keccak512_byte),
ripemd160_base: config.cost(ExtCosts::ripemd160_base),
ripemd160_block: config.cost(ExtCosts::ripemd160_block),
ed25519_verify_base: config.cost(ExtCosts::ed25519_verify_base),
ed25519_verify_byte: config.cost(ExtCosts::ed25519_verify_byte),
ecrecover_base: config.cost(ExtCosts::ecrecover_base),
log_base: config.cost(ExtCosts::log_base),
log_byte: config.cost(ExtCosts::log_byte),
storage_write_base: config.cost(ExtCosts::storage_write_base),
storage_write_key_byte: config.cost(ExtCosts::storage_write_key_byte),
storage_write_value_byte: config.cost(ExtCosts::storage_write_value_byte),
storage_write_evicted_byte: config.cost(ExtCosts::storage_write_evicted_byte),
storage_read_base: config.cost(ExtCosts::storage_read_base),
storage_read_key_byte: config.cost(ExtCosts::storage_read_key_byte),
storage_read_value_byte: config.cost(ExtCosts::storage_read_value_byte),
storage_remove_base: config.cost(ExtCosts::storage_remove_base),
storage_remove_key_byte: config.cost(ExtCosts::storage_remove_key_byte),
storage_remove_ret_value_byte: config.cost(ExtCosts::storage_remove_ret_value_byte),
storage_has_key_base: config.cost(ExtCosts::storage_has_key_base),
storage_has_key_byte: config.cost(ExtCosts::storage_has_key_byte),
storage_iter_create_prefix_base: config.cost(ExtCosts::storage_iter_create_prefix_base),
storage_iter_create_prefix_byte: config.cost(ExtCosts::storage_iter_create_prefix_byte),
storage_iter_create_range_base: config.cost(ExtCosts::storage_iter_create_range_base),
storage_iter_create_from_byte: config.cost(ExtCosts::storage_iter_create_from_byte),
storage_iter_create_to_byte: config.cost(ExtCosts::storage_iter_create_to_byte),
storage_iter_next_base: config.cost(ExtCosts::storage_iter_next_base),
storage_iter_next_key_byte: config.cost(ExtCosts::storage_iter_next_key_byte),
storage_iter_next_value_byte: config.cost(ExtCosts::storage_iter_next_value_byte),
touching_trie_node: config.cost(ExtCosts::touching_trie_node),
read_cached_trie_node: config.cost(ExtCosts::read_cached_trie_node),
promise_and_base: config.cost(ExtCosts::promise_and_base),
promise_and_per_promise: config.cost(ExtCosts::promise_and_per_promise),
promise_return: config.cost(ExtCosts::promise_return),
validator_stake_base: config.cost(ExtCosts::validator_stake_base),
validator_total_stake_base: config.cost(ExtCosts::validator_total_stake_base),
alt_bn128_g1_multiexp_base: config.cost(ExtCosts::alt_bn128_g1_multiexp_base),
alt_bn128_g1_multiexp_element: config.cost(ExtCosts::alt_bn128_g1_multiexp_element),
alt_bn128_g1_sum_base: config.cost(ExtCosts::alt_bn128_g1_sum_base),
alt_bn128_g1_sum_element: config.cost(ExtCosts::alt_bn128_g1_sum_element),
alt_bn128_pairing_check_base: config.cost(ExtCosts::alt_bn128_pairing_check_base),
alt_bn128_pairing_check_element: config.cost(ExtCosts::alt_bn128_pairing_check_element),
base: config.gas_cost(ExtCosts::base),
contract_loading_base: config.gas_cost(ExtCosts::contract_loading_base),
contract_loading_bytes: config.gas_cost(ExtCosts::contract_loading_bytes),
read_memory_base: config.gas_cost(ExtCosts::read_memory_base),
read_memory_byte: config.gas_cost(ExtCosts::read_memory_byte),
write_memory_base: config.gas_cost(ExtCosts::write_memory_base),
write_memory_byte: config.gas_cost(ExtCosts::write_memory_byte),
read_register_base: config.gas_cost(ExtCosts::read_register_base),
read_register_byte: config.gas_cost(ExtCosts::read_register_byte),
write_register_base: config.gas_cost(ExtCosts::write_register_base),
write_register_byte: config.gas_cost(ExtCosts::write_register_byte),
utf8_decoding_base: config.gas_cost(ExtCosts::utf8_decoding_base),
utf8_decoding_byte: config.gas_cost(ExtCosts::utf8_decoding_byte),
utf16_decoding_base: config.gas_cost(ExtCosts::utf16_decoding_base),
utf16_decoding_byte: config.gas_cost(ExtCosts::utf16_decoding_byte),
sha256_base: config.gas_cost(ExtCosts::sha256_base),
sha256_byte: config.gas_cost(ExtCosts::sha256_byte),
keccak256_base: config.gas_cost(ExtCosts::keccak256_base),
keccak256_byte: config.gas_cost(ExtCosts::keccak256_byte),
keccak512_base: config.gas_cost(ExtCosts::keccak512_base),
keccak512_byte: config.gas_cost(ExtCosts::keccak512_byte),
ripemd160_base: config.gas_cost(ExtCosts::ripemd160_base),
ripemd160_block: config.gas_cost(ExtCosts::ripemd160_block),
ed25519_verify_base: config.gas_cost(ExtCosts::ed25519_verify_base),
ed25519_verify_byte: config.gas_cost(ExtCosts::ed25519_verify_byte),
ecrecover_base: config.gas_cost(ExtCosts::ecrecover_base),
log_base: config.gas_cost(ExtCosts::log_base),
log_byte: config.gas_cost(ExtCosts::log_byte),
storage_write_base: config.gas_cost(ExtCosts::storage_write_base),
storage_write_key_byte: config.gas_cost(ExtCosts::storage_write_key_byte),
storage_write_value_byte: config.gas_cost(ExtCosts::storage_write_value_byte),
storage_write_evicted_byte: config.gas_cost(ExtCosts::storage_write_evicted_byte),
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_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),
storage_has_key_base: config.gas_cost(ExtCosts::storage_has_key_base),
storage_has_key_byte: config.gas_cost(ExtCosts::storage_has_key_byte),
storage_iter_create_prefix_base: config
.gas_cost(ExtCosts::storage_iter_create_prefix_base),
storage_iter_create_prefix_byte: config
.gas_cost(ExtCosts::storage_iter_create_prefix_byte),
storage_iter_create_range_base: config
.gas_cost(ExtCosts::storage_iter_create_range_base),
storage_iter_create_from_byte: config.gas_cost(ExtCosts::storage_iter_create_from_byte),
storage_iter_create_to_byte: config.gas_cost(ExtCosts::storage_iter_create_to_byte),
storage_iter_next_base: config.gas_cost(ExtCosts::storage_iter_next_base),
storage_iter_next_key_byte: config.gas_cost(ExtCosts::storage_iter_next_key_byte),
storage_iter_next_value_byte: config.gas_cost(ExtCosts::storage_iter_next_value_byte),
touching_trie_node: config.gas_cost(ExtCosts::touching_trie_node),
read_cached_trie_node: config.gas_cost(ExtCosts::read_cached_trie_node),
promise_and_base: config.gas_cost(ExtCosts::promise_and_base),
promise_and_per_promise: config.gas_cost(ExtCosts::promise_and_per_promise),
promise_return: config.gas_cost(ExtCosts::promise_return),
validator_stake_base: config.gas_cost(ExtCosts::validator_stake_base),
validator_total_stake_base: config.gas_cost(ExtCosts::validator_total_stake_base),
alt_bn128_g1_multiexp_base: config.gas_cost(ExtCosts::alt_bn128_g1_multiexp_base),
alt_bn128_g1_multiexp_element: config.gas_cost(ExtCosts::alt_bn128_g1_multiexp_element),
alt_bn128_g1_sum_base: config.gas_cost(ExtCosts::alt_bn128_g1_sum_base),
alt_bn128_g1_sum_element: config.gas_cost(ExtCosts::alt_bn128_g1_sum_element),
alt_bn128_pairing_check_base: config.gas_cost(ExtCosts::alt_bn128_pairing_check_base),
alt_bn128_pairing_check_element: config
.gas_cost(ExtCosts::alt_bn128_pairing_check_element),
// removed parameters
contract_compile_base: 0,
contract_compile_bytes: 0,
Expand Down Expand Up @@ -2814,7 +2818,8 @@ impl From<ExtCostsConfigView> for near_primitives_core::config::ExtCostsConfig {
ExtCosts::alt_bn128_g1_sum_element => view.alt_bn128_g1_sum_element,
ExtCosts::alt_bn128_pairing_check_base => view.alt_bn128_pairing_check_base,
ExtCosts::alt_bn128_pairing_check_element => view.alt_bn128_pairing_check_element,
};
}
.map(|_, value| ParameterCost { gas: value, compute: value });
Self { costs }
}
}
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/src/tests/client/process_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2845,13 +2845,13 @@ fn test_execution_metadata() {
{
"cost_category": "WASM_HOST_COST",
"cost": "BASE",
"gas_used": config.wasm_config.ext_costs.cost(ExtCosts::base).to_string()
"gas_used": config.wasm_config.ext_costs.gas_cost(ExtCosts::base).to_string()
},
// We include compilation costs into running the function.
{
"cost_category": "WASM_HOST_COST",
"cost": "CONTRACT_LOADING_BASE",
"gas_used": config.wasm_config.ext_costs.cost(ExtCosts::contract_loading_base).to_string()
"gas_used": config.wasm_config.ext_costs.gas_cost(ExtCosts::contract_loading_base).to_string()
},
{
"cost_category": "WASM_HOST_COST",
Expand Down
Loading

0 comments on commit 6082633

Please sign in to comment.