2019-01-07 11:33:07 +01:00
|
|
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
|
|
|
// This file is part of Parity Ethereum.
|
2017-05-26 11:06:48 +02:00
|
|
|
|
2019-01-07 11:33:07 +01:00
|
|
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
2017-05-26 11:06:48 +02:00
|
|
|
// 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.
|
|
|
|
|
2019-01-07 11:33:07 +01:00
|
|
|
// Parity Ethereum is distributed in the hope that it will be useful,
|
2017-05-26 11:06:48 +02:00
|
|
|
// 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
|
2019-01-07 11:33:07 +01:00
|
|
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
2017-05-26 11:06:48 +02:00
|
|
|
|
2019-08-07 16:51:08 +02:00
|
|
|
//! EVM runner.
|
2017-05-26 11:06:48 +02:00
|
|
|
|
|
|
|
use std::time::{Instant, Duration};
|
2019-02-26 13:49:33 +01:00
|
|
|
use ethcore::client::{self, EvmTestClient, EvmTestError, TransactErr, TransactSuccess};
|
2019-07-08 18:17:48 +02:00
|
|
|
use ethcore::{spec, TrieSpec};
|
2019-08-07 16:51:08 +02:00
|
|
|
use ethereum_types::{H256, U256};
|
2017-08-28 14:25:16 +02:00
|
|
|
use ethjson;
|
2019-07-08 18:17:48 +02:00
|
|
|
use pod::PodState;
|
2019-08-07 16:51:08 +02:00
|
|
|
use trace;
|
2019-01-04 14:05:46 +01:00
|
|
|
use types::transaction;
|
2017-10-20 15:40:25 +02:00
|
|
|
use vm::ActionParams;
|
2017-05-26 11:06:48 +02:00
|
|
|
|
2019-08-07 16:51:08 +02:00
|
|
|
/// EVM execution informant.
|
2017-05-26 11:06:48 +02:00
|
|
|
pub trait Informant: trace::VMTracer {
|
2018-11-25 20:12:59 +01:00
|
|
|
/// Sink to use with finish
|
|
|
|
type Sink;
|
2017-08-28 14:25:16 +02:00
|
|
|
/// Display a single run init message
|
2018-01-18 10:32:22 +01:00
|
|
|
fn before_test(&mut self, test: &str, action: &str);
|
2017-06-09 12:31:03 +02:00
|
|
|
/// Set initial gas.
|
|
|
|
fn set_gas(&mut self, _gas: U256) {}
|
2018-11-25 20:12:59 +01:00
|
|
|
/// Clone sink.
|
|
|
|
fn clone_sink(&self) -> Self::Sink;
|
2017-05-26 11:06:48 +02:00
|
|
|
/// Display final result.
|
2018-11-25 20:12:59 +01:00
|
|
|
fn finish(result: RunResult<Self::Output>, &mut Self::Sink);
|
2017-05-26 11:06:48 +02:00
|
|
|
}
|
|
|
|
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Execution finished correctly.
|
2017-10-20 15:40:25 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Success<T> {
|
2019-08-07 16:51:08 +02:00
|
|
|
/// State root.
|
2018-01-18 10:32:22 +01:00
|
|
|
pub state_root: H256,
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Used gas.
|
2017-05-26 11:06:48 +02:00
|
|
|
pub gas_used: U256,
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Output as bytes.
|
2017-05-26 11:06:48 +02:00
|
|
|
pub output: Vec<u8>,
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Time taken.
|
2017-05-26 11:06:48 +02:00
|
|
|
pub time: Duration,
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Traces.
|
2017-10-20 15:40:25 +02:00
|
|
|
pub traces: Option<T>,
|
2018-11-25 20:12:59 +01:00
|
|
|
/// Optional end state dump
|
2019-07-08 18:17:48 +02:00
|
|
|
pub end_state: Option<PodState>,
|
2017-05-26 11:06:48 +02:00
|
|
|
}
|
|
|
|
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Execution failed.
|
2017-10-20 15:40:25 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Failure<T> {
|
2019-08-07 16:51:08 +02:00
|
|
|
/// State root.
|
2018-11-25 20:12:59 +01:00
|
|
|
pub state_root: H256,
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Used gas.
|
2017-06-09 12:31:03 +02:00
|
|
|
pub gas_used: U256,
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Internal error.
|
2017-05-26 11:06:48 +02:00
|
|
|
pub error: EvmTestError,
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Duration.
|
2017-05-26 11:06:48 +02:00
|
|
|
pub time: Duration,
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Traces.
|
2017-10-20 15:40:25 +02:00
|
|
|
pub traces: Option<T>,
|
2018-11-25 20:12:59 +01:00
|
|
|
/// Optional end state dump
|
2019-07-08 18:17:48 +02:00
|
|
|
pub end_state: Option<PodState>,
|
2017-10-20 15:40:25 +02:00
|
|
|
}
|
|
|
|
|
2019-08-07 16:51:08 +02:00
|
|
|
/// EVM execution result.
|
2017-10-20 15:40:25 +02:00
|
|
|
pub type RunResult<T> = Result<Success<T>, Failure<T>>;
|
|
|
|
|
|
|
|
/// Execute given `ActionParams` and return the result.
|
|
|
|
pub fn run_action<T: Informant>(
|
|
|
|
spec: &spec::Spec,
|
2018-08-20 14:05:01 +02:00
|
|
|
mut params: ActionParams,
|
2017-10-20 15:40:25 +02:00
|
|
|
mut informant: T,
|
2018-11-25 20:12:59 +01:00
|
|
|
trie_spec: TrieSpec,
|
2017-10-20 15:40:25 +02:00
|
|
|
) -> RunResult<T::Output> {
|
|
|
|
informant.set_gas(params.gas);
|
2018-08-20 14:05:01 +02:00
|
|
|
|
|
|
|
// if the code is not overwritten from CLI, use code from spec file.
|
|
|
|
if params.code.is_none() {
|
2019-08-07 16:52:48 +02:00
|
|
|
if let Some(acc) = spec.genesis_state.get().get(¶ms.code_address) {
|
2018-08-20 14:05:01 +02:00
|
|
|
params.code = acc.code.clone().map(::std::sync::Arc::new);
|
|
|
|
params.code_hash = None;
|
|
|
|
}
|
|
|
|
}
|
2019-08-07 16:52:48 +02:00
|
|
|
run(spec, trie_spec, params.gas, &spec.genesis_state, |mut client| {
|
2018-08-29 19:13:45 +02:00
|
|
|
let result = match client.call(params, &mut trace::NoopTracer, &mut informant) {
|
2018-11-25 20:12:59 +01:00
|
|
|
Ok(r) => (Ok(r.return_data.to_vec()), Some(r.gas_left)),
|
2018-08-29 19:13:45 +02:00
|
|
|
Err(err) => (Err(err), None),
|
|
|
|
};
|
2019-06-03 15:36:21 +02:00
|
|
|
(result.0, H256::from_low_u64_be(0), None, result.1, informant.drain())
|
2017-10-20 15:40:25 +02:00
|
|
|
})
|
2017-05-26 11:06:48 +02:00
|
|
|
}
|
|
|
|
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Input data to run transaction.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct TxInput<'a, T> {
|
|
|
|
/// State test name associated with the transaction.
|
|
|
|
pub state_test_name: &'a str,
|
|
|
|
/// Transaction index from list of transactions within a state root hash corresponding to a chain.
|
|
|
|
pub tx_index: usize,
|
|
|
|
/// Fork specification (i.e. Constantinople, EIP150, EIP158, etc).
|
|
|
|
pub fork_spec_name: &'a ethjson::spec::ForkSpec,
|
|
|
|
/// State of all accounts in the system that is a binary tree mapping of each account address to account data
|
|
|
|
/// that is expressed as Plain Old Data containing the account balance, account nonce, account code in bytes,
|
|
|
|
/// and the account storage binary tree map.
|
|
|
|
pub pre_state: &'a PodState,
|
|
|
|
/// State root hash associated with the transaction.
|
|
|
|
pub post_root: H256,
|
|
|
|
/// Client environment information associated with the transaction's chain specification.
|
|
|
|
pub env_info: &'a client::EnvInfo,
|
|
|
|
/// Signed transaction accompanied by a signature that may be unverified and a successfully recovered
|
|
|
|
/// sender address. The unverified transaction contains a recoverable ECDSA signature that has been encoded
|
|
|
|
/// as RSV components and includes replay protection for the specified chain. Verification of the signed transaction
|
|
|
|
/// with a valid secret of an account's keypair and a specific chain may be used to recover the sender's public key
|
|
|
|
/// and their associated address by applying the Keccak-256 hash function.
|
|
|
|
pub transaction: transaction::SignedTransaction,
|
|
|
|
/// JSON formatting informant.
|
|
|
|
pub informant: T,
|
|
|
|
/// Trie specification (i.e. Generic trie, Secure trie, Secure with fat database).
|
|
|
|
pub trie_spec: TrieSpec,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Execute given transaction and verify resulting state root.
|
|
|
|
/// Returns true if the transaction executes successfully.
|
2017-08-28 14:25:16 +02:00
|
|
|
pub fn run_transaction<T: Informant>(
|
2019-08-07 16:51:08 +02:00
|
|
|
tx_input: TxInput<T>
|
|
|
|
) -> bool {
|
|
|
|
let TxInput {
|
|
|
|
state_test_name, tx_index, fork_spec_name, pre_state, post_root, env_info, transaction, mut informant, trie_spec, ..
|
|
|
|
} = tx_input;
|
|
|
|
let fork_spec_name_formatted = format!("{:?}", fork_spec_name).to_lowercase();
|
|
|
|
let fork_spec = match EvmTestClient::fork_spec_from_json(&fork_spec_name) {
|
2017-08-28 14:25:16 +02:00
|
|
|
Some(spec) => {
|
2019-08-07 16:51:08 +02:00
|
|
|
informant.before_test(
|
|
|
|
&format!("{}:{}:{}", &state_test_name, &fork_spec_name_formatted, tx_index), "starting");
|
2017-08-28 14:25:16 +02:00
|
|
|
spec
|
|
|
|
},
|
|
|
|
None => {
|
2019-08-07 16:51:08 +02:00
|
|
|
informant.before_test(&format!("{}:{}:{}",
|
|
|
|
&state_test_name, fork_spec_name_formatted, &tx_index), "skipping because of missing fork specification");
|
|
|
|
return false;
|
2017-08-28 14:25:16 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
informant.set_gas(env_info.gas_limit);
|
|
|
|
|
2018-11-25 20:12:59 +01:00
|
|
|
let mut sink = informant.clone_sink();
|
2019-08-07 16:51:08 +02:00
|
|
|
let result = run(&fork_spec, trie_spec, transaction.gas, &pre_state, |mut client| {
|
|
|
|
let result = client.transact(&env_info, transaction, trace::NoopTracer, informant);
|
2017-08-28 14:25:16 +02:00
|
|
|
match result {
|
2019-02-26 13:49:33 +01:00
|
|
|
Ok(TransactSuccess { state_root, gas_left, output, vm_trace, end_state, .. }) => {
|
2018-11-25 20:12:59 +01:00
|
|
|
if state_root != post_root {
|
|
|
|
(Err(EvmTestError::PostCondition(format!(
|
|
|
|
"State root mismatch (got: {:#x}, expected: {:#x})",
|
|
|
|
state_root,
|
|
|
|
post_root,
|
|
|
|
))), state_root, end_state, Some(gas_left), None)
|
|
|
|
} else {
|
|
|
|
(Ok(output), state_root, end_state, Some(gas_left), vm_trace)
|
|
|
|
}
|
2017-08-28 14:25:16 +02:00
|
|
|
},
|
2019-02-26 13:49:33 +01:00
|
|
|
Err(TransactErr { state_root, error, end_state }) => {
|
2017-10-20 15:40:25 +02:00
|
|
|
(Err(EvmTestError::PostCondition(format!(
|
2017-08-28 14:25:16 +02:00
|
|
|
"Unexpected execution error: {:?}", error
|
2018-11-25 20:12:59 +01:00
|
|
|
))), state_root, end_state, None, None)
|
2017-08-28 14:25:16 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-08-07 16:51:08 +02:00
|
|
|
let ok = result.is_ok();
|
|
|
|
T::finish(result, &mut sink);
|
|
|
|
ok
|
2018-11-25 20:12:59 +01:00
|
|
|
}
|
|
|
|
|
2019-08-07 16:51:08 +02:00
|
|
|
/// Execute EVM with given `ActionParams`.
|
2018-08-20 14:05:01 +02:00
|
|
|
pub fn run<'a, F, X>(
|
2017-10-20 15:40:25 +02:00
|
|
|
spec: &'a spec::Spec,
|
2018-11-25 20:12:59 +01:00
|
|
|
trie_spec: TrieSpec,
|
2017-10-20 15:40:25 +02:00
|
|
|
initial_gas: U256,
|
2019-07-08 18:17:48 +02:00
|
|
|
pre_state: &'a PodState,
|
2017-10-20 15:40:25 +02:00
|
|
|
run: F,
|
|
|
|
) -> RunResult<X> where
|
2019-07-08 18:17:48 +02:00
|
|
|
F: FnOnce(EvmTestClient) -> (Result<Vec<u8>, EvmTestError>, H256, Option<PodState>, Option<U256>, Option<X>),
|
2017-08-28 14:25:16 +02:00
|
|
|
{
|
2018-11-25 20:12:59 +01:00
|
|
|
let do_dump = trie_spec == TrieSpec::Fat;
|
|
|
|
|
|
|
|
let mut test_client = EvmTestClient::from_pod_state_with_trie(spec, pre_state.clone(), trie_spec)
|
2018-08-20 14:05:01 +02:00
|
|
|
.map_err(|error| Failure {
|
|
|
|
gas_used: 0.into(),
|
|
|
|
error,
|
|
|
|
time: Duration::from_secs(0),
|
|
|
|
traces: None,
|
2019-06-03 15:36:21 +02:00
|
|
|
state_root: H256::zero(),
|
2018-11-25 20:12:59 +01:00
|
|
|
end_state: None,
|
2018-08-20 14:05:01 +02:00
|
|
|
})?;
|
2017-05-26 11:06:48 +02:00
|
|
|
|
2018-11-25 20:12:59 +01:00
|
|
|
if do_dump {
|
2019-07-08 18:17:48 +02:00
|
|
|
test_client.set_dump_state();
|
2018-11-25 20:12:59 +01:00
|
|
|
}
|
|
|
|
|
2017-05-26 11:06:48 +02:00
|
|
|
let start = Instant::now();
|
2017-08-28 14:25:16 +02:00
|
|
|
let result = run(test_client);
|
2017-10-20 15:40:25 +02:00
|
|
|
let time = start.elapsed();
|
2017-05-26 11:06:48 +02:00
|
|
|
|
|
|
|
match result {
|
2018-11-25 20:12:59 +01:00
|
|
|
(Ok(output), state_root, end_state, gas_left, traces) => Ok(Success {
|
2018-01-18 10:32:22 +01:00
|
|
|
state_root,
|
2018-08-29 19:13:45 +02:00
|
|
|
gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas),
|
2017-10-20 15:40:25 +02:00
|
|
|
output,
|
|
|
|
time,
|
|
|
|
traces,
|
2018-11-25 20:12:59 +01:00
|
|
|
end_state,
|
2017-05-26 11:06:48 +02:00
|
|
|
}),
|
2018-11-25 20:12:59 +01:00
|
|
|
(Err(error), state_root, end_state, gas_left, traces) => Err(Failure {
|
2018-08-29 19:13:45 +02:00
|
|
|
gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas),
|
2017-10-20 15:40:25 +02:00
|
|
|
error,
|
|
|
|
time,
|
|
|
|
traces,
|
2018-11-25 20:12:59 +01:00
|
|
|
state_root,
|
|
|
|
end_state,
|
2017-05-26 11:06:48 +02:00
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
2017-10-20 15:40:25 +02:00
|
|
|
|
|
|
|
#[cfg(test)]
|
2018-01-18 10:32:22 +01:00
|
|
|
pub mod tests {
|
2017-10-20 15:40:25 +02:00
|
|
|
use std::sync::Arc;
|
|
|
|
use rustc_hex::FromHex;
|
|
|
|
use super::*;
|
2018-03-14 12:26:20 +01:00
|
|
|
use tempdir::TempDir;
|
2019-06-03 15:36:21 +02:00
|
|
|
use ethereum_types::Address;
|
2019-08-07 16:52:48 +02:00
|
|
|
use ethcore::spec::{self, Spec};
|
2017-10-20 15:40:25 +02:00
|
|
|
|
2018-01-18 10:32:22 +01:00
|
|
|
pub fn run_test<T, I, F>(
|
|
|
|
informant: I,
|
|
|
|
compare: F,
|
2017-10-20 15:40:25 +02:00
|
|
|
code: &str,
|
|
|
|
gas: T,
|
|
|
|
expected: &str,
|
2018-01-18 10:32:22 +01:00
|
|
|
) where
|
|
|
|
T: Into<U256>,
|
|
|
|
I: Informant,
|
|
|
|
F: FnOnce(Option<I::Output>, &str),
|
|
|
|
{
|
2017-10-20 15:40:25 +02:00
|
|
|
let mut params = ActionParams::default();
|
|
|
|
params.code = Some(Arc::new(code.from_hex().unwrap()));
|
|
|
|
params.gas = gas.into();
|
|
|
|
|
2018-03-14 12:26:20 +01:00
|
|
|
let tempdir = TempDir::new("").unwrap();
|
2019-08-07 16:52:48 +02:00
|
|
|
let spec = spec::new_foundation(&tempdir.path());
|
2018-11-25 20:12:59 +01:00
|
|
|
let result = run_action(&spec, params, informant, TrieSpec::Secure);
|
2017-10-20 15:40:25 +02:00
|
|
|
match result {
|
|
|
|
Ok(Success { traces, .. }) => {
|
2018-01-18 10:32:22 +01:00
|
|
|
compare(traces, expected)
|
2017-10-20 15:40:25 +02:00
|
|
|
},
|
|
|
|
Err(Failure { traces, .. }) => {
|
2018-01-18 10:32:22 +01:00
|
|
|
compare(traces, expected)
|
2017-10-20 15:40:25 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2018-08-20 14:05:01 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_call_account_from_spec() {
|
|
|
|
use display::std_json::tests::informant;
|
|
|
|
|
|
|
|
let (inf, res) = informant();
|
|
|
|
let mut params = ActionParams::default();
|
2019-06-03 15:36:21 +02:00
|
|
|
params.code_address = Address::from_low_u64_be(0x20);
|
2018-08-20 14:05:01 +02:00
|
|
|
params.gas = 0xffff.into();
|
|
|
|
|
2019-08-07 16:52:48 +02:00
|
|
|
let tempdir = TempDir::new("").unwrap();
|
|
|
|
let spec = Spec::load(&tempdir.path(), include_bytes!("../res/testchain.json") as &[u8]).unwrap();
|
2018-11-25 20:12:59 +01:00
|
|
|
let _result = run_action(&spec, params, inf, TrieSpec::Secure);
|
2018-08-20 14:05:01 +02:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
&String::from_utf8_lossy(&**res.lock().unwrap()),
|
2019-07-03 16:02:41 +02:00
|
|
|
r#"{"pc":0,"op":98,"opName":"PUSH3","gas":"0xffff","stack":[],"storage":{},"depth":1}
|
|
|
|
{"pc":4,"op":96,"opName":"PUSH1","gas":"0xfffc","stack":["0xaaaaaa"],"storage":{},"depth":1}
|
|
|
|
{"pc":6,"op":96,"opName":"PUSH1","gas":"0xfff9","stack":["0xaaaaaa","0xaa"],"storage":{},"depth":1}
|
|
|
|
{"pc":8,"op":80,"opName":"POP","gas":"0xfff6","stack":["0xaaaaaa","0xaa","0xaa"],"storage":{},"depth":1}
|
|
|
|
{"pc":9,"op":96,"opName":"PUSH1","gas":"0xfff4","stack":["0xaaaaaa","0xaa"],"storage":{},"depth":1}
|
|
|
|
{"pc":11,"op":96,"opName":"PUSH1","gas":"0xfff1","stack":["0xaaaaaa","0xaa","0xaa"],"storage":{},"depth":1}
|
|
|
|
{"pc":13,"op":96,"opName":"PUSH1","gas":"0xffee","stack":["0xaaaaaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1}
|
|
|
|
{"pc":15,"op":96,"opName":"PUSH1","gas":"0xffeb","stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1}
|
|
|
|
{"pc":17,"op":96,"opName":"PUSH1","gas":"0xffe8","stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1}
|
|
|
|
{"pc":19,"op":96,"opName":"PUSH1","gas":"0xffe5","stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1}
|
2018-08-20 14:05:01 +02:00
|
|
|
"#);
|
|
|
|
}
|
2017-10-20 15:40:25 +02:00
|
|
|
}
|