// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
//! VM runner.
use std::time::{Instant, Duration};
use bigint::prelude::U256;
use bigint::hash::H256;
use ethcore::{trace, spec, transaction, pod_state};
use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult};
use ethjson;
use vm::ActionParams;
/// VM execution informant
pub trait Informant: trace::VMTracer {
/// Display a single run init message
fn before_test(&self, test: &str, action: &str);
/// Set initial gas.
fn set_gas(&mut self, _gas: U256) {}
/// Display final result.
fn finish(result: RunResult);
}
/// Execution finished correctly
#[derive(Debug)]
pub struct Success {
/// Used gas
pub gas_used: U256,
/// Output as bytes
pub output: Vec,
/// Time Taken
pub time: Duration,
/// Traces
pub traces: Option,
}
/// Execution failed
#[derive(Debug)]
pub struct Failure {
/// Used gas
pub gas_used: U256,
/// Internal error
pub error: EvmTestError,
/// Duration
pub time: Duration,
/// Traces
pub traces: Option,
}
/// EVM Execution result
pub type RunResult = Result, Failure>;
/// Execute given `ActionParams` and return the result.
pub fn run_action(
spec: &spec::Spec,
params: ActionParams,
mut informant: T,
) -> RunResult {
informant.set_gas(params.gas);
run(spec, params.gas, None, |mut client| {
let result = client
.call(params, &mut informant)
.map(|r| (r.gas_left, r.return_data.to_vec()));
(result, informant.drain())
})
}
/// Execute given Transaction and verify resulting state root.
pub fn run_transaction(
name: &str,
idx: usize,
spec: ðjson::state::test::ForkSpec,
pre_state: &pod_state::PodState,
post_root: H256,
env_info: &client::EnvInfo,
transaction: transaction::SignedTransaction,
mut informant: T,
) {
let spec_name = format!("{:?}", spec).to_lowercase();
let spec = match EvmTestClient::spec_from_json(spec) {
Some(spec) => {
informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "starting");
spec
},
None => {
informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "skipping because of missing spec");
return;
},
};
informant.set_gas(env_info.gas_limit);
let result = run(spec, env_info.gas_limit, pre_state, |mut client| {
let result = client.transact(env_info, transaction, informant);
match result {
TransactResult::Ok { state_root, .. } if state_root != post_root => {
(Err(EvmTestError::PostCondition(format!(
"State root mismatch (got: {}, expected: {})",
state_root,
post_root,
))), None)
},
TransactResult::Ok { gas_left, output, vm_trace, .. } => {
(Ok((gas_left, output)), vm_trace)
},
TransactResult::Err { error, .. } => {
(Err(EvmTestError::PostCondition(format!(
"Unexpected execution error: {:?}", error
))), None)
},
}
});
T::finish(result)
}
/// Execute VM with given `ActionParams`
pub fn run<'a, F, T, X>(
spec: &'a spec::Spec,
initial_gas: U256,
pre_state: T,
run: F,
) -> RunResult where
F: FnOnce(EvmTestClient) -> (Result<(U256, Vec), EvmTestError>, Option),
T: Into