Skip to content

Commit

Permalink
Add measuring support.
Browse files Browse the repository at this point in the history
  • Loading branch information
olonho authored and joshuajbouw committed Jun 29, 2021
1 parent 2c4a48b commit 210ee55
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 35 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions runtime/near-vm-logic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ pub use near_vm_errors::{HostError, VMLogicError};
pub use types::ReturnData;

pub use gas_counter::with_ext_cost_counter;
#[cfg(feature = "protocol_feature_math_extension")]
pub use logic::ecrecover;
#[cfg(feature = "protocol_feature_math_extension")]
pub use logic::ripemd160;
46 changes: 31 additions & 15 deletions runtime/near-vm-logic/src/logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use crate::utils::split_method_names;
use crate::ValuePtr;
use byteorder::ByteOrder;
#[cfg(feature = "protocol_feature_math_extension")]
use near_crypto::Secp256K1PublicKey;
#[cfg(feature = "protocol_feature_math_extension")]
use near_crypto::Secp256K1Signature;
use near_primitives::checked_feature;
use near_primitives::version::is_implicit_account_creation_enabled;
Expand Down Expand Up @@ -979,10 +981,8 @@ impl<'a> VMLogic<'a> {

self.gas_counter.pay_per(ripemd160_block, message_blocks as u64)?;

use ripemd160::Digest;

let value_hash = ripemd160::Ripemd160::digest(&value);
self.internal_write_register(register_id, value_hash.as_slice().to_vec())
let value_hash = ripemd160(&value);
self.internal_write_register(register_id, value_hash)
}

