diff --git a/.travis.yml b/.travis.yml
index 0f0766ee4..230e7862f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,7 +33,7 @@ env:
global:
# GH_TOKEN
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
- - TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer -p ethjson -p ethcore-dapps -p ethcore-signer"
+ - TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer"
- ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
- KCOV_FEATURES=""
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov"
@@ -72,7 +72,6 @@ after_success: |
$KCOV_CMD target/debug/deps/ethcore_rpc-* &&
$KCOV_CMD target/debug/deps/ethcore_dapps-* &&
$KCOV_CMD target/debug/deps/ethcore_signer-* &&
- $KCOV_CMD target/debug/deps/ethminer-* &&
$KCOV_CMD target/debug/deps/ethjson-* &&
$KCOV_CMD target/debug/parity-* &&
[ $TRAVIS_BRANCH = master ] &&
diff --git a/Cargo.lock b/Cargo.lock
index e1f2a28b7..9f27c6b2d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,7 +17,6 @@ dependencies = [
"ethcore-rpc 1.2.0",
"ethcore-signer 1.2.0",
"ethcore-util 1.2.0",
- "ethminer 1.2.0",
"ethsync 1.2.0",
"fdlimit 0.1.0",
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -255,6 +254,7 @@ dependencies = [
"lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -272,6 +272,7 @@ dependencies = [
"jsonrpc-core 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)",
"parity-dapps-builtins 0.5.0 (git+https://github.com/ethcore/parity-dapps-builtins-rs.git)",
"parity-dapps-dao 0.3.0 (git+https://github.com/ethcore/parity-dapps-dao-rs.git)",
@@ -334,7 +335,6 @@ dependencies = [
"ethcore-devtools 1.2.0",
"ethcore-util 1.2.0",
"ethjson 0.1.0",
- "ethminer 1.2.0",
"ethsync 1.2.0",
"json-ipc-server 0.1.0 (git+https://github.com/ethcore/json-ipc-server.git)",
"jsonrpc-core 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -391,7 +391,7 @@ dependencies = [
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.1.0",
- "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -410,20 +410,6 @@ dependencies = [
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
-[[package]]
-name = "ethminer"
-version = "1.2.0"
-dependencies = [
- "clippy 0.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
- "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "ethcore 1.2.0",
- "ethcore-util 1.2.0",
- "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
[[package]]
name = "ethsync"
version = "1.2.0"
@@ -432,7 +418,6 @@ dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.2.0",
"ethcore-util 1.2.0",
- "ethminer 1.2.0",
"heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -874,7 +859,7 @@ dependencies = [
[[package]]
name = "parity-dapps-builtins"
version = "0.5.0"
-source = "git+https://github.com/ethcore/parity-dapps-builtins-rs.git#eb86a2954f04d3aa5547a8c4bb77ae7aad09bf55"
+source = "git+https://github.com/ethcore/parity-dapps-builtins-rs.git#8bbf0421e376f9496d70adc62c1c6d7f492df817"
dependencies = [
"parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)",
]
@@ -1182,6 +1167,11 @@ name = "slab"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "slab"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "solicit"
version = "0.4.4"
diff --git a/Cargo.toml b/Cargo.toml
index e93069c98..e4b8eeea2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,7 +27,6 @@ clippy = { version = "0.0.69", optional = true}
ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
ethsync = { path = "sync" }
-ethminer = { path = "miner" }
ethcore-devtools = { path = "devtools" }
ethcore-rpc = { path = "rpc", optional = true }
ethcore-signer = { path = "signer", optional = true }
@@ -46,7 +45,7 @@ default-features = false
default = ["rpc", "dapps", "ethcore-signer"]
rpc = ["ethcore-rpc"]
dapps = ["ethcore-dapps"]
-dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethminer/dev",
+dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev",
"ethcore-dapps/dev", "ethcore-signer/dev"]
travis-beta = ["ethcore/json-tests"]
travis-nightly = ["ethcore/json-tests", "dev"]
diff --git a/README.md b/README.md
index 903794e80..193cbeb8f 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-# ethcore
+# [Parity](https://ethcore.io/parity.html)
+### Fast, light, and robust Ethereum implementation
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/trogdoro/xiki][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
@@ -11,30 +12,64 @@
[license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
[license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html
-[Documentation](http://ethcore.github.io/parity/ethcore/index.html)
+[Internal Documentation](http://ethcore.github.io/parity/ethcore/index.html)
-### Building from source
+----
-First (if you don't already have it) get multirust:
+## About Parity
+
+Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and
+cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
+
+By default, Parity will run a JSONRPC server on `127.0.0.1:8545`. This is fully configurable and supports a number
+of RPC APIs.
+
+Parity also runs a server for running decentralized apps, or "Dapps", on `http://127.0.0.1:8080`.
+This includes a few useful Dapps, including Ethereum Wallet, Maker OTC, and a node status page.
+In a near-future release, it will be easy to install Dapps and use them through this web interface.
+
+If you run into an issue while using parity, feel free to file one in this repository
+or hop on our [gitter chat room]([gitter-url]) to ask a question. We are glad to help!
+
+Parity's current release is 1.1. You can download it at https://ethcore.io/parity.html or follow the instructions
+below to build from source.
+
+----
+
+## Building from source
+
+Parity is fully compatible with Stable Rust.
+
+We recommend installing Rust through [multirust](https://github.com/brson/multirust). If you don't already have multirust, you can install it like this:
- Linux:
```bash
-curl -sf https://raw.githubusercontent.com/brson/multirust/master/quick-install.sh | sh
+$ curl -sf https://raw.githubusercontent.com/brson/multirust/master/quick-install.sh | sh
```
- OSX with Homebrew:
```bash
-brew update && brew install multirust
-multirust default stable
+$ brew update && brew install multirust
+$ multirust default stable
```
Then, download and build Parity:
```bash
# download Parity code
-git clone https://github.com/ethcore/parity
-cd parity
+$ git clone https://github.com/ethcore/parity
+$ cd parity
# build in release mode
-cargo build --release
+$ cargo build --release
```
+
+This will produce an executable in the `target/release` subdirectory.
+Either run `cd target/release`, or copy `target/release/parity` to another location.
+
+To get started, just run
+```bash
+$ parity
+```
+
+and parity will begin syncing the Ethereum blockchain.
\ No newline at end of file
diff --git a/cov.sh b/cov.sh
index 084e95284..a13db50b9 100755
--- a/cov.sh
+++ b/cov.sh
@@ -22,7 +22,6 @@ cargo test \
-p ethsync \
-p ethcore-rpc \
-p parity \
- -p ethminer \
-p ethcore-signer \
-p ethcore-dapps \
--no-run || exit $?
@@ -37,5 +36,4 @@ kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage t
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_signer-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_dapps-*
-kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethminer-*
xdg-open target/coverage/index.html
diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml
index 5372c74a9..5a68bb751 100644
--- a/dapps/Cargo.toml
+++ b/dapps/Cargo.toml
@@ -27,6 +27,7 @@ parity-dapps-builtins = { git = "https://github.com/ethcore/parity-dapps-builtin
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs.git", version = "0.5.0", optional = true }
parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.3.0", optional = true }
parity-dapps-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.2.0", optional = true }
+mime_guess = { version = "1.6.1" }
clippy = { version = "0.0.69", optional = true}
[build-dependencies]
diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs
index c460dcf20..95b01d442 100644
--- a/dapps/src/api/api.rs
+++ b/dapps/src/api/api.rs
@@ -15,7 +15,7 @@
// along with Parity. If not, see .
use std::sync::Arc;
-use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
+use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath};
use api::response::as_json;
@@ -23,8 +23,8 @@ pub struct RestApi {
endpoints: Arc,
}
-#[derive(Debug, PartialEq, Serialize)]
-struct App {
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct App {
pub id: String,
pub name: String,
pub description: String,
@@ -34,6 +34,19 @@ struct App {
pub icon_url: String,
}
+impl App {
+ fn from_info(id: &str, info: &EndpointInfo) -> Self {
+ App {
+ id: id.to_owned(),
+ name: info.name.to_owned(),
+ description: info.description.to_owned(),
+ version: info.version.to_owned(),
+ author: info.author.to_owned(),
+ icon_url: info.icon_url.to_owned(),
+ }
+ }
+}
+
impl RestApi {
pub fn new(endpoints: Arc) -> Box {
Box::new(RestApi {
@@ -43,14 +56,7 @@ impl RestApi {
fn list_apps(&self) -> Vec {
self.endpoints.iter().filter_map(|(ref k, ref e)| {
- e.info().map(|ref info| App {
- id: k.to_owned().clone(),
- name: info.name.to_owned(),
- description: info.description.to_owned(),
- version: info.version.to_owned(),
- author: info.author.to_owned(),
- icon_url: info.icon_url.to_owned(),
- })
+ e.info().map(|ref info| App::from_info(k, info))
}).collect()
}
}
diff --git a/dapps/src/api/mod.rs.in b/dapps/src/api/mod.rs.in
index 0eff6b397..a069c06b0 100644
--- a/dapps/src/api/mod.rs.in
+++ b/dapps/src/api/mod.rs.in
@@ -18,3 +18,4 @@ mod api;
mod response;
pub use self::api::RestApi;
+pub use self::api::App;
diff --git a/dapps/src/apps/fs.rs b/dapps/src/apps/fs.rs
new file mode 100644
index 000000000..fa3b0ab4c
--- /dev/null
+++ b/dapps/src/apps/fs.rs
@@ -0,0 +1,116 @@
+// Copyright 2015, 2016 Ethcore (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 .
+
+use serde_json;
+use std::io;
+use std::io::Read;
+use std::fs;
+use std::path::PathBuf;
+use page::LocalPageEndpoint;
+use endpoint::{Endpoints, EndpointInfo};
+use api::App;
+
+struct LocalDapp {
+ id: String,
+ path: PathBuf,
+ info: EndpointInfo,
+}
+
+fn local_dapps(dapps_path: String) -> Vec {
+ let files = fs::read_dir(dapps_path.as_str());
+ if let Err(e) = files {
+ warn!(target: "dapps", "Unable to load local dapps from: {}. Reason: {:?}", dapps_path, e);
+ return vec![];
+ }
+
+ let files = files.expect("Check is done earlier");
+ files.map(|dir| {
+ let entry = try!(dir);
+ let file_type = try!(entry.file_type());
+
+ // skip files
+ if file_type.is_file() {
+ return Err(io::Error::new(io::ErrorKind::NotFound, "Not a file"));
+ }
+
+ // take directory name and path
+ entry.file_name().into_string()
+ .map(|name| (name, entry.path()))
+ .map_err(|e| {
+ info!(target: "dapps", "Unable to load dapp: {:?}. Reason: {:?}", entry.path(), e);
+ io::Error::new(io::ErrorKind::NotFound, "Invalid name")
+ })
+ })
+ .filter_map(|m| {
+ if let Err(ref e) = m {
+ debug!(target: "dapps", "Ignoring local dapp: {:?}", e);
+ }
+ m.ok()
+ })
+ .map(|(name, path)| {
+ // try to get manifest file
+ let info = read_manifest(&name, path.clone());
+ LocalDapp {
+ id: name,
+ path: path,
+ info: info,
+ }
+ })
+ .collect()
+}
+
+fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
+ path.push("manifest.json");
+
+ fs::File::open(path.clone())
+ .map_err(|e| format!("{:?}", e))
+ .and_then(|mut f| {
+ // Reat file
+ let mut s = String::new();
+ try!(f.read_to_string(&mut s).map_err(|e| format!("{:?}", e)));
+ // Try to deserialize manifest
+ serde_json::from_str::(&s).map_err(|e| format!("{:?}", e))
+ })
+ .map(|app| EndpointInfo {
+ name: app.name,
+ description: app.description,
+ version: app.version,
+ author: app.author,
+ icon_url: app.icon_url,
+ })
+ .unwrap_or_else(|e| {
+ warn!(target: "dapps", "Cannot read manifest file at: {:?}. Error: {:?}", path, e);
+
+ EndpointInfo {
+ name: name.into(),
+ description: name.into(),
+ version: "0.0.0".into(),
+ author: "?".into(),
+ icon_url: "icon.png".into(),
+ }
+ })
+}
+
+pub fn local_endpoints(dapps_path: String) -> Endpoints {
+ let mut pages = Endpoints::new();
+ for dapp in local_dapps(dapps_path) {
+ pages.insert(
+ dapp.id,
+ Box::new(LocalPageEndpoint::new(dapp.path, dapp.info))
+ );
+ }
+ pages
+}
diff --git a/dapps/src/apps.rs b/dapps/src/apps/mod.rs
similarity index 93%
rename from dapps/src/apps.rs
rename to dapps/src/apps/mod.rs
index 130b20fb9..7f849cf65 100644
--- a/dapps/src/apps.rs
+++ b/dapps/src/apps/mod.rs
@@ -19,10 +19,11 @@ use page::PageEndpoint;
use proxypac::ProxyPac;
use parity_dapps::WebApp;
+mod fs;
+
extern crate parity_dapps_status;
extern crate parity_dapps_builtins;
-
pub const DAPPS_DOMAIN : &'static str = ".parity";
pub const RPC_PATH : &'static str = "rpc";
pub const API_PATH : &'static str = "api";
@@ -36,22 +37,24 @@ pub fn utils() -> Box {
Box::new(PageEndpoint::with_prefix(parity_dapps_builtins::App::default(), UTILS_PATH.to_owned()))
}
-pub fn all_endpoints() -> Endpoints {
- let mut pages = Endpoints::new();
- pages.insert("proxy".into(), ProxyPac::boxed());
-
+pub fn all_endpoints(dapps_path: String) -> Endpoints {
+ // fetch fs dapps at first to avoid overwriting builtins
+ let mut pages = fs::local_endpoints(dapps_path);
// Home page needs to be safe embed
// because we use Cross-Origin LocalStorage.
// TODO [ToDr] Account naming should be moved to parity.
pages.insert("home".into(), Box::new(
PageEndpoint::new_safe_to_embed(parity_dapps_builtins::App::default())
));
+ pages.insert("proxy".into(), ProxyPac::boxed());
insert::(&mut pages, "status");
insert::(&mut pages, "parity");
+ // Optional dapps
wallet_page(&mut pages);
daodapp_page(&mut pages);
makerotc_page(&mut pages);
+
pages
}
diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs
index 28ca6ea11..592bc7f8f 100644
--- a/dapps/src/endpoint.rs
+++ b/dapps/src/endpoint.rs
@@ -30,17 +30,17 @@ pub struct EndpointPath {
pub port: u16,
}
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Clone)]
pub struct EndpointInfo {
- pub name: &'static str,
- pub description: &'static str,
- pub version: &'static str,
- pub author: &'static str,
- pub icon_url: &'static str,
+ pub name: String,
+ pub description: String,
+ pub version: String,
+ pub author: String,
+ pub icon_url: String,
}
pub trait Endpoint : Send + Sync {
- fn info(&self) -> Option { None }
+ fn info(&self) -> Option<&EndpointInfo> { None }
fn to_handler(&self, path: EndpointPath) -> Box>;
}
diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs
index 27c215108..a7fbd5963 100644
--- a/dapps/src/lib.rs
+++ b/dapps/src/lib.rs
@@ -52,6 +52,8 @@ extern crate serde_json;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate parity_dapps;
+extern crate ethcore_rpc;
+extern crate mime_guess;
mod endpoint;
mod apps;
@@ -66,37 +68,41 @@ use std::net::SocketAddr;
use std::collections::HashMap;
use jsonrpc_core::{IoHandler, IoDelegate};
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
+use ethcore_rpc::Extendable;
static DAPPS_DOMAIN : &'static str = ".parity";
/// Webapps HTTP+RPC server build.
pub struct ServerBuilder {
+ dapps_path: String,
handler: Arc,
}
+impl Extendable for ServerBuilder {
+ fn add_delegate(&self, delegate: IoDelegate) {
+ self.handler.add_delegate(delegate);
+ }
+}
+
impl ServerBuilder {
/// Construct new dapps server
- pub fn new() -> Self {
+ pub fn new(dapps_path: String) -> Self {
ServerBuilder {
+ dapps_path: dapps_path,
handler: Arc::new(IoHandler::new())
}
}
- /// Add io delegate.
- pub fn add_delegate(&self, delegate: IoDelegate) where D: Send + Sync + 'static {
- self.handler.add_delegate(delegate);
- }
-
/// Asynchronously start server with no authentication,
/// returns result with `Server` handle on success or an error.
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result {
- Server::start_http(addr, NoAuth, self.handler.clone())
+ Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone())
}
/// Asynchronously start server with `HTTP Basic Authentication`,
/// return result with `Server` handle on success or an error.
pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result {
- Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone())
+ Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone())
}
}
@@ -107,10 +113,10 @@ pub struct Server {
}
impl Server {
- fn start_http(addr: &SocketAddr, authorization: A, handler: Arc) -> Result {
+ fn start_http(addr: &SocketAddr, authorization: A, handler: Arc, dapps_path: String) -> Result {
let panic_handler = Arc::new(Mutex::new(None));
let authorization = Arc::new(authorization);
- let endpoints = Arc::new(apps::all_endpoints());
+ let endpoints = Arc::new(apps::all_endpoints(dapps_path));
let special = Arc::new({
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
diff --git a/dapps/src/page/builtin.rs b/dapps/src/page/builtin.rs
new file mode 100644
index 000000000..1c7ca32d4
--- /dev/null
+++ b/dapps/src/page/builtin.rs
@@ -0,0 +1,154 @@
+// Copyright 2015, 2016 Ethcore (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 .
+
+use page::handler;
+use std::sync::Arc;
+use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
+use parity_dapps::{WebApp, File, Info};
+
+pub struct PageEndpoint {
+ /// Content of the files
+ pub app: Arc,
+ /// Prefix to strip from the path (when `None` deducted from `app_id`)
+ pub prefix: Option,
+ /// Safe to be loaded in frame by other origin. (use wisely!)
+ safe_to_embed: bool,
+ info: EndpointInfo,
+}
+
+impl PageEndpoint {
+ /// Creates new `PageEndpoint` for builtin (compile time) Dapp.
+ pub fn new(app: T) -> Self {
+ let info = app.info();
+ PageEndpoint {
+ app: Arc::new(app),
+ prefix: None,
+ safe_to_embed: false,
+ info: EndpointInfo::from(info),
+ }
+ }
+
+ /// Create new `PageEndpoint` and specify prefix that should be removed before looking for a file.
+ /// It's used only for special endpoints (i.e. `/parity-utils/`)
+ /// So `/parity-utils/inject.js` will be resolved to `/inject.js` is prefix is set.
+ pub fn with_prefix(app: T, prefix: String) -> Self {
+ let info = app.info();
+ PageEndpoint {
+ app: Arc::new(app),
+ prefix: Some(prefix),
+ safe_to_embed: false,
+ info: EndpointInfo::from(info),
+ }
+ }
+
+ /// Creates new `PageEndpoint` which can be safely used in iframe
+ /// even from different origin. It might be dangerous (clickjacking).
+ /// Use wisely!
+ pub fn new_safe_to_embed(app: T) -> Self {
+ let info = app.info();
+ PageEndpoint {
+ app: Arc::new(app),
+ prefix: None,
+ safe_to_embed: true,
+ info: EndpointInfo::from(info),
+ }
+ }
+}
+
+impl Endpoint for PageEndpoint {
+
+ fn info(&self) -> Option<&EndpointInfo> {
+ Some(&self.info)
+ }
+
+ fn to_handler(&self, path: EndpointPath) -> Box {
+ Box::new(handler::PageHandler {
+ app: BuiltinDapp::new(self.app.clone()),
+ prefix: self.prefix.clone(),
+ path: path,
+ file: None,
+ safe_to_embed: self.safe_to_embed,
+ })
+ }
+}
+
+impl From for EndpointInfo {
+ fn from(info: Info) -> Self {
+ EndpointInfo {
+ name: info.name.into(),
+ description: info.description.into(),
+ author: info.author.into(),
+ icon_url: info.icon_url.into(),
+ version: info.version.into(),
+ }
+ }
+}
+
+struct BuiltinDapp {
+ app: Arc,
+}
+
+impl BuiltinDapp {
+ fn new(app: Arc) -> Self {
+ BuiltinDapp {
+ app: app,
+ }
+ }
+}
+
+impl handler::Dapp for BuiltinDapp {
+ type DappFile = BuiltinDappFile;
+
+ fn file(&self, path: &str) -> Option {
+ self.app.file(path).map(|_| {
+ BuiltinDappFile {
+ app: self.app.clone(),
+ path: path.into(),
+ write_pos: 0,
+ }
+ })
+ }
+}
+
+struct BuiltinDappFile {
+ app: Arc,
+ path: String,
+ write_pos: usize,
+}
+
+impl BuiltinDappFile {
+ fn file(&self) -> &File {
+ self.app.file(&self.path).expect("Check is done when structure is created.")
+ }
+}
+
+impl handler::DappFile for BuiltinDappFile {
+ fn content_type(&self) -> &str {
+ self.file().content_type
+ }
+
+ fn is_drained(&self) -> bool {
+ self.write_pos == self.file().content.len()
+ }
+
+ fn next_chunk(&mut self) -> &[u8] {
+ &self.file().content[self.write_pos..]
+ }
+
+ fn bytes_written(&mut self, bytes: usize) {
+ self.write_pos += bytes;
+ }
+}
diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs
new file mode 100644
index 000000000..5167c85c8
--- /dev/null
+++ b/dapps/src/page/handler.rs
@@ -0,0 +1,207 @@
+// Copyright 2015, 2016 Ethcore (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 .
+
+use std::io::Write;
+use hyper::header;
+use hyper::server;
+use hyper::uri::RequestUri;
+use hyper::net::HttpStream;
+use hyper::status::StatusCode;
+use hyper::{Decoder, Encoder, Next};
+use endpoint::EndpointPath;
+
+/// Represents a file that can be sent to client.
+/// Implementation should keep track of bytes already sent internally.
+pub trait DappFile: Send {
+ /// Returns a content-type of this file.
+ fn content_type(&self) -> &str;
+
+ /// Checks if all bytes from that file were written.
+ fn is_drained(&self) -> bool;
+
+ /// Fetch next chunk to write to the client.
+ fn next_chunk(&mut self) -> &[u8];
+
+ /// How many files have been written to the client.
+ fn bytes_written(&mut self, bytes: usize);
+}
+
+/// Dapp as a (dynamic) set of files.
+pub trait Dapp: Send + 'static {
+ /// File type
+ type DappFile: DappFile;
+
+ /// Returns file under given path.
+ fn file(&self, path: &str) -> Option;
+}
+
+/// A handler for a single webapp.
+/// Resolves correct paths and serves as a plumbing code between
+/// hyper server and dapp.
+pub struct PageHandler {
+ /// A Dapp.
+ pub app: T,
+ /// File currently being served (or `None` if file does not exist).
+ pub file: Option,
+ /// Optional prefix to strip from path.
+ pub prefix: Option,
+ /// Requested path.
+ pub path: EndpointPath,
+ /// Flag indicating if the file can be safely embeded (put in iframe).
+ pub safe_to_embed: bool,
+}
+
+impl PageHandler {
+ fn extract_path(&self, path: &str) -> String {
+ let app_id = &self.path.app_id;
+ let prefix = "/".to_owned() + self.prefix.as_ref().unwrap_or(app_id);
+ let prefix_with_slash = prefix.clone() + "/";
+ let query_pos = path.find('?').unwrap_or_else(|| path.len());
+
+ // Index file support
+ match path == "/" || path == &prefix || path == &prefix_with_slash {
+ true => "index.html".to_owned(),
+ false => if path.starts_with(&prefix_with_slash) {
+ path[prefix_with_slash.len()..query_pos].to_owned()
+ } else if path.starts_with("/") {
+ path[1..query_pos].to_owned()
+ } else {
+ path[0..query_pos].to_owned()
+ }
+ }
+ }
+}
+
+impl server::Handler for PageHandler {
+ fn on_request(&mut self, req: server::Request) -> Next {
+ self.file = match *req.uri() {
+ RequestUri::AbsolutePath(ref path) => {
+ self.app.file(&self.extract_path(path))
+ },
+ RequestUri::AbsoluteUri(ref url) => {
+ self.app.file(&self.extract_path(url.path()))
+ },
+ _ => None,
+ };
+ Next::write()
+ }
+
+ fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next {
+ Next::write()
+ }
+
+ fn on_response(&mut self, res: &mut server::Response) -> Next {
+ if let Some(ref f) = self.file {
+ res.set_status(StatusCode::Ok);
+ res.headers_mut().set(header::ContentType(f.content_type().parse().unwrap()));
+ if !self.safe_to_embed {
+ res.headers_mut().set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
+ }
+ Next::write()
+ } else {
+ res.set_status(StatusCode::NotFound);
+ Next::write()
+ }
+ }
+
+ fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next {
+ match self.file {
+ None => Next::end(),
+ Some(ref f) if f.is_drained() => Next::end(),
+ Some(ref mut f) => match encoder.write(f.next_chunk()) {
+ Ok(bytes) => {
+ f.bytes_written(bytes);
+ Next::write()
+ },
+ Err(e) => match e.kind() {
+ ::std::io::ErrorKind::WouldBlock => Next::write(),
+ _ => Next::end(),
+ },
+ }
+ }
+ }
+}
+
+
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ pub struct TestWebAppFile;
+
+ impl DappFile for TestWebAppFile {
+ fn content_type(&self) -> &str {
+ unimplemented!()
+ }
+
+ fn is_drained(&self) -> bool {
+ unimplemented!()
+ }
+
+ fn next_chunk(&mut self) -> &[u8] {
+ unimplemented!()
+ }
+
+ fn bytes_written(&mut self, _bytes: usize) {
+ unimplemented!()
+ }
+ }
+
+ #[derive(Default)]
+ pub struct TestWebapp;
+
+ impl Dapp for TestWebapp {
+ type DappFile = TestWebAppFile;
+
+ fn file(&self, _path: &str) -> Option {
+ None
+ }
+ }
+}
+
+#[test]
+fn should_extract_path_with_appid() {
+
+ // given
+ let path1 = "/";
+ let path2= "/test.css";
+ let path3 = "/app/myfile.txt";
+ let path4 = "/app/myfile.txt?query=123";
+ let page_handler = PageHandler {
+ app: test::TestWebapp,
+ prefix: None,
+ path: EndpointPath {
+ app_id: "app".to_owned(),
+ host: "".to_owned(),
+ port: 8080
+ },
+ file: None,
+ safe_to_embed: true,
+ };
+
+ // when
+ let res1 = page_handler.extract_path(path1);
+ let res2 = page_handler.extract_path(path2);
+ let res3 = page_handler.extract_path(path3);
+ let res4 = page_handler.extract_path(path4);
+
+ // then
+ assert_eq!(&res1, "index.html");
+ assert_eq!(&res2, "test.css");
+ assert_eq!(&res3, "myfile.txt");
+ assert_eq!(&res4, "myfile.txt");
+}
diff --git a/dapps/src/page/local.rs b/dapps/src/page/local.rs
new file mode 100644
index 000000000..52e32bf5e
--- /dev/null
+++ b/dapps/src/page/local.rs
@@ -0,0 +1,118 @@
+// Copyright 2015, 2016 Ethcore (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 .
+
+use mime_guess;
+use std::io::{Seek, Read, SeekFrom};
+use std::fs;
+use std::path::PathBuf;
+use page::handler;
+use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
+
+pub struct LocalPageEndpoint {
+ path: PathBuf,
+ info: EndpointInfo,
+}
+
+impl LocalPageEndpoint {
+ pub fn new(path: PathBuf, info: EndpointInfo) -> Self {
+ LocalPageEndpoint {
+ path: path,
+ info: info,
+ }
+ }
+}
+
+impl Endpoint for LocalPageEndpoint {
+ fn info(&self) -> Option<&EndpointInfo> {
+ Some(&self.info)
+ }
+
+ fn to_handler(&self, path: EndpointPath) -> Box {
+ Box::new(handler::PageHandler {
+ app: LocalDapp::new(self.path.clone()),
+ prefix: None,
+ path: path,
+ file: None,
+ safe_to_embed: false,
+ })
+ }
+}
+
+struct LocalDapp {
+ path: PathBuf,
+}
+
+impl LocalDapp {
+ fn new(path: PathBuf) -> Self {
+ LocalDapp {
+ path: path
+ }
+ }
+}
+
+impl handler::Dapp for LocalDapp {
+ type DappFile = LocalFile;
+
+ fn file(&self, file_path: &str) -> Option {
+ let mut path = self.path.clone();
+ for part in file_path.split('/') {
+ path.push(part);
+ }
+ // Check if file exists
+ fs::File::open(path.clone()).ok().map(|file| {
+ let content_type = mime_guess::guess_mime_type(path);
+ let len = file.metadata().ok().map_or(0, |meta| meta.len());
+ LocalFile {
+ content_type: content_type.to_string(),
+ buffer: [0; 4096],
+ file: file,
+ pos: 0,
+ len: len,
+ }
+ })
+ }
+}
+
+struct LocalFile {
+ content_type: String,
+ buffer: [u8; 4096],
+ file: fs::File,
+ len: u64,
+ pos: u64,
+}
+
+impl handler::DappFile for LocalFile {
+ fn content_type(&self) -> &str {
+ &self.content_type
+ }
+
+ fn is_drained(&self) -> bool {
+ self.pos == self.len
+ }
+
+ fn next_chunk(&mut self) -> &[u8] {
+ let _ = self.file.seek(SeekFrom::Start(self.pos));
+ if let Ok(n) = self.file.read(&mut self.buffer) {
+ &self.buffer[0..n]
+ } else {
+ &self.buffer[0..0]
+ }
+ }
+
+ fn bytes_written(&mut self, bytes: usize) {
+ self.pos += bytes as u64;
+ }
+}
diff --git a/dapps/src/page/mod.rs b/dapps/src/page/mod.rs
index 819988310..349c979c7 100644
--- a/dapps/src/page/mod.rs
+++ b/dapps/src/page/mod.rs
@@ -14,216 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-use std::sync::Arc;
-use std::io::Write;
-use hyper::uri::RequestUri;
-use hyper::server;
-use hyper::header;
-use hyper::status::StatusCode;
-use hyper::net::HttpStream;
-use hyper::{Decoder, Encoder, Next};
-use endpoint::{Endpoint, EndpointInfo, EndpointPath};
-use parity_dapps::{WebApp, Info};
-pub struct PageEndpoint {
- /// Content of the files
- pub app: Arc,
- /// Prefix to strip from the path (when `None` deducted from `app_id`)
- pub prefix: Option,
- /// Safe to be loaded in frame by other origin. (use wisely!)
- safe_to_embed: bool,
-}
+mod builtin;
+mod local;
+mod handler;
-impl PageEndpoint {
- pub fn new(app: T) -> Self {
- PageEndpoint {
- app: Arc::new(app),
- prefix: None,
- safe_to_embed: false,
- }
- }
+pub use self::local::LocalPageEndpoint;
+pub use self::builtin::PageEndpoint;
- pub fn with_prefix(app: T, prefix: String) -> Self {
- PageEndpoint {
- app: Arc::new(app),
- prefix: Some(prefix),
- safe_to_embed: false,
- }
- }
-
- /// Creates new `PageEndpoint` which can be safely used in iframe
- /// even from different origin. It might be dangerous (clickjacking).
- /// Use wisely!
- pub fn new_safe_to_embed(app: T) -> Self {
- PageEndpoint {
- app: Arc::new(app),
- prefix: None,
- safe_to_embed: true,
- }
- }
-}
-
-impl Endpoint for PageEndpoint {
-
- fn info(&self) -> Option {
- Some(EndpointInfo::from(self.app.info()))
- }
-
- fn to_handler(&self, path: EndpointPath) -> Box> {
- Box::new(PageHandler {
- app: self.app.clone(),
- prefix: self.prefix.clone(),
- path: path,
- file: None,
- write_pos: 0,
- safe_to_embed: self.safe_to_embed,
- })
- }
-}
-
-impl From for EndpointInfo {
- fn from(info: Info) -> Self {
- EndpointInfo {
- name: info.name,
- description: info.description,
- author: info.author,
- icon_url: info.icon_url,
- version: info.version,
- }
- }
-}
-
-struct PageHandler {
- app: Arc,
- prefix: Option,
- path: EndpointPath,
- file: Option,
- write_pos: usize,
- safe_to_embed: bool,
-}
-
-impl PageHandler {
- fn extract_path(&self, path: &str) -> String {
- let app_id = &self.path.app_id;
- let prefix = "/".to_owned() + self.prefix.as_ref().unwrap_or(app_id);
- let prefix_with_slash = prefix.clone() + "/";
- let query_pos = path.find('?').unwrap_or_else(|| path.len());
-
- // Index file support
- match path == "/" || path == &prefix || path == &prefix_with_slash {
- true => "index.html".to_owned(),
- false => if path.starts_with(&prefix_with_slash) {
- path[prefix_with_slash.len()..query_pos].to_owned()
- } else if path.starts_with("/") {
- path[1..query_pos].to_owned()
- } else {
- path[0..query_pos].to_owned()
- }
- }
- }
-}
-
-impl server::Handler for PageHandler {
- fn on_request(&mut self, req: server::Request) -> Next {
- self.file = match *req.uri() {
- RequestUri::AbsolutePath(ref path) => {
- Some(self.extract_path(path))
- },
- RequestUri::AbsoluteUri(ref url) => {
- Some(self.extract_path(url.path()))
- },
- _ => None,
- };
- Next::write()
- }
-
- fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next {
- Next::write()
- }
-
- fn on_response(&mut self, res: &mut server::Response) -> Next {
- if let Some(f) = self.file.as_ref().and_then(|f| self.app.file(f)) {
- res.set_status(StatusCode::Ok);
- res.headers_mut().set(header::ContentType(f.content_type.parse().unwrap()));
- if !self.safe_to_embed {
- res.headers_mut().set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
- }
- Next::write()
- } else {
- res.set_status(StatusCode::NotFound);
- Next::write()
- }
- }
-
- fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next {
- let (wrote, res) = {
- let file = self.file.as_ref().and_then(|f| self.app.file(f));
- match file {
- None => (None, Next::end()),
- Some(f) if self.write_pos == f.content.len() => (None, Next::end()),
- Some(f) => match encoder.write(&f.content[self.write_pos..]) {
- Ok(bytes) => (Some(bytes), Next::write()),
- Err(e) => match e.kind() {
- ::std::io::ErrorKind::WouldBlock => (None, Next::write()),
- _ => (None, Next::end())
- },
- }
- }
- };
- if let Some(bytes) = wrote {
- self.write_pos += bytes;
- }
- res
- }
-}
-
-
-#[cfg(test)]
-use parity_dapps::File;
-
-#[cfg(test)]
-#[derive(Default)]
-struct TestWebapp;
-
-#[cfg(test)]
-impl WebApp for TestWebapp {
- fn file(&self, _path: &str) -> Option<&File> {
- None
- }
- fn info(&self) -> Info {
- unimplemented!()
- }
-}
-
-#[test]
-fn should_extract_path_with_appid() {
- // given
- let path1 = "/";
- let path2= "/test.css";
- let path3 = "/app/myfile.txt";
- let path4 = "/app/myfile.txt?query=123";
- let page_handler = PageHandler {
- app: Arc::new(TestWebapp),
- prefix: None,
- path: EndpointPath {
- app_id: "app".to_owned(),
- host: "".to_owned(),
- port: 8080
- },
- file: None,
- write_pos: 0,
- safe_to_embed: true,
- };
-
- // when
- let res1 = page_handler.extract_path(path1);
- let res2 = page_handler.extract_path(path2);
- let res3 = page_handler.extract_path(path3);
- let res4 = page_handler.extract_path(path4);
-
- // then
- assert_eq!(&res1, "index.html");
- assert_eq!(&res2, "test.css");
- assert_eq!(&res3, "myfile.txt");
- assert_eq!(&res4, "myfile.txt");
-}
diff --git a/devtools/src/lib.rs b/devtools/src/lib.rs
index 3ab585d93..e37d5c528 100644
--- a/devtools/src/lib.rs
+++ b/devtools/src/lib.rs
@@ -21,6 +21,8 @@ extern crate rand;
pub mod random_path;
pub mod test_socket;
+pub mod stop_guard;
pub use random_path::*;
pub use test_socket::*;
+pub use stop_guard::*;
diff --git a/devtools/src/random_path.rs b/devtools/src/random_path.rs
index 990d375e3..09bee7e45 100644
--- a/devtools/src/random_path.rs
+++ b/devtools/src/random_path.rs
@@ -26,7 +26,11 @@ pub struct RandomTempPath {
}
pub fn random_filename() -> String {
- (0..8).map(|_| ((random::() * 26.0) as u8 + 97) as char).collect()
+ random_str(8)
+}
+
+pub fn random_str(len: usize) -> String {
+ (0..len).map(|_| ((random::() * 26.0) as u8 + 97) as char).collect()
}
impl RandomTempPath {
@@ -54,6 +58,12 @@ impl RandomTempPath {
pub fn as_str(&self) -> &str {
self.path.to_str().unwrap()
}
+
+ pub fn new_in(&self, name: &str) -> String {
+ let mut path = self.path.clone();
+ path.push(name);
+ path.to_str().unwrap().to_owned()
+ }
}
impl Drop for RandomTempPath {
diff --git a/miner/build.rs b/devtools/src/stop_guard.rs
similarity index 58%
rename from miner/build.rs
rename to devtools/src/stop_guard.rs
index 41b9a1b3e..f1db59725 100644
--- a/miner/build.rs
+++ b/devtools/src/stop_guard.rs
@@ -14,12 +14,32 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-extern crate rustc_version;
+//! Stop guard mod
-use rustc_version::{version_meta, Channel};
+use std::sync::Arc;
+use std::sync::atomic::*;
-fn main() {
- if let Channel::Nightly = version_meta().channel {
- println!("cargo:rustc-cfg=nightly");
+/// Stop guard that will set a stop flag on drop
+pub struct StopGuard {
+ flag: Arc,
+}
+
+impl StopGuard {
+ /// Create a stop guard
+ pub fn new() -> StopGuard {
+ StopGuard {
+ flag: Arc::new(AtomicBool::new(false))
+ }
+ }
+
+ /// Share stop guard between the threads
+ pub fn share(&self) -> Arc {
+ self.flag.clone()
+ }
+}
+
+impl Drop for StopGuard {
+ fn drop(&mut self) {
+ self.flag.store(true, Ordering::Relaxed)
}
}
diff --git a/doc.sh b/doc.sh
index 0b75f6c38..fb39ef272 100755
--- a/doc.sh
+++ b/doc.sh
@@ -10,4 +10,3 @@ cargo doc --no-deps --verbose \
-p ethcore-signer \
-p ethcore-dapps \
-p parity \
- -p ethminer
diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml
index 34a010794..1de24ee32 100644
--- a/ethcore/Cargo.toml
+++ b/ethcore/Cargo.toml
@@ -29,6 +29,7 @@ ethcore-devtools = { path = "../devtools" }
ethjson = { path = "../json" }
bloomchain = "0.1"
"ethcore-ipc" = { path = "../ipc/rpc" }
+rayon = "0.3.1"
[features]
jit = ["evmjit"]
diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json
index 1e385558d..0cc88ac64 100644
--- a/ethcore/res/ethereum/morden.json
+++ b/ethcore/res/ethereum/morden.json
@@ -34,17 +34,8 @@
"gasLimit": "0x2fefd8"
},
"nodes": [
- "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303",
- "enode://7ee7195bfac561ec938a72cd84cd1a5d2b334415263feddc325b20b5010446fc6c361297d13decab4039028fa659c1e27cca1396574b87cc7b29eea2985e97fe@108.61.197.28:30303",
- "enode://933c5d5470b77537e7d9c1ee686132b5032dd3e2a096d2f64d2004df4ce9fca4ad6da5e358edcc8f81e65f047e40045600181f5fb35066e771025f6cca8e7952@46.101.114.191:30303",
- "enode://ad4028ba28783d5bf58f512cb4e24a8ce980d768177c4974e1140b16b925132c947349db9ca3646752891b382dafc839a0c0716c3764c1ed9d424f09d13d01cf@148.251.220.116:30303",
- "enode://c54ddaacddc7029683c80edae91015520eb2712176fbe6fdb7a5a074659270638f1266cba1731681c7cb785bceb02ca8d8b23024e3ec736fc5579f2042be97ae@54.175.255.230:30303",
- "enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303",
"enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303",
- "enode://e941c58fed2709d792f552f408d2162c3d0a5597d22d1da617a9c9e6181f3251056a96adb45ae22eba70119355227298dc7e6dff805b092bae7da2f8564de422@85.25.217.23:30303",
- "enode://f4b73c9d11a780293ff0ca7afa12c67797afdc33a4797a7c2ecc5b87e455b32a8b9e9804f2004072bac38350bf82d52521d1a09590d2079705fc8357aef2bf9c@71.202.223.50:56603",
- "enode://1173eea53e0cb2b8da92423e44cf4cbafbc8ea16c1558cf06e18dfc5a2fc9b140cc802a4362b4c773fb1442541e6f2a225b200bb4c1f6b347e7510a50fa4873f@104.41.138.167:30300",
- "enode://1aad341327808738ad34655611f1b13293c4155dde36c8e3788128829f15cc6db2da9435f29520553d4efc134aadc50115690194ac3af519aac7a388b524811e@109.188.125.2:30303"
+ "enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303"
],
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs
index 7ba213393..5e0dc335a 100644
--- a/ethcore/src/account.rs
+++ b/ethcore/src/account.rs
@@ -51,8 +51,6 @@ impl Account {
}
}
- #[cfg(test)]
- #[cfg(feature = "json-tests")]
/// General constructor.
pub fn from_pod(pod: PodAccount) -> Account {
Account {
diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs
index 8fb0de9eb..97f8dd5a4 100644
--- a/ethcore/src/client/client.rs
+++ b/ethcore/src/client/client.rs
@@ -37,7 +37,7 @@ use filter::Filter;
use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
-use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, TraceFilter};
+use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics};
use client::Error as ClientError;
use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address};
@@ -48,6 +48,7 @@ use trace;
pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus;
use evm::Factory as EvmFactory;
+use miner::{Miner, MinerService, TransactionImportResult, AccountDetails};
impl fmt::Display for BlockChainInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -90,6 +91,7 @@ pub struct Client where V: Verifier {
panic_handler: Arc,
verifier: PhantomData,
vm_factory: Arc,
+ miner: Arc,
}
const HISTORY: u64 = 1200;
@@ -102,8 +104,8 @@ const CLIENT_DB_VER_STR: &'static str = "5.3";
impl Client {
/// Create a new client with given spec and DB path.
- pub fn new(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel ) -> Result, ClientError> {
- Client::::new_with_verifier(config, spec, path, message_channel)
+ pub fn new(config: ClientConfig, spec: Spec, path: &Path, miner: Arc, message_channel: IoChannel ) -> Result, ClientError> {
+ Client::::new_with_verifier(config, spec, path, miner, message_channel)
}
}
@@ -126,7 +128,14 @@ pub fn append_path(path: &Path, item: &str) -> String {
impl Client where V: Verifier {
/// Create a new client with given spec and DB path and custom verifier.
- pub fn new_with_verifier(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel ) -> Result>, ClientError> {
+ pub fn new_with_verifier(
+ config: ClientConfig,
+ spec: Spec,
+ path: &Path,
+ miner: Arc,
+ message_channel: IoChannel)
+ -> Result>, ClientError>
+ {
let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
let gb = spec.genesis_block();
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
@@ -155,6 +164,7 @@ impl Client where V: Verifier {
panic_handler: panic_handler,
verifier: PhantomData,
vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
+ miner: miner,
};
Ok(Arc::new(client))
@@ -328,6 +338,11 @@ impl Client where V: Verifier {
{
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
let (enacted, retracted) = self.calculate_enacted_retracted(import_results);
+
+ if self.queue_info().is_empty() {
+ self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
+ }
+
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
imported: imported_blocks,
invalid: invalid_blocks,
@@ -339,7 +354,7 @@ impl Client where V: Verifier {
{
if self.chain_info().best_block_hash != original_best {
- io.send(NetworkIoMessage::User(SyncMessage::NewChainHead)).unwrap();
+ self.miner.update_sealing(self);
}
}
@@ -414,14 +429,14 @@ impl Client where V: Verifier {
TransactionID::Hash(ref hash) => self.chain.transaction_address(hash),
TransactionID::Location(id, index) => Self::block_hash(&self.chain, id).map(|hash| TransactionAddress {
block_hash: hash,
- index: index
+ index: index,
})
}
}
}
impl BlockChainClient for Client where V: Verifier {
- fn call(&self, t: &SignedTransaction) -> Result {
+ fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result {
let header = self.block_header(BlockID::Latest).unwrap();
let view = HeaderView::new(&header);
let last_hashes = self.build_last_hashes(view.hash());
@@ -441,88 +456,27 @@ impl BlockChainClient for Client where V: Verifier {
ExecutionError::TransactionMalformed(message)
}));
let balance = state.balance(&sender);
- // give the sender max balance
- state.sub_balance(&sender, &balance);
- state.add_balance(&sender, &U256::max_value());
- let options = TransactOptions { tracing: false, check_nonce: false };
- Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options)
- }
-
- // TODO [todr] Should be moved to miner crate eventually.
- fn try_seal(&self, block: LockedBlock, seal: Vec) -> Result {
- block.try_seal(self.engine.deref().deref(), seal)
+ let needed_balance = t.value + t.gas * t.gas_price;
+ if balance < needed_balance {
+ // give the sender a sufficient balance
+ state.add_balance(&sender, &(needed_balance - balance));
+ }
+ let options = TransactOptions { tracing: false, vm_tracing: analytics.vm_tracing, check_nonce: false };
+ let mut ret = Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options);
+
+ // TODO gav move this into Executive.
+ if analytics.state_diffing {
+ if let Ok(ref mut x) = ret {
+ x.state_diff = Some(state.diff_from(self.state()));
+ }
+ }
+ ret
}
fn vm_factory(&self) -> &EvmFactory {
&self.vm_factory
}
- // TODO [todr] Should be moved to miner crate eventually.
- fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec)
- -> (Option, HashSet) {
- let engine = self.engine.deref().deref();
- let h = self.chain.best_block_hash();
- let mut invalid_transactions = HashSet::new();
-
- let mut b = OpenBlock::new(
- engine,
- &self.vm_factory,
- false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
- self.state_db.lock().unwrap().boxed_clone(),
- match self.chain.block_header(&h) { Some(ref x) => x, None => { return (None, invalid_transactions) } },
- self.build_last_hashes(h.clone()),
- author,
- gas_floor_target,
- extra_data,
- );
-
- // Add uncles
- self.chain
- .find_uncle_headers(&h, engine.maximum_uncle_age())
- .unwrap()
- .into_iter()
- .take(engine.maximum_uncle_count())
- .foreach(|h| {
- b.push_uncle(h).unwrap();
- });
-
- // Add transactions
- let block_number = b.block().header().number();
- let min_tx_gas = U256::from(self.engine.schedule(&b.env_info()).tx_gas);
-
- for tx in transactions {
- // Push transaction to block
- let hash = tx.hash();
- let import = b.push_transaction(tx, None);
-
- match import {
- Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => {
- trace!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash);
- // Exit early if gas left is smaller then min_tx_gas
- if gas_limit - gas_used < min_tx_gas {
- break;
- }
- },
- Err(e) => {
- invalid_transactions.insert(hash);
- trace!(target: "miner",
- "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}",
- block_number, hash, e);
- },
- _ => {}
- }
- }
-
- // And close
- let b = b.close();
- trace!(target: "miner", "Sealing: number={}, hash={}, diff={}",
- b.block().header().number(),
- b.hash(),
- b.block().header().difficulty()
- );
- (Some(b), invalid_transactions)
- }
-
fn block_header(&self, id: BlockID) -> Option {
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
}
@@ -561,7 +515,6 @@ impl BlockChainClient for Client where V: Verifier {
self.state_at(id).map(|s| s.nonce(address))
}
-
fn block_hash(&self, id: BlockID) -> Option {
Self::block_hash(&self.chain, id)
}
@@ -774,6 +727,89 @@ impl BlockChainClient for Client where V: Verifier {
fn last_hashes(&self) -> LastHashes {
self.build_last_hashes(self.chain.best_block_hash())
}
+
+ fn import_transactions(&self, transactions: Vec) -> Vec> {
+ let fetch_account = |a: &Address| AccountDetails {
+ nonce: self.latest_nonce(a),
+ balance: self.latest_balance(a),
+ };
+ self.miner.import_transactions(transactions, fetch_account)
+ }
+
+ fn all_transactions(&self) -> Vec {
+ self.miner.all_transactions()
+ }
+}
+
+impl MiningBlockChainClient for Client where V: Verifier {
+ fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec)
+ -> (Option, HashSet) {
+ let engine = self.engine.deref().deref();
+ let h = self.chain.best_block_hash();
+ let mut invalid_transactions = HashSet::new();
+
+ let mut b = OpenBlock::new(
+ engine,
+ &self.vm_factory,
+ false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
+ self.state_db.lock().unwrap().boxed_clone(),
+ match self.chain.block_header(&h) { Some(ref x) => x, None => { return (None, invalid_transactions) } },
+ self.build_last_hashes(h.clone()),
+ author,
+ gas_floor_target,
+ extra_data,
+ );
+
+ // Add uncles
+ self.chain
+ .find_uncle_headers(&h, engine.maximum_uncle_age())
+ .unwrap()
+ .into_iter()
+ .take(engine.maximum_uncle_count())
+ .foreach(|h| {
+ b.push_uncle(h).unwrap();
+ });
+
+ // Add transactions
+ let block_number = b.block().header().number();
+ let min_tx_gas = U256::from(self.engine.schedule(&b.env_info()).tx_gas);
+
+ for tx in transactions {
+ // Push transaction to block
+ let hash = tx.hash();
+ let import = b.push_transaction(tx, None);
+
+ match import {
+ Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => {
+ trace!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash);
+ // Exit early if gas left is smaller then min_tx_gas
+ if gas_limit - gas_used < min_tx_gas {
+ break;
+ }
+ },
+ Err(e) => {
+ invalid_transactions.insert(hash);
+ trace!(target: "miner",
+ "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}",
+ block_number, hash, e);
+ },
+ _ => {}
+ }
+ }
+
+ // And close
+ let b = b.close();
+ trace!(target: "miner", "Sealing: number={}, hash={}, diff={}",
+ b.block().header().number(),
+ b.hash(),
+ b.block().header().difficulty()
+ );
+ (Some(b), invalid_transactions)
+ }
+
+ fn try_seal(&self, block: LockedBlock, seal: Vec) -> Result {
+ block.try_seal(self.engine.deref().deref(), seal)
+ }
}
impl MayPanic for Client {
diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs
index 8e0a7b2dd..0dffb1a1c 100644
--- a/ethcore/src/client/mod.rs
+++ b/ethcore/src/client/mod.rs
@@ -46,6 +46,17 @@ use error::{ImportResult, ExecutionError};
use receipt::LocalizedReceipt;
use trace::LocalizedTrace;
use evm::Factory as EvmFactory;
+use miner::{TransactionImportResult};
+use error::Error as EthError;
+
+/// Options concerning what analytics we run on the call.
+#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)]
+pub struct CallAnalytics {
+ /// Make a VM trace.
+ pub vm_tracing: bool,
+ /// Make a diff.
+ pub state_diffing: bool,
+}
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
@@ -154,17 +165,9 @@ pub trait BlockChainClient : Sync + Send {
/// Returns logs matching given filter.
fn logs(&self, filter: Filter) -> Vec;
- // TODO [todr] Should be moved to miner crate eventually.
- /// Returns ClosedBlock prepared for sealing.
- fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec)
- -> (Option, HashSet);
-
- // TODO [todr] Should be moved to miner crate eventually.
- /// Attempts to seal given block. Returns `SealedBlock` on success and the same block in case of error.
- fn try_seal(&self, block: LockedBlock, seal: Vec) -> Result;
-
/// Makes a non-persistent transaction call.
- fn call(&self, t: &SignedTransaction) -> Result;
+ // TODO: should be able to accept blockchain location for call.
+ fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result;
/// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory;
@@ -183,5 +186,20 @@ pub trait BlockChainClient : Sync + Send {
/// Get last hashes starting from best block.
fn last_hashes(&self) -> LastHashes;
+
+ /// import transactions from network/other 3rd party
+ fn import_transactions(&self, transactions: Vec) -> Vec>;
+
+ /// list all transactions
+ fn all_transactions(&self) -> Vec;
}
+/// Extended client interface used for mining
+pub trait MiningBlockChainClient : BlockChainClient {
+ /// Attempts to seal given block. Returns `SealedBlock` on success and the same block in case of error.
+ fn try_seal(&self, block: LockedBlock, seal: Vec) -> Result;
+
+ /// Returns ClosedBlock prepared for sealing.
+ fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec)
+ -> (Option, HashSet);
+}
diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs
index de2973029..17905a905 100644
--- a/ethcore/src/client/test_client.rs
+++ b/ethcore/src/client/test_client.rs
@@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
use util::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute;
-use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes};
+use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics};
use header::{Header as BlockHeader, BlockNumber};
use filter::Filter;
use log_entry::LocalizedLogEntry;
@@ -28,6 +28,7 @@ use receipt::{Receipt, LocalizedReceipt};
use blockchain::extras::BlockReceipts;
use error::{ImportResult};
use evm::Factory as EvmFactory;
+use miner::{Miner, MinerService};
use block_queue::BlockQueueInfo;
use block::{SealedBlock, ClosedBlock, LockedBlock};
@@ -35,6 +36,9 @@ use executive::Executed;
use error::{ExecutionError};
use trace::LocalizedTrace;
+use miner::{TransactionImportResult, AccountDetails};
+use error::Error as EthError;
+
/// Test client.
pub struct TestBlockChainClient {
/// Blocks.
@@ -61,6 +65,8 @@ pub struct TestBlockChainClient {
pub receipts: RwLock>,
/// Block queue size.
pub queue_size: AtomicUsize,
+ /// Miner
+ pub miner: Arc,
}
#[derive(Clone)]
@@ -99,6 +105,7 @@ impl TestBlockChainClient {
execution_result: RwLock::new(None),
receipts: RwLock::new(HashMap::new()),
queue_size: AtomicUsize::new(0),
+ miner: Arc::new(Miner::default()),
};
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
client.genesis_hash = client.last_hash.read().unwrap().clone();
@@ -232,8 +239,19 @@ impl TestBlockChainClient {
}
}
+impl MiningBlockChainClient for TestBlockChainClient {
+ fn try_seal(&self, block: LockedBlock, _seal: Vec) -> Result {
+ Err(block)
+ }
+
+
+ fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec) -> (Option, HashSet) {
+ (None, HashSet::new())
+ }
+}
+
impl BlockChainClient for TestBlockChainClient {
- fn call(&self, _t: &SignedTransaction) -> Result {
+ fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result {
Ok(self.execution_result.read().unwrap().clone().unwrap())
}
@@ -296,14 +314,6 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!();
}
- fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec) -> (Option, HashSet) {
- (None, HashSet::new())
- }
-
- fn try_seal(&self, block: LockedBlock, _seal: Vec) -> Result {
- Err(block)
- }
-
fn block_header(&self, id: BlockID) -> Option {
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
}
@@ -476,4 +486,19 @@ impl BlockChainClient for TestBlockChainClient {
fn block_traces(&self, _trace: BlockID) -> Option> {
unimplemented!();
}
+
+ fn import_transactions(&self, transactions: Vec) -> Vec> {
+ let nonces = self.nonces.read().unwrap();
+ let balances = self.balances.read().unwrap();
+ let fetch_account = |a: &Address| AccountDetails {
+ nonce: nonces[a],
+ balance: balances[a],
+ };
+
+ self.miner.import_transactions(transactions, &fetch_account)
+ }
+
+ fn all_transactions(&self) -> Vec {
+ self.miner.all_transactions()
+ }
}
diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs
index b6c2debc5..740774f38 100644
--- a/ethcore/src/evm/evm.rs
+++ b/ethcore/src/evm/evm.rs
@@ -63,13 +63,43 @@ pub enum Error {
Internal,
}
-/// Evm result.
-///
-/// Returns `gas_left` if execution is successful, otherwise error.
-pub type Result = result::Result;
+/// A specialized version of Result over EVM errors.
+pub type Result = ::std::result::Result;
-/// Evm interface.
+/// Gas Left: either it is a known value, or it needs to be computed by processing
+/// a return instruction.
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum GasLeft<'a> {
+ /// Known gas left
+ Known(U256),
+ /// Return instruction must be processed.
+ NeedsReturn(U256, &'a [u8]),
+}
+
+/// Types that can be "finalized" using an EVM.
+///
+/// In practice, this is just used to define an inherent impl on
+/// `Reult>`.
+pub trait Finalize {
+ /// Consume the externalities, call return if necessary, and produce a final amount of gas left.
+ fn finalize(self, ext: E) -> Result;
+}
+
+impl<'a> Finalize for Result> {
+ fn finalize(self, ext: E) -> Result {
+ match self {
+ Ok(GasLeft::Known(gas)) => Ok(gas),
+ Ok(GasLeft::NeedsReturn(gas, ret_code)) => ext.ret(&gas, ret_code),
+ Err(err) => Err(err),
+ }
+ }
+}
+
+/// Evm interface
pub trait Evm {
/// This function should be used to execute transaction.
- fn exec(&self, params: ActionParams, ext: &mut Ext) -> Result;
+ ///
+ /// It returns either an error, a known amount of gas left, or parameters to be used
+ /// to compute the final gas left.
+ fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result;
}
diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs
index 4986b12c8..0aaa4dac6 100644
--- a/ethcore/src/evm/ext.rs
+++ b/ethcore/src/evm/ext.rs
@@ -17,7 +17,7 @@
//! Interface for Evm externalities.
use util::common::*;
-use evm::{Schedule, Error};
+use evm::{self, Schedule};
use env_info::*;
/// Result of externalities create function.
@@ -85,7 +85,7 @@ pub trait Ext {
/// Should be called when transaction calls `RETURN` opcode.
/// Returns gas_left if cost of returning the data is not too high.
- fn ret(&mut self, gas: &U256, data: &[u8]) -> Result;
+ fn ret(self, gas: &U256, data: &[u8]) -> evm::Result where Self: Sized;
/// Should be called when contract commits suicide.
/// Address to which funds should be refunded.
@@ -105,4 +105,10 @@ pub trait Ext {
/// Increments sstore refunds count by 1.
fn inc_sstore_clears(&mut self);
+
+ /// Prepare to trace an operation. Passthrough for the VM trace.
+ fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
+
+ /// Trace the finalised execution of a single instruction.
+ fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
}
diff --git a/ethcore/src/evm/factory.rs b/ethcore/src/evm/factory.rs
index 3e60e8808..f55e064c7 100644
--- a/ethcore/src/evm/factory.rs
+++ b/ethcore/src/evm/factory.rs
@@ -89,10 +89,10 @@ impl Factory {
pub fn create(&self) -> Box {
match self.evm {
VMType::Jit => {
- Box::new(super::jit::JitEvm)
+ Box::new(super::jit::JitEvm::default())
},
VMType::Interpreter => {
- Box::new(super::interpreter::Interpreter)
+ Box::new(super::interpreter::Interpreter::default())
}
}
}
@@ -102,7 +102,7 @@ impl Factory {
pub fn create(&self) -> Box {
match self.evm {
VMType::Interpreter => {
- Box::new(super::interpreter::Interpreter)
+ Box::new(super::interpreter::Interpreter::default())
}
}
}
diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs
index 6a1a06ba9..c313726f1 100644
--- a/ethcore/src/evm/instructions.rs
+++ b/ethcore/src/evm/instructions.rs
@@ -124,6 +124,7 @@ pub struct InstructionInfo {
pub side_effects: bool,
pub tier: GasPriceTier
}
+
impl InstructionInfo {
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo {
InstructionInfo {
@@ -139,7 +140,7 @@ impl InstructionInfo {
#[cfg_attr(rustfmt, rustfmt_skip)]
/// Return details about specific instruction
-pub fn get_info (instruction: Instruction) -> InstructionInfo {
+pub fn get_info(instruction: Instruction) -> InstructionInfo {
match instruction {
STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero),
ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow),
diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter.rs
index eb29ef257..1514b3e2e 100644
--- a/ethcore/src/evm/interpreter.rs
+++ b/ethcore/src/evm/interpreter.rs
@@ -17,10 +17,11 @@
///! Rust VM implementation
use common::*;
+use trace::VMTracer;
use super::instructions as instructions;
-use super::instructions::Instruction;
+use super::instructions::{Instruction, get_info};
use std::marker::Copy;
-use evm::{self, MessageCallResult, ContractCreateResult};
+use evm::{self, MessageCallResult, ContractCreateResult, GasLeft};
#[cfg(not(feature = "evm-debug"))]
macro_rules! evm_debug {
@@ -69,6 +70,8 @@ trait Stack {
fn push(&mut self, elem: T);
/// Get number of elements on Stack
fn size(&self) -> usize;
+ /// Returns all data on stack.
+ fn peek_top(&mut self, no_of_elems: usize) -> &[T];
}
struct VecStack {
@@ -131,6 +134,11 @@ impl Stack for VecStack {
fn size(&self) -> usize {
self.stack.len()
}
+
+ fn peek_top(&mut self, no_from_top: usize) -> &[S] {
+ assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
+ &self.stack[self.stack.len() - no_from_top .. self.stack.len()]
+ }
}
trait Memory {
@@ -271,21 +279,26 @@ enum InstructionResult {
GasLeft(U256),
UnusedGas(U256),
JumpToPosition(U256),
- StopExecutionWithGasLeft(U256),
- StopExecution
+ // gas left, init_orf, init_size
+ StopExecutionNeedsReturn(U256, U256, U256),
+ StopExecution,
}
/// Intepreter EVM implementation
-pub struct Interpreter;
+#[derive(Default)]
+pub struct Interpreter {
+ mem: Vec,
+}
impl evm::Evm for Interpreter {
- fn exec(&self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result {
+ fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result {
+ self.mem.clear();
+
let code = ¶ms.code.as_ref().unwrap();
let valid_jump_destinations = self.find_jump_destinations(&code);
let mut current_gas = params.gas;
let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero());
- let mut mem = vec![];
let mut reader = CodeReader {
position: 0,
code: &code
@@ -293,12 +306,17 @@ impl evm::Evm for Interpreter {
while reader.position < code.len() {
let instruction = code[reader.position];
- reader.position += 1;
// Calculate gas cost
- let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack));
+ let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &stack));
+
+ // TODO: make compile-time removable if too much of a performance hit.
+ let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost);
+
+ reader.position += 1;
+
try!(self.verify_gas(¤t_gas, &gas_cost));
- mem.expand(mem_size);
+ self.mem.expand(mem_size);
current_gas = current_gas - gas_cost; //TODO: use operator -=
evm_debug!({
@@ -311,10 +329,19 @@ impl evm::Evm for Interpreter {
);
});
+ let (mem_written, store_written) = match trace_executed {
+ true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)),
+ false => (None, None),
+ };
+
// Execute instruction
let result = try!(self.exec_instruction(
- current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack
- ));
+ current_gas, ¶ms, ext, instruction, &mut reader, &mut stack
+ ));
+
+ if trace_executed {
+ ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
+ }
// Advance
match result {
@@ -332,29 +359,25 @@ impl evm::Evm for Interpreter {
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
reader.position = pos;
},
- InstructionResult::StopExecutionWithGasLeft(gas_left) => {
- current_gas = gas_left;
- reader.position = code.len();
+ InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
+ return Ok(GasLeft::NeedsReturn(gas, self.mem.read_slice(off, size)));
},
- InstructionResult::StopExecution => {
- reader.position = code.len();
- }
+ InstructionResult::StopExecution => break,
}
}
- Ok(current_gas)
+ Ok(GasLeft::Known(current_gas))
}
}
impl Interpreter {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn get_gas_cost_mem(
- &self,
+ &mut self,
ext: &evm::Ext,
instruction: Instruction,
- mem: &mut Memory,
stack: &Stack
- ) -> Result<(U256, usize), evm::Error> {
+ ) -> evm::Result<(U256, usize)> {
let schedule = ext.schedule();
let info = instructions::get_info(instruction);
@@ -470,12 +493,12 @@ impl Interpreter {
Ok((gas, 0))
},
InstructionCost::GasMem(gas, mem_size) => {
- let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, mem.size(), &mem_size));
+ let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size));
let gas = overflowing!(gas.overflowing_add(mem_gas));
Ok((gas, new_mem_size))
},
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
- let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, mem.size(), &mem_size));
+ let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size));
let copy = overflowing!(add_u256_usize(©, 31));
let copy_gas = U256::from(schedule.copy_gas) * (copy / U256::from(32));
let gas = overflowing!(gas.overflowing_add(copy_gas));
@@ -485,7 +508,32 @@ impl Interpreter {
}
}
- fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> Result<(U256, usize), evm::Error> {
+ fn mem_written(
+ instruction: Instruction,
+ stack: &Stack
+ ) -> Option<(usize, usize)> {
+ match instruction {
+ instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)),
+ instructions::MSTORE8 => Some((stack.peek(0).low_u64() as usize, 1)),
+ instructions::CALLDATACOPY | instructions::CODECOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)),
+ instructions::EXTCODECOPY => Some((stack.peek(1).low_u64() as usize, stack.peek(3).low_u64() as usize)),
+ instructions::CALL | instructions::CALLCODE => Some((stack.peek(5).low_u64() as usize, stack.peek(6).low_u64() as usize)),
+ instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)),
+ _ => None,
+ }
+ }
+
+ fn store_written(
+ instruction: Instruction,
+ stack: &Stack
+ ) -> Option<(U256, U256)> {
+ match instruction {
+ instructions::SSTORE => Some((stack.peek(0).clone(), stack.peek(1).clone())),
+ _ => None,
+ }
+ }
+
+ fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> evm::Result<(U256, usize)> {
let gas_for_mem = |mem_size: U256| {
let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div
@@ -510,11 +558,11 @@ impl Interpreter {
}, req_mem_size_rounded.low_u64() as usize))
}
- fn mem_needed_const(&self, mem: &U256, add: usize) -> Result {
+ fn mem_needed_const(&self, mem: &U256, add: usize) -> evm::Result {
Ok(overflowing!(mem.overflowing_add(U256::from(add))))
}
- fn mem_needed(&self, offset: &U256, size: &U256) -> Result {
+ fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result {
if self.is_zero(size) {
return Ok(U256::zero());
}
@@ -524,15 +572,14 @@ impl Interpreter {
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn exec_instruction(
- &self,
+ &mut self,
gas: Gas,
params: &ActionParams,
ext: &mut evm::Ext,
instruction: Instruction,
code: &mut CodeReader,
- mem: &mut Memory,
stack: &mut Stack
- ) -> Result {
+ ) -> evm::Result {
match instruction {
instructions::JUMP => {
let jump = stack.pop_back();
@@ -557,7 +604,7 @@ impl Interpreter {
let init_off = stack.pop_back();
let init_size = stack.pop_back();
- let contract_code = mem.read_slice(init_off, init_size);
+ let contract_code = self.mem.read_slice(init_off, init_size);
let can_create = ext.balance(¶ms.address) >= endowment && ext.depth() < ext.schedule().max_depth;
if !can_create {
@@ -624,8 +671,8 @@ impl Interpreter {
let call_result = {
// we need to write and read from memory in the same time
// and we don't want to copy
- let input = unsafe { ::std::mem::transmute(mem.read_slice(in_off, in_size)) };
- let output = mem.writeable_slice(out_off, out_size);
+ let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
+ let output = self.mem.writeable_slice(out_off, out_size);
ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output)
};
@@ -643,11 +690,8 @@ impl Interpreter {
instructions::RETURN => {
let init_off = stack.pop_back();
let init_size = stack.pop_back();
- let return_code = mem.read_slice(init_off, init_size);
- let gas_left = try!(ext.ret(&gas, &return_code));
- return Ok(InstructionResult::StopExecutionWithGasLeft(
- gas_left
- ));
+
+ return Ok(InstructionResult::StopExecutionNeedsReturn(gas, init_off, init_size))
},
instructions::STOP => {
return Ok(InstructionResult::StopExecution);
@@ -666,7 +710,7 @@ impl Interpreter {
.iter()
.map(H256::from)
.collect();
- ext.log(topics, mem.read_slice(offset, size));
+ ext.log(topics, self.mem.read_slice(offset, size));
},
instructions::PUSH1...instructions::PUSH32 => {
let bytes = instructions::get_push_bytes(instruction);
@@ -674,26 +718,26 @@ impl Interpreter {
stack.push(val);
},
instructions::MLOAD => {
- let word = mem.read(stack.pop_back());
+ let word = self.mem.read(stack.pop_back());
stack.push(U256::from(word));
},
instructions::MSTORE => {
let offset = stack.pop_back();
let word = stack.pop_back();
- mem.write(offset, word);
+ Memory::write(&mut self.mem, offset, word);
},
instructions::MSTORE8 => {
let offset = stack.pop_back();
let byte = stack.pop_back();
- mem.write_byte(offset, byte);
+ self.mem.write_byte(offset, byte);
},
instructions::MSIZE => {
- stack.push(U256::from(mem.size()));
+ stack.push(U256::from(self.mem.size()));
},
instructions::SHA3 => {
let offset = stack.pop_back();
let size = stack.pop_back();
- let sha3 = mem.read_slice(offset, size).sha3();
+ let sha3 = self.mem.read_slice(offset, size).sha3();
stack.push(U256::from(sha3.as_slice()));
},
instructions::SLOAD => {
@@ -766,15 +810,15 @@ impl Interpreter {
stack.push(U256::from(len));
},
instructions::CALLDATACOPY => {
- self.copy_data_to_memory(mem, stack, ¶ms.data.clone().unwrap_or_else(|| vec![]));
+ self.copy_data_to_memory(stack, ¶ms.data.clone().unwrap_or_else(|| vec![]));
},
instructions::CODECOPY => {
- self.copy_data_to_memory(mem, stack, ¶ms.code.clone().unwrap_or_else(|| vec![]));
+ self.copy_data_to_memory(stack, ¶ms.code.clone().unwrap_or_else(|| vec![]));
},
instructions::EXTCODECOPY => {
let address = u256_to_address(&stack.pop_back());
let code = ext.extcode(&address);
- self.copy_data_to_memory(mem, stack, &code);
+ self.copy_data_to_memory(stack, &code);
},
instructions::GASPRICE => {
stack.push(params.gas_price.clone());
@@ -806,7 +850,7 @@ impl Interpreter {
Ok(InstructionResult::Ok)
}
- fn copy_data_to_memory(&self, mem: &mut Memory, stack: &mut Stack, data: &[u8]) {
+ fn copy_data_to_memory(&mut self, stack: &mut Stack, data: &[u8]) {
let dest_offset = stack.pop_back();
let source_offset = stack.pop_back();
let size = stack.pop_back();
@@ -815,9 +859,9 @@ impl Interpreter {
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
true => {
let zero_slice = if source_offset > source_size {
- mem.writeable_slice(dest_offset, size)
+ self.mem.writeable_slice(dest_offset, size)
} else {
- mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size)
+ self.mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size)
};
for i in zero_slice.iter_mut() {
*i = 0;
@@ -829,14 +873,16 @@ impl Interpreter {
if source_offset < source_size {
let output_begin = source_offset.low_u64() as usize;
- mem.write_slice(dest_offset, &data[output_begin..output_end]);
+ self.mem.write_slice(dest_offset, &data[output_begin..output_end]);
}
}
- fn verify_instructions_requirements(&self,
- info: &instructions::InstructionInfo,
- stack_limit: usize,
- stack: &Stack) -> Result<(), evm::Error> {
+ fn verify_instructions_requirements(
+ &self,
+ info: &instructions::InstructionInfo,
+ stack_limit: usize,
+ stack: &Stack
+ ) -> evm::Result<()> {
if !stack.has(info.args) {
Err(evm::Error::StackUnderflow {
instruction: info.name,
@@ -854,14 +900,14 @@ impl Interpreter {
}
}
- fn verify_gas(&self, current_gas: &U256, gas_cost: &U256) -> Result<(), evm::Error> {
+ fn verify_gas(&self, current_gas: &U256, gas_cost: &U256) -> evm::Result<()> {
match current_gas < gas_cost {
true => Err(evm::Error::OutOfGas),
false => Ok(())
}
}
- fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet) -> Result {
+ fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet) -> evm::Result {
let jump = jump_u.low_u64() as usize;
if valid_jump_destinations.contains(&jump) && jump_u < U256::from(!0 as usize) {
@@ -885,7 +931,7 @@ impl Interpreter {
}
}
- fn exec_stack_instruction(&self, instruction: Instruction, stack: &mut Stack) -> Result<(), evm::Error> {
+ fn exec_stack_instruction(&self, instruction: Instruction, stack: &mut Stack) -> evm::Result<()> {
match instruction {
instructions::DUP1...instructions::DUP16 => {
let position = instructions::get_dup_position(instruction);
@@ -1136,7 +1182,7 @@ fn address_to_u256(value: Address) -> U256 {
#[test]
fn test_mem_gas_cost() {
// given
- let interpreter = Interpreter;
+ let interpreter = Interpreter::default();
let schedule = evm::Schedule::default();
let current_mem_size = 5;
let mem_size = !U256::zero();
@@ -1159,7 +1205,7 @@ mod tests {
#[test]
fn test_find_jump_destinations() {
// given
- let interpreter = Interpreter;
+ let interpreter = Interpreter::default();
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
// when
@@ -1172,7 +1218,7 @@ mod tests {
#[test]
fn test_calculate_mem_cost() {
// given
- let interpreter = Interpreter;
+ let interpreter = Interpreter::default();
let schedule = evm::Schedule::default();
let current_mem_size = 0;
let mem_size = U256::from(5);
diff --git a/ethcore/src/evm/jit.rs b/ethcore/src/evm/jit.rs
index 6a22a1306..d46ad917f 100644
--- a/ethcore/src/evm/jit.rs
+++ b/ethcore/src/evm/jit.rs
@@ -16,8 +16,9 @@
//! Just in time compiler execution environment.
use common::*;
+use trace::VMTracer;
use evmjit;
-use evm;
+use evm::{self, Error, GasLeft};
/// Should be used to convert jit types to ethcore
trait FromJit: Sized {
@@ -106,8 +107,8 @@ impl IntoJit for Address {
}
/// Externalities adapter. Maps callbacks from evmjit to externalities trait.
-///
-/// Evmjit doesn't have to know about children execution failures.
+///
+/// Evmjit doesn't have to know about children execution failures.
/// This adapter 'catches' them and moves upstream.
struct ExtAdapter<'a> {
ext: &'a mut evm::Ext,
@@ -165,7 +166,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
init_beg: *const u8,
init_size: u64,
address: *mut evmjit::H256) {
-
+
let gas = unsafe { U256::from(*io_gas) };
let value = unsafe { U256::from_jit(&*value) };
let code = unsafe { slice::from_raw_parts(init_beg, init_size as usize) };
@@ -240,9 +241,9 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
}
match self.ext.call(
- &call_gas,
+ &call_gas,
&sender_address,
- &receive_address,
+ &receive_address,
value,
unsafe { slice::from_raw_parts(in_beg, in_size as usize) },
&code_address,
@@ -283,7 +284,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
if !topic4.is_null() {
topics.push(H256::from_jit(&*topic4));
}
-
+
let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize);
self.ext.log(topics, bytes_ref);
}
@@ -300,10 +301,13 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
}
}
-pub struct JitEvm;
+#[derive(Default)]
+pub struct JitEvm {
+ ctxt: Option,
+}
impl evm::Evm for JitEvm {
- fn exec(&self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result {
+ fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result {
// Dirty hack. This is unsafe, but we interact with ffi, so it's justified.
let ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext, params.address.clone())) };
let mut ext_handle = evmjit::ExtHandle::new(ext_adapter);
@@ -342,15 +346,17 @@ impl evm::Evm for JitEvm {
// don't really know why jit timestamp is int..
data.timestamp = ext.env_info().timestamp as i64;
- let mut context = unsafe { evmjit::ContextHandle::new(data, schedule, &mut ext_handle) };
+ self.context = Some(unsafe { evmjit::ContextHandle::new(data, schedule, &mut ext_handle) });
+ let context = self.context.as_ref_mut().unwrap();
let res = context.exec();
-
+
match res {
- evmjit::ReturnCode::Stop => Ok(U256::from(context.gas_left())),
- evmjit::ReturnCode::Return => ext.ret(&U256::from(context.gas_left()), context.output_data()),
- evmjit::ReturnCode::Suicide => {
+ evmjit::ReturnCode::Stop => Ok(GasLeft::Known(U256::from(context.gas_left()))),
+ evmjit::ReturnCode::Return =>
+ Ok(GasLeft::NeedsReturn(U256::from(context.gas_left()), context.output_data())),
+ evmjit::ReturnCode::Suicide => {
ext.suicide(&Address::from_jit(&context.suicide_refund_address()));
- Ok(U256::from(context.gas_left()))
+ Ok(GasLeft::Known(U256::from(context.gas_left())))
},
evmjit::ReturnCode::OutOfGas => Err(evm::Error::OutOfGas),
_err => Err(evm::Error::Internal)
@@ -371,7 +377,7 @@ fn test_to_and_from_h256() {
let h = H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap();
let j: ::evmjit::I256 = h.clone().into_jit();
let h2 = H256::from_jit(&j);
-
+
assert_eq!(h, h2);
let j: ::evmjit::H256 = h.clone().into_jit();
diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs
index b7816b99c..5e7b67cfb 100644
--- a/ethcore/src/evm/mod.rs
+++ b/ethcore/src/evm/mod.rs
@@ -29,7 +29,8 @@ mod jit;
#[cfg(test)]
mod tests;
-pub use self::evm::{Evm, Error, Result};
+pub use self::evm::{Evm, Error, Finalize, GasLeft, Result};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule;
+pub use self::instructions::get_info;
diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs
index 445c0be41..ba156e6dd 100644
--- a/ethcore/src/evm/tests.rs
+++ b/ethcore/src/evm/tests.rs
@@ -15,8 +15,7 @@
// along with Parity. If not, see .
use common::*;
-use evm;
-use evm::{Ext, Schedule, Factory, VMType, ContractCreateResult, MessageCallResult};
+use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
use std::fmt::Debug;
struct FakeLogEntry {
@@ -58,6 +57,15 @@ struct FakeExt {
calls: HashSet,
}
+// similar to the normal `finalize` function, but ignoring NeedsReturn.
+fn test_finalize(res: Result) -> Result {
+ match res {
+ Ok(GasLeft::Known(gas)) => Ok(gas),
+ Ok(GasLeft::NeedsReturn(_, _)) => unimplemented!(), // since ret is unimplemented.
+ Err(e) => Err(e),
+ }
+}
+
impl FakeExt {
fn new() -> Self {
FakeExt::default()
@@ -136,7 +144,7 @@ impl Ext for FakeExt {
});
}
- fn ret(&mut self, _gas: &U256, _data: &[u8]) -> result::Result {
+ fn ret(self, _gas: &U256, _data: &[u8]) -> evm::Result {
unimplemented!();
}
@@ -173,8 +181,8 @@ fn test_stack_underflow() {
let mut ext = FakeExt::new();
let err = {
- let vm : Box = Box::new(super::interpreter::Interpreter);
- vm.exec(params, &mut ext).unwrap_err()
+ let mut vm : Box = Box::new(super::interpreter::Interpreter::default());
+ test_finalize(vm.exec(params, &mut ext)).unwrap_err()
};
match err {
@@ -200,8 +208,8 @@ fn test_add(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_988));
@@ -220,8 +228,8 @@ fn test_sha3(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_961));
@@ -240,8 +248,8 @@ fn test_address(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
@@ -262,8 +270,8 @@ fn test_origin(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
@@ -284,8 +292,8 @@ fn test_sender(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
@@ -319,8 +327,8 @@ fn test_extcodecopy(factory: super::Factory) {
ext.codes.insert(sender, sender_code);
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_935));
@@ -339,8 +347,8 @@ fn test_log_empty(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(99_619));
@@ -371,8 +379,8 @@ fn test_log_sender(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(98_974));
@@ -396,8 +404,8 @@ fn test_blockhash(factory: super::Factory) {
ext.blockhashes.insert(U256::zero(), blockhash.clone());
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_974));
@@ -418,8 +426,8 @@ fn test_calldataload(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_991));
@@ -439,8 +447,8 @@ fn test_author(factory: super::Factory) {
ext.info.author = author;
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
@@ -459,8 +467,8 @@ fn test_timestamp(factory: super::Factory) {
ext.info.timestamp = timestamp;
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
@@ -479,8 +487,8 @@ fn test_number(factory: super::Factory) {
ext.info.number = number;
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
@@ -499,8 +507,8 @@ fn test_difficulty(factory: super::Factory) {
ext.info.difficulty = difficulty;
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
@@ -519,8 +527,8 @@ fn test_gas_limit(factory: super::Factory) {
ext.info.gas_limit = gas_limit;
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
@@ -537,8 +545,8 @@ fn test_mul(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "000000000000000000000000000000000000000000000000734349397b853383");
@@ -555,8 +563,8 @@ fn test_sub(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000012364ad0302");
@@ -573,8 +581,8 @@ fn test_div(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac");
@@ -591,8 +599,8 @@ fn test_div_zero(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@@ -609,8 +617,8 @@ fn test_mod(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b");
@@ -628,8 +636,8 @@ fn test_smod(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b");
@@ -647,8 +655,8 @@ fn test_sdiv(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac");
@@ -666,8 +674,8 @@ fn test_exp(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "90fd23767b60204c3d6fc8aec9e70a42a3f127140879c133a20129a597ed0c59");
@@ -686,8 +694,8 @@ fn test_comparison(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@@ -707,8 +715,8 @@ fn test_signed_comparison(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@@ -728,8 +736,8 @@ fn test_bitops(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0");
@@ -751,8 +759,8 @@ fn test_addmod_mulmod(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000001");
@@ -772,8 +780,8 @@ fn test_byte(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@@ -791,8 +799,8 @@ fn test_signextend(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000fff");
@@ -811,8 +819,8 @@ fn test_badinstruction_int() {
let mut ext = FakeExt::new();
let err = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap_err()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap_err()
};
match err {
@@ -831,8 +839,8 @@ fn test_pop(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0");
@@ -851,8 +859,8 @@ fn test_extops(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000004"); // PC / CALLDATASIZE
@@ -874,8 +882,8 @@ fn test_jumps(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(ext.sstore_clears, 1);
@@ -903,8 +911,8 @@ fn test_calls(factory: super::Factory) {
};
let gas_left = {
- let vm = factory.create();
- vm.exec(params, &mut ext).unwrap()
+ let mut vm = factory.create();
+ test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_set_contains(&ext.calls, &FakeCall {
diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs
index 291b4dba2..fdc1c7d18 100644
--- a/ethcore/src/executive.rs
+++ b/ethcore/src/executive.rs
@@ -18,12 +18,11 @@
use common::*;
use state::*;
use engine::*;
-use evm::{self, Ext, Factory};
+use evm::{self, Ext, Factory, Finalize};
use externalities::*;
use substate::*;
-use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
+use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use crossbeam;
-
pub use types::executed::{Executed, ExecutionResult};
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
@@ -43,6 +42,8 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
pub struct TransactOptions {
/// Enable call tracing.
pub tracing: bool,
+ /// Enable VM tracing.
+ pub vm_tracing: bool,
/// Check transaction nonce before execution.
pub check_nonce: bool,
}
@@ -80,21 +81,40 @@ impl<'a> Executive<'a> {
}
/// Creates `Externalities` from `Executive`.
- pub fn as_externalities<'_, T>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_, '_>, tracer: &'_ mut T) -> Externalities<'_, T> where T: Tracer {
- Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer)
+ pub fn as_externalities<'_, T, V>(
+ &'_ mut self,
+ origin_info: OriginInfo,
+ substate: &'_ mut Substate,
+ output: OutputPolicy<'_, '_>,
+ tracer: &'_ mut T,
+ vm_tracer: &'_ mut V
+ ) -> Externalities<'_, T, V> where T: Tracer, V: VMTracer {
+ Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer, vm_tracer)
}
/// This function should be used to execute transaction.
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result {
let check = options.check_nonce;
match options.tracing {
- true => self.transact_with_tracer(t, check, ExecutiveTracer::default()),
- false => self.transact_with_tracer(t, check, NoopTracer),
+ true => match options.vm_tracing {
+ true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::default()),
+ false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer),
+ },
+ false => match options.vm_tracing {
+ true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::default()),
+ false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer),
+ },
}
}
/// Execute transaction/call with tracing enabled
- pub fn transact_with_tracer(&'a mut self, t: &SignedTransaction, check_nonce: bool, mut tracer: T) -> Result where T: Tracer {
+ pub fn transact_with_tracer(
+ &'a mut self,
+ t: &SignedTransaction,
+ check_nonce: bool,
+ mut tracer: T,
+ mut vm_tracer: V
+ ) -> Result where T: Tracer, V: VMTracer {
let sender = try!(t.sender().map_err(|e| {
let message = format!("Transaction malformed: {:?}", e);
ExecutionError::TransactionMalformed(message)
@@ -154,7 +174,7 @@ impl<'a> Executive<'a> {
code: Some(t.data.clone()),
data: None,
};
- (self.create(params, &mut substate, &mut tracer), vec![])
+ (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
},
Action::Call(ref address) => {
let params = ActionParams {
@@ -170,22 +190,28 @@ impl<'a> Executive<'a> {
};
// TODO: move output upstream
let mut out = vec![];
- (self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer), out)
+ (self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer, &mut vm_tracer), out)
}
};
// finalize here!
- Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop())))
+ Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain())))
}
- fn exec_vm(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy, tracer: &mut T)
- -> evm::Result where T: Tracer {
+ fn exec_vm(
+ &mut self,
+ params: ActionParams,
+ unconfirmed_substate: &mut Substate,
+ output_policy: OutputPolicy,
+ tracer: &mut T,
+ vm_tracer: &mut V
+ ) -> evm::Result where T: Tracer, V: VMTracer {
// Ordinary execution - keep VM in same thread
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
let vm_factory = self.vm_factory;
- let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer);
+ let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
- return vm_factory.create().exec(params, &mut ext);
+ return vm_factory.create().exec(params, &mut ext).finalize(ext);
}
// Start in new thread to reset stack
@@ -193,10 +219,10 @@ impl<'a> Executive<'a> {
// https://github.com/aturon/crossbeam/issues/16
crossbeam::scope(|scope| {
let vm_factory = self.vm_factory;
- let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer);
+ let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
scope.spawn(move || {
- vm_factory.create().exec(params, &mut ext)
+ vm_factory.create().exec(params, &mut ext).finalize(ext)
})
}).join()
}
@@ -205,8 +231,14 @@ impl<'a> Executive<'a> {
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate and the output.
/// Returns either gas_left or `evm::Error`.
- pub fn call(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef, tracer: &mut T)
- -> evm::Result where T: Tracer {
+ pub fn call(
+ &mut self,
+ params: ActionParams,
+ substate: &mut Substate,
+ mut output: BytesRef,
+ tracer: &mut T,
+ vm_tracer: &mut V
+ ) -> evm::Result where T: Tracer, V: VMTracer {
// backup used in case of running out of gas
self.state.snapshot();
@@ -264,16 +296,22 @@ impl<'a> Executive<'a> {
let trace_info = tracer.prepare_trace_call(¶ms);
let mut trace_output = tracer.prepare_trace_output();
let mut subtracer = tracer.subtracer();
+
let gas = params.gas;
if params.code.is_some() {
// part of substate that may be reverted
let mut unconfirmed_substate = Substate::new();
+ // TODO: make ActionParams pass by ref then avoid copy altogether.
+ let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("scope is protected by params.code.is_some condition"));
+
let res = {
- self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer)
+ self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
};
+ vm_tracer.done_subtrace(subvmtracer);
+
trace!(target: "executive", "res={:?}", res);
let traces = subtracer.traces();
@@ -307,8 +345,13 @@ impl<'a> Executive<'a> {
/// Creates contract with given contract params.
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate.
- pub fn create(&mut self, params: ActionParams, substate: &mut Substate, tracer: &mut T) -> evm::Result where T:
- Tracer {
+ pub fn create(
+ &mut self,
+ params: ActionParams,
+ substate: &mut Substate,
+ tracer: &mut T,
+ vm_tracer: &mut V
+ ) -> evm::Result where T: Tracer, V: VMTracer {
// backup used in case of running out of gas
self.state.snapshot();
@@ -330,10 +373,14 @@ impl<'a> Executive<'a> {
let gas = params.gas;
let created = params.address.clone();
+ let mut subvmtracer = vm_tracer.prepare_subtrace(¶ms.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed"));
+
let res = {
- self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer)
+ self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
};
+ vm_tracer.done_subtrace(subvmtracer);
+
match res {
Ok(gas_left) => tracer.trace_create(
trace_info,
@@ -351,7 +398,15 @@ impl<'a> Executive<'a> {
}
/// Finalizes the transaction (does refunds and suicides).
- fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes, trace: Option) -> ExecutionResult {
+ fn finalize(
+ &mut self,
+ t: &SignedTransaction,
+ substate: Substate,
+ result: evm::Result,
+ output: Bytes,
+ trace: Option,
+ vm_trace: Option
+ ) -> ExecutionResult {
let schedule = self.engine.schedule(self.info);
// refunds from SSTORE nonzero -> zero
@@ -394,6 +449,8 @@ impl<'a> Executive<'a> {
contracts_created: vec![],
output: output,
trace: trace,
+ vm_trace: vm_trace,
+ state_diff: None,
})
},
_ => {
@@ -406,12 +463,14 @@ impl<'a> Executive<'a> {
contracts_created: substate.contracts_created,
output: output,
trace: trace,
+ vm_trace: vm_trace,
+ state_diff: None,
})
},
}
}
- fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate) {
+ fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate) {
match *result {
Err(evm::Error::OutOfGas)
| Err(evm::Error::BadJumpDestination {..})
@@ -438,6 +497,7 @@ mod tests {
use tests::helpers::*;
use trace::trace;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
+ use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
#[test]
fn test_contract_address() {
@@ -466,7 +526,7 @@ mod tests {
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- ex.create(params, &mut substate, &mut NoopTracer).unwrap()
+ ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
};
assert_eq!(gas_left, U256::from(79_975));
@@ -525,7 +585,7 @@ mod tests {
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- ex.create(params, &mut substate, &mut NoopTracer).unwrap()
+ ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
};
assert_eq!(gas_left, U256::from(62_976));
@@ -542,7 +602,7 @@ mod tests {
// 52
// 60 1d - push 29
// 60 03 - push 3
- // 60 17 - push 17
+ // 60 17 - push 23
// f0 - create
// 60 00 - push 0
// 55 sstore
@@ -578,13 +638,16 @@ mod tests {
let engine = TestEngine::new(5);
let mut substate = Substate::new();
let mut tracer = ExecutiveTracer::default();
+ let mut vm_tracer = ExecutiveVMTracer::default();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
let output = BytesRef::Fixed(&mut[0u8;0]);
- ex.call(params, &mut substate, output, &mut tracer).unwrap()
+ ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap()
};
+ assert_eq!(gas_left, U256::from(44_752));
+
let expected_trace = vec![ Trace {
depth: 0,
action: trace::Action::Call(trace::Call {
@@ -615,7 +678,39 @@ mod tests {
}]
}];
assert_eq!(tracer.traces(), expected_trace);
- assert_eq!(gas_left, U256::from(44_752));
+
+ let expected_vm_trace = VMTrace {
+ parent_step: 0,
+ code: vec![124, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85, 96, 0, 82, 96, 29, 96, 3, 96, 23, 240, 96, 0, 85],
+ operations: vec![
+ VMOperation { pc: 0, instruction: 124, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 30, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 32, instruction: 82, gas_cost: 6.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![0, 0, 0, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] }), store_diff: None }) },
+ VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec_into![29], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec_into![3], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![23], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec_into![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) }
+ ],
+ subs: vec![
+ VMTrace {
+ parent_step: 7,
+ code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85],
+ operations: vec![
+ VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67973.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67970.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67967.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 67958.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) },
+ VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) }
+ ],
+ subs: vec![]
+ }
+ ]
+ };
+ assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace);
}
evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int}
@@ -650,12 +745,15 @@ mod tests {
let engine = TestEngine::new(5);
let mut substate = Substate::new();
let mut tracer = ExecutiveTracer::default();
+ let mut vm_tracer = ExecutiveVMTracer::default();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- ex.create(params.clone(), &mut substate, &mut tracer).unwrap()
+ ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap()
};
+ assert_eq!(gas_left, U256::from(96_776));
+
let expected_trace = vec![Trace {
depth: 0,
action: trace::Action::Create(trace::Create {
@@ -671,9 +769,23 @@ mod tests {
}),
subs: vec![]
}];
-
assert_eq!(tracer.traces(), expected_trace);
- assert_eq!(gas_left, U256::from(96_776));
+
+ let expected_vm_trace = VMTrace {
+ parent_step: 0,
+ code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85],
+ operations: vec![
+ VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) },
+ VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
+ VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) }
+ ],
+ subs: vec![]
+ };
+ assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace);
}
evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int}
@@ -722,7 +834,7 @@ mod tests {
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- ex.create(params, &mut substate, &mut NoopTracer).unwrap()
+ ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
};
assert_eq!(gas_left, U256::from(62_976));
@@ -774,7 +886,7 @@ mod tests {
{
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- ex.create(params, &mut substate, &mut NoopTracer).unwrap();
+ ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap();
}
assert_eq!(substate.contracts_created.len(), 1);
@@ -835,7 +947,7 @@ mod tests {
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap()
+ ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
};
assert_eq!(gas_left, U256::from(73_237));
@@ -880,7 +992,7 @@ mod tests {
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap()
+ ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
};
assert_eq!(gas_left, U256::from(59_870));
@@ -913,7 +1025,7 @@ mod tests {
let executed = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- let opts = TransactOptions { check_nonce: true, tracing: false };
+ let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts).unwrap()
};
@@ -947,7 +1059,7 @@ mod tests {
let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- let opts = TransactOptions { check_nonce: true, tracing: false };
+ let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts)
};
@@ -979,7 +1091,7 @@ mod tests {
let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- let opts = TransactOptions { check_nonce: true, tracing: false };
+ let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts)
};
@@ -1013,7 +1125,7 @@ mod tests {
let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- let opts = TransactOptions { check_nonce: true, tracing: false };
+ let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts)
};
@@ -1047,7 +1159,7 @@ mod tests {
let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- let opts = TransactOptions { check_nonce: true, tracing: false };
+ let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts)
};
@@ -1082,7 +1194,7 @@ mod tests {
let result = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
- ex.create(params, &mut substate, &mut NoopTracer)
+ ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer)
};
match result {
diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs
index 99d2eed72..66509440a 100644
--- a/ethcore/src/externalities.rs
+++ b/ethcore/src/externalities.rs
@@ -21,7 +21,7 @@ use engine::*;
use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
use substate::*;
-use trace::Tracer;
+use trace::{Tracer, VMTracer};
/// Policy for handling output data on `RETURN` opcode.
pub enum OutputPolicy<'a, 'b> {
@@ -55,7 +55,7 @@ impl OriginInfo {
}
/// Implementation of evm Externalities.
-pub struct Externalities<'a, T> where T: 'a + Tracer {
+pub struct Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
state: &'a mut State,
env_info: &'a EnvInfo,
engine: &'a Engine,
@@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer {
schedule: Schedule,
output: OutputPolicy<'a, 'a>,
tracer: &'a mut T,
+ vm_tracer: &'a mut V,
}
-impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
-
+impl<'a, T, V> Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
#[cfg_attr(feature="dev", allow(too_many_arguments))]
/// Basic `Externalities` constructor.
pub fn new(state: &'a mut State,
@@ -81,6 +81,7 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
substate: &'a mut Substate,
output: OutputPolicy<'a, 'a>,
tracer: &'a mut T,
+ vm_tracer: &'a mut V,
) -> Self {
Externalities {
state: state,
@@ -93,11 +94,12 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
schedule: engine.schedule(env_info),
output: output,
tracer: tracer,
+ vm_tracer: vm_tracer,
}
}
}
-impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
+impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
fn storage_at(&self, key: &H256) -> H256 {
self.state.storage_at(&self.origin_info.address, key)
}
@@ -152,7 +154,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth);
// TODO: handle internal error separately
- match ex.create(params, self.substate, self.tracer) {
+ match ex.create(params, self.substate, self.tracer, self.vm_tracer) {
Ok(gas_left) => {
self.substate.contracts_created.push(address.clone());
ContractCreateResult::Created(address, gas_left)
@@ -190,7 +192,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth);
- match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer) {
+ match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) {
Ok(gas_left) => MessageCallResult::Success(gas_left),
_ => MessageCallResult::Failed
}
@@ -201,7 +203,8 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
}
#[cfg_attr(feature="dev", allow(match_ref_pats))]
- fn ret(&mut self, gas: &U256, data: &[u8]) -> Result {
+ fn ret(mut self, gas: &U256, data: &[u8]) -> evm::Result
+ where Self: Sized {
let handle_copy = |to: &mut Option<&mut Bytes>| {
to.as_mut().map(|b| **b = data.to_owned());
};
@@ -210,20 +213,14 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
handle_copy(copy);
let len = cmp::min(slice.len(), data.len());
- unsafe {
- ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len);
- }
+ (&mut slice[..len]).copy_from_slice(&data[..len]);
Ok(*gas)
},
OutputPolicy::Return(BytesRef::Flexible(ref mut vec), ref mut copy) => {
handle_copy(copy);
vec.clear();
- vec.reserve(data.len());
- unsafe {
- ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len());
- vec.set_len(data.len());
- }
+ vec.extend_from_slice(data);
Ok(*gas)
},
OutputPolicy::InitContract(ref mut copy) => {
@@ -238,11 +235,8 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
handle_copy(copy);
let mut code = vec![];
- code.reserve(data.len());
- unsafe {
- ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len());
- code.set_len(data.len());
- }
+ code.extend_from_slice(data);
+
self.state.init_code(&self.origin_info.address, code);
Ok(*gas - return_cost)
}
@@ -286,6 +280,14 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
fn inc_sstore_clears(&mut self) {
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
}
+
+ fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
+ self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost)
+ }
+
+ fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
+ self.vm_tracer.trace_executed(gas_used, stack_push, mem_diff, store_diff)
+ }
}
#[cfg(test)]
@@ -297,7 +299,7 @@ mod tests {
use substate::*;
use tests::helpers::*;
use super::*;
- use trace::{NoopTracer};
+ use trace::{NoopTracer, NoopVMTracer};
fn get_test_origin() -> OriginInfo {
OriginInfo {
@@ -349,9 +351,10 @@ mod tests {
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
let mut tracer = NoopTracer;
+ let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default();
- let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
+ let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
assert_eq!(ext.env_info().number, 100);
}
@@ -361,9 +364,10 @@ mod tests {
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
let mut tracer = NoopTracer;
+ let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default();
- let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
+ let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
@@ -383,9 +387,10 @@ mod tests {
}
let state = setup.state.reference_mut();
let mut tracer = NoopTracer;
+ let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default();
- let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
+ let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
@@ -398,9 +403,10 @@ mod tests {
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
let mut tracer = NoopTracer;
+ let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default();
- let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
+ let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
let mut output = vec![];
@@ -423,10 +429,11 @@ mod tests {
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
let mut tracer = NoopTracer;
+ let mut vm_tracer = NoopVMTracer;
{
let vm_factory = Default::default();
- let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
+ let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
ext.log(log_topics, &log_data);
}
@@ -440,10 +447,11 @@ mod tests {
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
let mut tracer = NoopTracer;
+ let mut vm_tracer = NoopVMTracer;
{
let vm_factory = Default::default();
- let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
+ let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
ext.suicide(&refund_account);
}
diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs
index a1154f6a9..53052d8dc 100644
--- a/ethcore/src/json_tests/chain.rs
+++ b/ethcore/src/json_tests/chain.rs
@@ -22,6 +22,7 @@ use tests::helpers::*;
use devtools::*;
use spec::Genesis;
use ethjson;
+use miner::Miner;
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec {
init_log();
@@ -53,7 +54,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec {
let temp = RandomTempPath::new();
{
- let client = Client::new(ClientConfig::default(), spec, temp.as_path(), IoChannel::disconnected()).unwrap();
+ let client = Client::new(ClientConfig::default(), spec, temp.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap();
for b in &blockchain.blocks_rlp() {
if Block::is_good(&b) {
let _ = client.import_block(b.clone());
diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs
index 9e9620169..f4a34a33e 100644
--- a/ethcore/src/json_tests/executive.rs
+++ b/ethcore/src/json_tests/executive.rs
@@ -19,14 +19,15 @@ use state::*;
use executive::*;
use engine::*;
use evm;
-use evm::{Schedule, Ext, Factory, VMType, ContractCreateResult, MessageCallResult};
+use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
use externalities::*;
use substate::*;
use tests::helpers::*;
use ethjson;
use trace::{Tracer, NoopTracer};
+use trace::{VMTracer, NoopVMTracer};
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Clone)]
struct CallCreate {
data: Bytes,
destination: Option,
@@ -48,32 +49,35 @@ impl From for CallCreate {
/// Tiny wrapper around executive externalities.
/// Stores callcreates.
-struct TestExt<'a, T> where T: 'a + Tracer {
- ext: Externalities<'a, T>,
+struct TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
+ ext: Externalities<'a, T, V>,
callcreates: Vec