Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

EVM benchmark utilities #8944

Merged
merged 13 commits into from
Jun 25, 2018
Merged
6 changes: 3 additions & 3 deletions ethcore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ keccak-hash = { path = "../util/hash" }
triehash = { path = "../util/triehash" }
unexpected = { path = "../util/unexpected" }
journaldb = { path = "../util/journaldb" }
tempdir = "0.3"
tempdir = { version = "0.3", optional = true }
kvdb-rocksdb = { path = "../util/kvdb-rocksdb" }

[dev-dependencies]
Expand All @@ -84,10 +84,10 @@ evm-debug-tests = ["evm-debug", "evm/evm-debug-tests"]
# EVM debug traces are printed.
slow-blocks = []
# Run JSON consensus tests.
json-tests = ["ethcore-transaction/json-tests"]
json-tests = ["ethcore-transaction/json-tests", "test-helpers", "tempdir"]
# Run memory/cpu heavy tests.
test-heavy = []
# Compile benches
benches = []
# Compile test helpers
test-helpers = []
test-helpers = ["tempdir"]
23 changes: 7 additions & 16 deletions ethcore/src/client/evm_test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,6 @@ impl fmt::Display for EvmTestError {
use ethereum;
use ethjson::state::test::ForkSpec;

lazy_static! {
pub static ref FRONTIER: spec::Spec = ethereum::new_frontier_test();
pub static ref HOMESTEAD: spec::Spec = ethereum::new_homestead_test();
pub static ref EIP150: spec::Spec = ethereum::new_eip150_test();
pub static ref EIP161: spec::Spec = ethereum::new_eip161_test();
pub static ref BYZANTIUM: spec::Spec = ethereum::new_byzantium_test();
pub static ref BYZANTIUM_TRANSITION: spec::Spec = ethereum::new_transition_test();
}

/// Simplified, single-block EVM test client.
pub struct EvmTestClient<'a> {
state: state::State<state_db::StateDB>,
Expand All @@ -90,14 +81,14 @@ impl<'a> fmt::Debug for EvmTestClient<'a> {

impl<'a> EvmTestClient<'a> {
/// Converts a json spec definition into spec.
pub fn spec_from_json(spec: &ForkSpec) -> Option<&'static spec::Spec> {
pub fn spec_from_json(spec: &ForkSpec) -> Option<spec::Spec> {
match *spec {
ForkSpec::Frontier => Some(&*FRONTIER),
ForkSpec::Homestead => Some(&*HOMESTEAD),
ForkSpec::EIP150 => Some(&*EIP150),
ForkSpec::EIP158 => Some(&*EIP161),
ForkSpec::Byzantium => Some(&*BYZANTIUM),
ForkSpec::EIP158ToByzantiumAt5 => Some(&BYZANTIUM_TRANSITION),
ForkSpec::Frontier => Some(ethereum::new_frontier_test()),
ForkSpec::Homestead => Some(ethereum::new_homestead_test()),
ForkSpec::EIP150 => Some(ethereum::new_eip150_test()),
ForkSpec::EIP158 => Some(ethereum::new_eip161_test()),
ForkSpec::Byzantium => Some(ethereum::new_byzantium_test()),
ForkSpec::EIP158ToByzantiumAt5 => Some(ethereum::new_transition_test()),
ForkSpec::FrontierToHomesteadAt5 | ForkSpec::HomesteadToDaoAt5 | ForkSpec::HomesteadToEIP150At5 => None,
_ => None,
}
Expand Down
27 changes: 23 additions & 4 deletions ethcore/src/json_tests/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

use std::path::Path;
use std::sync::Arc;
use client::{EvmTestClient, Client, ClientConfig, ChainInfo, ImportBlock};
use block::Block;
Expand All @@ -23,12 +24,26 @@ use miner::Miner;
use io::IoChannel;
use test_helpers;

pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
use super::HookType;

/// Run chain jsontests on a given folder.
pub fn run_test_path<H: FnMut(&str, HookType)>(p: &Path, skip: &[&'static str], h: &mut H) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 7 identical pub fn run_test_path added in this PR – would it be possible to avoid some boilerplate and have a single copy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those functions are wrappers for the parent run_test_path function. Before none of them are exposed, but I figured it would be helpful to add them there, as it simplify library user's work to figure out how those functions are meant to be used.

They're not entirely identical -- the function parameter passed is different, determining which type of tests it operates on. But I'm open to any idea if we can make this better and avoid duplications. :)

::json_tests::test_common::run_test_path(p, skip, json_chain_test, h)
}

/// Run chain jsontests on a given file.
pub fn run_test_file<H: FnMut(&str, HookType)>(p: &Path, h: &mut H) {
::json_tests::test_common::run_test_file(p, json_chain_test, h)
}

pub fn json_chain_test<H: FnMut(&str, HookType)>(json_data: &[u8], start_stop_hook: &mut H) -> Vec<String> {
::ethcore_logger::init_log();
let tests = ethjson::blockchain::Test::load(json_data).unwrap();
let mut failed = Vec::new();

for (name, blockchain) in tests.into_iter() {
start_stop_hook(&name, HookType::OnStart);

let mut fail = false;
{
let mut fail_unless = |cond: bool| if !cond && !fail {
Expand All @@ -42,7 +57,7 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {

let spec = {
let mut spec = match EvmTestClient::spec_from_json(&blockchain.network) {
Some(spec) => (*spec).clone(),
Some(spec) => spec,
None => {
println!(" - {} | {:?} Ignoring tests because of missing spec", name, blockchain.network);
continue;
Expand Down Expand Up @@ -82,17 +97,21 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
if !fail {
flushln!("ok");
}

start_stop_hook(&name, HookType::OnStop);
}

println!("!!! {:?} tests from failed.", failed.len());
failed
}

#[cfg(test)]
mod block_tests {
use super::json_chain_test;
use json_tests::HookType;

fn do_json_test(json_data: &[u8]) -> Vec<String> {
json_chain_test(json_data)
fn do_json_test<H: FnMut(&str, HookType)>(json_data: &[u8], h: &mut H) -> Vec<String> {
json_chain_test(json_data, h)
}

declare_test!{BlockchainTests_bcBlockGasLimitTest, "BlockchainTests/bcBlockGasLimitTest"}
Expand Down
18 changes: 13 additions & 5 deletions ethcore/src/json_tests/difficulty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ use header::Header;
use ethereum_types::U256;
use spec::Spec;

pub fn json_difficulty_test(json_data: &[u8], spec: Spec) -> Vec<String> {
use super::HookType;

pub fn json_difficulty_test<H: FnMut(&str, HookType)>(json_data: &[u8], spec: Spec, start_stop_hook: &mut H) -> Vec<String> {
::ethcore_logger::init_log();
let tests = ethjson::test::DifficultyTest::load(json_data).unwrap();
let engine = &spec.engine;

for (name, test) in tests.into_iter() {
start_stop_hook(&name, HookType::OnStart);

flush!(" - {}...", name);
println!(" - {}...", name);

Expand All @@ -42,15 +46,18 @@ pub fn json_difficulty_test(json_data: &[u8], spec: Spec) -> Vec<String> {
let expected_difficulty: U256 = test.current_difficulty.into();
assert_eq!(header.difficulty(), &expected_difficulty);
flushln!("ok");

start_stop_hook(&name, HookType::OnStop);
}
vec![]
}

mod difficulty_test_byzantium {
use super::json_difficulty_test;
use json_tests::HookType;

fn do_json_test(json_data: &[u8]) -> Vec<String> {
json_difficulty_test(json_data, ::ethereum::new_byzantium_test())
fn do_json_test<H: FnMut(&str, HookType)>(json_data: &[u8], h: &mut H) -> Vec<String> {
json_difficulty_test(json_data, ::ethereum::new_byzantium_test(), h)
}

declare_test!{DifficultyTests_difficultyByzantium, "BasicTests/difficultyByzantium.json"}
Expand All @@ -59,10 +66,11 @@ mod difficulty_test_byzantium {
mod difficulty_test_foundation {
use super::json_difficulty_test;
use tempdir::TempDir;
use json_tests::HookType;

fn do_json_test(json_data: &[u8]) -> Vec<String> {
fn do_json_test<H: FnMut(&str, HookType)>(json_data: &[u8], h: &mut H) -> Vec<String> {
let tempdir = TempDir::new("").unwrap();
json_difficulty_test(json_data, ::ethereum::new_foundation(&tempdir.path()))
json_difficulty_test(json_data, ::ethereum::new_foundation(&tempdir.path()), h)
}

declare_test!{DifficultyTests_difficultyMainNetwork, "BasicTests/difficultyMainNetwork.json"}
Expand Down
27 changes: 22 additions & 5 deletions ethcore/src/json_tests/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

use std::path::Path;
use std::sync::Arc;
use super::test_common::*;
use state::{Backend as StateBackend, State, Substate};
Expand All @@ -35,6 +36,18 @@ use rlp::RlpStream;
use hash::keccak;
use machine::EthereumMachine as Machine;

use super::HookType;

/// Run executive jsontests on a given folder.
pub fn run_test_path<H: FnMut(&str, HookType)>(p: &Path, skip: &[&'static str], h: &mut H) {
::json_tests::test_common::run_test_path(p, skip, do_json_test, h)
}

/// Run executive jsontests on a given file.
pub fn run_test_file<H: FnMut(&str, HookType)>(p: &Path, h: &mut H) {
::json_tests::test_common::run_test_file(p, do_json_test, h)
}

#[derive(Debug, PartialEq, Clone)]
struct CallCreate {
data: Bytes,
Expand Down Expand Up @@ -193,20 +206,22 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B>
}
}

fn do_json_test(json_data: &[u8]) -> Vec<String> {
fn do_json_test<H: FnMut(&str, HookType)>(json_data: &[u8], h: &mut H) -> Vec<String> {
let vms = VMType::all();
vms
.iter()
.flat_map(|vm| do_json_test_for(vm, json_data))
.flat_map(|vm| do_json_test_for(vm, json_data, h))
.collect()
}

fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
fn do_json_test_for<H: FnMut(&str, HookType)>(vm_type: &VMType, json_data: &[u8], start_stop_hook: &mut H) -> Vec<String> {
let tests = ethjson::vm::Test::load(json_data).unwrap();
let mut failed = Vec::new();

for (name, vm) in tests.into_iter() {
println!("name: {:?}", name);
start_stop_hook(&format!("{}-{}", name, vm_type), HookType::OnStart);

info!(target: "jsontests", "name: {:?}", name);
let mut fail = false;

let mut fail_unless = |cond: bool, s: &str | if !cond && !fail {
Expand Down Expand Up @@ -305,10 +320,12 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
fail_unless(Some(callcreates) == calls, "callcreates does not match");
}
};

start_stop_hook(&format!("{}-{}", name, vm_type), HookType::OnStop);
}

for f in &failed {
println!("FAILED: {:?}", f);
error!("FAILED: {:?}", f);
}

failed
Expand Down
19 changes: 19 additions & 0 deletions ethcore/src/json_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

//! Helpers and tests for operating on jsontests.

#[macro_use]
mod test_common;

Expand All @@ -22,4 +24,21 @@ mod executive;
mod state;
mod chain;
mod trie;

#[cfg(test)]
mod difficulty;

pub use self::test_common::HookType;

pub use self::transaction::run_test_path as run_transaction_test_path;
pub use self::transaction::run_test_file as run_transaction_test_file;
pub use self::executive::run_test_path as run_executive_test_path;
pub use self::executive::run_test_file as run_executive_test_file;
pub use self::state::run_test_path as run_state_test_path;
pub use self::state::run_test_file as run_state_test_file;
pub use self::chain::run_test_path as run_chain_test_path;
pub use self::chain::run_test_file as run_chain_test_file;
pub use self::trie::run_generic_test_path as run_generic_trie_test_path;
pub use self::trie::run_generic_test_file as run_generic_trie_test_file;
pub use self::trie::run_secure_test_path as run_secure_trie_test_path;
pub use self::trie::run_secure_test_file as run_secure_trie_test_file;
26 changes: 22 additions & 4 deletions ethcore/src/json_tests/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

use std::path::Path;
use super::test_common::*;
use pod_state::PodState;
use trace;
Expand All @@ -22,12 +23,26 @@ use ethjson;
use transaction::SignedTransaction;
use vm::EnvInfo;

pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
use super::HookType;

/// Run state jsontests on a given folder.
pub fn run_test_path<H: FnMut(&str, HookType)>(p: &Path, skip: &[&'static str], h: &mut H) {
::json_tests::test_common::run_test_path(p, skip, json_chain_test, h)
}

/// Run state jsontests on a given file.
pub fn run_test_file<H: FnMut(&str, HookType)>(p: &Path, h: &mut H) {
::json_tests::test_common::run_test_file(p, json_chain_test, h)
}

pub fn json_chain_test<H: FnMut(&str, HookType)>(json_data: &[u8], start_stop_hook: &mut H) -> Vec<String> {
::ethcore_logger::init_log();
let tests = ethjson::state::test::Test::load(json_data).unwrap();
let mut failed = Vec::new();

for (name, test) in tests.into_iter() {
start_stop_hook(&name, HookType::OnStart);

{
let multitransaction = test.transaction;
let env: EnvInfo = test.env.into();
Expand All @@ -50,7 +65,7 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
let transaction: SignedTransaction = multitransaction.select(&state.indexes).into();

let result = || -> Result<_, EvmTestError> {
Ok(EvmTestClient::from_pod_state(spec, pre.clone())?
Ok(EvmTestClient::from_pod_state(&spec, pre.clone())?
.transact(&env, transaction, trace::NoopTracer, trace::NoopVMTracer))
};
match result() {
Expand Down Expand Up @@ -81,6 +96,7 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
}
}

start_stop_hook(&name, HookType::OnStop);
}

if !failed.is_empty() {
Expand All @@ -89,11 +105,13 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
failed
}

#[cfg(test)]
mod state_tests {
use super::json_chain_test;
use json_tests::HookType;

fn do_json_test(json_data: &[u8]) -> Vec<String> {
json_chain_test(json_data)
fn do_json_test<H: FnMut(&str, HookType)>(json_data: &[u8], h: &mut H) -> Vec<String> {
json_chain_test(json_data, h)
}

declare_test!{GeneralStateTest_stAttackTest, "GeneralStateTests/stAttackTest/"}
Expand Down
Loading