/// Recovers an ECDSA signer address and returns it into `register_id`.
Expand Down Expand Up @@ -1018,7 +1018,7 @@ impl<'a> VMLogic<'a> {
) -> Result<u64> {
self.gas_counter.pay_base(ecrecover_base)?;

let signature = {
let signature_data = {
let vec = self.get_vec_from_memory_or_register(sig_ptr, sig_len)?;
if vec.len() != 64 {
return Err(VMLogicError::HostError(HostError::ECRecoverError {
Expand All @@ -1034,7 +1034,7 @@ impl<'a> VMLogic<'a> {

if v < 4 {
bytes[64] = v as u8;
Secp256K1Signature::from(bytes)
bytes
} else {
return Err(VMLogicError::HostError(HostError::ECRecoverError {
msg: format!("V recovery byte 0 through 3 are valid but was provided {}", v),
Expand All @@ -1058,16 +1058,13 @@ impl<'a> VMLogic<'a> {
bytes
};

if !signature.check_signature_values(malleability_flag != 0) {
return Ok(false as u64);
match ecrecover(signature_data, hash, malleability_flag) {
Some(key) => {
self.internal_write_register(register_id, key.as_ref().to_vec())?;
Ok(true as u64)
}
None => Ok(false as u64),
}

if let Ok(pk) = signature.recover(hash) {
self.internal_write_register(register_id, pk.as_ref().to_vec())?;
return Ok(true as u64);
};

Ok(false as u64)
}

/// Called by gas metering injected into Wasm. Counts both towards `burnt_gas` and `used_gas`.
Expand Down Expand Up @@ -2585,3 +2582,22 @@ impl std::fmt::Debug for VMOutcome {
)
}
}

#[cfg(feature = "protocol_feature_math_extension")]
pub fn ecrecover(
signature_data: [u8; 65],
hash_data: [u8; 32],
malleability_flag: u64,
) -> Option<Secp256K1PublicKey> {
let signature = Secp256K1Signature::from(signature_data);
if !signature.check_signature_values(malleability_flag != 0) {
return None;
}
signature.recover(hash_data).map_or(None, |result| Some(result))
}

#[cfg(feature = "protocol_feature_math_extension")]
pub fn ripemd160(data: &[u8]) -> Vec<u8> {
use ripemd160::Digest;
ripemd160::Ripemd160::digest(data).as_slice().to_vec()
}
1 change: 1 addition & 0 deletions runtime/runtime-params-estimator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ once_cell = "1"
num-traits = "0.2.12"
libc = "0.2.81"
wabt = "0.9"
ripemd160 = "0.9.0"

[features]
default = ["costs_counting"]
Expand Down
2 changes: 1 addition & 1 deletion runtime/runtime-params-estimator/src/cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ pub fn run(mut config: Config, only_compile: bool) -> RuntimeConfig {
// m.plot(PathBuf::from(&config.state_dump_path).as_path());
}

fn ratio_to_gas(gas_metric: GasMetric, value: Ratio<u64>) -> u64 {
pub(crate) fn ratio_to_gas(gas_metric: GasMetric, value: Ratio<u64>) -> u64 {
let divisor = match gas_metric {
// We use factor of 8 to approximately match the price of SHA256 operation between
// time-based and icount-based metric as measured on 3.2Ghz Core i5.
Expand Down
9 changes: 5 additions & 4 deletions runtime/runtime-params-estimator/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use std::path::Path;

use once_cell::sync::OnceCell;

/// Lists all cases that we want to measure.
pub mod cases;
// Generates runtime fees from the measurements.
Expand All @@ -12,12 +16,9 @@ pub mod stats;
pub mod testbed;
// Prepares transactions and feeds them to the testbed in batches. Performs the warm up, takes care
// of nonces.
mod math_ops_cost;
pub mod testbed_runners;

use std::path::Path;

use once_cell::sync::OnceCell;

/// Lazily loads contract's code from a directory in the source tree.
pub(crate) struct TestContract {
path: &'static str,
Expand Down
101 changes: 101 additions & 0 deletions runtime/runtime-params-estimator/src/math_ops_cost.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#[cfg(feature = "protocol_feature_math_extension")]
mod math_ops_cost {
use crate::cases::{ratio_to_gas, ratio_to_gas_signed};
use crate::testbed_runners::{end_count, start_count, GasMetric};
use crate::vm_estimator::least_squares_method;
use near_primitives::num_rational::Ratio;
use near_vm_logic::{ecrecover, ripemd160};

#[derive(Debug)]
pub struct EvmMathCosts {
ripemd160_base: i64,
ripemd160_per_byte: i64,
ecrecover_base: i64,
}

#[used]
static mut SINK: i64 = 0;

fn measure_operation<F: FnOnce(usize) -> i64>(
gas_metric: GasMetric,
count: usize,
op: F,
) -> u64 {
let start = start_count(gas_metric);
let result = op(count);
let end = end_count(gas_metric, &start);
// Do this to ensure call to measurer isn't optimized away.
unsafe {
SINK = result;
}
end
}

fn compute_ripemd160_cost(gas_metric: GasMetric) -> (i64, i64) {
let mut xs = vec![];
let mut ys = vec![];
for i in vec![3, 30, 300, 30000] {
xs.push(i as u64);
ys.push(measure_operation(gas_metric, i, |repeat| {
let value = vec![1u8; repeat];
let digest = ripemd160(&value);
digest.as_slice()[0] as i64
}));
}

let (a, b, _err) = least_squares_method(&xs, &ys);
(ratio_to_gas_signed(gas_metric, a), ratio_to_gas_signed(gas_metric, b))
}

fn compute_ecrecover_base(gas_metric: GasMetric) -> i64 {
let cost = measure_operation(gas_metric, 1, |_| do_ecrecover());
ratio_to_gas(gas_metric, Ratio::new(cost, 1)) as i64
}

fn do_ecrecover() -> i64 {
let hash: [u8; 32] = [
0x7d, 0xba, 0xf5, 0x58, 0xb0, 0xa1, 0xa5, 0xdc, 0x7a, 0x67, 0x20, 0x21, 0x17, 0xab,
0x14, 0x3c, 0x1d, 0x86, 0x05, 0xa9, 0x83, 0xe4, 0xa7, 0x43, 0xbc, 0x06, 0xfc, 0xc0,
0x31, 0x62, 0xdc, 0x0d,
];
let signature: [u8; 65] = [
0x5d, 0x99, 0xb6, 0xf7, 0xf6, 0xd1, 0xf7, 0x3d, 0x1a, 0x26, 0x49, 0x7f, 0x2b, 0x1c,
0x89, 0xb2, 0x4c, 0x09, 0x93, 0x91, 0x3f, 0x86, 0xe9, 0xa2, 0xd0, 0x2c, 0xd6, 0x98,
0x87, 0xd9, 0xc9, 0x4f, 0x3c, 0x88, 0x03, 0x58, 0x57, 0x9d, 0x81, 0x1b, 0x21, 0xdd,
0x1b, 0x7f, 0xd9, 0xbb, 0x01, 0xc1, 0xd8, 0x1d, 0x10, 0xe6, 0x9f, 0x03, 0x84, 0xe6,
0x75, 0xc3, 0x2b, 0x39, 0x64, 0x3b, 0xe8, 0x92, /* version */ 0,
];
let result = ecrecover(signature, hash, 0).unwrap();
result.as_ref()[0] as i64
}

pub fn cost_of_math(gas_metric: GasMetric) -> EvmMathCosts {
#[cfg(debug_assertions)]
println!("WARNING: must run in release mode to provide accurate results!");
let (ripemd160_base, ripemd160_per_byte) = compute_ripemd160_cost(gas_metric);
let ecrecover_base = compute_ecrecover_base(gas_metric);
EvmMathCosts { ripemd160_base, ripemd160_per_byte, ecrecover_base }
}

#[test]
fn test_math_cost_time() {
// cargo test --release --features protocol_feature_math_extension \
// --package runtime-params-estimator \
// --lib math_ops_cost::math_ops_cost::test_math_cost_time \
// -- --exact --nocapture
println!("{:?}", cost_of_math(GasMetric::Time));
}

#[test]
fn test_math_cost_icount() {
// CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER=./runner.sh cargo test --release \
// --features protocol_feature_math_extension \
// --package runtime-params-estimator \
// --lib math_ops_cost::math_ops_cost::test_math_cost_icount \
// -- --exact --nocapture
// Where runner.sh is
// /host/nearcore/runtime/runtime-params-estimator/emu-cost/counter_plugin/qemu-x86_64 \
// -cpu Westmere-v1 -plugin file=/host/nearcore/runtime/runtime-params-estimator/emu-cost/counter_plugin/libcounter.so $@
println!("{:?}", cost_of_math(GasMetric::ICount));
}
}
7 changes: 5 additions & 2 deletions runtime/runtime-params-estimator/src/vm_estimator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const SIGNER_ACCOUNT_ID: &str = "bob";
const SIGNER_ACCOUNT_PK: [u8; 3] = [0, 1, 2];
const PREDECESSOR_ACCOUNT_ID: &str = "carol";

fn create_context(input: Vec<u8>) -> VMContext {
pub(crate) fn create_context(input: Vec<u8>) -> VMContext {
VMContext {
current_account_id: CURRENT_ACCOUNT_ID.to_owned(),
signer_account_id: SIGNER_ACCOUNT_ID.to_owned(),
Expand Down Expand Up @@ -162,7 +162,10 @@ impl CompiledContractCache for MockCompiledContractCache {
}
}

fn least_squares_method(xs: &Vec<u64>, ys: &Vec<u64>) -> (Ratio<i128>, Ratio<i128>, Vec<i128>) {
pub(crate) fn least_squares_method(
xs: &Vec<u64>,
ys: &Vec<u64>,
) -> (Ratio<i128>, Ratio<i128>, Vec<i128>) {
let n = xs.len();
let n128 = n as i128;

Expand Down
27 changes: 14 additions & 13 deletions runtime/runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
use std::cmp::max;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::sync::Arc;

use log::debug;

use near_chain_configs::Genesis;
pub use near_crypto;
use near_crypto::PublicKey;
pub use near_primitives;
#[cfg(feature = "sandbox")]
use near_primitives::contract::ContractCode;
pub use near_primitives::runtime::apply_state::ApplyState;
use near_primitives::runtime::fees::RuntimeFeesConfig;
use near_primitives::runtime::get_insufficient_storage_stake;
use near_primitives::runtime::migration_data::{MigrationData, MigrationFlags};
use near_primitives::transaction::ExecutionMetadata;
use near_primitives::version::{
is_implicit_account_creation_enabled, ProtocolFeature, ProtocolVersion,
};
use near_primitives::{
account::Account,
errors::{ActionError, ActionErrorKind, RuntimeError, TxExecutionError},
Expand Down Expand Up @@ -50,17 +61,6 @@ use crate::config::{
use crate::genesis::{GenesisStateApplier, StorageComputer};
use crate::verifier::validate_receipt;
pub use crate::verifier::{validate_transaction, verify_and_charge_transaction};
#[cfg(feature = "sandbox")]
use near_primitives::contract::ContractCode;
pub use near_primitives::runtime::apply_state::ApplyState;
use near_primitives::runtime::fees::RuntimeFeesConfig;
use near_primitives::runtime::migration_data::{MigrationData, MigrationFlags};
use near_primitives::transaction::ExecutionMetadata;
use near_primitives::version::{
is_implicit_account_creation_enabled, ProtocolFeature, ProtocolVersion,
};
use std::rc::Rc;
use std::sync::Arc;

mod actions;
pub mod adapter;
Expand Down Expand Up @@ -1443,7 +1443,7 @@ impl Runtime {

#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;

use near_crypto::{InMemorySigner, KeyType, Signer};
use near_primitives::account::AccessKey;
Expand All @@ -1459,9 +1459,10 @@ mod tests {
use near_store::set_access_key;
use near_store::test_utils::create_tries;
use near_store::StoreCompiledContractCache;
use std::sync::Arc;
use testlib::runtime_utils::{alice_account, bob_account};

use super::*;

const GAS_PRICE: Balance = 5000;

fn to_yocto(near: Balance) -> Balance {
Expand Down

0 comments on commit 210ee55

Please sign in to comment.