From 108590d924bcc00e62b894de02516ffb2378be11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 20 Aug 2018 14:05:01 +0200 Subject: [PATCH] Allow calling contracts in genesis state. (#9375) --- Cargo.lock | 1 + ethcore/src/ethereum/mod.rs | 3 +- ethcore/src/spec/spec.rs | 5 +++ evmbin/Cargo.toml | 1 + evmbin/res/testchain.json | 38 +++++++++++++++++++++ evmbin/src/display/std_json.rs | 6 ++-- evmbin/src/info.rs | 60 ++++++++++++++++++++++++++-------- evmbin/src/main.rs | 2 ++ 8 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 evmbin/res/testchain.json diff --git a/Cargo.lock b/Cargo.lock index b44d65ac3..38f1cbb5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1018,6 +1018,7 @@ name = "evmbin" version = "0.1.0" dependencies = [ "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.12.0", "ethcore-transaction 0.1.0", "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index 4aab4b71f..a31f9ed50 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -30,7 +30,8 @@ pub use self::denominations::*; use machine::EthereumMachine; use super::spec::*; -fn load<'a, T: Into>>>(params: T, b: &[u8]) -> Spec { +/// Load chain spec from `SpecParams` and JSON. +pub fn load<'a, T: Into>>>(params: T, b: &[u8]) -> Spec { match params.into() { Some(params) => Spec::load(params, b), None => Spec::load(&::std::env::temp_dir(), b) diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index bcfa64ed1..a83046a72 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -770,6 +770,11 @@ impl Spec { Ok(()) } + /// Return genesis state as Plain old data. + pub fn genesis_state(&self) -> &PodState { + &self.genesis_state + } + /// Returns `false` if the memoized state root is invalid. `true` otherwise. pub fn is_state_root_valid(&self) -> bool { // TODO: get rid of this function and ensure state root always is valid. diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index 43264f042..a4cc451be 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -10,6 +10,7 @@ path = "./src/main.rs" [dependencies] docopt = "0.8" +env_logger = "0.5" ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests"] } ethjson = { path = "../json" } parity-bytes = { git = "https://github.com/paritytech/parity-common" } diff --git a/evmbin/res/testchain.json b/evmbin/res/testchain.json new file mode 100644 index 000000000..be3455179 --- /dev/null +++ b/evmbin/res/testchain.json @@ -0,0 +1,38 @@ +{ + "name": "lab", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x1", + "difficultyBoundDivisor": "0x800" + } + } + }, + "accounts": { + "0000000000000000000000000000000000000020": { + "nonce": "0x0", + "balance": "0x64", + "code": "0x62aaaaaa60aa60aa5060aa60aa60aa60aa60aa60aa" + } + }, + "params":{ + "networkID": "0x42", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1", + "gasLimitBoundDivisor": "0x400" + }, + "genesis": { + "gasLimit": "0x8000000", + "seal": { + "ethereum": { + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000042" + } + }, + "difficulty": "0x400", + "extraData": "0x0", + "author": "0x3333333333333333333333333333333333333333", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } +} diff --git a/evmbin/src/display/std_json.rs b/evmbin/src/display/std_json.rs index b3533ea2c..ebf099bcf 100644 --- a/evmbin/src/display/std_json.rs +++ b/evmbin/src/display/std_json.rs @@ -167,13 +167,13 @@ impl trace::VMTracer for Informant { } #[cfg(test)] -mod tests { +pub mod tests { use std::sync::{Arc, Mutex}; use super::*; use info::tests::run_test; #[derive(Debug, Clone, Default)] - struct TestWriter(pub Arc>>); + pub struct TestWriter(pub Arc>>); impl Writer for TestWriter { fn clone(&self) -> Self { Clone::clone(self) } @@ -189,7 +189,7 @@ mod tests { } } - fn informant() -> (Informant, Arc>>) { + pub fn informant() -> (Informant, Arc>>) { let writer = TestWriter::default(); let res = writer.0.clone(); (Informant::new(writer), res) diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index ce824fe17..080c0c7ff 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -68,11 +68,19 @@ pub type RunResult = Result, Failure>; /// Execute given `ActionParams` and return the result. pub fn run_action( spec: &spec::Spec, - params: ActionParams, + mut params: ActionParams, mut informant: T, ) -> RunResult { informant.set_gas(params.gas); - run(spec, params.gas, None, |mut client| { + + // if the code is not overwritten from CLI, use code from spec file. + if params.code.is_none() { + if let Some(acc) = spec.genesis_state().get().get(¶ms.code_address) { + params.code = acc.code.clone().map(::std::sync::Arc::new); + params.code_hash = None; + } + } + run(spec, params.gas, spec.genesis_state(), |mut client| { let result = client .call(params, &mut trace::NoopTracer, &mut informant) .map(|r| (0.into(), r.gas_left, r.return_data.to_vec())); @@ -130,24 +138,21 @@ pub fn run_transaction( } /// Execute VM with given `ActionParams` -pub fn run<'a, F, T, X>( +pub fn run<'a, F, X>( spec: &'a spec::Spec, initial_gas: U256, - pre_state: T, + pre_state: &'a pod_state::PodState, run: F, ) -> RunResult where F: FnOnce(EvmTestClient) -> (Result<(H256, U256, Vec), EvmTestError>, Option), - T: Into>, { - let test_client = match pre_state.into() { - Some(pre_state) => EvmTestClient::from_pod_state(spec, pre_state.clone()), - None => EvmTestClient::new(spec), - }.map_err(|error| Failure { - gas_used: 0.into(), - error, - time: Duration::from_secs(0), - traces: None, - })?; + let test_client = EvmTestClient::from_pod_state(spec, pre_state.clone()) + .map_err(|error| Failure { + gas_used: 0.into(), + error, + time: Duration::from_secs(0), + traces: None, + })?; let start = Instant::now(); let result = run(test_client); @@ -204,4 +209,31 @@ pub mod tests { }, } } + + #[test] + fn should_call_account_from_spec() { + use display::std_json::tests::informant; + + let (inf, res) = informant(); + let mut params = ActionParams::default(); + params.code_address = 0x20.into(); + params.gas = 0xffff.into(); + + let spec = ::ethcore::ethereum::load(None, include_bytes!("../res/testchain.json")); + let _result = run_action(&spec, params, inf); + + assert_eq!( + &String::from_utf8_lossy(&**res.lock().unwrap()), +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} +"#); + } } diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index 144c99fb3..78fc47488 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -31,6 +31,7 @@ extern crate ethereum_types; extern crate vm; extern crate evm; extern crate panic_hook; +extern crate env_logger; #[cfg(test)] #[macro_use] @@ -92,6 +93,7 @@ General options: fn main() { panic_hook::set_abort(); + env_logger::init(); let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());