diff --git a/packages/vm/src/environment.rs b/packages/vm/src/environment.rs index b88c552138..ed98a879ea 100644 --- a/packages/vm/src/environment.rs +++ b/packages/vm/src/environment.rs @@ -14,6 +14,68 @@ use crate::errors::{VmError, VmResult}; #[derive(Debug)] pub enum Never {} +/** gas config data */ + +#[derive(Clone, PartialEq, Debug)] +pub struct GasConfig { + /// Gas costs of VM (not Backend) provided functionality + /// secp256k1 signature verification cost + pub secp256k1_verify_cost: u64, + /// secp256k1 public key recovery cost + pub secp256k1_recover_pubkey_cost: u64, + /// ed25519 signature verification cost + pub ed25519_verify_cost: u64, + /// ed25519 batch signature verification cost + pub ed25519_batch_verify_cost: u64, + /// ed25519 batch signature verification cost (single public key) + pub ed25519_batch_verify_one_pubkey_cost: u64, +} + +impl GasConfig { + // Base crypto-verify gas cost: 1000 Cosmos SDK * 100 CosmWasm factor + const BASE_CRYPTO_COST: u64 = 100_000; + + // secp256k1 cost factor (reference) + const SECP256K1_VERIFY_FACTOR: (u64, u64) = (154, 154); // ~154 us in crypto benchmarks + + // Gas cost factors, relative to secp256k1_verify cost + const SECP256K1_RECOVER_PUBKEY_FACTOR: (u64, u64) = (162, 154); // 162 us / 154 us ~ 1.05 + const ED25519_VERIFY_FACTOR: (u64, u64) = (63, 154); // 63 us / 154 us ~ 0.41 + + // Gas cost factors, relative to ed25519_verify cost + // From https://docs.rs/ed25519-zebra/2.2.0/ed25519_zebra/batch/index.html + const ED255219_BATCH_VERIFY_FACTOR: (u64, u64) = ( + GasConfig::ED25519_VERIFY_FACTOR.0, + GasConfig::ED25519_VERIFY_FACTOR.1 * 2, + ); // 0.41 / 2. ~ 0.21 + const ED255219_BATCH_VERIFY_ONE_PUBKEY_FACTOR: (u64, u64) = ( + GasConfig::ED25519_VERIFY_FACTOR.0, + GasConfig::ED25519_VERIFY_FACTOR.1 * 4, + ); // 0.41 / 4. ~ 0.1 + + fn calc_crypto_cost(factor: (u64, u64)) -> u64 { + (GasConfig::BASE_CRYPTO_COST * factor.0) / factor.1 + } +} + +impl Default for GasConfig { + fn default() -> Self { + Self { + secp256k1_verify_cost: GasConfig::calc_crypto_cost(GasConfig::SECP256K1_VERIFY_FACTOR), + secp256k1_recover_pubkey_cost: GasConfig::calc_crypto_cost( + GasConfig::SECP256K1_RECOVER_PUBKEY_FACTOR, + ), + ed25519_verify_cost: GasConfig::calc_crypto_cost(GasConfig::ED25519_VERIFY_FACTOR), + ed25519_batch_verify_cost: GasConfig::calc_crypto_cost( + GasConfig::ED255219_BATCH_VERIFY_FACTOR, + ), + ed25519_batch_verify_one_pubkey_cost: GasConfig::calc_crypto_cost( + GasConfig::ED255219_BATCH_VERIFY_ONE_PUBKEY_FACTOR, + ), + } + } +} + /** context data **/ #[derive(Clone, PartialEq, Debug, Default)] @@ -39,6 +101,7 @@ impl GasState { pub struct Environment { pub api: A, pub print_debug: bool, + pub gas_config: GasConfig, data: Arc>>, } @@ -51,6 +114,7 @@ impl Clone for Environment { Environment { api: self.api, print_debug: self.print_debug, + gas_config: self.gas_config.clone(), data: self.data.clone(), } } @@ -67,6 +131,7 @@ impl Environment { Environment { api, print_debug, + gas_config: GasConfig::default(), data: Arc::new(RwLock::new(ContextData::new(gas_limit))), } } diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs index 397217a896..b4f5b56e76 100644 --- a/packages/vm/src/imports.rs +++ b/packages/vm/src/imports.rs @@ -1,5 +1,6 @@ //! Import implementations +use std::cmp::max; use std::convert::TryInto; use cosmwasm_crypto::{ @@ -27,11 +28,6 @@ use crate::sections::encode_sections; use crate::serde::to_vec; use crate::GasInfo; -const GAS_COST_SECP256K1_VERIFY_SIGNATURE: u64 = 100; -const GAS_COST_SECP256K1_RECOVER_PUBKEY_SIGNATURE: u64 = 100; -const GAS_COST_VERIFY_ED25519_SIGNATURE: u64 = 100; -const GAS_COST_BATCH_VERIFY_ED25519_SIGNATURE: u64 = GAS_COST_VERIFY_ED25519_SIGNATURE / 3; - /// A kibi (kilo binary) const KI: usize = 1024; /// A mibi (mega binary) @@ -298,7 +294,7 @@ fn do_secp256k1_verify( let pubkey = read_region(&env.memory(), pubkey_ptr, ECDSA_PUBKEY_MAX_LEN)?; let result = secp256k1_verify(&hash, &signature, &pubkey); - let gas_info = GasInfo::with_cost(GAS_COST_SECP256K1_VERIFY_SIGNATURE); + let gas_info = GasInfo::with_cost(env.gas_config.secp256k1_verify_cost); process_gas_info::(env, gas_info)?; Ok(result.map_or_else( |err| match err { @@ -328,7 +324,7 @@ fn do_secp256k1_recover_pubkey( }; let result = secp256k1_recover_pubkey(&hash, &signature, recover_param); - let gas_info = GasInfo::with_cost(GAS_COST_SECP256K1_RECOVER_PUBKEY_SIGNATURE); + let gas_info = GasInfo::with_cost(env.gas_config.secp256k1_recover_pubkey_cost); process_gas_info::(env, gas_info)?; match result { Ok(pubkey) => { @@ -358,7 +354,7 @@ fn do_ed25519_verify( let pubkey = read_region(&env.memory(), pubkey_ptr, EDDSA_PUBKEY_LEN)?; let result = ed25519_verify(&message, &signature, &pubkey); - let gas_info = GasInfo::with_cost(GAS_COST_VERIFY_ED25519_SIGNATURE); + let gas_info = GasInfo::with_cost(env.gas_config.ed25519_verify_cost); process_gas_info::(env, gas_info)?; Ok(result.map_or_else( |err| match err { @@ -403,8 +399,12 @@ fn do_ed25519_batch_verify( let public_keys = decode_sections(&public_keys); let result = ed25519_batch_verify(&messages, &signatures, &public_keys); - let gas_info = - GasInfo::with_cost(GAS_COST_BATCH_VERIFY_ED25519_SIGNATURE * signatures.len() as u64); + let gas_cost = if public_keys.len() == 1 { + env.gas_config.ed25519_batch_verify_one_pubkey_cost + } else { + env.gas_config.ed25519_batch_verify_cost + } * signatures.len() as u64; + let gas_info = GasInfo::with_cost(max(gas_cost, env.gas_config.ed25519_verify_cost)); process_gas_info::(env, gas_info)?; Ok(result.map_or_else( |err| match err { @@ -1840,6 +1840,7 @@ mod tests { match result.unwrap_err() { VmError::CommunicationErr { source: CommunicationError::InvalidOrder { .. }, + .. } => {} e => panic!("Unexpected error: {:?}", e), } @@ -1888,6 +1889,7 @@ mod tests { match result.unwrap_err() { VmError::BackendErr { source: BackendError::IteratorDoesNotExist { id, .. }, + .. } => assert_eq!(id, non_existent_id), e => panic!("Unexpected error: {:?}", e), }