From 914dd0738346e13c7692f49d15daf5364b6b3056 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 26 Feb 2021 00:40:52 +0100 Subject: [PATCH 1/4] Add benchmarks-based signature verification gas costs --- packages/vm/src/imports.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs index 397217a896..56a50abdf7 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,10 +28,17 @@ 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; +// 1000 Cosmos SDK * 100 CosmWasm factor (~154 us in crypto benchmarks) +const GAS_COST_SECP256K1_VERIFY: u64 = 100000; + +// Gas costs relative to secp256k1_verify cost +const GAS_COST_SECP256K1_RECOVER_PUBKEY: u64 = 105195; // 100_000 * 162 us / 154 us +const GAS_COST_ED25519_VERIFY: u64 = 40909; // 100_000 * 63 us / 154 us + +// Gas costs relative to ed25519_verify cost +// From https://docs.rs/ed25519-zebra/2.2.0/ed25519_zebra/batch/index.html +const GAS_COST_ED25519_BATCH_VERIFY: u64 = GAS_COST_ED25519_VERIFY / 2; +const GAS_COST_ED25519_BATCH_VERIFY_ONE_PUBKEY: u64 = GAS_COST_ED25519_VERIFY / 4; /// A kibi (kilo binary) const KI: usize = 1024; @@ -298,7 +306,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(GAS_COST_SECP256K1_VERIFY); process_gas_info::(env, gas_info)?; Ok(result.map_or_else( |err| match err { @@ -328,7 +336,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(GAS_COST_SECP256K1_RECOVER_PUBKEY); process_gas_info::(env, gas_info)?; match result { Ok(pubkey) => { @@ -358,7 +366,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(GAS_COST_ED25519_VERIFY); process_gas_info::(env, gas_info)?; Ok(result.map_or_else( |err| match err { @@ -403,8 +411,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 { + GAS_COST_ED25519_BATCH_VERIFY_ONE_PUBKEY + } else { + GAS_COST_ED25519_BATCH_VERIFY + } * signatures.len() as u64; + let gas_info = GasInfo::with_cost(max(gas_cost, GAS_COST_ED25519_VERIFY)); process_gas_info::(env, gas_info)?; Ok(result.map_or_else( |err| match err { From e0552b036643a14f47309dd370277f2b11df248f Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 1 Mar 2021 16:35:21 +0100 Subject: [PATCH 2/4] Add GasConfig struct / info to Environment --- packages/vm/src/environment.rs | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/packages/vm/src/environment.rs b/packages/vm/src/environment.rs index b88c552138..b033608711 100644 --- a/packages/vm/src/environment.rs +++ b/packages/vm/src/environment.rs @@ -14,6 +14,62 @@ 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: f32 = 100_000.0; + + // secp256k1 cost factor (reference) + const SECP256K1_VERIFY_FACTOR: f32 = 1.; // ~154 us in crypto benchmarks + + // Gas cost factors, relative to secp256k1_verify cost + const SECP256K1_RECOVER_PUBKEY_FACTOR: f32 = 162. / 154.; // 162 us / 154 us ~ 1.05 + const ED25519_VERIFY_FACTOR: f32 = 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: f32 = GasConfig::ED25519_VERIFY_FACTOR / 2.; // 0.41 / 2. ~ 0.21 + const ED255219_BATCH_VERIFY_ONE_PUBKEY_FACTOR: f32 = GasConfig::ED25519_VERIFY_FACTOR / 4.; // 0.41 / 4. ~ 0.1 + + fn calc_crypto_cost(factor: f32) -> u64 { + (GasConfig::BASE_CRYPTO_COST * factor).round() as u64 + } +} + +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 +95,7 @@ impl GasState { pub struct Environment { pub api: A, pub print_debug: bool, + pub gas_config: GasConfig, data: Arc>>, } @@ -51,6 +108,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 +125,7 @@ impl Environment { Environment { api, print_debug, + gas_config: GasConfig::default(), data: Arc::new(RwLock::new(ContextData::new(gas_limit))), } } From cc8458c29d49d79e9c8ce5bdcb0aa7928535da3a Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 1 Mar 2021 16:36:09 +0100 Subject: [PATCH 3/4] Use GasConfig info for crypto gas costs --- packages/vm/src/imports.rs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs index 56a50abdf7..b4f5b56e76 100644 --- a/packages/vm/src/imports.rs +++ b/packages/vm/src/imports.rs @@ -28,18 +28,6 @@ use crate::sections::encode_sections; use crate::serde::to_vec; use crate::GasInfo; -// 1000 Cosmos SDK * 100 CosmWasm factor (~154 us in crypto benchmarks) -const GAS_COST_SECP256K1_VERIFY: u64 = 100000; - -// Gas costs relative to secp256k1_verify cost -const GAS_COST_SECP256K1_RECOVER_PUBKEY: u64 = 105195; // 100_000 * 162 us / 154 us -const GAS_COST_ED25519_VERIFY: u64 = 40909; // 100_000 * 63 us / 154 us - -// Gas costs relative to ed25519_verify cost -// From https://docs.rs/ed25519-zebra/2.2.0/ed25519_zebra/batch/index.html -const GAS_COST_ED25519_BATCH_VERIFY: u64 = GAS_COST_ED25519_VERIFY / 2; -const GAS_COST_ED25519_BATCH_VERIFY_ONE_PUBKEY: u64 = GAS_COST_ED25519_VERIFY / 4; - /// A kibi (kilo binary) const KI: usize = 1024; /// A mibi (mega binary) @@ -306,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); + 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 { @@ -336,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); + let gas_info = GasInfo::with_cost(env.gas_config.secp256k1_recover_pubkey_cost); process_gas_info::(env, gas_info)?; match result { Ok(pubkey) => { @@ -366,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_ED25519_VERIFY); + 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 { @@ -412,11 +400,11 @@ fn do_ed25519_batch_verify( let result = ed25519_batch_verify(&messages, &signatures, &public_keys); let gas_cost = if public_keys.len() == 1 { - GAS_COST_ED25519_BATCH_VERIFY_ONE_PUBKEY + env.gas_config.ed25519_batch_verify_one_pubkey_cost } else { - GAS_COST_ED25519_BATCH_VERIFY + env.gas_config.ed25519_batch_verify_cost } * signatures.len() as u64; - let gas_info = GasInfo::with_cost(max(gas_cost, GAS_COST_ED25519_VERIFY)); + 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 { @@ -1852,6 +1840,7 @@ mod tests { match result.unwrap_err() { VmError::CommunicationErr { source: CommunicationError::InvalidOrder { .. }, + .. } => {} e => panic!("Unexpected error: {:?}", e), } @@ -1900,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), } From fce4dac79e690f6b68d478dd4d48b2525930e0ec Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 1 Mar 2021 17:51:21 +0100 Subject: [PATCH 4/4] Avoid floating point numbers / ops --- packages/vm/src/environment.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/vm/src/environment.rs b/packages/vm/src/environment.rs index b033608711..ed98a879ea 100644 --- a/packages/vm/src/environment.rs +++ b/packages/vm/src/environment.rs @@ -33,22 +33,28 @@ pub struct GasConfig { impl GasConfig { // Base crypto-verify gas cost: 1000 Cosmos SDK * 100 CosmWasm factor - const BASE_CRYPTO_COST: f32 = 100_000.0; + const BASE_CRYPTO_COST: u64 = 100_000; // secp256k1 cost factor (reference) - const SECP256K1_VERIFY_FACTOR: f32 = 1.; // ~154 us in crypto benchmarks + 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: f32 = 162. / 154.; // 162 us / 154 us ~ 1.05 - const ED25519_VERIFY_FACTOR: f32 = 63. / 154.; // 63 us / 154 us ~ 0.41 + 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: f32 = GasConfig::ED25519_VERIFY_FACTOR / 2.; // 0.41 / 2. ~ 0.21 - const ED255219_BATCH_VERIFY_ONE_PUBKEY_FACTOR: f32 = GasConfig::ED25519_VERIFY_FACTOR / 4.; // 0.41 / 4. ~ 0.1 - - fn calc_crypto_cost(factor: f32) -> u64 { - (GasConfig::BASE_CRYPTO_COST * factor).round() as u64 + 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 } }