Skip to content

Add benchmarks-based signature verification gas costs #804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions packages/vm/src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -39,6 +95,7 @@ impl GasState {
pub struct Environment<A: BackendApi, S: Storage, Q: Querier> {
pub api: A,
pub print_debug: bool,
pub gas_config: GasConfig,
data: Arc<RwLock<ContextData<S, Q>>>,
}

Expand All @@ -51,6 +108,7 @@ impl<A: BackendApi, S: Storage, Q: Querier> Clone for Environment<A, S, Q> {
Environment {
api: self.api,
print_debug: self.print_debug,
gas_config: self.gas_config.clone(),
data: self.data.clone(),
}
}
Expand All @@ -67,6 +125,7 @@ impl<A: BackendApi, S: Storage, Q: Querier> Environment<A, S, Q> {
Environment {
api,
print_debug,
gas_config: GasConfig::default(),
data: Arc::new(RwLock::new(ContextData::new(gas_limit))),
}
}
Expand Down
30 changes: 21 additions & 9 deletions packages/vm/src/imports.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Import implementations

use std::cmp::max;
use std::convert::TryInto;

use cosmwasm_crypto::{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -298,7 +306,7 @@ fn do_secp256k1_verify<A: BackendApi, S: Storage, Q: Querier>(
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::<A, S, Q>(env, gas_info)?;
Ok(result.map_or_else(
|err| match err {
Expand Down Expand Up @@ -328,7 +336,7 @@ fn do_secp256k1_recover_pubkey<A: BackendApi, S: Storage, Q: Querier>(
};

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::<A, S, Q>(env, gas_info)?;
match result {
Ok(pubkey) => {
Expand Down Expand Up @@ -358,7 +366,7 @@ fn do_ed25519_verify<A: BackendApi, S: Storage, Q: Querier>(
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::<A, S, Q>(env, gas_info)?;
Ok(result.map_or_else(
|err| match err {
Expand Down Expand Up @@ -403,8 +411,12 @@ fn do_ed25519_batch_verify<A: BackendApi, S: Storage, Q: Querier>(
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::<A, S, Q>(env, gas_info)?;
Ok(result.map_or_else(
|err| match err {
Expand Down