Merge branch 'master' into serde-bump

Conflicts:
	ethcore/src/types/state_diff.rs
This commit is contained in:
Tomasz Drwięga 2016-06-06 10:10:06 +02:00
commit a063a63ac7
118 changed files with 4073 additions and 1850 deletions

View File

@ -33,7 +33,7 @@ env:
global: global:
# GH_TOKEN # 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= - 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}" - ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
- KCOV_FEATURES="" - 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" - 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_rpc-* &&
$KCOV_CMD target/debug/deps/ethcore_dapps-* && $KCOV_CMD target/debug/deps/ethcore_dapps-* &&
$KCOV_CMD target/debug/deps/ethcore_signer-* && $KCOV_CMD target/debug/deps/ethcore_signer-* &&
$KCOV_CMD target/debug/deps/ethminer-* &&
$KCOV_CMD target/debug/deps/ethjson-* && $KCOV_CMD target/debug/deps/ethjson-* &&
$KCOV_CMD target/debug/parity-* && $KCOV_CMD target/debug/parity-* &&
[ $TRAVIS_BRANCH = master ] && [ $TRAVIS_BRANCH = master ] &&

28
Cargo.lock generated
View File

@ -17,7 +17,6 @@ dependencies = [
"ethcore-rpc 1.2.0", "ethcore-rpc 1.2.0",
"ethcore-signer 1.2.0", "ethcore-signer 1.2.0",
"ethcore-util 1.2.0", "ethcore-util 1.2.0",
"ethminer 1.2.0",
"ethsync 1.2.0", "ethsync 1.2.0",
"fdlimit 0.1.0", "fdlimit 0.1.0",
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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)", "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)", "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-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)", "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)", "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 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-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)", "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-devtools 1.2.0",
"ethcore-util 1.2.0", "ethcore-util 1.2.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethminer 1.2.0",
"ethsync 1.2.0", "ethsync 1.2.0",
"json-ipc-server 0.1.0 (git+https://github.com/ethcore/json-ipc-server.git)", "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)", "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)", "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)", "serde 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.1.0", "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)", "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)", "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)", "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)", "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]] [[package]]
name = "ethsync" name = "ethsync"
version = "1.2.0" version = "1.2.0"
@ -432,7 +418,6 @@ dependencies = [
"env_logger 0.3.3 (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 1.2.0",
"ethcore-util 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)", "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)", "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)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@ -874,7 +859,7 @@ dependencies = [
[[package]] [[package]]
name = "parity-dapps-builtins" name = "parity-dapps-builtins"
version = "0.5.0" 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 = [ dependencies = [
"parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)",
] ]
@ -1182,6 +1167,11 @@ name = "slab"
version = "0.1.3" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "solicit" name = "solicit"
version = "0.4.4" version = "0.4.4"

View File

@ -27,7 +27,6 @@ clippy = { version = "0.0.69", optional = true}
ethcore = { path = "ethcore" } ethcore = { path = "ethcore" }
ethcore-util = { path = "util" } ethcore-util = { path = "util" }
ethsync = { path = "sync" } ethsync = { path = "sync" }
ethminer = { path = "miner" }
ethcore-devtools = { path = "devtools" } ethcore-devtools = { path = "devtools" }
ethcore-rpc = { path = "rpc", optional = true } ethcore-rpc = { path = "rpc", optional = true }
ethcore-signer = { path = "signer", optional = true } ethcore-signer = { path = "signer", optional = true }
@ -46,7 +45,7 @@ default-features = false
default = ["rpc", "dapps", "ethcore-signer"] default = ["rpc", "dapps", "ethcore-signer"]
rpc = ["ethcore-rpc"] rpc = ["ethcore-rpc"]
dapps = ["ethcore-dapps"] 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"] "ethcore-dapps/dev", "ethcore-signer/dev"]
travis-beta = ["ethcore/json-tests"] travis-beta = ["ethcore/json-tests"]
travis-nightly = ["ethcore/json-tests", "dev"] travis-nightly = ["ethcore/json-tests", "dev"]

View File

@ -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] [![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-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
[license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html [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: - Linux:
```bash ```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: - OSX with Homebrew:
```bash ```bash
brew update && brew install multirust $ brew update && brew install multirust
multirust default stable $ multirust default stable
``` ```
Then, download and build Parity: Then, download and build Parity:
```bash ```bash
# download Parity code # download Parity code
git clone https://github.com/ethcore/parity $ git clone https://github.com/ethcore/parity
cd parity $ cd parity
# build in release mode # 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.

2
cov.sh
View File

@ -22,7 +22,6 @@ cargo test \
-p ethsync \ -p ethsync \
-p ethcore-rpc \ -p ethcore-rpc \
-p parity \ -p parity \
-p ethminer \
-p ethcore-signer \ -p ethcore-signer \
-p ethcore-dapps \ -p ethcore-dapps \
--no-run || exit $? --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_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_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/ethcore_dapps-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethminer-*
xdg-open target/coverage/index.html xdg-open target/coverage/index.html

View File

@ -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-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-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 } 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} clippy = { version = "0.0.69", optional = true}
[build-dependencies] [build-dependencies]

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc; use std::sync::Arc;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath}; use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath};
use api::response::as_json; use api::response::as_json;
@ -23,8 +23,8 @@ pub struct RestApi {
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
} }
#[derive(Debug, PartialEq, Serialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
struct App { pub struct App {
pub id: String, pub id: String,
pub name: String, pub name: String,
pub description: String, pub description: String,
@ -34,6 +34,19 @@ struct App {
pub icon_url: String, 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 { impl RestApi {
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> { pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> {
Box::new(RestApi { Box::new(RestApi {
@ -43,14 +56,7 @@ impl RestApi {
fn list_apps(&self) -> Vec<App> { fn list_apps(&self) -> Vec<App> {
self.endpoints.iter().filter_map(|(ref k, ref e)| { self.endpoints.iter().filter_map(|(ref k, ref e)| {
e.info().map(|ref info| App { e.info().map(|ref info| App::from_info(k, info))
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(),
})
}).collect() }).collect()
} }
} }

View File

@ -18,3 +18,4 @@ mod api;
mod response; mod response;
pub use self::api::RestApi; pub use self::api::RestApi;
pub use self::api::App;

116
dapps/src/apps/fs.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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<LocalDapp> {
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::<App>(&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
}

View File

@ -19,10 +19,11 @@ use page::PageEndpoint;
use proxypac::ProxyPac; use proxypac::ProxyPac;
use parity_dapps::WebApp; use parity_dapps::WebApp;
mod fs;
extern crate parity_dapps_status; extern crate parity_dapps_status;
extern crate parity_dapps_builtins; extern crate parity_dapps_builtins;
pub const DAPPS_DOMAIN : &'static str = ".parity"; pub const DAPPS_DOMAIN : &'static str = ".parity";
pub const RPC_PATH : &'static str = "rpc"; pub const RPC_PATH : &'static str = "rpc";
pub const API_PATH : &'static str = "api"; pub const API_PATH : &'static str = "api";
@ -36,22 +37,24 @@ pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_dapps_builtins::App::default(), UTILS_PATH.to_owned())) Box::new(PageEndpoint::with_prefix(parity_dapps_builtins::App::default(), UTILS_PATH.to_owned()))
} }
pub fn all_endpoints() -> Endpoints { pub fn all_endpoints(dapps_path: String) -> Endpoints {
let mut pages = Endpoints::new(); // fetch fs dapps at first to avoid overwriting builtins
pages.insert("proxy".into(), ProxyPac::boxed()); let mut pages = fs::local_endpoints(dapps_path);
// Home page needs to be safe embed // Home page needs to be safe embed
// because we use Cross-Origin LocalStorage. // because we use Cross-Origin LocalStorage.
// TODO [ToDr] Account naming should be moved to parity. // TODO [ToDr] Account naming should be moved to parity.
pages.insert("home".into(), Box::new( pages.insert("home".into(), Box::new(
PageEndpoint::new_safe_to_embed(parity_dapps_builtins::App::default()) PageEndpoint::new_safe_to_embed(parity_dapps_builtins::App::default())
)); ));
pages.insert("proxy".into(), ProxyPac::boxed());
insert::<parity_dapps_status::App>(&mut pages, "status"); insert::<parity_dapps_status::App>(&mut pages, "status");
insert::<parity_dapps_status::App>(&mut pages, "parity"); insert::<parity_dapps_status::App>(&mut pages, "parity");
// Optional dapps
wallet_page(&mut pages); wallet_page(&mut pages);
daodapp_page(&mut pages); daodapp_page(&mut pages);
makerotc_page(&mut pages); makerotc_page(&mut pages);
pages pages
} }

View File

@ -30,17 +30,17 @@ pub struct EndpointPath {
pub port: u16, pub port: u16,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct EndpointInfo { pub struct EndpointInfo {
pub name: &'static str, pub name: String,
pub description: &'static str, pub description: String,
pub version: &'static str, pub version: String,
pub author: &'static str, pub author: String,
pub icon_url: &'static str, pub icon_url: String,
} }
pub trait Endpoint : Send + Sync { pub trait Endpoint : Send + Sync {
fn info(&self) -> Option<EndpointInfo> { None } fn info(&self) -> Option<&EndpointInfo> { None }
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream>>; fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream>>;
} }

View File

@ -52,6 +52,8 @@ extern crate serde_json;
extern crate jsonrpc_core; extern crate jsonrpc_core;
extern crate jsonrpc_http_server; extern crate jsonrpc_http_server;
extern crate parity_dapps; extern crate parity_dapps;
extern crate ethcore_rpc;
extern crate mime_guess;
mod endpoint; mod endpoint;
mod apps; mod apps;
@ -66,37 +68,41 @@ use std::net::SocketAddr;
use std::collections::HashMap; use std::collections::HashMap;
use jsonrpc_core::{IoHandler, IoDelegate}; use jsonrpc_core::{IoHandler, IoDelegate};
use router::auth::{Authorization, NoAuth, HttpBasicAuth}; use router::auth::{Authorization, NoAuth, HttpBasicAuth};
use ethcore_rpc::Extendable;
static DAPPS_DOMAIN : &'static str = ".parity"; static DAPPS_DOMAIN : &'static str = ".parity";
/// Webapps HTTP+RPC server build. /// Webapps HTTP+RPC server build.
pub struct ServerBuilder { pub struct ServerBuilder {
dapps_path: String,
handler: Arc<IoHandler>, handler: Arc<IoHandler>,
} }
impl Extendable for ServerBuilder {
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
self.handler.add_delegate(delegate);
}
}
impl ServerBuilder { impl ServerBuilder {
/// Construct new dapps server /// Construct new dapps server
pub fn new() -> Self { pub fn new(dapps_path: String) -> Self {
ServerBuilder { ServerBuilder {
dapps_path: dapps_path,
handler: Arc::new(IoHandler::new()) handler: Arc::new(IoHandler::new())
} }
} }
/// Add io delegate.
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
self.handler.add_delegate(delegate);
}
/// Asynchronously start server with no authentication, /// Asynchronously start server with no authentication,
/// returns result with `Server` handle on success or an error. /// returns result with `Server` handle on success or an error.
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> { pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
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`, /// Asynchronously start server with `HTTP Basic Authentication`,
/// return result with `Server` handle on success or an error. /// 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, ServerError> { pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result<Server, ServerError> {
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 { impl Server {
fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>) -> Result<Server, ServerError> { fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>, dapps_path: String) -> Result<Server, ServerError> {
let panic_handler = Arc::new(Mutex::new(None)); let panic_handler = Arc::new(Mutex::new(None));
let authorization = Arc::new(authorization); 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 special = Arc::new({
let mut special = HashMap::new(); let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone())); special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));

154
dapps/src/page/builtin.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
use page::handler;
use std::sync::Arc;
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
use parity_dapps::{WebApp, File, Info};
pub struct PageEndpoint<T : WebApp + 'static> {
/// Content of the files
pub app: Arc<T>,
/// Prefix to strip from the path (when `None` deducted from `app_id`)
pub prefix: Option<String>,
/// Safe to be loaded in frame by other origin. (use wisely!)
safe_to_embed: bool,
info: EndpointInfo,
}
impl<T: WebApp + 'static> PageEndpoint<T> {
/// 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<T: WebApp> Endpoint for PageEndpoint<T> {
fn info(&self) -> Option<&EndpointInfo> {
Some(&self.info)
}
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
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<Info> 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<T: WebApp + 'static> {
app: Arc<T>,
}
impl<T: WebApp + 'static> BuiltinDapp<T> {
fn new(app: Arc<T>) -> Self {
BuiltinDapp {
app: app,
}
}
}
impl<T: WebApp + 'static> handler::Dapp for BuiltinDapp<T> {
type DappFile = BuiltinDappFile<T>;
fn file(&self, path: &str) -> Option<Self::DappFile> {
self.app.file(path).map(|_| {
BuiltinDappFile {
app: self.app.clone(),
path: path.into(),
write_pos: 0,
}
})
}
}
struct BuiltinDappFile<T: WebApp + 'static> {
app: Arc<T>,
path: String,
write_pos: usize,
}
impl<T: WebApp + 'static> BuiltinDappFile<T> {
fn file(&self) -> &File {
self.app.file(&self.path).expect("Check is done when structure is created.")
}
}
impl<T: WebApp + 'static> handler::DappFile for BuiltinDappFile<T> {
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;
}
}

207
dapps/src/page/handler.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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<Self::DappFile>;
}
/// A handler for a single webapp.
/// Resolves correct paths and serves as a plumbing code between
/// hyper server and dapp.
pub struct PageHandler<T: Dapp> {
/// A Dapp.
pub app: T,
/// File currently being served (or `None` if file does not exist).
pub file: Option<T::DappFile>,
/// Optional prefix to strip from path.
pub prefix: Option<String>,
/// Requested path.
pub path: EndpointPath,
/// Flag indicating if the file can be safely embeded (put in iframe).
pub safe_to_embed: bool,
}
impl<T: Dapp> PageHandler<T> {
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<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
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<HttpStream>) -> 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<HttpStream>) -> 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<Self::DappFile> {
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");
}

118
dapps/src/page/local.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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<Handler> {
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<Self::DappFile> {
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;
}
}

View File

@ -14,216 +14,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
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<T : WebApp + 'static> { mod builtin;
/// Content of the files mod local;
pub app: Arc<T>, mod handler;
/// Prefix to strip from the path (when `None` deducted from `app_id`)
pub prefix: Option<String>,
/// Safe to be loaded in frame by other origin. (use wisely!)
safe_to_embed: bool,
}
impl<T: WebApp + 'static> PageEndpoint<T> { pub use self::local::LocalPageEndpoint;
pub fn new(app: T) -> Self { pub use self::builtin::PageEndpoint;
PageEndpoint {
app: Arc::new(app),
prefix: None,
safe_to_embed: false,
}
}
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<T: WebApp> Endpoint for PageEndpoint<T> {
fn info(&self) -> Option<EndpointInfo> {
Some(EndpointInfo::from(self.app.info()))
}
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream>> {
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<Info> 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<T: WebApp + 'static> {
app: Arc<T>,
prefix: Option<String>,
path: EndpointPath,
file: Option<String>,
write_pos: usize,
safe_to_embed: bool,
}
impl<T: WebApp + 'static> PageHandler<T> {
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<T: WebApp + 'static> server::Handler<HttpStream> for PageHandler<T> {
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<HttpStream>) -> 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<HttpStream>) -> 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");
}

View File

@ -21,6 +21,8 @@ extern crate rand;
pub mod random_path; pub mod random_path;
pub mod test_socket; pub mod test_socket;
pub mod stop_guard;
pub use random_path::*; pub use random_path::*;
pub use test_socket::*; pub use test_socket::*;
pub use stop_guard::*;

View File

@ -26,7 +26,11 @@ pub struct RandomTempPath {
} }
pub fn random_filename() -> String { pub fn random_filename() -> String {
(0..8).map(|_| ((random::<f32>() * 26.0) as u8 + 97) as char).collect() random_str(8)
}
pub fn random_str(len: usize) -> String {
(0..len).map(|_| ((random::<f32>() * 26.0) as u8 + 97) as char).collect()
} }
impl RandomTempPath { impl RandomTempPath {
@ -54,6 +58,12 @@ impl RandomTempPath {
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
self.path.to_str().unwrap() 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 { impl Drop for RandomTempPath {

View File

@ -14,12 +14,32 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate rustc_version; //! Stop guard mod
use rustc_version::{version_meta, Channel}; use std::sync::Arc;
use std::sync::atomic::*;
fn main() { /// Stop guard that will set a stop flag on drop
if let Channel::Nightly = version_meta().channel { pub struct StopGuard {
println!("cargo:rustc-cfg=nightly"); flag: Arc<AtomicBool>,
}
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<AtomicBool> {
self.flag.clone()
}
}
impl Drop for StopGuard {
fn drop(&mut self) {
self.flag.store(true, Ordering::Relaxed)
} }
} }

1
doc.sh
View File

@ -10,4 +10,3 @@ cargo doc --no-deps --verbose \
-p ethcore-signer \ -p ethcore-signer \
-p ethcore-dapps \ -p ethcore-dapps \
-p parity \ -p parity \
-p ethminer

View File

@ -29,6 +29,7 @@ ethcore-devtools = { path = "../devtools" }
ethjson = { path = "../json" } ethjson = { path = "../json" }
bloomchain = "0.1" bloomchain = "0.1"
"ethcore-ipc" = { path = "../ipc/rpc" } "ethcore-ipc" = { path = "../ipc/rpc" }
rayon = "0.3.1"
[features] [features]
jit = ["evmjit"] jit = ["evmjit"]

View File

@ -34,17 +34,8 @@
"gasLimit": "0x2fefd8" "gasLimit": "0x2fefd8"
}, },
"nodes": [ "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://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303",
"enode://e941c58fed2709d792f552f408d2162c3d0a5597d22d1da617a9c9e6181f3251056a96adb45ae22eba70119355227298dc7e6dff805b092bae7da2f8564de422@85.25.217.23:30303", "enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303"
"enode://f4b73c9d11a780293ff0ca7afa12c67797afdc33a4797a7c2ecc5b87e455b32a8b9e9804f2004072bac38350bf82d52521d1a09590d2079705fc8357aef2bf9c@71.202.223.50:56603",
"enode://1173eea53e0cb2b8da92423e44cf4cbafbc8ea16c1558cf06e18dfc5a2fc9b140cc802a4362b4c773fb1442541e6f2a225b200bb4c1f6b347e7510a50fa4873f@104.41.138.167:30300",
"enode://1aad341327808738ad34655611f1b13293c4155dde36c8e3788128829f15cc6db2da9435f29520553d4efc134aadc50115690194ac3af519aac7a388b524811e@109.188.125.2:30303"
], ],
"accounts": { "accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@ -51,8 +51,6 @@ impl Account {
} }
} }
#[cfg(test)]
#[cfg(feature = "json-tests")]
/// General constructor. /// General constructor.
pub fn from_pod(pod: PodAccount) -> Account { pub fn from_pod(pod: PodAccount) -> Account {
Account { Account {

View File

@ -37,7 +37,7 @@ use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo}; use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; 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 client::Error as ClientError;
use env_info::EnvInfo; use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address}; use executive::{Executive, Executed, TransactOptions, contract_address};
@ -48,6 +48,7 @@ use trace;
pub use types::blockchain_info::BlockChainInfo; pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus; pub use types::block_status::BlockStatus;
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
use miner::{Miner, MinerService, TransactionImportResult, AccountDetails};
impl fmt::Display for BlockChainInfo { impl fmt::Display for BlockChainInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -90,6 +91,7 @@ pub struct Client<V = CanonVerifier> where V: Verifier {
panic_handler: Arc<PanicHandler>, panic_handler: Arc<PanicHandler>,
verifier: PhantomData<V>, verifier: PhantomData<V>,
vm_factory: Arc<EvmFactory>, vm_factory: Arc<EvmFactory>,
miner: Arc<Miner>,
} }
const HISTORY: u64 = 1200; const HISTORY: u64 = 1200;
@ -102,8 +104,8 @@ const CLIENT_DB_VER_STR: &'static str = "5.3";
impl Client<CanonVerifier> { impl Client<CanonVerifier> {
/// Create a new client with given spec and DB path. /// Create a new client with given spec and DB path.
pub fn new(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, ClientError> { pub fn new(config: ClientConfig, spec: Spec, path: &Path, miner: Arc<Miner>, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, ClientError> {
Client::<CanonVerifier>::new_with_verifier(config, spec, path, message_channel) Client::<CanonVerifier>::new_with_verifier(config, spec, path, miner, message_channel)
} }
} }
@ -126,7 +128,14 @@ pub fn append_path(path: &Path, item: &str) -> String {
impl<V> Client<V> where V: Verifier { impl<V> Client<V> where V: Verifier {
/// Create a new client with given spec and DB path and custom 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<NetSyncMessage> ) -> Result<Arc<Client<V>>, ClientError> { pub fn new_with_verifier(
config: ClientConfig,
spec: Spec,
path: &Path,
miner: Arc<Miner>,
message_channel: IoChannel<NetSyncMessage>)
-> Result<Arc<Client<V>>, ClientError>
{
let path = get_db_path(path, config.pruning, spec.genesis_header().hash()); let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
let gb = spec.genesis_block(); let gb = spec.genesis_block();
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path)); let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
@ -155,6 +164,7 @@ impl<V> Client<V> where V: Verifier {
panic_handler: panic_handler, panic_handler: panic_handler,
verifier: PhantomData, verifier: PhantomData,
vm_factory: Arc::new(EvmFactory::new(config.vm_type)), vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
miner: miner,
}; };
Ok(Arc::new(client)) Ok(Arc::new(client))
@ -328,6 +338,11 @@ impl<V> Client<V> where V: Verifier {
{ {
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() { if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
let (enacted, retracted) = self.calculate_enacted_retracted(import_results); 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 { io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
imported: imported_blocks, imported: imported_blocks,
invalid: invalid_blocks, invalid: invalid_blocks,
@ -339,7 +354,7 @@ impl<V> Client<V> where V: Verifier {
{ {
if self.chain_info().best_block_hash != original_best { 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<V> Client<V> where V: Verifier {
TransactionID::Hash(ref hash) => self.chain.transaction_address(hash), TransactionID::Hash(ref hash) => self.chain.transaction_address(hash),
TransactionID::Location(id, index) => Self::block_hash(&self.chain, id).map(|hash| TransactionAddress { TransactionID::Location(id, index) => Self::block_hash(&self.chain, id).map(|hash| TransactionAddress {
block_hash: hash, block_hash: hash,
index: index index: index,
}) })
} }
} }
} }
impl<V> BlockChainClient for Client<V> where V: Verifier { impl<V> BlockChainClient for Client<V> where V: Verifier {
fn call(&self, t: &SignedTransaction) -> Result<Executed, ExecutionError> { fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let header = self.block_header(BlockID::Latest).unwrap(); let header = self.block_header(BlockID::Latest).unwrap();
let view = HeaderView::new(&header); let view = HeaderView::new(&header);
let last_hashes = self.build_last_hashes(view.hash()); let last_hashes = self.build_last_hashes(view.hash());
@ -441,88 +456,27 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
ExecutionError::TransactionMalformed(message) ExecutionError::TransactionMalformed(message)
})); }));
let balance = state.balance(&sender); let balance = state.balance(&sender);
// give the sender max balance let needed_balance = t.value + t.gas * t.gas_price;
state.sub_balance(&sender, &balance); if balance < needed_balance {
state.add_balance(&sender, &U256::max_value()); // give the sender a sufficient balance
let options = TransactOptions { tracing: false, check_nonce: false }; state.add_balance(&sender, &(needed_balance - balance));
Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options)
} }
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 [todr] Should be moved to miner crate eventually. // TODO gav move this into Executive.
fn try_seal(&self, block: LockedBlock, seal: Vec<Bytes>) -> Result<SealedBlock, LockedBlock> { if analytics.state_diffing {
block.try_seal(self.engine.deref().deref(), seal) if let Ok(ref mut x) = ret {
x.state_diff = Some(state.diff_from(self.state()));
}
}
ret
} }
fn vm_factory(&self) -> &EvmFactory { fn vm_factory(&self) -> &EvmFactory {
&self.vm_factory &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<SignedTransaction>)
-> (Option<ClosedBlock>, HashSet<H256>) {
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<Bytes> { fn block_header(&self, id: BlockID) -> Option<Bytes> {
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())) 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<V> BlockChainClient for Client<V> where V: Verifier {
self.state_at(id).map(|s| s.nonce(address)) self.state_at(id).map(|s| s.nonce(address))
} }
fn block_hash(&self, id: BlockID) -> Option<H256> { fn block_hash(&self, id: BlockID) -> Option<H256> {
Self::block_hash(&self.chain, id) Self::block_hash(&self.chain, id)
} }
@ -774,6 +727,89 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
fn last_hashes(&self) -> LastHashes { fn last_hashes(&self) -> LastHashes {
self.build_last_hashes(self.chain.best_block_hash()) self.build_last_hashes(self.chain.best_block_hash())
} }
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, Error>> {
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<SignedTransaction> {
self.miner.all_transactions()
}
}
impl<V> MiningBlockChainClient for Client<V> where V: Verifier {
fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec<SignedTransaction>)
-> (Option<ClosedBlock>, HashSet<H256>) {
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<Bytes>) -> Result<SealedBlock, LockedBlock> {
block.try_seal(self.engine.deref().deref(), seal)
}
} }
impl MayPanic for Client { impl MayPanic for Client {

View File

@ -46,6 +46,17 @@ use error::{ImportResult, ExecutionError};
use receipt::LocalizedReceipt; use receipt::LocalizedReceipt;
use trace::LocalizedTrace; use trace::LocalizedTrace;
use evm::Factory as EvmFactory; 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. /// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send { pub trait BlockChainClient : Sync + Send {
@ -154,17 +165,9 @@ pub trait BlockChainClient : Sync + Send {
/// Returns logs matching given filter. /// Returns logs matching given filter.
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>; fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
// 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<SignedTransaction>)
-> (Option<ClosedBlock>, HashSet<H256>);
// 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<Bytes>) -> Result<SealedBlock, LockedBlock>;
/// Makes a non-persistent transaction call. /// Makes a non-persistent transaction call.
fn call(&self, t: &SignedTransaction) -> Result<Executed, ExecutionError>; // TODO: should be able to accept blockchain location for call.
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>;
/// Returns EvmFactory. /// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory; fn vm_factory(&self) -> &EvmFactory;
@ -183,5 +186,20 @@ pub trait BlockChainClient : Sync + Send {
/// Get last hashes starting from best block. /// Get last hashes starting from best block.
fn last_hashes(&self) -> LastHashes; fn last_hashes(&self) -> LastHashes;
/// import transactions from network/other 3rd party
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, EthError>>;
/// list all transactions
fn all_transactions(&self) -> Vec<SignedTransaction>;
} }
/// 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<Bytes>) -> Result<SealedBlock, LockedBlock>;
/// Returns ClosedBlock prepared for sealing.
fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec<SignedTransaction>)
-> (Option<ClosedBlock>, HashSet<H256>);
}

View File

@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
use util::*; use util::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute; 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 header::{Header as BlockHeader, BlockNumber};
use filter::Filter; use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
@ -28,6 +28,7 @@ use receipt::{Receipt, LocalizedReceipt};
use blockchain::extras::BlockReceipts; use blockchain::extras::BlockReceipts;
use error::{ImportResult}; use error::{ImportResult};
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
use miner::{Miner, MinerService};
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::{SealedBlock, ClosedBlock, LockedBlock}; use block::{SealedBlock, ClosedBlock, LockedBlock};
@ -35,6 +36,9 @@ use executive::Executed;
use error::{ExecutionError}; use error::{ExecutionError};
use trace::LocalizedTrace; use trace::LocalizedTrace;
use miner::{TransactionImportResult, AccountDetails};
use error::Error as EthError;
/// Test client. /// Test client.
pub struct TestBlockChainClient { pub struct TestBlockChainClient {
/// Blocks. /// Blocks.
@ -61,6 +65,8 @@ pub struct TestBlockChainClient {
pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>, pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>,
/// Block queue size. /// Block queue size.
pub queue_size: AtomicUsize, pub queue_size: AtomicUsize,
/// Miner
pub miner: Arc<Miner>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -99,6 +105,7 @@ impl TestBlockChainClient {
execution_result: RwLock::new(None), execution_result: RwLock::new(None),
receipts: RwLock::new(HashMap::new()), receipts: RwLock::new(HashMap::new()),
queue_size: AtomicUsize::new(0), queue_size: AtomicUsize::new(0),
miner: Arc::new(Miner::default()),
}; };
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
client.genesis_hash = client.last_hash.read().unwrap().clone(); 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<Bytes>) -> Result<SealedBlock, LockedBlock> {
Err(block)
}
fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec<SignedTransaction>) -> (Option<ClosedBlock>, HashSet<H256>) {
(None, HashSet::new())
}
}
impl BlockChainClient for TestBlockChainClient { impl BlockChainClient for TestBlockChainClient {
fn call(&self, _t: &SignedTransaction) -> Result<Executed, ExecutionError> { fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
Ok(self.execution_result.read().unwrap().clone().unwrap()) Ok(self.execution_result.read().unwrap().clone().unwrap())
} }
@ -296,14 +314,6 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!(); unimplemented!();
} }
fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec<SignedTransaction>) -> (Option<ClosedBlock>, HashSet<H256>) {
(None, HashSet::new())
}
fn try_seal(&self, block: LockedBlock, _seal: Vec<Bytes>) -> Result<SealedBlock, LockedBlock> {
Err(block)
}
fn block_header(&self, id: BlockID) -> Option<Bytes> { fn block_header(&self, id: BlockID) -> Option<Bytes> {
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec())) 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<Vec<LocalizedTrace>> { fn block_traces(&self, _trace: BlockID) -> Option<Vec<LocalizedTrace>> {
unimplemented!(); unimplemented!();
} }
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, EthError>> {
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<SignedTransaction> {
self.miner.all_transactions()
}
} }

View File

@ -63,13 +63,43 @@ pub enum Error {
Internal, Internal,
} }
/// Evm result. /// A specialized version of Result over EVM errors.
/// pub type Result<T> = ::std::result::Result<T, Error>;
/// Returns `gas_left` if execution is successful, otherwise error.
pub type Result = result::Result<U256, Error>;
/// 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<GasLeft<'a>>`.
pub trait Finalize {
/// Consume the externalities, call return if necessary, and produce a final amount of gas left.
fn finalize<E: Ext>(self, ext: E) -> Result<U256>;
}
impl<'a> Finalize for Result<GasLeft<'a>> {
fn finalize<E: Ext>(self, ext: E) -> Result<U256> {
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 { pub trait Evm {
/// This function should be used to execute transaction. /// 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<GasLeft>;
} }

View File

@ -17,7 +17,7 @@
//! Interface for Evm externalities. //! Interface for Evm externalities.
use util::common::*; use util::common::*;
use evm::{Schedule, Error}; use evm::{self, Schedule};
use env_info::*; use env_info::*;
/// Result of externalities create function. /// Result of externalities create function.
@ -85,7 +85,7 @@ pub trait Ext {
/// Should be called when transaction calls `RETURN` opcode. /// Should be called when transaction calls `RETURN` opcode.
/// Returns gas_left if cost of returning the data is not too high. /// Returns gas_left if cost of returning the data is not too high.
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, Error>; fn ret(self, gas: &U256, data: &[u8]) -> evm::Result<U256> where Self: Sized;
/// Should be called when contract commits suicide. /// Should be called when contract commits suicide.
/// Address to which funds should be refunded. /// Address to which funds should be refunded.
@ -105,4 +105,10 @@ pub trait Ext {
/// Increments sstore refunds count by 1. /// Increments sstore refunds count by 1.
fn inc_sstore_clears(&mut self); 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)>) {}
} }

View File

@ -89,10 +89,10 @@ impl Factory {
pub fn create(&self) -> Box<Evm> { pub fn create(&self) -> Box<Evm> {
match self.evm { match self.evm {
VMType::Jit => { VMType::Jit => {
Box::new(super::jit::JitEvm) Box::new(super::jit::JitEvm::default())
}, },
VMType::Interpreter => { VMType::Interpreter => {
Box::new(super::interpreter::Interpreter) Box::new(super::interpreter::Interpreter::default())
} }
} }
} }
@ -102,7 +102,7 @@ impl Factory {
pub fn create(&self) -> Box<Evm> { pub fn create(&self) -> Box<Evm> {
match self.evm { match self.evm {
VMType::Interpreter => { VMType::Interpreter => {
Box::new(super::interpreter::Interpreter) Box::new(super::interpreter::Interpreter::default())
} }
} }
} }

View File

@ -124,6 +124,7 @@ pub struct InstructionInfo {
pub side_effects: bool, pub side_effects: bool,
pub tier: GasPriceTier pub tier: GasPriceTier
} }
impl InstructionInfo { impl InstructionInfo {
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo { pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo {
InstructionInfo { InstructionInfo {
@ -139,7 +140,7 @@ impl InstructionInfo {
#[cfg_attr(rustfmt, rustfmt_skip)] #[cfg_attr(rustfmt, rustfmt_skip)]
/// Return details about specific instruction /// Return details about specific instruction
pub fn get_info (instruction: Instruction) -> InstructionInfo { pub fn get_info(instruction: Instruction) -> InstructionInfo {
match instruction { match instruction {
STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero), STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero),
ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow), ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow),

View File

@ -17,10 +17,11 @@
///! Rust VM implementation ///! Rust VM implementation
use common::*; use common::*;
use trace::VMTracer;
use super::instructions as instructions; use super::instructions as instructions;
use super::instructions::Instruction; use super::instructions::{Instruction, get_info};
use std::marker::Copy; use std::marker::Copy;
use evm::{self, MessageCallResult, ContractCreateResult}; use evm::{self, MessageCallResult, ContractCreateResult, GasLeft};
#[cfg(not(feature = "evm-debug"))] #[cfg(not(feature = "evm-debug"))]
macro_rules! evm_debug { macro_rules! evm_debug {
@ -69,6 +70,8 @@ trait Stack<T> {
fn push(&mut self, elem: T); fn push(&mut self, elem: T);
/// Get number of elements on Stack /// Get number of elements on Stack
fn size(&self) -> usize; fn size(&self) -> usize;
/// Returns all data on stack.
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
} }
struct VecStack<S> { struct VecStack<S> {
@ -131,6 +134,11 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
fn size(&self) -> usize { fn size(&self) -> usize {
self.stack.len() 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 { trait Memory {
@ -271,21 +279,26 @@ enum InstructionResult {
GasLeft(U256), GasLeft(U256),
UnusedGas(U256), UnusedGas(U256),
JumpToPosition(U256), JumpToPosition(U256),
StopExecutionWithGasLeft(U256), // gas left, init_orf, init_size
StopExecution StopExecutionNeedsReturn(U256, U256, U256),
StopExecution,
} }
/// Intepreter EVM implementation /// Intepreter EVM implementation
pub struct Interpreter; #[derive(Default)]
pub struct Interpreter {
mem: Vec<u8>,
}
impl evm::Evm for Interpreter { 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<GasLeft> {
self.mem.clear();
let code = &params.code.as_ref().unwrap(); let code = &params.code.as_ref().unwrap();
let valid_jump_destinations = self.find_jump_destinations(&code); let valid_jump_destinations = self.find_jump_destinations(&code);
let mut current_gas = params.gas; let mut current_gas = params.gas;
let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero()); let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero());
let mut mem = vec![];
let mut reader = CodeReader { let mut reader = CodeReader {
position: 0, position: 0,
code: &code code: &code
@ -293,12 +306,17 @@ impl evm::Evm for Interpreter {
while reader.position < code.len() { while reader.position < code.len() {
let instruction = code[reader.position]; let instruction = code[reader.position];
reader.position += 1;
// Calculate gas cost // 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(&current_gas, &gas_cost)); try!(self.verify_gas(&current_gas, &gas_cost));
mem.expand(mem_size); self.mem.expand(mem_size);
current_gas = current_gas - gas_cost; //TODO: use operator -= current_gas = current_gas - gas_cost; //TODO: use operator -=
evm_debug!({ evm_debug!({
@ -311,11 +329,20 @@ 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 // Execute instruction
let result = try!(self.exec_instruction( let result = try!(self.exec_instruction(
current_gas, &params, ext, instruction, &mut reader, &mut mem, &mut stack current_gas, &params, 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 // Advance
match result { match result {
InstructionResult::Ok => {}, InstructionResult::Ok => {},
@ -332,29 +359,25 @@ impl evm::Evm for Interpreter {
let pos = try!(self.verify_jump(position, &valid_jump_destinations)); let pos = try!(self.verify_jump(position, &valid_jump_destinations));
reader.position = pos; reader.position = pos;
}, },
InstructionResult::StopExecutionWithGasLeft(gas_left) => { InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
current_gas = gas_left; return Ok(GasLeft::NeedsReturn(gas, self.mem.read_slice(off, size)));
reader.position = code.len();
}, },
InstructionResult::StopExecution => { InstructionResult::StopExecution => break,
reader.position = code.len();
}
} }
} }
Ok(current_gas) Ok(GasLeft::Known(current_gas))
} }
} }
impl Interpreter { impl Interpreter {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn get_gas_cost_mem( fn get_gas_cost_mem(
&self, &mut self,
ext: &evm::Ext, ext: &evm::Ext,
instruction: Instruction, instruction: Instruction,
mem: &mut Memory,
stack: &Stack<U256> stack: &Stack<U256>
) -> Result<(U256, usize), evm::Error> { ) -> evm::Result<(U256, usize)> {
let schedule = ext.schedule(); let schedule = ext.schedule();
let info = instructions::get_info(instruction); let info = instructions::get_info(instruction);
@ -470,12 +493,12 @@ impl Interpreter {
Ok((gas, 0)) Ok((gas, 0))
}, },
InstructionCost::GasMem(gas, mem_size) => { 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)); let gas = overflowing!(gas.overflowing_add(mem_gas));
Ok((gas, new_mem_size)) Ok((gas, new_mem_size))
}, },
InstructionCost::GasMemCopy(gas, mem_size, copy) => { 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(&copy, 31)); let copy = overflowing!(add_u256_usize(&copy, 31));
let copy_gas = U256::from(schedule.copy_gas) * (copy / U256::from(32)); let copy_gas = U256::from(schedule.copy_gas) * (copy / U256::from(32));
let gas = overflowing!(gas.overflowing_add(copy_gas)); 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<U256>
) -> 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<U256>
) -> 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 gas_for_mem = |mem_size: U256| {
let s = mem_size >> 5; let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div // s * memory_gas + s * s / quad_coeff_div
@ -510,11 +558,11 @@ impl Interpreter {
}, req_mem_size_rounded.low_u64() as usize)) }, req_mem_size_rounded.low_u64() as usize))
} }
fn mem_needed_const(&self, mem: &U256, add: usize) -> Result<U256, evm::Error> { fn mem_needed_const(&self, mem: &U256, add: usize) -> evm::Result<U256> {
Ok(overflowing!(mem.overflowing_add(U256::from(add)))) Ok(overflowing!(mem.overflowing_add(U256::from(add))))
} }
fn mem_needed(&self, offset: &U256, size: &U256) -> Result<U256, ::evm::Error> { fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result<U256> {
if self.is_zero(size) { if self.is_zero(size) {
return Ok(U256::zero()); return Ok(U256::zero());
} }
@ -524,15 +572,14 @@ impl Interpreter {
#[cfg_attr(feature="dev", allow(too_many_arguments))] #[cfg_attr(feature="dev", allow(too_many_arguments))]
fn exec_instruction( fn exec_instruction(
&self, &mut self,
gas: Gas, gas: Gas,
params: &ActionParams, params: &ActionParams,
ext: &mut evm::Ext, ext: &mut evm::Ext,
instruction: Instruction, instruction: Instruction,
code: &mut CodeReader, code: &mut CodeReader,
mem: &mut Memory,
stack: &mut Stack<U256> stack: &mut Stack<U256>
) -> Result<InstructionResult, evm::Error> { ) -> evm::Result<InstructionResult> {
match instruction { match instruction {
instructions::JUMP => { instructions::JUMP => {
let jump = stack.pop_back(); let jump = stack.pop_back();
@ -557,7 +604,7 @@ impl Interpreter {
let init_off = stack.pop_back(); let init_off = stack.pop_back();
let init_size = 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(&params.address) >= endowment && ext.depth() < ext.schedule().max_depth; let can_create = ext.balance(&params.address) >= endowment && ext.depth() < ext.schedule().max_depth;
if !can_create { if !can_create {
@ -624,8 +671,8 @@ impl Interpreter {
let call_result = { let call_result = {
// we need to write and read from memory in the same time // we need to write and read from memory in the same time
// and we don't want to copy // and we don't want to copy
let input = unsafe { ::std::mem::transmute(mem.read_slice(in_off, in_size)) }; let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
let output = mem.writeable_slice(out_off, out_size); let output = self.mem.writeable_slice(out_off, out_size);
ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output) ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output)
}; };
@ -643,11 +690,8 @@ impl Interpreter {
instructions::RETURN => { instructions::RETURN => {
let init_off = stack.pop_back(); let init_off = stack.pop_back();
let init_size = 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::StopExecutionNeedsReturn(gas, init_off, init_size))
return Ok(InstructionResult::StopExecutionWithGasLeft(
gas_left
));
}, },
instructions::STOP => { instructions::STOP => {
return Ok(InstructionResult::StopExecution); return Ok(InstructionResult::StopExecution);
@ -666,7 +710,7 @@ impl Interpreter {
.iter() .iter()
.map(H256::from) .map(H256::from)
.collect(); .collect();
ext.log(topics, mem.read_slice(offset, size)); ext.log(topics, self.mem.read_slice(offset, size));
}, },
instructions::PUSH1...instructions::PUSH32 => { instructions::PUSH1...instructions::PUSH32 => {
let bytes = instructions::get_push_bytes(instruction); let bytes = instructions::get_push_bytes(instruction);
@ -674,26 +718,26 @@ impl Interpreter {
stack.push(val); stack.push(val);
}, },
instructions::MLOAD => { instructions::MLOAD => {
let word = mem.read(stack.pop_back()); let word = self.mem.read(stack.pop_back());
stack.push(U256::from(word)); stack.push(U256::from(word));
}, },
instructions::MSTORE => { instructions::MSTORE => {
let offset = stack.pop_back(); let offset = stack.pop_back();
let word = stack.pop_back(); let word = stack.pop_back();
mem.write(offset, word); Memory::write(&mut self.mem, offset, word);
}, },
instructions::MSTORE8 => { instructions::MSTORE8 => {
let offset = stack.pop_back(); let offset = stack.pop_back();
let byte = stack.pop_back(); let byte = stack.pop_back();
mem.write_byte(offset, byte); self.mem.write_byte(offset, byte);
}, },
instructions::MSIZE => { instructions::MSIZE => {
stack.push(U256::from(mem.size())); stack.push(U256::from(self.mem.size()));
}, },
instructions::SHA3 => { instructions::SHA3 => {
let offset = stack.pop_back(); let offset = stack.pop_back();
let size = 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())); stack.push(U256::from(sha3.as_slice()));
}, },
instructions::SLOAD => { instructions::SLOAD => {
@ -766,15 +810,15 @@ impl Interpreter {
stack.push(U256::from(len)); stack.push(U256::from(len));
}, },
instructions::CALLDATACOPY => { instructions::CALLDATACOPY => {
self.copy_data_to_memory(mem, stack, &params.data.clone().unwrap_or_else(|| vec![])); self.copy_data_to_memory(stack, &params.data.clone().unwrap_or_else(|| vec![]));
}, },
instructions::CODECOPY => { instructions::CODECOPY => {
self.copy_data_to_memory(mem, stack, &params.code.clone().unwrap_or_else(|| vec![])); self.copy_data_to_memory(stack, &params.code.clone().unwrap_or_else(|| vec![]));
}, },
instructions::EXTCODECOPY => { instructions::EXTCODECOPY => {
let address = u256_to_address(&stack.pop_back()); let address = u256_to_address(&stack.pop_back());
let code = ext.extcode(&address); let code = ext.extcode(&address);
self.copy_data_to_memory(mem, stack, &code); self.copy_data_to_memory(stack, &code);
}, },
instructions::GASPRICE => { instructions::GASPRICE => {
stack.push(params.gas_price.clone()); stack.push(params.gas_price.clone());
@ -806,7 +850,7 @@ impl Interpreter {
Ok(InstructionResult::Ok) Ok(InstructionResult::Ok)
} }
fn copy_data_to_memory(&self, mem: &mut Memory, stack: &mut Stack<U256>, data: &[u8]) { fn copy_data_to_memory(&mut self, stack: &mut Stack<U256>, data: &[u8]) {
let dest_offset = stack.pop_back(); let dest_offset = stack.pop_back();
let source_offset = stack.pop_back(); let source_offset = stack.pop_back();
let size = 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 { let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
true => { true => {
let zero_slice = if source_offset > source_size { let zero_slice = if source_offset > source_size {
mem.writeable_slice(dest_offset, size) self.mem.writeable_slice(dest_offset, size)
} else { } 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() { for i in zero_slice.iter_mut() {
*i = 0; *i = 0;
@ -829,14 +873,16 @@ impl Interpreter {
if source_offset < source_size { if source_offset < source_size {
let output_begin = source_offset.low_u64() as usize; 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, fn verify_instructions_requirements(
&self,
info: &instructions::InstructionInfo, info: &instructions::InstructionInfo,
stack_limit: usize, stack_limit: usize,
stack: &Stack<U256>) -> Result<(), evm::Error> { stack: &Stack<U256>
) -> evm::Result<()> {
if !stack.has(info.args) { if !stack.has(info.args) {
Err(evm::Error::StackUnderflow { Err(evm::Error::StackUnderflow {
instruction: info.name, 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 { match current_gas < gas_cost {
true => Err(evm::Error::OutOfGas), true => Err(evm::Error::OutOfGas),
false => Ok(()) false => Ok(())
} }
} }
fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet<usize>) -> Result<usize, evm::Error> { fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet<usize>) -> evm::Result<usize> {
let jump = jump_u.low_u64() as usize; let jump = jump_u.low_u64() as usize;
if valid_jump_destinations.contains(&jump) && jump_u < U256::from(!0 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<U256>) -> Result<(), evm::Error> { fn exec_stack_instruction(&self, instruction: Instruction, stack: &mut Stack<U256>) -> evm::Result<()> {
match instruction { match instruction {
instructions::DUP1...instructions::DUP16 => { instructions::DUP1...instructions::DUP16 => {
let position = instructions::get_dup_position(instruction); let position = instructions::get_dup_position(instruction);
@ -1136,7 +1182,7 @@ fn address_to_u256(value: Address) -> U256 {
#[test] #[test]
fn test_mem_gas_cost() { fn test_mem_gas_cost() {
// given // given
let interpreter = Interpreter; let interpreter = Interpreter::default();
let schedule = evm::Schedule::default(); let schedule = evm::Schedule::default();
let current_mem_size = 5; let current_mem_size = 5;
let mem_size = !U256::zero(); let mem_size = !U256::zero();
@ -1159,7 +1205,7 @@ mod tests {
#[test] #[test]
fn test_find_jump_destinations() { fn test_find_jump_destinations() {
// given // given
let interpreter = Interpreter; let interpreter = Interpreter::default();
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap(); let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
// when // when
@ -1172,7 +1218,7 @@ mod tests {
#[test] #[test]
fn test_calculate_mem_cost() { fn test_calculate_mem_cost() {
// given // given
let interpreter = Interpreter; let interpreter = Interpreter::default();
let schedule = evm::Schedule::default(); let schedule = evm::Schedule::default();
let current_mem_size = 0; let current_mem_size = 0;
let mem_size = U256::from(5); let mem_size = U256::from(5);

View File

@ -16,8 +16,9 @@
//! Just in time compiler execution environment. //! Just in time compiler execution environment.
use common::*; use common::*;
use trace::VMTracer;
use evmjit; use evmjit;
use evm; use evm::{self, Error, GasLeft};
/// Should be used to convert jit types to ethcore /// Should be used to convert jit types to ethcore
trait FromJit<T>: Sized { trait FromJit<T>: Sized {
@ -300,10 +301,13 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
} }
} }
pub struct JitEvm; #[derive(Default)]
pub struct JitEvm {
ctxt: Option<evmjit::ContextHandle>,
}
impl evm::Evm for JitEvm { 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<GasLeft> {
// Dirty hack. This is unsafe, but we interact with ffi, so it's justified. // 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 ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext, params.address.clone())) };
let mut ext_handle = evmjit::ExtHandle::new(ext_adapter); 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.. // don't really know why jit timestamp is int..
data.timestamp = ext.env_info().timestamp as i64; 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(); let res = context.exec();
match res { match res {
evmjit::ReturnCode::Stop => Ok(U256::from(context.gas_left())), evmjit::ReturnCode::Stop => Ok(GasLeft::Known(U256::from(context.gas_left()))),
evmjit::ReturnCode::Return => ext.ret(&U256::from(context.gas_left()), context.output_data()), evmjit::ReturnCode::Return =>
Ok(GasLeft::NeedsReturn(U256::from(context.gas_left()), context.output_data())),
evmjit::ReturnCode::Suicide => { evmjit::ReturnCode::Suicide => {
ext.suicide(&Address::from_jit(&context.suicide_refund_address())); 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), evmjit::ReturnCode::OutOfGas => Err(evm::Error::OutOfGas),
_err => Err(evm::Error::Internal) _err => Err(evm::Error::Internal)

View File

@ -29,7 +29,8 @@ mod jit;
#[cfg(test)] #[cfg(test)]
mod tests; 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::ext::{Ext, ContractCreateResult, MessageCallResult};
pub use self::factory::{Factory, VMType}; pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule; pub use self::schedule::Schedule;
pub use self::instructions::get_info;

View File

@ -15,8 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use common::*; use common::*;
use evm; use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
use evm::{Ext, Schedule, Factory, VMType, ContractCreateResult, MessageCallResult};
use std::fmt::Debug; use std::fmt::Debug;
struct FakeLogEntry { struct FakeLogEntry {
@ -58,6 +57,15 @@ struct FakeExt {
calls: HashSet<FakeCall>, calls: HashSet<FakeCall>,
} }
// similar to the normal `finalize` function, but ignoring NeedsReturn.
fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
match res {
Ok(GasLeft::Known(gas)) => Ok(gas),
Ok(GasLeft::NeedsReturn(_, _)) => unimplemented!(), // since ret is unimplemented.
Err(e) => Err(e),
}
}
impl FakeExt { impl FakeExt {
fn new() -> Self { fn new() -> Self {
FakeExt::default() FakeExt::default()
@ -136,7 +144,7 @@ impl Ext for FakeExt {
}); });
} }
fn ret(&mut self, _gas: &U256, _data: &[u8]) -> result::Result<U256, evm::Error> { fn ret(self, _gas: &U256, _data: &[u8]) -> evm::Result<U256> {
unimplemented!(); unimplemented!();
} }
@ -173,8 +181,8 @@ fn test_stack_underflow() {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let err = { let err = {
let vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter); let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::default());
vm.exec(params, &mut ext).unwrap_err() test_finalize(vm.exec(params, &mut ext)).unwrap_err()
}; };
match err { match err {
@ -200,8 +208,8 @@ fn test_add(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_988)); assert_eq!(gas_left, U256::from(79_988));
@ -220,8 +228,8 @@ fn test_sha3(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_961)); assert_eq!(gas_left, U256::from(79_961));
@ -240,8 +248,8 @@ fn test_address(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -262,8 +270,8 @@ fn test_origin(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -284,8 +292,8 @@ fn test_sender(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -319,8 +327,8 @@ fn test_extcodecopy(factory: super::Factory) {
ext.codes.insert(sender, sender_code); ext.codes.insert(sender, sender_code);
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_935)); 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 mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(99_619)); 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 mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(98_974)); 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()); ext.blockhashes.insert(U256::zero(), blockhash.clone());
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_974)); assert_eq!(gas_left, U256::from(79_974));
@ -418,8 +426,8 @@ fn test_calldataload(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_991)); assert_eq!(gas_left, U256::from(79_991));
@ -439,8 +447,8 @@ fn test_author(factory: super::Factory) {
ext.info.author = author; ext.info.author = author;
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -459,8 +467,8 @@ fn test_timestamp(factory: super::Factory) {
ext.info.timestamp = timestamp; ext.info.timestamp = timestamp;
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -479,8 +487,8 @@ fn test_number(factory: super::Factory) {
ext.info.number = number; ext.info.number = number;
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -499,8 +507,8 @@ fn test_difficulty(factory: super::Factory) {
ext.info.difficulty = difficulty; ext.info.difficulty = difficulty;
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); 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; ext.info.gas_limit = gas_limit;
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -537,8 +545,8 @@ fn test_mul(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "000000000000000000000000000000000000000000000000734349397b853383"); assert_store(&ext, 0, "000000000000000000000000000000000000000000000000734349397b853383");
@ -555,8 +563,8 @@ fn test_sub(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000012364ad0302"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000012364ad0302");
@ -573,8 +581,8 @@ fn test_div(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac"); assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac");
@ -591,8 +599,8 @@ fn test_div_zero(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@ -609,8 +617,8 @@ fn test_mod(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b");
@ -628,8 +636,8 @@ fn test_smod(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b");
@ -647,8 +655,8 @@ fn test_sdiv(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac"); assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac");
@ -666,8 +674,8 @@ fn test_exp(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "90fd23767b60204c3d6fc8aec9e70a42a3f127140879c133a20129a597ed0c59"); assert_store(&ext, 0, "90fd23767b60204c3d6fc8aec9e70a42a3f127140879c133a20129a597ed0c59");
@ -686,8 +694,8 @@ fn test_comparison(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@ -707,8 +715,8 @@ fn test_signed_comparison(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@ -728,8 +736,8 @@ fn test_bitops(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0"); assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0");
@ -751,8 +759,8 @@ fn test_addmod_mulmod(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000001"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000001");
@ -772,8 +780,8 @@ fn test_byte(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@ -791,8 +799,8 @@ fn test_signextend(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000fff"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000fff");
@ -811,8 +819,8 @@ fn test_badinstruction_int() {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let err = { let err = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap_err() test_finalize(vm.exec(params, &mut ext)).unwrap_err()
}; };
match err { match err {
@ -831,8 +839,8 @@ fn test_pop(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0"); assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0");
@ -851,8 +859,8 @@ fn test_extops(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000004"); // PC / CALLDATASIZE assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000004"); // PC / CALLDATASIZE
@ -874,8 +882,8 @@ fn test_jumps(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_eq!(ext.sstore_clears, 1); assert_eq!(ext.sstore_clears, 1);
@ -903,8 +911,8 @@ fn test_calls(factory: super::Factory) {
}; };
let gas_left = { let gas_left = {
let vm = factory.create(); let mut vm = factory.create();
vm.exec(params, &mut ext).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
assert_set_contains(&ext.calls, &FakeCall { assert_set_contains(&ext.calls, &FakeCall {

View File

@ -18,12 +18,11 @@
use common::*; use common::*;
use state::*; use state::*;
use engine::*; use engine::*;
use evm::{self, Ext, Factory}; use evm::{self, Ext, Factory, Finalize};
use externalities::*; use externalities::*;
use substate::*; use substate::*;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use crossbeam; use crossbeam;
pub use types::executed::{Executed, ExecutionResult}; pub use types::executed::{Executed, ExecutionResult};
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM) /// 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 { pub struct TransactOptions {
/// Enable call tracing. /// Enable call tracing.
pub tracing: bool, pub tracing: bool,
/// Enable VM tracing.
pub vm_tracing: bool,
/// Check transaction nonce before execution. /// Check transaction nonce before execution.
pub check_nonce: bool, pub check_nonce: bool,
} }
@ -80,21 +81,40 @@ impl<'a> Executive<'a> {
} }
/// Creates `Externalities` from `Executive`. /// 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 { pub fn as_externalities<'_, T, V>(
Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer) &'_ 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. /// This function should be used to execute transaction.
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> { pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
let check = options.check_nonce; let check = options.check_nonce;
match options.tracing { match options.tracing {
true => self.transact_with_tracer(t, check, ExecutiveTracer::default()), true => match options.vm_tracing {
false => self.transact_with_tracer(t, check, NoopTracer), 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 /// Execute transaction/call with tracing enabled
pub fn transact_with_tracer<T>(&'a mut self, t: &SignedTransaction, check_nonce: bool, mut tracer: T) -> Result<Executed, ExecutionError> where T: Tracer { pub fn transact_with_tracer<T, V>(
&'a mut self,
t: &SignedTransaction,
check_nonce: bool,
mut tracer: T,
mut vm_tracer: V
) -> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer {
let sender = try!(t.sender().map_err(|e| { let sender = try!(t.sender().map_err(|e| {
let message = format!("Transaction malformed: {:?}", e); let message = format!("Transaction malformed: {:?}", e);
ExecutionError::TransactionMalformed(message) ExecutionError::TransactionMalformed(message)
@ -154,7 +174,7 @@ impl<'a> Executive<'a> {
code: Some(t.data.clone()), code: Some(t.data.clone()),
data: None, 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) => { Action::Call(ref address) => {
let params = ActionParams { let params = ActionParams {
@ -170,22 +190,28 @@ impl<'a> Executive<'a> {
}; };
// TODO: move output upstream // TODO: move output upstream
let mut out = vec![]; 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! // 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<T>(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy, tracer: &mut T) fn exec_vm<T, V>(
-> evm::Result where T: Tracer { &mut self,
params: ActionParams,
unconfirmed_substate: &mut Substate,
output_policy: OutputPolicy,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
// Ordinary execution - keep VM in same thread // Ordinary execution - keep VM in same thread
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 { if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
let vm_factory = self.vm_factory; let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer); let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); 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 // Start in new thread to reset stack
@ -193,10 +219,10 @@ impl<'a> Executive<'a> {
// https://github.com/aturon/crossbeam/issues/16 // https://github.com/aturon/crossbeam/issues/16
crossbeam::scope(|scope| { crossbeam::scope(|scope| {
let vm_factory = self.vm_factory; let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer); let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
scope.spawn(move || { scope.spawn(move || {
vm_factory.create().exec(params, &mut ext) vm_factory.create().exec(params, &mut ext).finalize(ext)
}) })
}).join() }).join()
} }
@ -205,8 +231,14 @@ impl<'a> Executive<'a> {
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate and the output. /// Modifies the substate and the output.
/// Returns either gas_left or `evm::Error`. /// Returns either gas_left or `evm::Error`.
pub fn call<T>(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef, tracer: &mut T) pub fn call<T, V>(
-> evm::Result where T: Tracer { &mut self,
params: ActionParams,
substate: &mut Substate,
mut output: BytesRef,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
// backup used in case of running out of gas // backup used in case of running out of gas
self.state.snapshot(); self.state.snapshot();
@ -264,16 +296,22 @@ impl<'a> Executive<'a> {
let trace_info = tracer.prepare_trace_call(&params); let trace_info = tracer.prepare_trace_call(&params);
let mut trace_output = tracer.prepare_trace_output(); let mut trace_output = tracer.prepare_trace_output();
let mut subtracer = tracer.subtracer(); let mut subtracer = tracer.subtracer();
let gas = params.gas; let gas = params.gas;
if params.code.is_some() { if params.code.is_some() {
// part of substate that may be reverted // part of substate that may be reverted
let mut unconfirmed_substate = Substate::new(); 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 = { 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); trace!(target: "executive", "res={:?}", res);
let traces = subtracer.traces(); let traces = subtracer.traces();
@ -307,8 +345,13 @@ impl<'a> Executive<'a> {
/// Creates contract with given contract params. /// Creates contract with given contract params.
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate. /// Modifies the substate.
pub fn create<T>(&mut self, params: ActionParams, substate: &mut Substate, tracer: &mut T) -> evm::Result where T: pub fn create<T, V>(
Tracer { &mut self,
params: ActionParams,
substate: &mut Substate,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
// backup used in case of running out of gas // backup used in case of running out of gas
self.state.snapshot(); self.state.snapshot();
@ -330,10 +373,14 @@ impl<'a> Executive<'a> {
let gas = params.gas; let gas = params.gas;
let created = params.address.clone(); let created = params.address.clone();
let mut subvmtracer = vm_tracer.prepare_subtrace(&params.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed"));
let res = { 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 { match res {
Ok(gas_left) => tracer.trace_create( Ok(gas_left) => tracer.trace_create(
trace_info, trace_info,
@ -351,7 +398,15 @@ impl<'a> Executive<'a> {
} }
/// Finalizes the transaction (does refunds and suicides). /// Finalizes the transaction (does refunds and suicides).
fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes, trace: Option<Trace>) -> ExecutionResult { fn finalize(
&mut self,
t: &SignedTransaction,
substate: Substate,
result: evm::Result<U256>,
output: Bytes,
trace: Option<Trace>,
vm_trace: Option<VMTrace>
) -> ExecutionResult {
let schedule = self.engine.schedule(self.info); let schedule = self.engine.schedule(self.info);
// refunds from SSTORE nonzero -> zero // refunds from SSTORE nonzero -> zero
@ -394,6 +449,8 @@ impl<'a> Executive<'a> {
contracts_created: vec![], contracts_created: vec![],
output: output, output: output,
trace: trace, trace: trace,
vm_trace: vm_trace,
state_diff: None,
}) })
}, },
_ => { _ => {
@ -406,12 +463,14 @@ impl<'a> Executive<'a> {
contracts_created: substate.contracts_created, contracts_created: substate.contracts_created,
output: output, output: output,
trace: trace, 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<U256>, substate: &mut Substate, un_substate: Substate) {
match *result { match *result {
Err(evm::Error::OutOfGas) Err(evm::Error::OutOfGas)
| Err(evm::Error::BadJumpDestination {..}) | Err(evm::Error::BadJumpDestination {..})
@ -438,6 +497,7 @@ mod tests {
use tests::helpers::*; use tests::helpers::*;
use trace::trace; use trace::trace;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
#[test] #[test]
fn test_contract_address() { fn test_contract_address() {
@ -466,7 +526,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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)); assert_eq!(gas_left, U256::from(79_975));
@ -525,7 +585,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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)); assert_eq!(gas_left, U256::from(62_976));
@ -542,7 +602,7 @@ mod tests {
// 52 // 52
// 60 1d - push 29 // 60 1d - push 29
// 60 03 - push 3 // 60 03 - push 3
// 60 17 - push 17 // 60 17 - push 23
// f0 - create // f0 - create
// 60 00 - push 0 // 60 00 - push 0
// 55 sstore // 55 sstore
@ -578,13 +638,16 @@ mod tests {
let engine = TestEngine::new(5); let engine = TestEngine::new(5);
let mut substate = Substate::new(); let mut substate = Substate::new();
let mut tracer = ExecutiveTracer::default(); let mut tracer = ExecutiveTracer::default();
let mut vm_tracer = ExecutiveVMTracer::default();
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
let output = BytesRef::Fixed(&mut[0u8;0]); 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 { let expected_trace = vec![ Trace {
depth: 0, depth: 0,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
@ -615,7 +678,39 @@ mod tests {
}] }]
}]; }];
assert_eq!(tracer.traces(), expected_trace); 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} 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 engine = TestEngine::new(5);
let mut substate = Substate::new(); let mut substate = Substate::new();
let mut tracer = ExecutiveTracer::default(); let mut tracer = ExecutiveTracer::default();
let mut vm_tracer = ExecutiveVMTracer::default();
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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 { let expected_trace = vec![Trace {
depth: 0, depth: 0,
action: trace::Action::Create(trace::Create { action: trace::Action::Create(trace::Create {
@ -671,9 +769,23 @@ mod tests {
}), }),
subs: vec![] subs: vec![]
}]; }];
assert_eq!(tracer.traces(), expected_trace); 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} 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 gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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)); assert_eq!(gas_left, U256::from(62_976));
@ -774,7 +886,7 @@ mod tests {
{ {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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); assert_eq!(substate.contracts_created.len(), 1);
@ -835,7 +947,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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)); assert_eq!(gas_left, U256::from(73_237));
@ -880,7 +992,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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)); assert_eq!(gas_left, U256::from(59_870));
@ -913,7 +1025,7 @@ mod tests {
let executed = { let executed = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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() ex.transact(&t, opts).unwrap()
}; };
@ -947,7 +1059,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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) ex.transact(&t, opts)
}; };
@ -979,7 +1091,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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) ex.transact(&t, opts)
}; };
@ -1013,7 +1125,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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) ex.transact(&t, opts)
}; };
@ -1047,7 +1159,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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) ex.transact(&t, opts)
}; };
@ -1082,7 +1194,7 @@ mod tests {
let result = { let result = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); 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 { match result {

View File

@ -21,7 +21,7 @@ use engine::*;
use executive::*; use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
use substate::*; use substate::*;
use trace::Tracer; use trace::{Tracer, VMTracer};
/// Policy for handling output data on `RETURN` opcode. /// Policy for handling output data on `RETURN` opcode.
pub enum OutputPolicy<'a, 'b> { pub enum OutputPolicy<'a, 'b> {
@ -55,7 +55,7 @@ impl OriginInfo {
} }
/// Implementation of evm Externalities. /// 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, state: &'a mut State,
env_info: &'a EnvInfo, env_info: &'a EnvInfo,
engine: &'a Engine, engine: &'a Engine,
@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer {
schedule: Schedule, schedule: Schedule,
output: OutputPolicy<'a, 'a>, output: OutputPolicy<'a, 'a>,
tracer: &'a mut T, 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))] #[cfg_attr(feature="dev", allow(too_many_arguments))]
/// Basic `Externalities` constructor. /// Basic `Externalities` constructor.
pub fn new(state: &'a mut State, 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, substate: &'a mut Substate,
output: OutputPolicy<'a, 'a>, output: OutputPolicy<'a, 'a>,
tracer: &'a mut T, tracer: &'a mut T,
vm_tracer: &'a mut V,
) -> Self { ) -> Self {
Externalities { Externalities {
state: state, state: state,
@ -93,11 +94,12 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
schedule: engine.schedule(env_info), schedule: engine.schedule(env_info),
output: output, output: output,
tracer: tracer, 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 { fn storage_at(&self, key: &H256) -> H256 {
self.state.storage_at(&self.origin_info.address, key) 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); let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth);
// TODO: handle internal error separately // 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) => { Ok(gas_left) => {
self.substate.contracts_created.push(address.clone()); self.substate.contracts_created.push(address.clone());
ContractCreateResult::Created(address, gas_left) 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); 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), Ok(gas_left) => MessageCallResult::Success(gas_left),
_ => MessageCallResult::Failed _ => 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))] #[cfg_attr(feature="dev", allow(match_ref_pats))]
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> { fn ret(mut self, gas: &U256, data: &[u8]) -> evm::Result<U256>
where Self: Sized {
let handle_copy = |to: &mut Option<&mut Bytes>| { let handle_copy = |to: &mut Option<&mut Bytes>| {
to.as_mut().map(|b| **b = data.to_owned()); 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); handle_copy(copy);
let len = cmp::min(slice.len(), data.len()); let len = cmp::min(slice.len(), data.len());
unsafe { (&mut slice[..len]).copy_from_slice(&data[..len]);
ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len);
}
Ok(*gas) Ok(*gas)
}, },
OutputPolicy::Return(BytesRef::Flexible(ref mut vec), ref mut copy) => { OutputPolicy::Return(BytesRef::Flexible(ref mut vec), ref mut copy) => {
handle_copy(copy); handle_copy(copy);
vec.clear(); vec.clear();
vec.reserve(data.len()); vec.extend_from_slice(data);
unsafe {
ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len());
vec.set_len(data.len());
}
Ok(*gas) Ok(*gas)
}, },
OutputPolicy::InitContract(ref mut copy) => { OutputPolicy::InitContract(ref mut copy) => {
@ -238,11 +235,8 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
handle_copy(copy); handle_copy(copy);
let mut code = vec![]; let mut code = vec![];
code.reserve(data.len()); code.extend_from_slice(data);
unsafe {
ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len());
code.set_len(data.len());
}
self.state.init_code(&self.origin_info.address, code); self.state.init_code(&self.origin_info.address, code);
Ok(*gas - return_cost) 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) { fn inc_sstore_clears(&mut self) {
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); 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)] #[cfg(test)]
@ -297,7 +299,7 @@ mod tests {
use substate::*; use substate::*;
use tests::helpers::*; use tests::helpers::*;
use super::*; use super::*;
use trace::{NoopTracer}; use trace::{NoopTracer, NoopVMTracer};
fn get_test_origin() -> OriginInfo { fn get_test_origin() -> OriginInfo {
OriginInfo { OriginInfo {
@ -349,9 +351,10 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default(); 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); assert_eq!(ext.env_info().number, 100);
} }
@ -361,9 +364,10 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default(); 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()); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
@ -383,9 +387,10 @@ mod tests {
} }
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default(); 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()); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
@ -398,9 +403,10 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default(); 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![]; let mut output = vec![];
@ -423,10 +429,11 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
{ {
let vm_factory = Default::default(); 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); ext.log(log_topics, &log_data);
} }
@ -440,10 +447,11 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
{ {
let vm_factory = Default::default(); 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); ext.suicide(&refund_account);
} }

View File

@ -22,6 +22,7 @@ use tests::helpers::*;
use devtools::*; use devtools::*;
use spec::Genesis; use spec::Genesis;
use ethjson; use ethjson;
use miner::Miner;
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> { pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
init_log(); init_log();
@ -53,7 +54,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
let temp = RandomTempPath::new(); 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() { for b in &blockchain.blocks_rlp() {
if Block::is_good(&b) { if Block::is_good(&b) {
let _ = client.import_block(b.clone()); let _ = client.import_block(b.clone());

View File

@ -19,14 +19,15 @@ use state::*;
use executive::*; use executive::*;
use engine::*; use engine::*;
use evm; use evm;
use evm::{Schedule, Ext, Factory, VMType, ContractCreateResult, MessageCallResult}; use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
use externalities::*; use externalities::*;
use substate::*; use substate::*;
use tests::helpers::*; use tests::helpers::*;
use ethjson; use ethjson;
use trace::{Tracer, NoopTracer}; use trace::{Tracer, NoopTracer};
use trace::{VMTracer, NoopVMTracer};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
struct CallCreate { struct CallCreate {
data: Bytes, data: Bytes,
destination: Option<Address>, destination: Option<Address>,
@ -48,14 +49,15 @@ impl From<ethjson::vm::Call> for CallCreate {
/// Tiny wrapper around executive externalities. /// Tiny wrapper around executive externalities.
/// Stores callcreates. /// Stores callcreates.
struct TestExt<'a, T> where T: 'a + Tracer { struct TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
ext: Externalities<'a, T>, ext: Externalities<'a, T, V>,
callcreates: Vec<CallCreate>, callcreates: Vec<CallCreate>,
contract_address: Address contract_address: Address
} }
impl<'a, T> TestExt<'a, T> where T: 'a + Tracer { impl<'a, T, V> TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
fn new(state: &'a mut State, fn new(
state: &'a mut State,
info: &'a EnvInfo, info: &'a EnvInfo,
engine: &'a Engine, engine: &'a Engine,
vm_factory: &'a Factory, vm_factory: &'a Factory,
@ -64,16 +66,18 @@ impl<'a, T> TestExt<'a, T> where T: 'a + Tracer {
substate: &'a mut Substate, substate: &'a mut Substate,
output: OutputPolicy<'a, 'a>, output: OutputPolicy<'a, 'a>,
address: Address, address: Address,
tracer: &'a mut T) -> Self { tracer: &'a mut T,
vm_tracer: &'a mut V,
) -> Self {
TestExt { TestExt {
contract_address: contract_address(&address, &state.nonce(&address)), contract_address: contract_address(&address, &state.nonce(&address)),
ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer), ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer, vm_tracer),
callcreates: vec![] callcreates: vec![]
} }
} }
} }
impl<'a, T> Ext for TestExt<'a, T> where T: Tracer { impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
fn storage_at(&self, key: &H256) -> H256 { fn storage_at(&self, key: &H256) -> H256 {
self.ext.storage_at(key) self.ext.storage_at(key)
} }
@ -129,7 +133,7 @@ impl<'a, T> Ext for TestExt<'a, T> where T: Tracer {
self.ext.log(topics, data) self.ext.log(topics, data)
} }
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> { fn ret(self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> {
self.ext.ret(gas, data) self.ext.ret(gas, data)
} }
@ -186,6 +190,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
let mut substate = Substate::new(); let mut substate = Substate::new();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let mut output = vec![]; let mut output = vec![];
// execute // execute
@ -201,10 +206,13 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
OutputPolicy::Return(BytesRef::Flexible(&mut output), None), OutputPolicy::Return(BytesRef::Flexible(&mut output), None),
params.address.clone(), params.address.clone(),
&mut tracer, &mut tracer,
&mut vm_tracer,
); );
let evm = vm_factory.create(); let mut evm = vm_factory.create();
let res = evm.exec(params, &mut ex); let res = evm.exec(params, &mut ex);
(res, ex.callcreates) // a return in finalize will not alter callcreates
let callcreates = ex.callcreates.clone();
(res.finalize(ex), callcreates)
}; };
match res { match res {

View File

@ -16,8 +16,7 @@
use super::test_common::*; use super::test_common::*;
use tests::helpers::*; use tests::helpers::*;
use pod_state::*; use pod_state::{self, PodState};
use state_diff::*;
use ethereum; use ethereum;
use ethjson; use ethjson;
@ -71,7 +70,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
let our_post = state.to_pod(); let our_post = state.to_pod();
println!("Got:\n{}", our_post); println!("Got:\n{}", our_post);
println!("Expect:\n{}", post); println!("Expect:\n{}", post);
println!("Diff ---expect -> +++got:\n{}", StateDiff::diff_pod(&post, &our_post)); println!("Diff ---expect -> +++got:\n{}", pod_state::diff_pod(&post, &our_post));
} }
if let Ok(r) = res { if let Ok(r) = res {

View File

@ -90,6 +90,7 @@ extern crate crossbeam;
extern crate ethjson; extern crate ethjson;
extern crate bloomchain; extern crate bloomchain;
#[macro_use] extern crate ethcore_ipc as ipc; #[macro_use] extern crate ethcore_ipc as ipc;
extern crate rayon;
#[cfg(test)] extern crate ethcore_devtools as devtools; #[cfg(test)] extern crate ethcore_devtools as devtools;
#[cfg(feature = "jit" )] extern crate evmjit; #[cfg(feature = "jit" )] extern crate evmjit;
@ -109,6 +110,7 @@ pub mod views;
pub mod pod_state; pub mod pod_state;
pub mod engine; pub mod engine;
pub mod migrations; pub mod migrations;
pub mod miner;
mod blooms; mod blooms;
mod db; mod db;
@ -117,8 +119,6 @@ mod basic_types;
#[macro_use] mod evm; #[macro_use] mod evm;
mod env_info; mod env_info;
mod pod_account; mod pod_account;
mod account_diff;
mod state_diff;
mod state; mod state;
mod account; mod account;
mod account_db; mod account_db;
@ -139,3 +139,5 @@ mod tests;
mod json_tests; mod json_tests;
pub use types::*; pub use types::*;
pub use evm::get_info;
pub use executive::contract_address;

View File

@ -18,17 +18,16 @@ use rayon::prelude::*;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use util::*; use util::*;
use util::keys::store::AccountProvider; use util::keys::store::{AccountProvider};
use ethcore::views::{BlockView, HeaderView}; use views::{BlockView, HeaderView};
use ethcore::client::{BlockChainClient, BlockID}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockChainClient, BlockID, CallAnalytics};
use ethcore::block::{ClosedBlock, IsBlock}; use block::{ClosedBlock, IsBlock};
use ethcore::error::*; use error::*;
use ethcore::client::{Executive, Executed, EnvInfo, TransactOptions}; use transaction::SignedTransaction;
use ethcore::transaction::SignedTransaction; use receipt::{Receipt};
use ethcore::receipt::Receipt; use spec::Spec;
use ethcore::spec::Spec; use engine::Engine;
use ethcore::engine::Engine; use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
use super::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
/// Keeps track of transactions using priority queue and holds currently mined block. /// Keeps track of transactions using priority queue and holds currently mined block.
pub struct Miner { pub struct Miner {
@ -104,7 +103,7 @@ impl Miner {
/// Prepares new block for sealing including top transactions from queue. /// Prepares new block for sealing including top transactions from queue.
#[cfg_attr(feature="dev", allow(match_same_arms))] #[cfg_attr(feature="dev", allow(match_same_arms))]
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn prepare_sealing(&self, chain: &BlockChainClient) { fn prepare_sealing(&self, chain: &MiningBlockChainClient) {
trace!(target: "miner", "prepare_sealing: entering"); trace!(target: "miner", "prepare_sealing: entering");
let transactions = self.transaction_queue.lock().unwrap().top_transactions(); let transactions = self.transaction_queue.lock().unwrap().top_transactions();
let mut sealing_work = self.sealing_work.lock().unwrap(); let mut sealing_work = self.sealing_work.lock().unwrap();
@ -205,14 +204,14 @@ impl Miner {
trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash())); trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash()));
} }
fn update_gas_limit(&self, chain: &BlockChainClient) { fn update_gas_limit(&self, chain: &MiningBlockChainClient) {
let gas_limit = HeaderView::new(&chain.best_block_header()).gas_limit(); let gas_limit = HeaderView::new(&chain.best_block_header()).gas_limit();
let mut queue = self.transaction_queue.lock().unwrap(); let mut queue = self.transaction_queue.lock().unwrap();
queue.set_gas_limit(gas_limit); queue.set_gas_limit(gas_limit);
} }
/// Returns true if we had to prepare new pending block /// Returns true if we had to prepare new pending block
fn enable_and_prepare_sealing(&self, chain: &BlockChainClient) -> bool { fn enable_and_prepare_sealing(&self, chain: &MiningBlockChainClient) -> bool {
trace!(target: "miner", "enable_and_prepare_sealing: entering"); trace!(target: "miner", "enable_and_prepare_sealing: entering");
let have_work = self.sealing_work.lock().unwrap().peek_last_ref().is_some(); let have_work = self.sealing_work.lock().unwrap().peek_last_ref().is_some();
trace!(target: "miner", "enable_and_prepare_sealing: have_work={}", have_work); trace!(target: "miner", "enable_and_prepare_sealing: have_work={}", have_work);
@ -236,7 +235,7 @@ const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5;
impl MinerService for Miner { impl MinerService for Miner {
fn clear_and_reset(&self, chain: &BlockChainClient) { fn clear_and_reset(&self, chain: &MiningBlockChainClient) {
self.transaction_queue.lock().unwrap().clear(); self.transaction_queue.lock().unwrap().clear();
self.update_sealing(chain); self.update_sealing(chain);
} }
@ -251,11 +250,13 @@ impl MinerService for Miner {
} }
} }
fn call(&self, chain: &BlockChainClient, t: &SignedTransaction) -> Result<Executed, ExecutionError> { fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
match sealing_work.peek_last_ref() { match sealing_work.peek_last_ref() {
Some(work) => { Some(work) => {
let block = work.block(); let block = work.block();
// TODO: merge this code with client.rs's fn call somwhow.
let header = block.header(); let header = block.header();
let last_hashes = chain.last_hashes(); let last_hashes = chain.last_hashes();
let env_info = EnvInfo { let env_info = EnvInfo {
@ -274,20 +275,29 @@ impl MinerService for Miner {
ExecutionError::TransactionMalformed(message) ExecutionError::TransactionMalformed(message)
})); }));
let balance = state.balance(&sender); let balance = state.balance(&sender);
// give the sender max balance let needed_balance = t.value + t.gas * t.gas_price;
state.sub_balance(&sender, &balance); if balance < needed_balance {
state.add_balance(&sender, &U256::max_value()); // give the sender a sufficient balance
let options = TransactOptions { tracing: false, check_nonce: false }; 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(), chain.vm_factory()).transact(t, options);
Executive::new(&mut state, &env_info, self.engine(), chain.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(block.state().clone()));
}
}
ret
}, },
None => { None => {
chain.call(t) chain.call(t, analytics)
} }
} }
} }
fn balance(&self, chain: &BlockChainClient, address: &Address) -> U256 { fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else( sealing_work.peek_last_ref().map_or_else(
|| chain.latest_balance(address), || chain.latest_balance(address),
@ -295,7 +305,7 @@ impl MinerService for Miner {
) )
} }
fn storage_at(&self, chain: &BlockChainClient, address: &Address, position: &H256) -> H256 { fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else( sealing_work.peek_last_ref().map_or_else(
|| chain.latest_storage_at(address, position), || chain.latest_storage_at(address, position),
@ -303,12 +313,12 @@ impl MinerService for Miner {
) )
} }
fn nonce(&self, chain: &BlockChainClient, address: &Address) -> U256 { fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address)) sealing_work.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address))
} }
fn code(&self, chain: &BlockChainClient, address: &Address) -> Option<Bytes> { fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address)) sealing_work.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address))
} }
@ -375,7 +385,7 @@ impl MinerService for Miner {
.collect() .collect()
} }
fn import_own_transaction<T>(&self, chain: &BlockChainClient, transaction: SignedTransaction, fetch_account: T) -> fn import_own_transaction<T>(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction, fetch_account: T) ->
Result<TransactionImportResult, Error> Result<TransactionImportResult, Error>
where T: Fn(&Address) -> AccountDetails { where T: Fn(&Address) -> AccountDetails {
let hash = transaction.hash(); let hash = transaction.hash();
@ -469,7 +479,7 @@ impl MinerService for Miner {
self.transaction_queue.lock().unwrap().last_nonce(address) self.transaction_queue.lock().unwrap().last_nonce(address)
} }
fn update_sealing(&self, chain: &BlockChainClient) { fn update_sealing(&self, chain: &MiningBlockChainClient) {
if self.sealing_enabled.load(atomic::Ordering::Relaxed) { if self.sealing_enabled.load(atomic::Ordering::Relaxed) {
let current_no = chain.chain_info().best_block_number; let current_no = chain.chain_info().best_block_number;
let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions(); let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions();
@ -489,7 +499,7 @@ impl MinerService for Miner {
} }
} }
fn map_sealing_work<F, T>(&self, chain: &BlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T { fn map_sealing_work<F, T>(&self, chain: &MiningBlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
trace!(target: "miner", "map_sealing_work: entering"); trace!(target: "miner", "map_sealing_work: entering");
self.enable_and_prepare_sealing(chain); self.enable_and_prepare_sealing(chain);
trace!(target: "miner", "map_sealing_work: sealing prepared"); trace!(target: "miner", "map_sealing_work: sealing prepared");
@ -499,7 +509,7 @@ impl MinerService for Miner {
ret.map(f) ret.map(f)
} }
fn submit_seal(&self, chain: &BlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> { fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
if let Some(b) = self.sealing_work.lock().unwrap().take_used_if(|b| &b.hash() == &pow_hash) { if let Some(b) = self.sealing_work.lock().unwrap().take_used_if(|b| &b.hash() == &pow_hash) {
match chain.try_seal(b.lock(), seal) { match chain.try_seal(b.lock(), seal) {
Err(_) => { Err(_) => {
@ -522,8 +532,8 @@ impl MinerService for Miner {
} }
} }
fn chain_new_blocks(&self, chain: &BlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) { fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
fn fetch_transactions(chain: &BlockChainClient, hash: &H256) -> Vec<SignedTransaction> { fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
let block = chain let block = chain
.block(BlockID::Hash(*hash)) .block(BlockID::Hash(*hash))
// Client should send message after commit to db and inserting to chain. // Client should send message after commit to db and inserting to chain.
@ -584,13 +594,13 @@ impl MinerService for Miner {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use MinerService; use super::super::MinerService;
use super::{Miner}; use super::Miner;
use util::*; use util::*;
use ethcore::client::{TestBlockChainClient, EachBlockWith}; use client::{TestBlockChainClient, EachBlockWith};
use ethcore::block::*; use block::*;
// TODO [ToDr] To uncomment when TestBlockChainClient can actually return a ClosedBlock. // TODO [ToDr] To uncomment` when TestBlockChainClient can actually return a ClosedBlock.
#[ignore] #[ignore]
#[test] #[test]
fn should_prepare_block_to_seal() { fn should_prepare_block_to_seal() {

View File

@ -26,12 +26,10 @@
//! ```rust //! ```rust
//! extern crate ethcore_util as util; //! extern crate ethcore_util as util;
//! extern crate ethcore; //! extern crate ethcore;
//! extern crate ethminer;
//! use std::env; //! use std::env;
//! use util::network::{NetworkService, NetworkConfiguration}; //! use util::network::{NetworkService, NetworkConfiguration};
//! use ethcore::client::{Client, ClientConfig}; //! use ethcore::client::{Client, ClientConfig};
//! use ethcore::ethereum; //! use ethcore::miner::{Miner, MinerService};
//! use ethminer::{Miner, MinerService};
//! //!
//! fn main() { //! fn main() {
//! let miner: Miner = Miner::default(); //! let miner: Miner = Miner::default();
@ -43,30 +41,21 @@
//! } //! }
//! ``` //! ```
#[macro_use]
extern crate log;
#[macro_use]
extern crate ethcore_util as util;
extern crate ethcore;
extern crate env_logger;
extern crate rayon;
mod miner; mod miner;
mod external; mod external;
mod transaction_queue; mod transaction_queue;
pub use transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
pub use miner::{Miner}; pub use self::miner::{Miner};
pub use external::{ExternalMiner, ExternalMinerService}; pub use self::external::{ExternalMiner, ExternalMinerService};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use util::{H256, U256, Address, Bytes}; use util::{H256, U256, Address, Bytes};
use ethcore::client::{BlockChainClient, Executed}; use client::{MiningBlockChainClient, Executed, CallAnalytics};
use ethcore::block::ClosedBlock; use block::ClosedBlock;
use ethcore::receipt::Receipt; use receipt::Receipt;
use ethcore::error::{Error, ExecutionError}; use error::{Error, ExecutionError};
use ethcore::transaction::SignedTransaction; use transaction::SignedTransaction;
/// Miner client API /// Miner client API
pub trait MinerService : Send + Sync { pub trait MinerService : Send + Sync {
@ -110,7 +99,7 @@ pub trait MinerService : Send + Sync {
where T: Fn(&Address) -> AccountDetails, Self: Sized; where T: Fn(&Address) -> AccountDetails, Self: Sized;
/// Imports own (node owner) transaction to queue. /// Imports own (node owner) transaction to queue.
fn import_own_transaction<T>(&self, chain: &BlockChainClient, transaction: SignedTransaction, fetch_account: T) -> fn import_own_transaction<T>(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction, fetch_account: T) ->
Result<TransactionImportResult, Error> Result<TransactionImportResult, Error>
where T: Fn(&Address) -> AccountDetails, Self: Sized; where T: Fn(&Address) -> AccountDetails, Self: Sized;
@ -118,20 +107,20 @@ pub trait MinerService : Send + Sync {
fn pending_transactions_hashes(&self) -> Vec<H256>; fn pending_transactions_hashes(&self) -> Vec<H256>;
/// Removes all transactions from the queue and restart mining operation. /// Removes all transactions from the queue and restart mining operation.
fn clear_and_reset(&self, chain: &BlockChainClient); fn clear_and_reset(&self, chain: &MiningBlockChainClient);
/// Called when blocks are imported to chain, updates transactions queue. /// Called when blocks are imported to chain, updates transactions queue.
fn chain_new_blocks(&self, chain: &BlockChainClient, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]); fn chain_new_blocks(&self, chain: &MiningBlockChainClient, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]);
/// New chain head event. Restart mining operation. /// New chain head event. Restart mining operation.
fn update_sealing(&self, chain: &BlockChainClient); fn update_sealing(&self, chain: &MiningBlockChainClient);
/// Submit `seal` as a valid solution for the header of `pow_hash`. /// Submit `seal` as a valid solution for the header of `pow_hash`.
/// Will check the seal, but not actually insert the block into the chain. /// Will check the seal, but not actually insert the block into the chain.
fn submit_seal(&self, chain: &BlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error>; fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error>;
/// Get the sealing work package and if `Some`, apply some transform. /// Get the sealing work package and if `Some`, apply some transform.
fn map_sealing_work<F, T>(&self, chain: &BlockChainClient, f: F) -> Option<T> fn map_sealing_work<F, T>(&self, chain: &MiningBlockChainClient, f: F) -> Option<T>
where F: FnOnce(&ClosedBlock) -> T, Self: Sized; where F: FnOnce(&ClosedBlock) -> T, Self: Sized;
/// Query pending transactions for hash. /// Query pending transactions for hash.
@ -156,19 +145,19 @@ pub trait MinerService : Send + Sync {
fn sensible_gas_limit(&self) -> U256 { 21000.into() } fn sensible_gas_limit(&self) -> U256 { 21000.into() }
/// Latest account balance in pending state. /// Latest account balance in pending state.
fn balance(&self, chain: &BlockChainClient, address: &Address) -> U256; fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256;
/// Call into contract code using pending state. /// Call into contract code using pending state.
fn call(&self, chain: &BlockChainClient, t: &SignedTransaction) -> Result<Executed, ExecutionError>; fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>;
/// Get storage value in pending state. /// Get storage value in pending state.
fn storage_at(&self, chain: &BlockChainClient, address: &Address, position: &H256) -> H256; fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256;
/// Get account nonce in pending state. /// Get account nonce in pending state.
fn nonce(&self, chain: &BlockChainClient, address: &Address) -> U256; fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256;
/// Get contract code in pending state. /// Get contract code in pending state.
fn code(&self, chain: &BlockChainClient, address: &Address) -> Option<Bytes>; fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes>;
} }
/// Mining status /// Mining status

View File

@ -26,13 +26,12 @@
//! ```rust //! ```rust
//! extern crate ethcore_util as util; //! extern crate ethcore_util as util;
//! extern crate ethcore; //! extern crate ethcore;
//! extern crate ethminer;
//! extern crate rustc_serialize; //! extern crate rustc_serialize;
//! //!
//! use util::crypto::KeyPair; //! use util::crypto::KeyPair;
//! use util::hash::Address; //! use util::hash::Address;
//! use util::numbers::{Uint, U256}; //! use util::numbers::{Uint, U256};
//! use ethminer::{TransactionQueue, AccountDetails, TransactionOrigin}; //! use ethcore::miner::{TransactionQueue, AccountDetails, TransactionOrigin};
//! use ethcore::transaction::*; //! use ethcore::transaction::*;
//! use rustc_serialize::hex::FromHex; //! use rustc_serialize::hex::FromHex;
//! //!
@ -89,8 +88,8 @@ use std::collections::{HashMap, BTreeSet};
use util::numbers::{Uint, U256}; use util::numbers::{Uint, U256};
use util::hash::{Address, H256}; use util::hash::{Address, H256};
use util::table::*; use util::table::*;
use ethcore::transaction::*; use transaction::*;
use ethcore::error::{Error, TransactionError}; use error::{Error, TransactionError};
/// Transaction origin /// Transaction origin
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -778,8 +777,8 @@ mod test {
extern crate rustc_serialize; extern crate rustc_serialize;
use util::table::*; use util::table::*;
use util::*; use util::*;
use ethcore::transaction::*; use transaction::*;
use ethcore::error::{Error, TransactionError}; use error::{Error, TransactionError};
use super::*; use super::*;
use super::{TransactionSet, TransactionOrder, VerifiedTransaction}; use super::{TransactionSet, TransactionOrder, VerifiedTransaction};

View File

@ -18,6 +18,7 @@ use util::*;
use account::*; use account::*;
use account_db::*; use account_db::*;
use ethjson; use ethjson;
use types::account_diff::*;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
/// An account, expressed as Plain-Old-Data (hence the name). /// An account, expressed as Plain-Old-Data (hence the name).
@ -106,17 +107,58 @@ impl fmt::Display for PodAccount {
} }
} }
/// Determine difference between two optionally existant `Account`s. Returns None
/// if they are the same.
pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<AccountDiff> {
match (pre, post) {
(None, Some(x)) => Some(AccountDiff {
balance: Diff::Born(x.balance),
nonce: Diff::Born(x.nonce),
code: Diff::Born(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(),
}),
(Some(x), None) => Some(AccountDiff {
balance: Diff::Died(x.balance),
nonce: Diff::Died(x.nonce),
code: Diff::Died(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(),
}),
(Some(pre), Some(post)) => {
let storage: Vec<_> = pre.storage.keys().merge(post.storage.keys())
.filter(|k| pre.storage.get(k).unwrap_or(&H256::new()) != post.storage.get(k).unwrap_or(&H256::new()))
.collect();
let r = AccountDiff {
balance: Diff::new(pre.balance, post.balance),
nonce: Diff::new(pre.nonce, post.nonce),
code: Diff::new(pre.code.clone(), post.code.clone()),
storage: storage.into_iter().map(|k|
(k.clone(), Diff::new(
pre.storage.get(&k).cloned().unwrap_or_else(H256::new),
post.storage.get(&k).cloned().unwrap_or_else(H256::new)
))).collect(),
};
if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.is_empty() {
None
} else {
Some(r)
}
},
_ => None,
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use common::*; use common::*;
use account_diff::*; use types::account_diff::*;
use super::*; use super::{PodAccount, diff_pod};
#[test] #[test]
fn existence() { fn existence() {
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]}; let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&a)), None); assert_eq!(diff_pod(Some(&a), Some(&a)), None);
assert_eq!(AccountDiff::diff_pod(None, Some(&a)), Some(AccountDiff{ assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{
balance: Diff::Born(69.into()), balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()), nonce: Diff::Born(0.into()),
code: Diff::Born(vec![]), code: Diff::Born(vec![]),
@ -128,7 +170,7 @@ mod test {
fn basic() { fn basic() {
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]}; let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]};
let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: vec![], storage: map![]}; let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: vec![], storage: map![]};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff { assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Changed(69.into(), 42.into()), balance: Diff::Changed(69.into(), 42.into()),
nonce: Diff::Changed(0.into(), 1.into()), nonce: Diff::Changed(0.into(), 1.into()),
code: Diff::Same, code: Diff::Same,
@ -140,7 +182,7 @@ mod test {
fn code() { fn code() {
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: vec![], storage: map![]}; let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: vec![], storage: map![]};
let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: vec![0], storage: map![]}; let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: vec![0], storage: map![]};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff { assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same, balance: Diff::Same,
nonce: Diff::Changed(0.into(), 1.into()), nonce: Diff::Changed(0.into(), 1.into()),
code: Diff::Changed(vec![], vec![0]), code: Diff::Changed(vec![], vec![0]),
@ -162,7 +204,7 @@ mod test {
code: vec![], code: vec![],
storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9] storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9]
}; };
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff { assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same, balance: Diff::Same,
nonce: Diff::Same, nonce: Diff::Same,
code: Diff::Same, code: Diff::Same,

View File

@ -17,7 +17,8 @@
//! State of all accounts in the system expressed in Plain Old Data. //! State of all accounts in the system expressed in Plain Old Data.
use util::*; use util::*;
use pod_account::*; use pod_account::{self, PodAccount};
use types::state_diff::StateDiff;
use ethjson; use ethjson;
/// State of all accounts in the system expressed in Plain Old Data. /// State of all accounts in the system expressed in Plain Old Data.
@ -29,7 +30,6 @@ impl PodState {
pub fn new() -> PodState { Default::default() } pub fn new() -> PodState { Default::default() }
/// Contruct a new object from the `m`. /// Contruct a new object from the `m`.
#[cfg(test)]
pub fn from(m: BTreeMap<Address, PodAccount>) -> PodState { PodState(m) } pub fn from(m: BTreeMap<Address, PodAccount>) -> PodState { PodState(m) }
/// Get the underlying map. /// Get the underlying map.
@ -41,8 +41,6 @@ impl PodState {
} }
/// Drain object to get the underlying map. /// Drain object to get the underlying map.
#[cfg(test)]
#[cfg(feature = "json-tests")]
pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 } pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 }
} }
@ -72,3 +70,83 @@ impl fmt::Display for PodState {
} }
} }
/// Calculate and return diff between `pre` state and `post` state.
pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff {
StateDiff(pre.get().keys().merge(post.get().keys()).filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect())
}
#[cfg(test)]
mod test {
use common::*;
use types::state_diff::*;
use types::account_diff::*;
use pod_account::PodAccount;
use super::PodState;
#[test]
fn create_delete() {
let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]);
assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff(map![
1.into() => AccountDiff{
balance: Diff::Died(69.into()),
nonce: Diff::Died(0.into()),
code: Diff::Died(vec![]),
storage: map![],
}
]));
assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff(map![
1.into() => AccountDiff{
balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()),
code: Diff::Born(vec![]),
storage: map![],
}
]));
}
#[test]
fn create_delete_with_unchanged() {
let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]);
let b = PodState::from(map![
1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]),
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
]);
assert_eq!(super::diff_pod(&a, &b), StateDiff(map![
2.into() => AccountDiff{
balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()),
code: Diff::Born(vec![]),
storage: map![],
}
]));
assert_eq!(super::diff_pod(&b, &a), StateDiff(map![
2.into() => AccountDiff{
balance: Diff::Died(69.into()),
nonce: Diff::Died(0.into()),
code: Diff::Died(vec![]),
storage: map![],
}
]));
}
#[test]
fn change_with_unchanged() {
let a = PodState::from(map![
1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]),
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
]);
let b = PodState::from(map![
1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]),
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
]);
assert_eq!(super::diff_pod(&a, &b), StateDiff(map![
1.into() => AccountDiff{
balance: Diff::Same,
nonce: Diff::Changed(0.into(), 1.into()),
code: Diff::Same,
storage: map![],
}
]));
}
}

View File

@ -21,6 +21,7 @@ use util::panics::*;
use spec::Spec; use spec::Spec;
use error::*; use error::*;
use client::{Client, ClientConfig}; use client::{Client, ClientConfig};
use miner::Miner;
/// Message type for external and internal events /// Message type for external and internal events
#[derive(Clone)] #[derive(Clone)]
@ -54,14 +55,14 @@ pub struct ClientService {
impl ClientService { impl ClientService {
/// Start the service in a separate thread. /// Start the service in a separate thread.
pub fn start(config: ClientConfig, spec: Spec, net_config: NetworkConfiguration, db_path: &Path) -> Result<ClientService, Error> { pub fn start(config: ClientConfig, spec: Spec, net_config: NetworkConfiguration, db_path: &Path, miner: Arc<Miner>) -> Result<ClientService, Error> {
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
let mut net_service = try!(NetworkService::start(net_config)); let mut net_service = try!(NetworkService::start(net_config));
panic_handler.forward_from(&net_service); panic_handler.forward_from(&net_service);
info!("Starting {}", net_service.host_info()); info!("Starting {}", net_service.host_info());
info!("Configured for {} using {:?} engine", spec.name, spec.engine.name()); info!("Configured for {} using {:?} engine", spec.name, spec.engine.name());
let client = try!(Client::new(config, spec, db_path, net_service.io().channel())); let client = try!(Client::new(config, spec, db_path, miner, net_service.io().channel()));
panic_handler.forward_from(client.deref()); panic_handler.forward_from(client.deref());
let client_io = Arc::new(ClientIoHandler { let client_io = Arc::new(ClientIoHandler {
client: client.clone() client: client.clone()
@ -141,12 +142,14 @@ mod tests {
use util::network::*; use util::network::*;
use devtools::*; use devtools::*;
use client::ClientConfig; use client::ClientConfig;
use std::sync::Arc;
use miner::Miner;
#[test] #[test]
fn it_can_be_started() { fn it_can_be_started() {
let spec = get_test_spec(); let spec = get_test_spec();
let temp_path = RandomTempPath::new(); let temp_path = RandomTempPath::new();
let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_local(), &temp_path.as_path()); let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_local(), &temp_path.as_path(), Arc::new(Miner::default()));
assert!(service.is_ok()); assert!(service.is_ok());
} }
} }

View File

@ -20,13 +20,9 @@ use executive::{Executive, TransactOptions};
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
use account_db::*; use account_db::*;
use trace::Trace; use trace::Trace;
#[cfg(test)]
#[cfg(feature = "json-tests")]
use pod_account::*; use pod_account::*;
#[cfg(test)] use pod_state::{self, PodState};
#[cfg(feature = "json-tests")] use types::state_diff::StateDiff;
use pod_state::PodState;
//use state_diff::*; // TODO: uncomment once to_pod() works correctly.
/// Used to return information about an `State::apply` operation. /// Used to return information about an `State::apply` operation.
pub struct ApplyOutcome { pub struct ApplyOutcome {
@ -183,16 +179,14 @@ impl State {
/// Add `incr` to the balance of account `a`. /// Add `incr` to the balance of account `a`.
pub fn add_balance(&mut self, a: &Address, incr: &U256) { pub fn add_balance(&mut self, a: &Address, incr: &U256) {
let old = self.balance(a); trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a));
self.require(a, false).add_balance(incr); self.require(a, false).add_balance(incr);
trace!("state: add_balance({}, {}): {} -> {}\n", a, incr, old, self.balance(a));
} }
/// Subtract `decr` from the balance of account `a`. /// Subtract `decr` from the balance of account `a`.
pub fn sub_balance(&mut self, a: &Address, decr: &U256) { pub fn sub_balance(&mut self, a: &Address, decr: &U256) {
let old = self.balance(a); trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a));
self.require(a, false).sub_balance(decr); self.require(a, false).sub_balance(decr);
trace!("state: sub_balance({}, {}): {} -> {}\n", a, decr, old, self.balance(a));
} }
/// Subtracts `by` from the balance of `from` and adds it to that of `to`. /// Subtracts `by` from the balance of `from` and adds it to that of `to`.
@ -222,11 +216,11 @@ impl State {
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult { pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult {
// let old = self.to_pod(); // let old = self.to_pod();
let options = TransactOptions { tracing: tracing, check_nonce: true }; let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options)); let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options));
// TODO uncomment once to_pod() works correctly. // TODO uncomment once to_pod() works correctly.
// trace!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod())); // trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
self.commit(); self.commit();
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs); let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
// trace!("Transaction receipt: {:?}", receipt); // trace!("Transaction receipt: {:?}", receipt);
@ -277,12 +271,11 @@ impl State {
} }
} }
#[cfg(test)]
#[cfg(feature = "json-tests")]
/// Populate a PodAccount map from this state. /// Populate a PodAccount map from this state.
pub fn to_pod(&self) -> PodState { pub fn to_pod(&self) -> PodState {
assert!(self.snapshots.borrow().is_empty()); assert!(self.snapshots.borrow().is_empty());
// TODO: handle database rather than just the cache. // TODO: handle database rather than just the cache.
// will need fat db.
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
if let Some(ref acc) = *opt { if let Some(ref acc) = *opt {
m.insert(add.clone(), PodAccount::from_account(acc)); m.insert(add.clone(), PodAccount::from_account(acc));
@ -291,6 +284,25 @@ impl State {
})) }))
} }
fn query_pod(&mut self, query: &PodState) {
for (ref address, ref pod_account) in query.get() {
if self.get(address, true).is_some() {
for (ref key, _) in &pod_account.storage {
self.storage_at(address, key);
}
}
}
}
/// Returns a `StateDiff` describing the difference from `orig` to `self`.
/// Consumes self.
pub fn diff_from(&self, orig: State) -> StateDiff {
let pod_state_post = self.to_pod();
let mut state_pre = orig;
state_pre.query_pod(&pod_state_post);
pod_state::diff_pod(&state_pre.to_pod(), &pod_state_post)
}
/// Pull account `a` in our cache from the trie DB and return it. /// Pull account `a` in our cache from the trie DB and return it.
/// `require_code` requires that the code be cached, too. /// `require_code` requires that the code be cached, too.
fn get<'a>(&'a self, a: &Address, require_code: bool) -> &'a Option<Account> { fn get<'a>(&'a self, a: &Address, require_code: bool) -> &'a Option<Account> {

View File

@ -14,16 +14,17 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use client::{BlockChainClient, Client, ClientConfig, BlockID}; use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID};
use block::IsBlock; use block::IsBlock;
use tests::helpers::*; use tests::helpers::*;
use common::*; use common::*;
use devtools::*; use devtools::*;
use miner::Miner;
#[test] #[test]
fn imports_from_empty() { fn imports_from_empty() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap();
client.import_verified_blocks(&IoChannel::disconnected()); client.import_verified_blocks(&IoChannel::disconnected());
client.flush_queue(); client.flush_queue();
} }
@ -41,7 +42,7 @@ fn returns_state_root_basic() {
#[test] #[test]
fn imports_good_block() { fn imports_good_block() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap();
let good_block = get_good_dummy_block(); let good_block = get_good_dummy_block();
if let Err(_) = client.import_block(good_block) { if let Err(_) = client.import_block(good_block) {
panic!("error importing block being good by definition"); panic!("error importing block being good by definition");
@ -56,7 +57,7 @@ fn imports_good_block() {
#[test] #[test]
fn query_none_block() { fn query_none_block() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap();
let non_existant = client.block_header(BlockID::Number(188)); let non_existant = client.block_header(BlockID::Number(188));
assert!(non_existant.is_none()); assert!(non_existant.is_none());

View File

@ -23,6 +23,7 @@ use evm::Schedule;
use engine::*; use engine::*;
use ethereum; use ethereum;
use devtools::*; use devtools::*;
use miner::Miner;
#[cfg(feature = "json-tests")] #[cfg(feature = "json-tests")]
pub enum ChainEra { pub enum ChainEra {
@ -139,7 +140,7 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTrans
pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>> { pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap();
let test_spec = get_test_spec(); let test_spec = get_test_spec();
let test_engine = &test_spec.engine; let test_engine = &test_spec.engine;
let state_root = test_spec.genesis_header().state_root; let state_root = test_spec.genesis_header().state_root;
@ -205,7 +206,7 @@ pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting
pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> { pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap();
for block in &blocks { for block in &blocks {
if let Err(_) = client.import_block(block.clone()) { if let Err(_) = client.import_block(block.clone()) {
panic!("panic importing block which is well-formed"); panic!("panic importing block which is well-formed");

View File

@ -18,13 +18,13 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult}; use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
use trace::Tracer; use trace::{Tracer, VMTracer};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
#[derive(Default)] #[derive(Default)]
pub struct ExecutiveTracer { pub struct ExecutiveTracer {
traces: Vec<Trace> traces: Vec<Trace>,
} }
impl Tracer for ExecutiveTracer { impl Tracer for ExecutiveTracer {
@ -40,8 +40,7 @@ impl Tracer for ExecutiveTracer {
Some(vec![]) Some(vec![])
} }
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>, delegate_call: bool) {
Vec<Trace>, delegate_call: bool) {
// don't trace if it's DELEGATECALL or CALLCODE. // don't trace if it's DELEGATECALL or CALLCODE.
if delegate_call { if delegate_call {
return; return;
@ -106,3 +105,46 @@ impl Tracer for ExecutiveTracer {
self.traces self.traces
} }
} }
/// Simple VM tracer. Traces all operations.
#[derive(Default)]
pub struct ExecutiveVMTracer {
data: VMTrace,
}
impl VMTracer for ExecutiveVMTracer {
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
self.data.operations.push(VMOperation {
pc: pc,
instruction: instruction,
gas_cost: gas_cost.clone(),
executed: None,
});
true
}
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
let ex = VMExecutedOperation {
gas_used: gas_used,
stack_push: stack_push.iter().cloned().collect(),
mem_diff: mem_diff.map(|(s, r)| MemoryDiff{ offset: s, data: r.iter().cloned().collect() }),
store_diff: store_diff.map(|(l, v)| StorageDiff{ location: l, value: v }),
};
self.data.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute").executed = Some(ex);
}
fn prepare_subtrace(&self, code: &Bytes) -> Self {
ExecutiveVMTracer { data: VMTrace {
parent_step: self.data.operations.len(),
code: code.clone(),
operations: vec![],
subs: vec![],
}}
}
fn done_subtrace(&mut self, sub: Self) {
self.data.subs.push(sub.data);
}
fn drain(mut self) -> Option<VMTrace> { self.data.subs.pop() }
}

View File

@ -31,9 +31,9 @@ pub use self::block::BlockTraces;
pub use self::config::{Config, Switch}; pub use self::config::{Config, Switch};
pub use self::db::TraceDB; pub use self::db::TraceDB;
pub use self::error::Error; pub use self::error::Error;
pub use types::trace_types::trace::Trace; pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
pub use self::noop_tracer::NoopTracer; pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
pub use self::executive_tracer::ExecutiveTracer; pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
pub use types::trace_types::filter::{Filter, AddressesFilter}; pub use types::trace_types::filter::{Filter, AddressesFilter};
pub use self::import::ImportRequest; pub use self::import::ImportRequest;
pub use self::localized::LocalizedTrace; pub use self::localized::LocalizedTrace;
@ -81,13 +81,32 @@ pub trait Tracer: Send {
/// Stores failed create trace. /// Stores failed create trace.
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>); fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>);
/// Spawn subracer which will be used to trace deeper levels of execution. /// Spawn subtracer which will be used to trace deeper levels of execution.
fn subtracer(&self) -> Self where Self: Sized; fn subtracer(&self) -> Self where Self: Sized;
/// Consumes self and returns all traces. /// Consumes self and returns all traces.
fn traces(self) -> Vec<Trace>; fn traces(self) -> Vec<Trace>;
} }
/// Used by executive to build VM traces.
pub trait VMTracer: Send {
/// Trace the preparation to execute a single instruction.
/// @returns true if `trace_executed` should be called.
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)>) {}
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn prepare_subtrace(&self, code: &Bytes) -> Self where Self: Sized;
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn done_subtrace(&mut self, sub: Self) where Self: Sized;
/// Consumes self and returns the VM trace.
fn drain(self) -> Option<VMTrace>;
}
/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, /// `DbExtras` provides an interface to query extra data which is not stored in tracesdb,
/// but necessary to work correctly. /// but necessary to work correctly.
pub trait DatabaseExtras { pub trait DatabaseExtras {

View File

@ -18,8 +18,8 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::Tracer; use trace::{Tracer, VMTracer};
use trace::trace::{Trace, Call, Create}; use trace::trace::{Trace, Call, Create, VMTrace};
/// Nonoperative tracer. Does not trace anything. /// Nonoperative tracer. Does not trace anything.
pub struct NoopTracer; pub struct NoopTracer;
@ -63,3 +63,23 @@ impl Tracer for NoopTracer {
vec![] vec![]
} }
} }
/// Nonoperative VM tracer. Does not trace anything.
pub struct NoopVMTracer;
impl VMTracer for NoopVMTracer {
/// Trace the preparation to execute a single instruction.
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)>) {}
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn prepare_subtrace(&self, _code: &Bytes) -> Self { NoopVMTracer }
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn done_subtrace(&mut self, _sub: Self) {}
/// Consumes self and returns all VM traces.
fn drain(self) -> Option<VMTrace> { None }
}

View File

@ -17,10 +17,48 @@
//! Diff between two accounts. //! Diff between two accounts.
use util::*; use util::*;
#[cfg(test)]
use pod_account::*;
#[derive(Debug,Clone,PartialEq,Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
/// Diff type for specifying a change (or not).
pub enum Diff<T> where T: Eq {
/// Both sides are the same.
Same,
/// Left (pre, source) side doesn't include value, right side (post, destination) does.
Born(T),
/// Both sides include data; it chaged value between them.
Changed(T, T),
/// Left (pre, source) side does include value, right side (post, destination) does not.
Died(T),
}
impl<T> Diff<T> where T: Eq {
/// Construct new object with given `pre` and `post`.
pub fn new(pre: T, post: T) -> Self { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } }
/// Get the before value, if there is one.
pub fn pre(&self) -> Option<&T> { match *self { Diff::Died(ref x) | Diff::Changed(ref x, _) => Some(x), _ => None } }
/// Get the after value, if there is one.
pub fn post(&self) -> Option<&T> { match *self { Diff::Born(ref x) | Diff::Changed(_, ref x) => Some(x), _ => None } }
/// Determine whether there was a change or not.
pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false }}
}
#[derive(Debug, PartialEq, Eq, Clone)]
/// Account diff.
pub struct AccountDiff {
/// Change in balance, allowed to be `Diff::Same`.
pub balance: Diff<U256>,
/// Change in nonce, allowed to be `Diff::Same`.
pub nonce: Diff<U256>, // Allowed to be Same
/// Change in code, allowed to be `Diff::Same`.
pub code: Diff<Bytes>, // Allowed to be Same
/// Change in storage, values are not allowed to be `Diff::Same`.
pub storage: BTreeMap<H256, Diff<H256>>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
/// Change in existance type. /// Change in existance type.
// TODO: include other types of change. // TODO: include other types of change.
pub enum Existance { pub enum Existance {
@ -43,19 +81,6 @@ impl fmt::Display for Existance {
} }
} }
#[derive(Debug,Clone,PartialEq,Eq)]
/// Account diff.
pub struct AccountDiff {
/// Change in balance, allowed to be `Diff::Same`.
pub balance: Diff<U256>,
/// Change in nonce, allowed to be `Diff::Same`.
pub nonce: Diff<U256>, // Allowed to be Same
/// Change in code, allowed to be `Diff::Same`.
pub code: Diff<Bytes>, // Allowed to be Same
/// Change in storage, values are not allowed to be `Diff::Same`.
pub storage: BTreeMap<H256, Diff<H256>>,
}
impl AccountDiff { impl AccountDiff {
/// Get `Existance` projection. /// Get `Existance` projection.
pub fn existance(&self) -> Existance { pub fn existance(&self) -> Existance {
@ -65,47 +90,6 @@ impl AccountDiff {
_ => Existance::Alive, _ => Existance::Alive,
} }
} }
#[cfg(test)]
/// Determine difference between two optionally existant `Account`s. Returns None
/// if they are the same.
pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<AccountDiff> {
match (pre, post) {
(None, Some(x)) => Some(AccountDiff {
balance: Diff::Born(x.balance),
nonce: Diff::Born(x.nonce),
code: Diff::Born(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(),
}),
(Some(x), None) => Some(AccountDiff {
balance: Diff::Died(x.balance),
nonce: Diff::Died(x.nonce),
code: Diff::Died(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(),
}),
(Some(pre), Some(post)) => {
let storage: Vec<_> = pre.storage.keys().merge(post.storage.keys())
.filter(|k| pre.storage.get(k).unwrap_or(&H256::new()) != post.storage.get(k).unwrap_or(&H256::new()))
.collect();
let r = AccountDiff {
balance: Diff::new(pre.balance, post.balance),
nonce: Diff::new(pre.nonce, post.nonce),
code: Diff::new(pre.code.clone(), post.code.clone()),
storage: storage.into_iter().map(|k|
(k.clone(), Diff::new(
pre.storage.get(&k).cloned().unwrap_or_else(H256::new),
post.storage.get(&k).cloned().unwrap_or_else(H256::new)
))).collect(),
};
if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.is_empty() {
None
} else {
Some(r)
}
},
_ => None,
}
}
} }
// TODO: refactor into something nicer. // TODO: refactor into something nicer.

View File

@ -18,15 +18,16 @@
use util::numbers::*; use util::numbers::*;
use util::Bytes; use util::Bytes;
use trace::Trace; use trace::{Trace, VMTrace};
use types::log_entry::LogEntry; use types::log_entry::LogEntry;
use types::state_diff::StateDiff;
use ipc::binary::BinaryConvertError; use ipc::binary::BinaryConvertError;
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use std::collections::VecDeque; use std::collections::VecDeque;
/// Transaction execution receipt. /// Transaction execution receipt.
#[derive(Debug, PartialEq, Clone, Binary)] #[derive(Debug, PartialEq, Clone)]
pub struct Executed { pub struct Executed {
/// Gas paid up front for execution of transaction. /// Gas paid up front for execution of transaction.
pub gas: U256, pub gas: U256,
@ -59,6 +60,10 @@ pub struct Executed {
pub output: Bytes, pub output: Bytes,
/// The trace of this transaction. /// The trace of this transaction.
pub trace: Option<Trace>, pub trace: Option<Trace>,
/// The VM trace of this transaction.
pub vm_trace: Option<VMTrace>,
/// The state diff, if we traced it.
pub state_diff: Option<StateDiff>,
} }
/// Result of executing the transaction. /// Result of executing the transaction.

View File

@ -23,3 +23,5 @@ pub mod log_entry;
pub mod trace_types; pub mod trace_types;
pub mod executed; pub mod executed;
pub mod block_status; pub mod block_status;
pub mod account_diff;
pub mod state_diff;

View File

@ -0,0 +1,49 @@
// 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 <http://www.gnu.org/licenses/>.
//! State diff module.
use util::*;
use account_diff::*;
#[derive(Debug, PartialEq, Eq, Clone)]
/// Expression for the delta between two system states. Encoded the
/// delta of every altered account.
pub struct StateDiff (pub BTreeMap<Address, AccountDiff>);
impl StateDiff {
/// Get the actual data.
pub fn get(&self) -> &BTreeMap<Address, AccountDiff> {
&self.0
}
}
impl fmt::Display for StateDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (add, acc) in &self.0 {
try!(write!(f, "{} {}: {}", acc.existance(), add, acc));
}
Ok(())
}
}
impl Deref for StateDiff {
type Target = BTreeMap<Address, AccountDiff>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@ -349,6 +349,170 @@ impl Trace {
} }
} }
#[derive(Debug, Clone, PartialEq, Binary)]
/// A diff of some chunk of memory.
pub struct MemoryDiff {
/// Offset into memory the change begins.
pub offset: usize,
/// The changed data.
pub data: Bytes,
}
impl Encodable for MemoryDiff {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
s.append(&self.offset);
s.append(&self.data);
}
}
impl Decodable for MemoryDiff {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
Ok(MemoryDiff {
offset: try!(d.val_at(0)),
data: try!(d.val_at(1)),
})
}
}
#[derive(Debug, Clone, PartialEq, Binary)]
/// A diff of some storage value.
pub struct StorageDiff {
/// Which key in storage is changed.
pub location: U256,
/// What the value has been changed to.
pub value: U256,
}
impl Encodable for StorageDiff {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
s.append(&self.location);
s.append(&self.value);
}
}
impl Decodable for StorageDiff {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
Ok(StorageDiff {
location: try!(d.val_at(0)),
value: try!(d.val_at(1)),
})
}
}
#[derive(Debug, Clone, PartialEq, Binary)]
/// A record of an executed VM operation.
pub struct VMExecutedOperation {
/// The total gas used.
pub gas_used: U256,
/// The stack item placed, if any.
pub stack_push: Vec<U256>,
/// If altered, the memory delta.
pub mem_diff: Option<MemoryDiff>,
/// The altered storage value, if any.
pub store_diff: Option<StorageDiff>,
}
impl Encodable for VMExecutedOperation {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.gas_used);
s.append(&self.stack_push);
s.append(&self.mem_diff);
s.append(&self.store_diff);
}
}
impl Decodable for VMExecutedOperation {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
Ok(VMExecutedOperation {
gas_used: try!(d.val_at(0)),
stack_push: try!(d.val_at(1)),
mem_diff: try!(d.val_at(2)),
store_diff: try!(d.val_at(3)),
})
}
}
#[derive(Debug, Clone, PartialEq, Binary)]
/// A record of the execution of a single VM operation.
pub struct VMOperation {
/// The program counter.
pub pc: usize,
/// The instruction executed.
pub instruction: u8,
/// The gas cost for this instruction.
pub gas_cost: U256,
/// Information concerning the execution of the operation.
pub executed: Option<VMExecutedOperation>,
}
impl Encodable for VMOperation {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.pc);
s.append(&self.instruction);
s.append(&self.gas_cost);
s.append(&self.executed);
}
}
impl Decodable for VMOperation {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let res = VMOperation {
pc: try!(d.val_at(0)),
instruction: try!(d.val_at(1)),
gas_cost: try!(d.val_at(2)),
executed: try!(d.val_at(3)),
};
Ok(res)
}
}
#[derive(Debug, Clone, PartialEq, Binary, Default)]
/// A record of a full VM trace for a CALL/CREATE.
pub struct VMTrace {
/// The step (i.e. index into operations) at which this trace corresponds.
pub parent_step: usize,
/// The code to be executed.
pub code: Bytes,
/// The operations executed.
pub operations: Vec<VMOperation>,
/// The sub traces for each interior action performed as part of this call/create.
/// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction.
pub subs: Vec<VMTrace>,
}
impl Encodable for VMTrace {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.parent_step);
s.append(&self.code);
s.append(&self.operations);
s.append(&self.subs);
}
}
impl Decodable for VMTrace {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let res = VMTrace {
parent_step: try!(d.val_at(0)),
code: try!(d.val_at(1)),
operations: try!(d.val_at(2)),
subs: try!(d.val_at(3)),
};
Ok(res)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use util::{Address, U256, FixedHash}; use util::{Address, U256, FixedHash};

View File

@ -7,6 +7,6 @@ echo "set -e" >> $FILE
echo "cargo build --features dev" >> $FILE echo "cargo build --features dev" >> $FILE
# Build tests # Build tests
echo "cargo test --no-run --features dev \\" >> $FILE echo "cargo test --no-run --features dev \\" >> $FILE
echo " -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer -p ethcore-dapps -p ethcore-signer" >> $FILE echo " -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethcore-dapps -p ethcore-signer" >> $FILE
echo "" >> $FILE echo "" >> $FILE
chmod +x $FILE chmod +x $FILE

View File

@ -392,16 +392,13 @@ fn implement_client_method_body(
}); });
request_serialization_statements.push( request_serialization_statements.push(
quote_stmt!(cx, let mut socket_ref = self.socket.borrow_mut())); quote_stmt!(cx, let mut socket = self.socket.write().unwrap(); ));
request_serialization_statements.push(
quote_stmt!(cx, let mut socket = socket_ref.deref_mut()));
request_serialization_statements.push( request_serialization_statements.push(
quote_stmt!(cx, let serialized_payload = ::ipc::binary::serialize(&payload).unwrap())); quote_stmt!(cx, let serialized_payload = ::ipc::binary::serialize(&payload).unwrap()));
request_serialization_statements.push( request_serialization_statements.push(
quote_stmt!(cx, ::ipc::invoke($index_ident, &Some(serialized_payload), &mut socket))); quote_stmt!(cx, ::ipc::invoke($index_ident, &Some(serialized_payload), &mut *socket)));
request_serialization_statements request_serialization_statements
@ -409,17 +406,15 @@ fn implement_client_method_body(
else { else {
let mut request_serialization_statements = Vec::new(); let mut request_serialization_statements = Vec::new();
request_serialization_statements.push( request_serialization_statements.push(
quote_stmt!(cx, let mut socket_ref = self.socket.borrow_mut())); quote_stmt!(cx, let mut socket = self.socket.write().unwrap(); ));
request_serialization_statements.push( request_serialization_statements.push(
quote_stmt!(cx, let mut socket = socket_ref.deref_mut())); quote_stmt!(cx, ::ipc::invoke($index_ident, &None, &mut *socket)));
request_serialization_statements.push(
quote_stmt!(cx, ::ipc::invoke($index_ident, &None, &mut socket)));
request_serialization_statements request_serialization_statements
}; };
if let Some(ref return_ty) = dispatch.return_type_ty { if let Some(ref return_ty) = dispatch.return_type_ty {
let return_expr = quote_expr!(cx, let return_expr = quote_expr!(cx,
::ipc::binary::deserialize_from::<$return_ty, _>(&mut socket).unwrap() ::ipc::binary::deserialize_from::<$return_ty, _>(&mut *socket).unwrap()
); );
quote_expr!(cx, { quote_expr!(cx, {
$request $request
@ -525,7 +520,7 @@ fn push_client_struct(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map:
let client_struct_item = quote_item!(cx, let client_struct_item = quote_item!(cx,
pub struct $client_short_ident $generics { pub struct $client_short_ident $generics {
socket: ::std::cell::RefCell<S>, socket: ::std::sync::RwLock<S>,
phantom: $phantom, phantom: $phantom,
}); });
@ -560,7 +555,7 @@ fn push_with_socket_client_implementation(
impl $generics ::ipc::WithSocket<S> for $client_ident $where_clause { impl $generics ::ipc::WithSocket<S> for $client_ident $where_clause {
fn init(socket: S) -> $client_ident { fn init(socket: S) -> $client_ident {
$client_short_ident { $client_short_ident {
socket: ::std::cell::RefCell::new(socket), socket: ::std::sync::RwLock::new(socket),
phantom: ::std::marker::PhantomData, phantom: ::std::marker::PhantomData,
} }
} }
@ -594,15 +589,13 @@ fn push_client_implementation(
reserved: vec![0u8; 64], reserved: vec![0u8; 64],
}; };
let mut socket_ref = self.socket.borrow_mut();
let mut socket = socket_ref.deref_mut();
::ipc::invoke( ::ipc::invoke(
0, 0,
&Some(::ipc::binary::serialize(&payload).unwrap()), &Some(::ipc::binary::serialize(&payload).unwrap()),
&mut socket); &mut *self.socket.write().unwrap());
let mut result = vec![0u8; 1]; let mut result = vec![0u8; 1];
if try!(socket.read(&mut result).map_err(|_| ::ipc::Error::HandshakeFailed)) == 1 { if try!(self.socket.write().unwrap().read(&mut result).map_err(|_| ::ipc::Error::HandshakeFailed)) == 1 {
match result[0] { match result[0] {
1 => Ok(()), 1 => Ok(()),
_ => Err(::ipc::Error::RemoteServiceUnsupported), _ => Err(::ipc::Error::RemoteServiceUnsupported),
@ -613,7 +606,7 @@ fn push_client_implementation(
let socket_item = quote_impl_item!(cx, let socket_item = quote_impl_item!(cx,
#[cfg(test)] #[cfg(test)]
pub fn socket(&self) -> &::std::cell::RefCell<S> { pub fn socket(&self) -> &::std::sync::RwLock<S> {
&self.socket &self.socket
}).unwrap(); }).unwrap();

View File

@ -230,6 +230,9 @@ fn binary_expr_struct(
let field_amount = builder.id(&format!("{}",fields.len())); let field_amount = builder.id(&format!("{}",fields.len()));
map_stmts.push(quote_stmt!(cx, let mut map = vec![0usize; $field_amount];).unwrap()); map_stmts.push(quote_stmt!(cx, let mut map = vec![0usize; $field_amount];).unwrap());
map_stmts.push(quote_stmt!(cx, let mut total = 0usize;).unwrap()); map_stmts.push(quote_stmt!(cx, let mut total = 0usize;).unwrap());
let mut post_write_stmts = Vec::<ast::Stmt>::new();
for (index, field) in fields.iter().enumerate() { for (index, field) in fields.iter().enumerate() {
let field_type_ident = builder.id( let field_type_ident = builder.id(
&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty))); &::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)));
@ -249,6 +252,7 @@ fn binary_expr_struct(
}; };
let raw_ident = ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)); let raw_ident = ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty));
let range_ident = builder.id(format!("r{}", index));
match raw_ident.as_ref() { match raw_ident.as_ref() {
"u8" => { "u8" => {
write_stmts.push(quote_stmt!(cx, let next_line = offset + 1;).unwrap()); write_stmts.push(quote_stmt!(cx, let next_line = offset + 1;).unwrap());
@ -258,15 +262,17 @@ fn binary_expr_struct(
write_stmts.push(quote_stmt!(cx, let size = $member_expr .len();).unwrap()); write_stmts.push(quote_stmt!(cx, let size = $member_expr .len();).unwrap());
write_stmts.push(quote_stmt!(cx, let next_line = offset + size;).unwrap()); write_stmts.push(quote_stmt!(cx, let next_line = offset + size;).unwrap());
write_stmts.push(quote_stmt!(cx, length_stack.push_back(size);).unwrap()); write_stmts.push(quote_stmt!(cx, length_stack.push_back(size);).unwrap());
write_stmts.push(quote_stmt!(cx, buffer[offset..next_line].clone_from_slice($member_expr); ).unwrap()); write_stmts.push(quote_stmt!(cx, let $range_ident = offset..next_line; ).unwrap());
post_write_stmts.push(quote_stmt!(cx, buffer[$range_ident].clone_from_slice($member_expr); ).unwrap());
} }
_ => { _ => {
write_stmts.push(quote_stmt!(cx, let next_line = offset + match $field_type_ident_qualified::len_params() { write_stmts.push(quote_stmt!(cx, let next_line = offset + match $field_type_ident_qualified::len_params() {
0 => mem::size_of::<$field_type_ident>(), 0 => mem::size_of::<$field_type_ident>(),
_ => { let size = $member_expr .size(); length_stack.push_back(size); size }, _ => { let size = $member_expr .size(); length_stack.push_back(size); size },
}).unwrap()); }).unwrap());
write_stmts.push(quote_stmt!(cx, write_stmts.push(quote_stmt!(cx, let $range_ident = offset..next_line; ).unwrap());
if let Err(e) = $member_expr .to_bytes(&mut buffer[offset..next_line], length_stack) { return Err(e) };).unwrap()); post_write_stmts.push(quote_stmt!(cx,
if let Err(e) = $member_expr .to_bytes(&mut buffer[$range_ident], length_stack) { return Err(e) };).unwrap());
} }
} }
@ -312,7 +318,7 @@ fn binary_expr_struct(
Ok(BinaryExpressions { Ok(BinaryExpressions {
size: total_size_expr, size: total_size_expr,
write: quote_expr!(cx, { $write_stmts; Ok(()) } ), write: quote_expr!(cx, { $write_stmts; $post_write_stmts; Ok(()) } ),
read: read_expr, read: read_expr,
}) })
} }

View File

@ -30,6 +30,7 @@ use nanomsg::{Socket, Protocol, Error, Endpoint, PollRequest, PollFd, PollInOut}
use std::ops::Deref; use std::ops::Deref;
const POLL_TIMEOUT: isize = 100; const POLL_TIMEOUT: isize = 100;
const CLIENT_CONNECTION_TIMEOUT: isize = 2500;
/// Generic worker to handle service (binded) sockets /// Generic worker to handle service (binded) sockets
pub struct Worker<S> where S: IpcInterface<S> { pub struct Worker<S> where S: IpcInterface<S> {
@ -46,6 +47,12 @@ pub struct GuardedSocket<S> where S: WithSocket<Socket> {
_endpoint: Endpoint, _endpoint: Endpoint,
} }
impl<S> GuardedSocket<S> where S: WithSocket<Socket> {
pub fn service(&self) -> Arc<S> {
self.client.clone()
}
}
impl<S> Deref for GuardedSocket<S> where S: WithSocket<Socket> { impl<S> Deref for GuardedSocket<S> where S: WithSocket<Socket> {
type Target = S; type Target = S;
@ -63,6 +70,9 @@ pub fn init_duplex_client<S>(socket_addr: &str) -> Result<GuardedSocket<S>, Sock
SocketError::DuplexLink SocketError::DuplexLink
})); }));
// 2500 ms default timeout
socket.set_receive_timeout(CLIENT_CONNECTION_TIMEOUT).unwrap();
let endpoint = try!(socket.connect(socket_addr).map_err(|e| { let endpoint = try!(socket.connect(socket_addr).map_err(|e| {
warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e); warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e);
SocketError::DuplexLink SocketError::DuplexLink
@ -83,6 +93,9 @@ pub fn init_client<S>(socket_addr: &str) -> Result<GuardedSocket<S>, SocketError
SocketError::RequestLink SocketError::RequestLink
})); }));
// 2500 ms default timeout
socket.set_receive_timeout(CLIENT_CONNECTION_TIMEOUT).unwrap();
let endpoint = try!(socket.connect(socket_addr).map_err(|e| { let endpoint = try!(socket.connect(socket_addr).map_err(|e| {
warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e); warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e);
SocketError::RequestLink SocketError::RequestLink

View File

@ -104,14 +104,31 @@ impl<R: BinaryConvertable, E: BinaryConvertable> BinaryConvertable for Result<R,
fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> { fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> {
match *self { match *self {
Ok(ref r) => { buffer[0] = 0; Ok(try!(r.to_bytes(&mut buffer[1..], length_stack))) }, Ok(ref r) => {
Err(ref e) => { buffer[1] = 1; Ok(try!(e.to_bytes(&mut buffer[1..], length_stack))) }, buffer[0] = 0;
if r.size() > 0 {
Ok(try!(r.to_bytes(&mut buffer[1..], length_stack)))
}
else { Ok(()) }
},
Err(ref e) => {
buffer[0] = 1;
if e.size() > 0 {
Ok(try!(e.to_bytes(&mut buffer[1..], length_stack)))
}
else { Ok(()) }
},
} }
} }
fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> { fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> {
match buffer[0] { match buffer[0] {
0 => Ok(Ok(try!(R::from_bytes(&buffer[1..], length_stack)))), 0 => {
match buffer.len() {
1 => Ok(Ok(try!(R::from_empty_bytes()))),
_ => Ok(Ok(try!(R::from_bytes(&buffer[1..], length_stack)))),
}
}
1 => Ok(Err(try!(E::from_bytes(&buffer[1..], length_stack)))), 1 => Ok(Err(try!(E::from_bytes(&buffer[1..], length_stack)))),
_ => Err(BinaryConvertError) _ => Err(BinaryConvertError)
} }
@ -154,6 +171,8 @@ impl<T> BinaryConvertable for Vec<T> where T: BinaryConvertable {
_ => 128, _ => 128,
}); });
if buffer.len() == 0 { return Ok(result); }
loop { loop {
let next_size = match T::len_params() { let next_size = match T::len_params() {
0 => mem::size_of::<T>(), 0 => mem::size_of::<T>(),
@ -300,8 +319,8 @@ pub fn deserialize_from<T, R>(r: &mut R) -> Result<T, BinaryConvertError>
let mut payload = Vec::new(); let mut payload = Vec::new();
try!(r.read_to_end(&mut payload).map_err(|_| BinaryConvertError)); try!(r.read_to_end(&mut payload).map_err(|_| BinaryConvertError));
let mut length_stack = VecDeque::<usize>::new();
let stack_len = try!(u64::from_bytes(&payload[0..8], &mut fake_stack)) as usize; let stack_len = try!(u64::from_bytes(&payload[0..8], &mut fake_stack)) as usize;
let mut length_stack = VecDeque::<usize>::with_capacity(stack_len);
if stack_len > 0 { if stack_len > 0 {
for idx in 0..stack_len { for idx in 0..stack_len {
@ -607,7 +626,7 @@ fn deserialize_simple_err() {
} }
#[test] #[test]
fn deserialize_opt_vec_in_out() { fn serialize_opt_vec_in_out() {
use std::io::{Cursor, SeekFrom, Seek}; use std::io::{Cursor, SeekFrom, Seek};
let mut buff = Cursor::new(Vec::new()); let mut buff = Cursor::new(Vec::new());
@ -619,3 +638,17 @@ fn deserialize_opt_vec_in_out() {
assert!(vec.is_none()); assert!(vec.is_none());
} }
#[test]
fn serialize_err_opt_vec_in_out() {
use std::io::{Cursor, SeekFrom, Seek};
let mut buff = Cursor::new(Vec::new());
let optional_vec: Result<Option<Vec<u8>>, u32> = Ok(None);
serialize_into(&optional_vec, &mut buff).unwrap();
buff.seek(SeekFrom::Start(0)).unwrap();
let vec = deserialize_from::<Result<Option<Vec<u8>>, u32>, _>(&mut buff).unwrap();
assert!(vec.is_ok());
}

View File

@ -40,7 +40,7 @@ pub struct Transaction {
/// To. /// To.
pub to: MaybeEmpty<Address>, pub to: MaybeEmpty<Address>,
/// Value. /// Value.
pub value: Uint pub value: Uint,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,24 +0,0 @@
[package]
description = "Ethminer library"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "ethminer"
version = "1.2.0"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[build-dependencies]
rustc_version = "0.1"
[dependencies]
ethcore-util = { path = "../util" }
ethcore = { path = "../ethcore" }
log = "0.3"
env_logger = "0.3"
rustc-serialize = "0.3"
rayon = "0.3.1"
clippy = { version = "0.0.69", optional = true}
[features]
default = []
dev = ["clippy"]

View File

@ -96,6 +96,8 @@ API and Console Options:
asked for password on startup. asked for password on startup.
--dapps-pass PASSWORD Specify password for Dapps server. Use only in --dapps-pass PASSWORD Specify password for Dapps server. Use only in
conjunction with --dapps-user. conjunction with --dapps-user.
--dapps-path PATH Specify directory where dapps should be installed.
[default: $HOME/.parity/dapps]
--signer Enable Trusted Signer WebSocket endpoint used by --signer Enable Trusted Signer WebSocket endpoint used by
System UIs. System UIs.
@ -239,6 +241,7 @@ pub struct Args {
pub flag_dapps_interface: String, pub flag_dapps_interface: String,
pub flag_dapps_user: Option<String>, pub flag_dapps_user: Option<String>,
pub flag_dapps_pass: Option<String>, pub flag_dapps_pass: Option<String>,
pub flag_dapps_path: String,
pub flag_signer: bool, pub flag_signer: bool,
pub flag_signer_port: u16, pub flag_signer_port: u16,
pub flag_force_sealing: bool, pub flag_force_sealing: bool,

View File

@ -40,6 +40,7 @@ pub struct Configuration {
pub struct Directories { pub struct Directories {
pub keys: String, pub keys: String,
pub db: String, pub db: String,
pub dapps: String,
} }
impl Configuration { impl Configuration {
@ -325,11 +326,14 @@ impl Configuration {
&self.args.flag_keys_path &self.args.flag_keys_path
} }
); );
::std::fs::create_dir_all(&db_path).unwrap_or_else(|e| die_with_io_error("main", e)); ::std::fs::create_dir_all(&keys_path).unwrap_or_else(|e| die_with_io_error("main", e));
let dapps_path = Configuration::replace_home(&self.args.flag_dapps_path);
::std::fs::create_dir_all(&dapps_path).unwrap_or_else(|e| die_with_io_error("main", e));
Directories { Directories {
keys: keys_path, keys: keys_path,
db: db_path, db: db_path,
dapps: dapps_path,
} }
} }

View File

@ -17,14 +17,9 @@
use std::sync::Arc; use std::sync::Arc;
use std::str::FromStr; use std::str::FromStr;
use std::net::SocketAddr; use std::net::SocketAddr;
use ethcore::client::Client;
use ethsync::EthSync;
use ethminer::{Miner, ExternalMiner};
use util::RotatingLogger;
use util::panics::PanicHandler; use util::panics::PanicHandler;
use util::keys::store::AccountService;
use util::network_settings::NetworkSettings;
use die::*; use die::*;
use rpc_apis;
#[cfg(feature = "dapps")] #[cfg(feature = "dapps")]
pub use ethcore_dapps::Server as WebappServer; pub use ethcore_dapps::Server as WebappServer;
@ -37,17 +32,12 @@ pub struct Configuration {
pub port: u16, pub port: u16,
pub user: Option<String>, pub user: Option<String>,
pub pass: Option<String>, pub pass: Option<String>,
pub dapps_path: String,
} }
pub struct Dependencies { pub struct Dependencies {
pub panic_handler: Arc<PanicHandler>, pub panic_handler: Arc<PanicHandler>,
pub client: Arc<Client>, pub apis: Arc<rpc_apis::Dependencies>,
pub sync: Arc<EthSync>,
pub secret_store: Arc<AccountService>,
pub miner: Arc<Miner>,
pub external_miner: Arc<ExternalMiner>,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
} }
pub fn new(configuration: Configuration, deps: Dependencies) -> Option<WebappServer> { pub fn new(configuration: Configuration, deps: Dependencies) -> Option<WebappServer> {
@ -74,12 +64,13 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Option<WebappSer
(username.to_owned(), password) (username.to_owned(), password)
}); });
Some(setup_dapps_server(deps, &addr, auth)) Some(setup_dapps_server(deps, configuration.dapps_path, &addr, auth))
} }
#[cfg(not(feature = "dapps"))] #[cfg(not(feature = "dapps"))]
pub fn setup_dapps_server( pub fn setup_dapps_server(
_deps: Dependencies, _deps: Dependencies,
_dapps_path: String,
_url: &SocketAddr, _url: &SocketAddr,
_auth: Option<(String, String)>, _auth: Option<(String, String)>,
) -> ! { ) -> ! {
@ -89,20 +80,14 @@ pub fn setup_dapps_server(
#[cfg(feature = "dapps")] #[cfg(feature = "dapps")]
pub fn setup_dapps_server( pub fn setup_dapps_server(
deps: Dependencies, deps: Dependencies,
dapps_path: String,
url: &SocketAddr, url: &SocketAddr,
auth: Option<(String, String)> auth: Option<(String, String)>
) -> WebappServer { ) -> WebappServer {
use ethcore_rpc::v1::*;
use ethcore_dapps as dapps; use ethcore_dapps as dapps;
let server = dapps::ServerBuilder::new(); let server = dapps::ServerBuilder::new(dapps_path);
server.add_delegate(Web3Client::new().to_delegate()); let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate());
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate());
let start_result = match auth { let start_result = match auth {
None => { None => {
server.start_unsecure_http(url) server.start_unsecure_http(url)

View File

@ -27,7 +27,6 @@ extern crate rustc_serialize;
extern crate ethcore_util as util; extern crate ethcore_util as util;
extern crate ethcore; extern crate ethcore;
extern crate ethsync; extern crate ethsync;
extern crate ethminer;
#[macro_use] #[macro_use]
extern crate log as rlog; extern crate log as rlog;
extern crate env_logger; extern crate env_logger;
@ -67,6 +66,7 @@ mod cli;
mod configuration; mod configuration;
mod migration; mod migration;
mod signer; mod signer;
mod rpc_apis;
use std::io::{Write, Read, BufReader, BufRead}; use std::io::{Write, Read, BufReader, BufRead};
use std::ops::Deref; use std::ops::Deref;
@ -85,7 +85,7 @@ use ethcore::error::{Error, ImportError};
use ethcore::service::ClientService; use ethcore::service::ClientService;
use ethcore::spec::Spec; use ethcore::spec::Spec;
use ethsync::EthSync; use ethsync::EthSync;
use ethminer::{Miner, MinerService, ExternalMiner}; use ethcore::miner::{Miner, MinerService, ExternalMiner};
use daemonize::Daemonize; use daemonize::Daemonize;
use migration::migrate; use migration::migrate;
use informant::Informant; use informant::Informant;
@ -173,14 +173,6 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
// Secret Store // Secret Store
let account_service = Arc::new(conf.account_service()); let account_service = Arc::new(conf.account_service());
// Build client
let mut service = ClientService::start(
client_config, spec, net_settings, Path::new(&conf.path())
).unwrap_or_else(|e| die_with_error("Client", e));
panic_handler.forward_from(&service);
let client = service.client();
// Miner // Miner
let miner = Miner::with_accounts(conf.args.flag_force_sealing, conf.spec(), account_service.clone()); let miner = Miner::with_accounts(conf.args.flag_force_sealing, conf.spec(), account_service.clone());
miner.set_author(conf.author()); miner.set_author(conf.author());
@ -189,14 +181,23 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
miner.set_minimal_gas_price(conf.gas_price()); miner.set_minimal_gas_price(conf.gas_price());
miner.set_transactions_limit(conf.args.flag_tx_limit); miner.set_transactions_limit(conf.args.flag_tx_limit);
// Build client
let mut service = ClientService::start(
client_config, spec, net_settings, Path::new(&conf.path()), miner.clone()
).unwrap_or_else(|e| die_with_error("Client", e));
panic_handler.forward_from(&service);
let client = service.client();
let external_miner = Arc::new(ExternalMiner::default()); let external_miner = Arc::new(ExternalMiner::default());
let network_settings = Arc::new(conf.network_settings()); let network_settings = Arc::new(conf.network_settings());
// Sync // Sync
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone()); let sync = EthSync::register(service.network(), sync_config, client.clone());
let dependencies = Arc::new(rpc::Dependencies { let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies {
panic_handler: panic_handler.clone(), signer_enabled: conf.args.flag_signer,
signer_queue: Arc::new(rpc_apis::ConfirmationsQueue::default()),
client: client.clone(), client: client.clone(),
sync: sync.clone(), sync: sync.clone(),
secret_store: account_service.clone(), secret_store: account_service.clone(),
@ -206,6 +207,11 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
settings: network_settings.clone(), settings: network_settings.clone(),
}); });
let dependencies = rpc::Dependencies {
panic_handler: panic_handler.clone(),
apis: deps_for_rpc_apis.clone(),
};
// Setup http rpc // Setup http rpc
let rpc_server = rpc::new_http(rpc::HttpConfiguration { let rpc_server = rpc::new_http(rpc::HttpConfiguration {
enabled: network_settings.rpc_enabled, enabled: network_settings.rpc_enabled,
@ -225,28 +231,19 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
port: conf.args.flag_dapps_port, port: conf.args.flag_dapps_port,
user: conf.args.flag_dapps_user.clone(), user: conf.args.flag_dapps_user.clone(),
pass: conf.args.flag_dapps_pass.clone(), pass: conf.args.flag_dapps_pass.clone(),
dapps_path: conf.directories().dapps,
}, dapps::Dependencies { }, dapps::Dependencies {
panic_handler: panic_handler.clone(), panic_handler: panic_handler.clone(),
client: client.clone(), apis: deps_for_rpc_apis.clone(),
sync: sync.clone(),
secret_store: account_service.clone(),
miner: miner.clone(),
external_miner: external_miner.clone(),
logger: logger.clone(),
settings: network_settings.clone(),
}); });
// Set up a signer // Set up a signer
let signer_server = signer::start(signer::Configuration { let signer_server = signer::start(signer::Configuration {
enabled: conf.args.flag_signer, enabled: deps_for_rpc_apis.signer_enabled,
port: conf.args.flag_signer_port, port: conf.args.flag_signer_port,
}, signer::Dependencies { }, signer::Dependencies {
panic_handler: panic_handler.clone(), panic_handler: panic_handler.clone(),
client: client.clone(), apis: deps_for_rpc_apis.clone(),
sync: sync.clone(),
secret_store: account_service.clone(),
miner: miner.clone(),
external_miner: external_miner.clone(),
}); });
// Register IO handler // Register IO handler
@ -295,7 +292,7 @@ fn execute_export(conf: Configuration) {
// Build client // Build client
let service = ClientService::start( let service = ClientService::start(
client_config, spec, net_settings, Path::new(&conf.path()) client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()),
).unwrap_or_else(|e| die_with_error("Client", e)); ).unwrap_or_else(|e| die_with_error("Client", e));
panic_handler.forward_from(&service); panic_handler.forward_from(&service);
@ -366,7 +363,7 @@ fn execute_import(conf: Configuration) {
// Build client // Build client
let service = ClientService::start( let service = ClientService::start(
client_config, spec, net_settings, Path::new(&conf.path()) client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()),
).unwrap_or_else(|e| die_with_error("Client", e)); ).unwrap_or_else(|e| die_with_error("Client", e));
panic_handler.forward_from(&service); panic_handler.forward_from(&service);

View File

@ -15,19 +15,13 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::BTreeMap;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::net::SocketAddr; use std::net::SocketAddr;
use ethcore::client::Client;
use ethsync::EthSync;
use ethminer::{Miner, ExternalMiner};
use util::RotatingLogger;
use util::panics::PanicHandler; use util::panics::PanicHandler;
use util::keys::store::AccountService;
use util::network_settings::NetworkSettings;
use die::*; use die::*;
use jsonipc; use jsonipc;
use rpc_apis;
#[cfg(feature = "rpc")] #[cfg(feature = "rpc")]
pub use ethcore_rpc::Server as RpcServer; pub use ethcore_rpc::Server as RpcServer;
@ -52,16 +46,10 @@ pub struct IpcConfiguration {
pub struct Dependencies { pub struct Dependencies {
pub panic_handler: Arc<PanicHandler>, pub panic_handler: Arc<PanicHandler>,
pub client: Arc<Client>, pub apis: Arc<rpc_apis::Dependencies>,
pub sync: Arc<EthSync>,
pub secret_store: Arc<AccountService>,
pub miner: Arc<Miner>,
pub external_miner: Arc<ExternalMiner>,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
} }
pub fn new_http(conf: HttpConfiguration, deps: &Arc<Dependencies>) -> Option<RpcServer> { pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Option<RpcServer> {
if !conf.enabled { if !conf.enabled {
return None; return None;
} }
@ -78,58 +66,23 @@ pub fn new_http(conf: HttpConfiguration, deps: &Arc<Dependencies>) -> Option<Rpc
Some(setup_http_rpc_server(deps, &addr, conf.cors, apis)) Some(setup_http_rpc_server(deps, &addr, conf.cors, apis))
} }
pub fn new_ipc(conf: IpcConfiguration, deps: &Arc<Dependencies>) -> Option<jsonipc::Server> { pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Option<jsonipc::Server> {
if !conf.enabled { return None; } if !conf.enabled { return None; }
let apis = conf.apis.split(',').collect(); let apis = conf.apis.split(',').collect();
Some(setup_ipc_rpc_server(deps, &conf.socket_addr, apis)) Some(setup_ipc_rpc_server(deps, &conf.socket_addr, apis))
} }
fn setup_rpc_server(apis: Vec<&str>, deps: &Arc<Dependencies>) -> Server { fn setup_rpc_server(apis: Vec<&str>, deps: &Dependencies) -> Server {
use ethcore_rpc::v1::*; let apis = rpc_apis::from_str(apis);
let server = Server::new(); let server = Server::new();
let mut modules = BTreeMap::new(); rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::List(apis))
for api in apis.into_iter() {
match api {
"web3" => {
modules.insert("web3".to_owned(), "1.0".to_owned());
server.add_delegate(Web3Client::new().to_delegate());
},
"net" => {
modules.insert("net".to_owned(), "1.0".to_owned());
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
},
"eth" => {
modules.insert("eth".to_owned(), "1.0".to_owned());
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
},
"personal" => {
modules.insert("personal".to_owned(), "1.0".to_owned());
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate())
},
"ethcore" => {
modules.insert("ethcore".to_owned(), "1.0".to_owned());
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
},
"traces" => {
modules.insert("traces".to_owned(), "1.0".to_owned());
server.add_delegate(TracesClient::new(&deps.client).to_delegate())
},
_ => {
die!("{}: Invalid API name to be enabled.", api);
},
}
}
server.add_delegate(RpcClient::new(modules).to_delegate());
server
} }
#[cfg(not(feature = "rpc"))] #[cfg(not(feature = "rpc"))]
pub fn setup_http_rpc_server( pub fn setup_http_rpc_server(
_deps: Dependencies, _deps: &Dependencies,
_url: &SocketAddr, _url: &SocketAddr,
_cors_domain: Option<String>, _cors_domain: Vec<String>,
_apis: Vec<&str>, _apis: Vec<&str>,
) -> ! { ) -> ! {
die!("Your Parity version has been compiled without JSON-RPC support.") die!("Your Parity version has been compiled without JSON-RPC support.")
@ -137,27 +90,31 @@ pub fn setup_http_rpc_server(
#[cfg(feature = "rpc")] #[cfg(feature = "rpc")]
pub fn setup_http_rpc_server( pub fn setup_http_rpc_server(
dependencies: &Arc<Dependencies>, dependencies: &Dependencies,
url: &SocketAddr, url: &SocketAddr,
cors_domains: Vec<String>, cors_domains: Vec<String>,
apis: Vec<&str>, apis: Vec<&str>,
) -> RpcServer { ) -> RpcServer {
let server = setup_rpc_server(apis, dependencies); let server = setup_rpc_server(apis, dependencies);
let start_result = server.start_http(url, cors_domains); let start_result = server.start_http(url, cors_domains);
let deps = dependencies.clone(); let ph = dependencies.panic_handler.clone();
match start_result { match start_result {
Err(RpcServerError::IoError(err)) => die_with_io_error("RPC", err), Err(RpcServerError::IoError(err)) => die_with_io_error("RPC", err),
Err(e) => die!("RPC: {:?}", e), Err(e) => die!("RPC: {:?}", e),
Ok(server) => { Ok(server) => {
server.set_panic_handler(move || { server.set_panic_handler(move || {
deps.panic_handler.notify_all("Panic in RPC thread.".to_owned()); ph.notify_all("Panic in RPC thread.".to_owned());
}); });
server server
}, },
} }
} }
#[cfg(not(feature = "rpc"))]
pub fn setup_ipc_rpc_server(dependencies: &Arc<Dependencies>, addr: &str, apis: Vec<&str>) -> jsonipc::Server { pub fn setup_ipc_rpc_server(_dependencies: &Dependencies, _addr: &str, _apis: Vec<&str>) -> ! {
die!("Your Parity version has been compiled without JSON-RPC support.")
}
#[cfg(feature = "rpc")]
pub fn setup_ipc_rpc_server(dependencies: &Dependencies, addr: &str, apis: Vec<&str>) -> jsonipc::Server {
let server = setup_rpc_server(apis, dependencies); let server = setup_rpc_server(apis, dependencies);
match server.start_ipc(addr) { match server.start_ipc(addr) {
Err(jsonipc::Error::Io(io_error)) => die_with_io_error("RPC", io_error), Err(jsonipc::Error::Io(io_error)) => die_with_io_error("RPC", io_error),

168
parity/rpc_apis.rs Normal file
View File

@ -0,0 +1,168 @@
// 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 <http://www.gnu.org/licenses/>.
use std::collections::BTreeMap;
use std::str::FromStr;
use std::sync::Arc;
use die::*;
use ethsync::EthSync;
use ethcore::miner::{Miner, ExternalMiner};
use ethcore::client::Client;
use util::RotatingLogger;
use util::keys::store::AccountService;
use util::network_settings::NetworkSettings;
#[cfg(feature="rpc")]
pub use ethcore_rpc::ConfirmationsQueue;
#[cfg(not(feature="rpc"))]
#[derive(Default)]
pub struct ConfirmationsQueue;
#[cfg(feature="rpc")]
use ethcore_rpc::Extendable;
pub enum Api {
Web3,
Net,
Eth,
Personal,
Ethcore,
Traces,
Rpc,
}
pub enum ApiError {
UnknownApi(String)
}
pub enum ApiSet {
SafeContext,
UnsafeContext,
List(Vec<Api>),
}
impl FromStr for Api {
type Err = ApiError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use self::Api::*;
match s {
"web3" => Ok(Web3),
"net" => Ok(Net),
"eth" => Ok(Eth),
"personal" => Ok(Personal),
"ethcore" => Ok(Ethcore),
"traces" => Ok(Traces),
"rpc" => Ok(Rpc),
e => Err(ApiError::UnknownApi(e.into())),
}
}
}
pub struct Dependencies {
pub signer_enabled: bool,
pub signer_queue: Arc<ConfirmationsQueue>,
pub client: Arc<Client>,
pub sync: Arc<EthSync>,
pub secret_store: Arc<AccountService>,
pub miner: Arc<Miner>,
pub external_miner: Arc<ExternalMiner>,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
}
fn to_modules(apis: &[Api]) -> BTreeMap<String, String> {
let mut modules = BTreeMap::new();
for api in apis {
let (name, version) = match *api {
Api::Web3 => ("web3", "1.0"),
Api::Net => ("net", "1.0"),
Api::Eth => ("eth", "1.0"),
Api::Personal => ("personal", "1.0"),
Api::Ethcore => ("ethcore", "1.0"),
Api::Traces => ("traces", "1.0"),
Api::Rpc => ("rpc", "1.0"),
};
modules.insert(name.into(), version.into());
}
modules
}
pub fn from_str(apis: Vec<&str>) -> Vec<Api> {
apis.into_iter()
.map(Api::from_str)
.collect::<Result<Vec<Api>, ApiError>>()
.unwrap_or_else(|e| match e {
ApiError::UnknownApi(s) => die!("Unknown RPC API specified: {}", s),
})
}
fn list_apis(apis: ApiSet, signer_enabled: bool) -> Vec<Api> {
match apis {
ApiSet::List(apis) => apis,
ApiSet::UnsafeContext if signer_enabled => {
vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc]
}
_ => {
vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Ethcore, Api::Traces, Api::Rpc]
}
}
}
pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet) -> T {
use ethcore_rpc::v1::*;
let apis = list_apis(apis, deps.signer_enabled);
for api in &apis {
match *api {
Api::Web3 => {
server.add_delegate(Web3Client::new().to_delegate());
},
Api::Net => {
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
},
Api::Eth => {
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
if deps.signer_enabled {
server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue).to_delegate());
} else {
server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate());
}
},
Api::Personal => {
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate());
if deps.signer_enabled {
server.add_delegate(SignerClient::new(&deps.secret_store, &deps.client, &deps.miner, &deps.signer_queue).to_delegate());
}
},
Api::Ethcore => {
server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
},
Api::Traces => {
server.add_delegate(TracesClient::new(&deps.client).to_delegate())
},
Api::Rpc => {
let modules = to_modules(&apis);
server.add_delegate(RpcClient::new(modules).to_delegate());
}
}
}
server
}

View File

@ -15,12 +15,9 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc; use std::sync::Arc;
use ethcore::client::Client;
use ethsync::EthSync;
use ethminer::{Miner, ExternalMiner};
use util::keys::store::AccountService;
use util::panics::{PanicHandler, ForwardPanic}; use util::panics::{PanicHandler, ForwardPanic};
use die::*; use die::*;
use rpc_apis;
#[cfg(feature = "ethcore-signer")] #[cfg(feature = "ethcore-signer")]
use ethcore_signer as signer; use ethcore_signer as signer;
@ -36,11 +33,7 @@ pub struct Configuration {
pub struct Dependencies { pub struct Dependencies {
pub panic_handler: Arc<PanicHandler>, pub panic_handler: Arc<PanicHandler>,
pub client: Arc<Client>, pub apis: Arc<rpc_apis::Dependencies>,
pub sync: Arc<EthSync>,
pub secret_store: Arc<AccountService>,
pub miner: Arc<Miner>,
pub external_miner: Arc<ExternalMiner>,
} }
pub fn start(conf: Configuration, deps: Dependencies) -> Option<SignerServer> { pub fn start(conf: Configuration, deps: Dependencies) -> Option<SignerServer> {
@ -58,13 +51,8 @@ fn do_start(conf: Configuration, deps: Dependencies) -> SignerServer {
}); });
let start_result = { let start_result = {
use ethcore_rpc::v1::*; let server = signer::ServerBuilder::new(deps.apis.signer_queue.clone());
let server = signer::ServerBuilder::new(); let server = rpc_apis::setup_rpc(server, deps.apis, rpc_apis::ApiSet::SafeContext);
server.add_delegate(Web3Client::new().to_delegate());
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate());
server.start(addr) server.start(addr)
}; };

View File

@ -18,7 +18,6 @@ ethcore-util = { path = "../util" }
ethcore = { path = "../ethcore" } ethcore = { path = "../ethcore" }
ethash = { path = "../ethash" } ethash = { path = "../ethash" }
ethsync = { path = "../sync" } ethsync = { path = "../sync" }
ethminer = { path = "../miner" }
ethjson = { path = "../json" } ethjson = { path = "../json" }
ethcore-devtools = { path = "../devtools" } ethcore-devtools = { path = "../devtools" }
rustc-serialize = "0.3" rustc-serialize = "0.3"
@ -34,4 +33,4 @@ syntex = "*"
[features] [features]
default = ["serde_codegen"] default = ["serde_codegen"]
nightly = ["serde_macros"] nightly = ["serde_macros"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethminer/dev"] dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev"]

View File

@ -26,10 +26,10 @@ extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate jsonrpc_core; extern crate jsonrpc_core;
extern crate jsonrpc_http_server; extern crate jsonrpc_http_server;
#[macro_use]
extern crate ethcore_util as util; extern crate ethcore_util as util;
extern crate ethcore; extern crate ethcore;
extern crate ethsync; extern crate ethsync;
extern crate ethminer;
extern crate transient_hashmap; extern crate transient_hashmap;
extern crate json_ipc_server as ipc; extern crate json_ipc_server as ipc;
@ -44,12 +44,26 @@ use self::jsonrpc_core::{IoHandler, IoDelegate};
pub use jsonrpc_http_server::{Server, RpcServerError}; pub use jsonrpc_http_server::{Server, RpcServerError};
pub mod v1; pub mod v1;
pub use v1::{SigningQueue, ConfirmationsQueue};
/// An object that can be extended with `IoDelegates`
pub trait Extendable {
/// Add `Delegate` to this object.
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>);
}
/// Http server. /// Http server.
pub struct RpcServer { pub struct RpcServer {
handler: Arc<jsonrpc_core::io::IoHandler>, handler: Arc<jsonrpc_core::io::IoHandler>,
} }
impl Extendable for RpcServer {
/// Add io delegate.
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
self.handler.add_delegate(delegate);
}
}
impl RpcServer { impl RpcServer {
/// Construct new http server object. /// Construct new http server object.
pub fn new() -> RpcServer { pub fn new() -> RpcServer {
@ -58,11 +72,6 @@ impl RpcServer {
} }
} }
/// Add io delegate.
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
self.handler.add_delegate(delegate);
}
/// Start http server asynchronously and returns result with `Server` handle on success or an error. /// Start http server asynchronously and returns result with `Server` handle on success or an error.
pub fn start_http(&self, addr: &SocketAddr, cors_domains: Vec<String>) -> Result<Server, RpcServerError> { pub fn start_http(&self, addr: &SocketAddr, cors_domains: Vec<String>) -> Result<Server, RpcServerError> {
let cors_domains = cors_domains.into_iter() let cors_domains = cors_domains.into_iter()

View File

@ -16,6 +16,8 @@
mod poll_manager; mod poll_manager;
mod poll_filter; mod poll_filter;
mod signing_queue;
pub use self::poll_manager::PollManager; pub use self::poll_manager::PollManager;
pub use self::poll_filter::PollFilter; pub use self::poll_filter::PollFilter;
pub use self::signing_queue::{ConfirmationsQueue, SigningQueue};

View File

@ -0,0 +1,367 @@
// 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 <http://www.gnu.org/licenses/>.
use std::thread;
use std::time::{Instant, Duration};
use std::sync::{mpsc, Mutex, RwLock, Arc};
use std::collections::HashMap;
use v1::types::{TransactionRequest, TransactionConfirmation};
use util::{U256, H256};
/// Possible events happening in the queue that can be listened to.
#[derive(Debug, PartialEq)]
pub enum QueueEvent {
/// Receiver should stop work upon receiving `Finish` message.
Finish,
/// Informs about new request.
NewRequest(U256),
/// Request rejected.
RequestRejected(U256),
/// Request resolved.
RequestConfirmed(U256),
}
/// Defines possible errors returned from queue receiving method.
#[derive(Debug, PartialEq)]
pub enum QueueError {
/// Returned when method has been already used (no receiver available).
AlreadyUsed,
/// Returned when receiver encounters an error.
ReceiverError(mpsc::RecvError),
}
/// Message Receiver type
pub type QueueEventReceiver = mpsc::Receiver<QueueEvent>;
/// A queue of transactions awaiting to be confirmed and signed.
pub trait SigningQueue: Send + Sync {
/// Add new request to the queue.
/// Returns a `ConfirmationPromise` that can be used to await for resolution of given request.
fn add_request(&self, transaction: TransactionRequest) -> ConfirmationPromise;
/// Removes a request from the queue.
/// Notifies possible token holders that transaction was rejected.
fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation>;
/// Removes a request from the queue.
/// Notifies possible token holders that transaction was confirmed and given hash was assigned.
fn request_confirmed(&self, id: U256, hash: H256) -> Option<TransactionConfirmation>;
/// Returns a request if it is contained in the queue.
fn peek(&self, id: &U256) -> Option<TransactionConfirmation>;
/// Return copy of all the requests in the queue.
fn requests(&self) -> Vec<TransactionConfirmation>;
}
#[derive(Debug, PartialEq)]
enum ConfirmationResult {
/// The transaction has not yet been confirmed nor rejected.
Waiting,
/// The transaction has been rejected.
Rejected,
/// The transaction has been confirmed.
Confirmed(H256),
}
/// Time you need to confirm the transaction in UI.
/// This is the amount of time token holder will wait before
/// returning `None`.
/// Unless we have a multi-threaded RPC this will lock
/// any other incoming call!
const QUEUE_TIMEOUT_DURATION_SEC : u64 = 20;
/// A handle to submitted request.
/// Allows to block and wait for a resolution of that request.
pub struct ConfirmationToken {
result: Arc<Mutex<ConfirmationResult>>,
handle: thread::Thread,
request: TransactionConfirmation,
}
pub struct ConfirmationPromise {
id: U256,
result: Arc<Mutex<ConfirmationResult>>,
}
impl ConfirmationToken {
/// Submit solution to all listeners
fn resolve(&self, result: Option<H256>) {
let mut res = self.result.lock().unwrap();
*res = result.map_or(ConfirmationResult::Rejected, |h| ConfirmationResult::Confirmed(h));
// Notify listener
self.handle.unpark();
}
fn as_promise(&self) -> ConfirmationPromise {
ConfirmationPromise {
id: self.request.id,
result: self.result.clone(),
}
}
}
impl ConfirmationPromise {
/// Blocks current thread and awaits for
/// resolution of the transaction (rejected / confirmed)
/// Returns `None` if transaction was rejected or timeout reached.
/// Returns `Some(hash)` if transaction was confirmed.
pub fn wait_with_timeout(&self) -> Option<H256> {
let timeout = Duration::from_secs(QUEUE_TIMEOUT_DURATION_SEC);
let deadline = Instant::now() + timeout;
info!(target: "own_tx", "Signer: Awaiting transaction confirmation... ({:?}).", self.id);
loop {
let now = Instant::now();
if now >= deadline {
break;
}
// Park thread (may wake up spuriously)
thread::park_timeout(deadline - now);
// Take confirmation result
let res = self.result.lock().unwrap();
// Check the result
match *res {
ConfirmationResult::Rejected => return None,
ConfirmationResult::Confirmed(h) => return Some(h),
ConfirmationResult::Waiting => continue,
}
}
// We reached the timeout. Just return `None`
trace!(target: "own_tx", "Signer: Confirmation timeout reached... ({:?}).", self.id);
None
}
}
/// Queue for all unconfirmed transactions.
pub struct ConfirmationsQueue {
id: Mutex<U256>,
queue: RwLock<HashMap<U256, ConfirmationToken>>,
sender: Mutex<mpsc::Sender<QueueEvent>>,
receiver: Mutex<Option<mpsc::Receiver<QueueEvent>>>,
}
impl Default for ConfirmationsQueue {
fn default() -> Self {
let (send, recv) = mpsc::channel();
ConfirmationsQueue {
id: Mutex::new(U256::from(0)),
queue: RwLock::new(HashMap::new()),
sender: Mutex::new(send),
receiver: Mutex::new(Some(recv)),
}
}
}
impl ConfirmationsQueue {
/// Blocks the thread and starts listening for notifications regarding all actions in the queue.
/// For each event, `listener` callback will be invoked.
/// This method can be used only once (only single consumer of events can exist).
pub fn start_listening<F>(&self, listener: F) -> Result<(), QueueError>
where F: Fn(QueueEvent) -> () {
let recv = self.receiver.lock().unwrap().take();
if let None = recv {
return Err(QueueError::AlreadyUsed);
}
let recv = recv.expect("Check for none is done earlier.");
loop {
let message = try!(recv.recv().map_err(|e| QueueError::ReceiverError(e)));
if let QueueEvent::Finish = message {
return Ok(());
}
listener(message);
}
}
/// Notifies consumer that the communcation is over.
/// No more events will be sent after this function is invoked.
pub fn finish(&self) {
self.notify(QueueEvent::Finish);
}
/// Notifies receiver about the event happening in this queue.
fn notify(&self, message: QueueEvent) {
// We don't really care about the result
let _ = self.sender.lock().unwrap().send(message);
}
/// Removes transaction from this queue and notifies `ConfirmationPromise` holders about the result.
/// Notifies also a receiver about that event.
fn remove(&self, id: U256, result: Option<H256>) -> Option<TransactionConfirmation> {
let token = self.queue.write().unwrap().remove(&id);
if let Some(token) = token {
// notify receiver about the event
self.notify(result.map_or_else(
|| QueueEvent::RequestRejected(id),
|_| QueueEvent::RequestConfirmed(id)
));
// notify token holders about resolution
token.resolve(result);
// return a result
return Some(token.request.clone());
}
None
}
}
impl Drop for ConfirmationsQueue {
fn drop(&mut self) {
self.finish();
}
}
impl SigningQueue for ConfirmationsQueue {
fn add_request(&self, transaction: TransactionRequest) -> ConfirmationPromise {
// Increment id
let id = {
let mut last_id = self.id.lock().unwrap();
*last_id = *last_id + U256::from(1);
*last_id
};
// Add request to queue
let res = {
let mut queue = self.queue.write().unwrap();
queue.insert(id, ConfirmationToken {
result: Arc::new(Mutex::new(ConfirmationResult::Waiting)),
handle: thread::current(),
request: TransactionConfirmation {
id: id,
transaction: transaction,
},
});
debug!(target: "own_tx", "Signer: New transaction ({:?}) in confirmation queue.", id);
queue.get(&id).map(|token| token.as_promise()).expect("Token was just inserted.")
};
// Notify listeners
self.notify(QueueEvent::NewRequest(id));
res
}
fn peek(&self, id: &U256) -> Option<TransactionConfirmation> {
self.queue.read().unwrap().get(id).map(|token| token.request.clone())
}
fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation> {
debug!(target: "own_tx", "Signer: Transaction rejected ({:?}).", id);
self.remove(id, None)
}
fn request_confirmed(&self, id: U256, hash: H256) -> Option<TransactionConfirmation> {
debug!(target: "own_tx", "Signer: Transaction confirmed ({:?}).", id);
self.remove(id, Some(hash))
}
fn requests(&self) -> Vec<TransactionConfirmation> {
let queue = self.queue.read().unwrap();
queue.values().map(|token| token.request.clone()).collect()
}
}
#[cfg(test)]
mod test {
use std::time::Duration;
use std::thread;
use std::sync::{Arc, Mutex};
use util::hash::Address;
use util::numbers::{U256, H256};
use v1::types::TransactionRequest;
use super::*;
fn request() -> TransactionRequest {
TransactionRequest {
from: Address::from(1),
to: Some(Address::from(2)),
gas_price: None,
gas: None,
value: Some(U256::from(10_000_000)),
data: None,
nonce: None,
}
}
#[test]
fn should_wait_for_hash() {
// given
let queue = Arc::new(ConfirmationsQueue::default());
let request = request();
// when
let q = queue.clone();
let handle = thread::spawn(move || {
let v = q.add_request(request);
v.wait_with_timeout().expect("Should return hash")
});
let id = U256::from(1);
while queue.peek(&id).is_none() {
// Just wait for the other thread to start
thread::sleep(Duration::from_millis(100));
}
queue.request_confirmed(id, H256::from(1));
// then
assert_eq!(handle.join().expect("Thread should finish nicely"), H256::from(1));
}
#[test]
fn should_receive_notification() {
// given
let received = Arc::new(Mutex::new(None));
let queue = Arc::new(ConfirmationsQueue::default());
let request = request();
// when
let q = queue.clone();
let r = received.clone();
let handle = thread::spawn(move || {
q.start_listening(move |notification| {
let mut v = r.lock().unwrap();
*v = Some(notification);
}).expect("Should be closed nicely.")
});
queue.add_request(request);
queue.finish();
// then
handle.join().expect("Thread should finish nicely");
let r = received.lock().unwrap().take();
assert_eq!(r, Some(QueueEvent::NewRequest(U256::from(1))));
}
#[test]
fn should_add_transactions() {
// given
let queue = ConfirmationsQueue::default();
let request = request();
// when
queue.add_request(request.clone());
let all = queue.requests();
// then
assert_eq!(all.len(), 1);
let el = all.get(0).unwrap();
assert_eq!(el.id, U256::from(1));
assert_eq!(el.transaction, request);
}
}

View File

@ -18,17 +18,16 @@
extern crate ethash; extern crate ethash;
use std::collections::HashSet;
use std::sync::{Arc, Weak, Mutex}; use std::sync::{Arc, Weak, Mutex};
use std::ops::Deref; use std::ops::Deref;
use ethsync::{SyncProvider, SyncState}; use ethsync::{SyncProvider, SyncState};
use ethminer::{MinerService, ExternalMinerService}; use ethcore::miner::{MinerService, ExternalMinerService};
use jsonrpc_core::*; use jsonrpc_core::*;
use util::numbers::*; use util::numbers::*;
use util::sha3::*; use util::sha3::*;
use util::rlp::{encode, decode, UntrustedRlp, View}; use util::rlp::{encode, decode, UntrustedRlp, View};
use util::keys::store::AccountProvider; use util::keys::store::AccountProvider;
use ethcore::client::{BlockChainClient, BlockID, TransactionID, UncleID}; use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID};
use ethcore::block::IsBlock; use ethcore::block::IsBlock;
use ethcore::views::*; use ethcore::views::*;
use ethcore::ethereum::Ethash; use ethcore::ethereum::Ethash;
@ -36,15 +35,14 @@ use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Act
use ethcore::log_entry::LogEntry; use ethcore::log_entry::LogEntry;
use ethcore::filter::Filter as EthcoreFilter; use ethcore::filter::Filter as EthcoreFilter;
use self::ethash::SeedHashCompute; use self::ethash::SeedHashCompute;
use v1::traits::{Eth, EthFilter}; use v1::traits::Eth;
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
use v1::helpers::{PollFilter, PollManager}; use v1::impls::dispatch_transaction;
use v1::impls::{dispatch_transaction, sign_and_dispatch};
use serde; use serde;
/// Eth rpc implementation. /// Eth rpc implementation.
pub struct EthClient<C, S, A, M, EM> where pub struct EthClient<C, S, A, M, EM> where
C: BlockChainClient, C: MiningBlockChainClient,
S: SyncProvider, S: SyncProvider,
A: AccountProvider, A: AccountProvider,
M: MinerService, M: MinerService,
@ -59,7 +57,7 @@ pub struct EthClient<C, S, A, M, EM> where
} }
impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> where impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> where
C: BlockChainClient, C: MiningBlockChainClient,
S: SyncProvider, S: SyncProvider,
A: AccountProvider, A: AccountProvider,
M: MinerService, M: MinerService,
@ -170,6 +168,25 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> where
} }
} }
pub fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
let receipts = miner.pending_receipts();
let pending_logs = receipts.into_iter()
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
.collect::<Vec<(H256, LogEntry)>>();
let result = pending_logs.into_iter()
.filter(|pair| filter.matches(&pair.1))
.map(|pair| {
let mut log = Log::from(pair.1);
log.transaction_hash = Some(pair.0);
log
})
.collect();
result
}
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6. const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
fn params_len(params: &Params) -> usize { fn params_len(params: &Params) -> usize {
@ -193,25 +210,6 @@ fn from_params_default_third<F1, F2>(params: Params) -> Result<(F1, F2, BlockNum
} }
} }
fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
let receipts = miner.pending_receipts();
let pending_logs = receipts.into_iter()
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
.collect::<Vec<(H256, LogEntry)>>();
let result = pending_logs.into_iter()
.filter(|pair| filter.matches(&pair.1))
.map(|pair| {
let mut log = Log::from(pair.1);
log.transaction_hash = Some(pair.0);
log
})
.collect();
result
}
// must be in range [-32099, -32000] // must be in range [-32099, -32000]
const UNSUPPORTED_REQUEST_CODE: i64 = -32000; const UNSUPPORTED_REQUEST_CODE: i64 = -32000;
@ -224,7 +222,7 @@ fn make_unsupported_err() -> Error {
} }
impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
C: BlockChainClient + 'static, C: MiningBlockChainClient + 'static,
S: SyncProvider + 'static, S: SyncProvider + 'static,
A: AccountProvider + 'static, A: AccountProvider + 'static,
M: MinerService + 'static, M: MinerService + 'static,
@ -496,29 +494,12 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
}) })
} }
fn sign(&self, params: Params) -> Result<Value, Error> {
from_params::<(Address, H256)>(params).and_then(|(addr, msg)| {
to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero()))
})
}
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, )>(params)
.and_then(|(request, )| {
let accounts = take_weak!(self.accounts);
match accounts.account_secret(&request.from) {
Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret),
Err(_) => to_value(&H256::zero())
}
})
}
fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> { fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(Bytes, )>(params) from_params::<(Bytes, )>(params)
.and_then(|(raw_transaction, )| { .and_then(|(raw_transaction, )| {
let raw_transaction = raw_transaction.to_vec(); let raw_transaction = raw_transaction.to_vec();
match UntrustedRlp::new(&raw_transaction).as_val() { match UntrustedRlp::new(&raw_transaction).as_val() {
Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction), Ok(signed_transaction) => to_value(&dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction)),
Err(_) => to_value(&H256::zero()), Err(_) => to_value(&H256::zero()),
} }
}) })
@ -530,8 +511,8 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
.and_then(|(request, block_number,)| { .and_then(|(request, block_number,)| {
let signed = try!(self.sign_call(request)); let signed = try!(self.sign_call(request));
let r = match block_number { let r = match block_number {
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed), BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()),
BlockNumber::Latest => take_weak!(self.client).call(&signed), BlockNumber::Latest => take_weak!(self.client).call(&signed, Default::default()),
_ => panic!("{:?}", block_number), _ => panic!("{:?}", block_number),
}; };
to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![]))) to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![])))
@ -543,8 +524,8 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
.and_then(|(request, block_number,)| { .and_then(|(request, block_number,)| {
let signed = try!(self.sign_call(request)); let signed = try!(self.sign_call(request));
let r = match block_number { let r = match block_number {
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed), BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()),
BlockNumber::Latest => take_weak!(self.client).call(&signed), BlockNumber::Latest => take_weak!(self.client).call(&signed, Default::default()),
_ => return Err(Error::invalid_params()), _ => return Err(Error::invalid_params()),
}; };
to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))) to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))
@ -563,186 +544,3 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
rpc_unimplemented!() rpc_unimplemented!()
} }
} }
/// Eth filter rpc implementation.
pub struct EthFilterClient<C, M> where
C: BlockChainClient,
M: MinerService {
client: Weak<C>,
miner: Weak<M>,
polls: Mutex<PollManager<PollFilter>>,
}
impl<C, M> EthFilterClient<C, M> where
C: BlockChainClient,
M: MinerService {
/// Creates new Eth filter client.
pub fn new(client: &Arc<C>, miner: &Arc<M>) -> Self {
EthFilterClient {
client: Arc::downgrade(client),
miner: Arc::downgrade(miner),
polls: Mutex::new(PollManager::new()),
}
}
}
impl<C, M> EthFilter for EthFilterClient<C, M> where
C: BlockChainClient + 'static,
M: MinerService + 'static {
fn new_filter(&self, params: Params) -> Result<Value, Error> {
from_params::<(Filter,)>(params)
.and_then(|(filter,)| {
let mut polls = self.polls.lock().unwrap();
let block_number = take_weak!(self.client).chain_info().best_block_number;
let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter));
to_value(&U256::from(id))
})
}
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => {
let mut polls = self.polls.lock().unwrap();
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
to_value(&U256::from(id))
},
_ => Err(Error::invalid_params())
}
}
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => {
let mut polls = self.polls.lock().unwrap();
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
to_value(&U256::from(id))
},
_ => Err(Error::invalid_params())
}
}
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
let client = take_weak!(self.client);
from_params::<(Index,)>(params)
.and_then(|(index,)| {
let mut polls = self.polls.lock().unwrap();
match polls.poll_mut(&index.value()) {
None => Ok(Value::Array(vec![] as Vec<Value>)),
Some(filter) => match *filter {
PollFilter::Block(ref mut block_number) => {
// + 1, cause we want to return hashes including current block hash.
let current_number = client.chain_info().best_block_number + 1;
let hashes = (*block_number..current_number).into_iter()
.map(BlockID::Number)
.filter_map(|id| client.block_hash(id))
.collect::<Vec<H256>>();
*block_number = current_number;
to_value(&hashes)
},
PollFilter::PendingTransaction(ref mut previous_hashes) => {
// get hashes of pending transactions
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
let new_hashes =
{
let previous_hashes_set = previous_hashes.iter().collect::<HashSet<_>>();
// find all new hashes
current_hashes
.iter()
.filter(|hash| !previous_hashes_set.contains(hash))
.cloned()
.collect::<Vec<H256>>()
};
// save all hashes of pending transactions
*previous_hashes = current_hashes;
// return new hashes
to_value(&new_hashes)
},
PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => {
// retrive the current block number
let current_number = client.chain_info().best_block_number;
// check if we need to check pending hashes
let include_pending = filter.to_block == Some(BlockNumber::Pending);
// build appropriate filter
let mut filter: EthcoreFilter = filter.clone().into();
filter.from_block = BlockID::Number(*block_number);
filter.to_block = BlockID::Latest;
// retrieve logs in range from_block..min(BlockID::Latest..to_block)
let mut logs = client.logs(filter.clone())
.into_iter()
.map(From::from)
.collect::<Vec<Log>>();
// additionally retrieve pending logs
if include_pending {
let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter);
// remove logs about which client was already notified about
let new_pending_logs: Vec<_> = pending_logs.iter()
.filter(|p| !previous_logs.contains(p))
.cloned()
.collect();
// save all logs retrieved by client
*previous_logs = pending_logs.into_iter().collect();
// append logs array with new pending logs
logs.extend(new_pending_logs);
}
// save current block number as next from block number
*block_number = current_number;
to_value(&logs)
}
}
}
})
}
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
from_params::<(Index,)>(params)
.and_then(|(index,)| {
let mut polls = self.polls.lock().unwrap();
match polls.poll(&index.value()) {
Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => {
let include_pending = filter.to_block == Some(BlockNumber::Pending);
let filter: EthcoreFilter = filter.clone().into();
let mut logs = take_weak!(self.client).logs(filter.clone())
.into_iter()
.map(From::from)
.collect::<Vec<Log>>();
if include_pending {
logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter));
}
to_value(&logs)
},
// just empty array
_ => Ok(Value::Array(vec![] as Vec<Value>)),
}
})
}
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
from_params::<(Index,)>(params)
.and_then(|(index,)| {
self.polls.lock().unwrap().remove_poll(&index.value());
to_value(&true)
})
}
}

View File

@ -0,0 +1,215 @@
// 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 <http://www.gnu.org/licenses/>.
//! Eth Filter RPC implementation
use std::ops::Deref;
use std::sync::{Arc, Weak, Mutex};
use std::collections::HashSet;
use jsonrpc_core::*;
use util::numbers::*;
use ethcore::miner::MinerService;
use ethcore::filter::Filter as EthcoreFilter;
use ethcore::client::{BlockChainClient, BlockID};
use v1::traits::EthFilter;
use v1::types::{BlockNumber, Index, Filter, Log};
use v1::helpers::{PollFilter, PollManager};
use v1::impls::eth::pending_logs;
/// Eth filter rpc implementation.
pub struct EthFilterClient<C, M> where
C: BlockChainClient,
M: MinerService {
client: Weak<C>,
miner: Weak<M>,
polls: Mutex<PollManager<PollFilter>>,
}
impl<C, M> EthFilterClient<C, M> where
C: BlockChainClient,
M: MinerService {
/// Creates new Eth filter client.
pub fn new(client: &Arc<C>, miner: &Arc<M>) -> Self {
EthFilterClient {
client: Arc::downgrade(client),
miner: Arc::downgrade(miner),
polls: Mutex::new(PollManager::new()),
}
}
}
impl<C, M> EthFilter for EthFilterClient<C, M> where
C: BlockChainClient + 'static,
M: MinerService + 'static {
fn new_filter(&self, params: Params) -> Result<Value, Error> {
from_params::<(Filter,)>(params)
.and_then(|(filter,)| {
let mut polls = self.polls.lock().unwrap();
let block_number = take_weak!(self.client).chain_info().best_block_number;
let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter));
to_value(&U256::from(id))
})
}
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => {
let mut polls = self.polls.lock().unwrap();
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
to_value(&U256::from(id))
},
_ => Err(Error::invalid_params())
}
}
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => {
let mut polls = self.polls.lock().unwrap();
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
to_value(&U256::from(id))
},
_ => Err(Error::invalid_params())
}
}
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
let client = take_weak!(self.client);
from_params::<(Index,)>(params)
.and_then(|(index,)| {
let mut polls = self.polls.lock().unwrap();
match polls.poll_mut(&index.value()) {
None => Ok(Value::Array(vec![] as Vec<Value>)),
Some(filter) => match *filter {
PollFilter::Block(ref mut block_number) => {
// + 1, cause we want to return hashes including current block hash.
let current_number = client.chain_info().best_block_number + 1;
let hashes = (*block_number..current_number).into_iter()
.map(BlockID::Number)
.filter_map(|id| client.block_hash(id))
.collect::<Vec<H256>>();
*block_number = current_number;
to_value(&hashes)
},
PollFilter::PendingTransaction(ref mut previous_hashes) => {
// get hashes of pending transactions
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
let new_hashes =
{
let previous_hashes_set = previous_hashes.iter().collect::<HashSet<_>>();
// find all new hashes
current_hashes
.iter()
.filter(|hash| !previous_hashes_set.contains(hash))
.cloned()
.collect::<Vec<H256>>()
};
// save all hashes of pending transactions
*previous_hashes = current_hashes;
// return new hashes
to_value(&new_hashes)
},
PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => {
// retrive the current block number
let current_number = client.chain_info().best_block_number;
// check if we need to check pending hashes
let include_pending = filter.to_block == Some(BlockNumber::Pending);
// build appropriate filter
let mut filter: EthcoreFilter = filter.clone().into();
filter.from_block = BlockID::Number(*block_number);
filter.to_block = BlockID::Latest;
// retrieve logs in range from_block..min(BlockID::Latest..to_block)
let mut logs = client.logs(filter.clone())
.into_iter()
.map(From::from)
.collect::<Vec<Log>>();
// additionally retrieve pending logs
if include_pending {
let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter);
// remove logs about which client was already notified about
let new_pending_logs: Vec<_> = pending_logs.iter()
.filter(|p| !previous_logs.contains(p))
.cloned()
.collect();
// save all logs retrieved by client
*previous_logs = pending_logs.into_iter().collect();
// append logs array with new pending logs
logs.extend(new_pending_logs);
}
// save the number of the next block as a first block from which
// we want to get logs
*block_number = current_number + 1;
to_value(&logs)
}
}
}
})
}
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
from_params::<(Index,)>(params)
.and_then(|(index,)| {
let mut polls = self.polls.lock().unwrap();
match polls.poll(&index.value()) {
Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => {
let include_pending = filter.to_block == Some(BlockNumber::Pending);
let filter: EthcoreFilter = filter.clone().into();
let mut logs = take_weak!(self.client).logs(filter.clone())
.into_iter()
.map(From::from)
.collect::<Vec<Log>>();
if include_pending {
logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter));
}
to_value(&logs)
},
// just empty array
_ => Ok(Value::Array(vec![] as Vec<Value>)),
}
})
}
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
from_params::<(Index,)>(params)
.and_then(|(index,)| {
self.polls.lock().unwrap().remove_poll(&index.value());
to_value(&true)
})
}
}

View File

@ -0,0 +1,111 @@
// 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 <http://www.gnu.org/licenses/>.
//! Eth Signing RPC implementation.
use std::sync::{Arc, Weak};
use jsonrpc_core::*;
use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use util::numbers::*;
use util::keys::store::AccountProvider;
use v1::helpers::{SigningQueue, ConfirmationsQueue};
use v1::traits::EthSigning;
use v1::types::TransactionRequest;
use v1::impls::sign_and_dispatch;
/// Implementation of functions that require signing when no trusted signer is used.
pub struct EthSigningQueueClient {
queue: Weak<ConfirmationsQueue>,
}
impl EthSigningQueueClient {
/// Creates a new signing queue client given shared signing queue.
pub fn new(queue: &Arc<ConfirmationsQueue>) -> Self {
EthSigningQueueClient {
queue: Arc::downgrade(queue),
}
}
}
impl EthSigning for EthSigningQueueClient {
fn sign(&self, _params: Params) -> Result<Value, Error> {
// TODO [ToDr] Implement sign when rest of the signing queue is ready.
rpc_unimplemented!()
}
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, )>(params)
.and_then(|(request, )| {
let queue = take_weak!(self.queue);
let id = queue.add_request(request);
let result = id.wait_with_timeout();
to_value(&result.unwrap_or_else(H256::new))
})
}
}
/// Implementation of functions that require signing when no trusted signer is used.
pub struct EthSigningUnsafeClient<C, A, M> where
C: MiningBlockChainClient,
A: AccountProvider,
M: MinerService {
client: Weak<C>,
accounts: Weak<A>,
miner: Weak<M>,
}
impl<C, A, M> EthSigningUnsafeClient<C, A, M> where
C: MiningBlockChainClient,
A: AccountProvider,
M: MinerService {
/// Creates new EthClient.
pub fn new(client: &Arc<C>, accounts: &Arc<A>, miner: &Arc<M>)
-> Self {
EthSigningUnsafeClient {
client: Arc::downgrade(client),
miner: Arc::downgrade(miner),
accounts: Arc::downgrade(accounts),
}
}
}
impl<C, A, M> EthSigning for EthSigningUnsafeClient<C, A, M> where
C: MiningBlockChainClient + 'static,
A: AccountProvider + 'static,
M: MinerService + 'static {
fn sign(&self, params: Params) -> Result<Value, Error> {
from_params::<(Address, H256)>(params).and_then(|(addr, msg)| {
to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero()))
})
}
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, )>(params)
.and_then(|(request, )| {
let accounts = take_weak!(self.accounts);
match accounts.account_secret(&request.from) {
Ok(secret) => to_value(&sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, secret)),
Err(_) => to_value(&H256::zero())
}
})
}
}

View File

@ -15,37 +15,151 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Ethcore-specific rpc implementation. //! Ethcore-specific rpc implementation.
use util::{U256, Address, RotatingLogger}; use util::{U256, Address, RotatingLogger, FixedHash, Uint};
use util::network_settings::NetworkSettings; use util::network_settings::NetworkSettings;
use util::misc::version_data; use util::misc::version_data;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::ops::Deref; use std::ops::Deref;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use jsonrpc_core::*; use jsonrpc_core::*;
use ethminer::MinerService; use serde;
use ethcore::miner::MinerService;
use ethcore::state_diff::StateDiff;
use ethcore::account_diff::{Diff, Existance};
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
use ethcore::client::{BlockChainClient, CallAnalytics};
use ethcore::trace::VMTrace;
use v1::traits::Ethcore; use v1::traits::Ethcore;
use v1::types::Bytes; use v1::types::{Bytes, CallRequest};
/// Ethcore implementation. /// Ethcore implementation.
pub struct EthcoreClient<M> pub struct EthcoreClient<C, M> where
where M: MinerService { C: BlockChainClient,
M: MinerService {
client: Weak<C>,
miner: Weak<M>, miner: Weak<M>,
logger: Arc<RotatingLogger>, logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>, settings: Arc<NetworkSettings>,
} }
impl<M> EthcoreClient<M> where M: MinerService { impl<C, M> EthcoreClient<C, M> where C: BlockChainClient, M: MinerService {
/// Creates new `EthcoreClient`. /// Creates new `EthcoreClient`.
pub fn new(miner: &Arc<M>, logger: Arc<RotatingLogger>, settings: Arc<NetworkSettings>) -> Self { pub fn new(client: &Arc<C>, miner: &Arc<M>, logger: Arc<RotatingLogger>, settings: Arc<NetworkSettings>) -> Self {
EthcoreClient { EthcoreClient {
client: Arc::downgrade(client),
miner: Arc::downgrade(miner), miner: Arc::downgrade(miner),
logger: logger, logger: logger,
settings: settings, settings: settings,
} }
} }
// TODO: share with eth.rs
fn sign_call(&self, request: CallRequest) -> Result<SignedTransaction, Error> {
let client = take_weak!(self.client);
let miner = take_weak!(self.miner);
let from = request.from.unwrap_or(Address::zero());
Ok(EthTransaction {
nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)),
action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or(U256::from(50_000_000)),
gas_price: request.gas_price.unwrap_or_else(|| miner.sensible_gas_price()),
value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
}.fake_sign(from))
}
} }
impl<M> Ethcore for EthcoreClient<M> where M: MinerService + 'static { fn vm_trace_to_object(t: &VMTrace) -> Value {
let mut ret = BTreeMap::new();
ret.insert("code".to_owned(), to_value(&t.code).unwrap());
let mut subs = t.subs.iter();
let mut next_sub = subs.next();
let ops = t.operations
.iter()
.enumerate()
.map(|(i, op)| {
let mut m = map![
"pc".to_owned() => to_value(&op.pc).unwrap(),
"cost".to_owned() => match op.gas_cost <= U256::from(!0u64) {
true => to_value(&op.gas_cost.low_u64()),
false => to_value(&op.gas_cost),
}.unwrap()
];
if let Some(ref ex) = op.executed {
let mut em = map![
"used".to_owned() => to_value(&ex.gas_used.low_u64()).unwrap(),
"push".to_owned() => to_value(&ex.stack_push).unwrap()
];
if let Some(ref md) = ex.mem_diff {
em.insert("mem".to_owned(), Value::Object(map![
"off".to_owned() => to_value(&md.offset).unwrap(),
"data".to_owned() => to_value(&md.data).unwrap()
]));
}
if let Some(ref sd) = ex.store_diff {
em.insert("store".to_owned(), Value::Object(map![
"key".to_owned() => to_value(&sd.location).unwrap(),
"val".to_owned() => to_value(&sd.value).unwrap()
]));
}
m.insert("ex".to_owned(), Value::Object(em));
}
if next_sub.is_some() && next_sub.unwrap().parent_step == i {
m.insert("sub".to_owned(), vm_trace_to_object(next_sub.unwrap()));
next_sub = subs.next();
}
Value::Object(m)
})
.collect::<Vec<_>>();
ret.insert("ops".to_owned(), Value::Array(ops));
Value::Object(ret)
}
fn diff_to_object<T>(d: &Diff<T>) -> Value where T: serde::Serialize + Eq {
let mut ret = BTreeMap::new();
match *d {
Diff::Same => {
ret.insert("diff".to_owned(), Value::String("=".to_owned()));
}
Diff::Born(ref x) => {
ret.insert("diff".to_owned(), Value::String("+".to_owned()));
ret.insert("+".to_owned(), to_value(x).unwrap());
}
Diff::Died(ref x) => {
ret.insert("diff".to_owned(), Value::String("-".to_owned()));
ret.insert("-".to_owned(), to_value(x).unwrap());
}
Diff::Changed(ref from, ref to) => {
ret.insert("diff".to_owned(), Value::String("*".to_owned()));
ret.insert("-".to_owned(), to_value(from).unwrap());
ret.insert("+".to_owned(), to_value(to).unwrap());
}
};
Value::Object(ret)
}
fn state_diff_to_object(t: &StateDiff) -> Value {
Value::Object(t.iter().map(|(address, account)| {
(address.hex(), Value::Object(map![
"existance".to_owned() => Value::String(match account.existance() {
Existance::Born => "+",
Existance::Alive => ".",
Existance::Died => "-",
}.to_owned()),
"balance".to_owned() => diff_to_object(&account.balance),
"nonce".to_owned() => diff_to_object(&account.nonce),
"code".to_owned() => diff_to_object(&account.code),
"storage".to_owned() => Value::Object(account.storage.iter().map(|(key, val)| {
(key.hex(), diff_to_object(&val))
}).collect::<BTreeMap<_, _>>())
]))
}).collect::<BTreeMap<_, _>>())
}
impl<C, M> Ethcore for EthcoreClient<C, M> where C: BlockChainClient + 'static, M: MinerService + 'static {
fn set_min_gas_price(&self, params: Params) -> Result<Value, Error> { fn set_min_gas_price(&self, params: Params) -> Result<Value, Error> {
from_params::<(U256,)>(params).and_then(|(gas_price,)| { from_params::<(U256,)>(params).and_then(|(gas_price,)| {
@ -135,4 +249,34 @@ impl<M> Ethcore for EthcoreClient<M> where M: MinerService + 'static {
let version = version_data(); let version = version_data();
to_value(&Bytes::new(version)) to_value(&Bytes::new(version))
} }
fn vm_trace_call(&self, params: Params) -> Result<Value, Error> {
trace!(target: "jsonrpc", "vm_trace_call: {:?}", params);
from_params(params)
.and_then(|(request,)| {
let signed = try!(self.sign_call(request));
let r = take_weak!(self.client).call(&signed, CallAnalytics{ vm_tracing: true, state_diffing: false });
if let Ok(executed) = r {
if let Some(vm_trace) = executed.vm_trace {
return Ok(vm_trace_to_object(&vm_trace));
}
}
Ok(Value::Null)
})
}
fn state_diff_call(&self, params: Params) -> Result<Value, Error> {
trace!(target: "jsonrpc", "state_diff_call: {:?}", params);
from_params(params)
.and_then(|(request,)| {
let signed = try!(self.sign_call(request));
let r = take_weak!(self.client).call(&signed, CallAnalytics{ vm_tracing: false, state_diffing: true });
if let Ok(executed) = r {
if let Some(state_diff) = executed.state_diff {
return Ok(state_diff_to_object(&state_diff));
}
}
Ok(Value::Null)
})
}
} }

View File

@ -31,32 +31,36 @@ macro_rules! rpc_unimplemented {
mod web3; mod web3;
mod eth; mod eth;
mod eth_filter;
mod eth_signing;
mod net; mod net;
mod personal; mod personal;
mod personal_signer;
mod ethcore; mod ethcore;
mod traces; mod traces;
mod rpc; mod rpc;
pub use self::web3::Web3Client; pub use self::web3::Web3Client;
pub use self::eth::{EthClient, EthFilterClient}; pub use self::eth::EthClient;
pub use self::eth_filter::EthFilterClient;
pub use self::eth_signing::{EthSigningUnsafeClient, EthSigningQueueClient};
pub use self::net::NetClient; pub use self::net::NetClient;
pub use self::personal::PersonalClient; pub use self::personal::PersonalClient;
pub use self::personal_signer::SignerClient;
pub use self::ethcore::EthcoreClient; pub use self::ethcore::EthcoreClient;
pub use self::traces::TracesClient; pub use self::traces::TracesClient;
pub use self::rpc::RpcClient; pub use self::rpc::RpcClient;
use v1::types::TransactionRequest; use v1::types::TransactionRequest;
use std::sync::Weak; use ethcore::miner::{AccountDetails, MinerService};
use ethminer::{AccountDetails, MinerService}; use ethcore::client::MiningBlockChainClient;
use ethcore::client::BlockChainClient;
use ethcore::transaction::{Action, SignedTransaction, Transaction}; use ethcore::transaction::{Action, SignedTransaction, Transaction};
use util::numbers::*; use util::numbers::*;
use util::rlp::encode; use util::rlp::encode;
use util::bytes::ToPretty; use util::bytes::ToPretty;
use jsonrpc_core::{Error, to_value, Value};
fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error> fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> H256
where C: BlockChainClient, M: MinerService { where C: MiningBlockChainClient, M: MinerService {
let hash = signed_transaction.hash(); let hash = signed_transaction.hash();
let import = miner.import_own_transaction(client, signed_transaction, |a: &Address| { let import = miner.import_own_transaction(client, signed_transaction, |a: &Address| {
@ -66,13 +70,11 @@ fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedT
} }
}); });
to_value(&import.map(|_| hash).unwrap_or(H256::zero())) import.map(|_| hash).unwrap_or(H256::zero())
} }
fn sign_and_dispatch<C, M>(client: &Weak<C>, miner: &Weak<M>, request: TransactionRequest, secret: H256) -> Result<Value, Error> fn sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, secret: H256) -> H256
where C: BlockChainClient, M: MinerService { where C: MiningBlockChainClient, M: MinerService {
let client = take_weak!(client);
let miner = take_weak!(miner);
let signed_transaction = { let signed_transaction = {
Transaction { Transaction {

View File

@ -20,21 +20,21 @@ use jsonrpc_core::*;
use v1::traits::Personal; use v1::traits::Personal;
use v1::types::TransactionRequest; use v1::types::TransactionRequest;
use v1::impls::sign_and_dispatch; use v1::impls::sign_and_dispatch;
use util::keys::store::*; use util::keys::store::AccountProvider;
use util::numbers::*; use util::numbers::*;
use ethcore::client::BlockChainClient; use ethcore::client::MiningBlockChainClient;
use ethminer::MinerService; use ethcore::miner::MinerService;
/// Account management (personal) rpc implementation. /// Account management (personal) rpc implementation.
pub struct PersonalClient<A, C, M> pub struct PersonalClient<A, C, M>
where A: AccountProvider, C: BlockChainClient, M: MinerService { where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
accounts: Weak<A>, accounts: Weak<A>,
client: Weak<C>, client: Weak<C>,
miner: Weak<M>, miner: Weak<M>,
} }
impl<A, C, M> PersonalClient<A, C, M> impl<A, C, M> PersonalClient<A, C, M>
where A: AccountProvider, C: BlockChainClient, M: MinerService { where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
/// Creates new PersonalClient /// Creates new PersonalClient
pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>) -> Self { pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>) -> Self {
PersonalClient { PersonalClient {
@ -46,7 +46,7 @@ impl<A, C, M> PersonalClient<A, C, M>
} }
impl<A: 'static, C: 'static, M: 'static> Personal for PersonalClient<A, C, M> impl<A: 'static, C: 'static, M: 'static> Personal for PersonalClient<A, C, M>
where A: AccountProvider, C: BlockChainClient, M: MinerService { where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
fn accounts(&self, _: Params) -> Result<Value, Error> { fn accounts(&self, _: Params) -> Result<Value, Error> {
let store = take_weak!(self.accounts); let store = take_weak!(self.accounts);
match store.accounts() { match store.accounts() {
@ -83,7 +83,7 @@ impl<A: 'static, C: 'static, M: 'static> Personal for PersonalClient<A, C, M>
.and_then(|(request, password)| { .and_then(|(request, password)| {
let accounts = take_weak!(self.accounts); let accounts = take_weak!(self.accounts);
match accounts.locked_account_secret(&request.from, &password) { match accounts.locked_account_secret(&request.from, &password) {
Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret), Ok(secret) => to_value(&sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, secret)),
Err(_) => to_value(&H256::zero()), Err(_) => to_value(&H256::zero()),
} }
}) })

View File

@ -0,0 +1,101 @@
// 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 <http://www.gnu.org/licenses/>.
//! Transactions Confirmations (personal) rpc implementation
use std::sync::{Arc, Weak};
use jsonrpc_core::*;
use v1::traits::PersonalSigner;
use v1::types::TransactionModification;
use v1::impls::sign_and_dispatch;
use v1::helpers::{SigningQueue, ConfirmationsQueue};
use util::keys::store::AccountProvider;
use util::numbers::*;
use ethcore::client::MiningBlockChainClient;
use ethcore::miner::MinerService;
/// Transactions confirmation (personal) rpc implementation.
pub struct SignerClient<A, C, M>
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
queue: Weak<ConfirmationsQueue>,
accounts: Weak<A>,
client: Weak<C>,
miner: Weak<M>,
}
impl<A: 'static, C: 'static, M: 'static> SignerClient<A, C, M>
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
/// Create new instance of signer client.
pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>, queue: &Arc<ConfirmationsQueue>) -> Self {
SignerClient {
queue: Arc::downgrade(queue),
accounts: Arc::downgrade(store),
client: Arc::downgrade(client),
miner: Arc::downgrade(miner),
}
}
}
impl<A: 'static, C: 'static, M: 'static> PersonalSigner for SignerClient<A, C, M>
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
fn transactions_to_confirm(&self, _params: Params) -> Result<Value, Error> {
let queue = take_weak!(self.queue);
to_value(&queue.requests())
}
fn confirm_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(U256, TransactionModification, String)>(params).and_then(
|(id, modification, pass)| {
let accounts = take_weak!(self.accounts);
let queue = take_weak!(self.queue);
let client = take_weak!(self.client);
let miner = take_weak!(self.miner);
queue.peek(&id).and_then(|confirmation| {
let mut request = confirmation.transaction;
// apply modification
if let Some(gas_price) = modification.gas_price {
request.gas_price = Some(gas_price);
}
match accounts.locked_account_secret(&request.from, &pass) {
Ok(secret) => {
let hash = sign_and_dispatch(&*client, &*miner, request, secret);
queue.request_confirmed(id, hash);
Some(to_value(&hash))
},
Err(_) => None
}
})
.unwrap_or_else(|| {
queue.request_rejected(id);
to_value(&H256::zero())
})
}
)
}
fn reject_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(U256, )>(params).and_then(
|(id, )| {
let queue = take_weak!(self.queue);
let res = queue.request_rejected(id);
to_value(&res.is_some())
}
)
}
}

View File

@ -25,5 +25,6 @@ pub mod traits;
pub mod tests; pub mod tests;
pub mod types; pub mod types;
pub use self::traits::{Web3, Eth, EthFilter, Personal, Net, Ethcore, Traces, Rpc}; pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Personal, PersonalSigner, Net, Ethcore, Traces, Rpc};
pub use self::impls::*; pub use self::impls::*;
pub use self::helpers::{SigningQueue, ConfirmationsQueue};

View File

@ -19,13 +19,13 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::str::FromStr; use std::str::FromStr;
use ethcore::client::{BlockChainClient, Client, ClientConfig};
use ethcore::ids::BlockID; use ethcore::ids::BlockID;
use ethcore::client::{Client, BlockChainClient, ClientConfig};
use ethcore::spec::{Genesis, Spec}; use ethcore::spec::{Genesis, Spec};
use ethcore::block::Block; use ethcore::block::Block;
use ethcore::views::BlockView; use ethcore::views::BlockView;
use ethcore::ethereum; use ethcore::ethereum;
use ethminer::{Miner, MinerService, ExternalMiner}; use ethcore::miner::{MinerService, ExternalMiner, Miner};
use devtools::RandomTempPath; use devtools::RandomTempPath;
use util::Hashable; use util::Hashable;
use util::io::IoChannel; use util::io::IoChannel;
@ -35,8 +35,8 @@ use util::keys::{AccountProvider, TestAccount, TestAccountProvider};
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use ethjson::blockchain::BlockChain; use ethjson::blockchain::BlockChain;
use v1::traits::eth::Eth; use v1::traits::eth::{Eth, EthSigning};
use v1::impls::EthClient; use v1::impls::{EthClient, EthSigningUnsafeClient};
use v1::tests::helpers::{TestSyncProvider, Config}; use v1::tests::helpers::{TestSyncProvider, Config};
fn account_provider() -> Arc<TestAccountProvider> { fn account_provider() -> Arc<TestAccountProvider> {
@ -68,8 +68,8 @@ fn make_spec(chain: &BlockChain) -> Spec {
} }
struct EthTester { struct EthTester {
_miner: Arc<MinerService>,
client: Arc<Client>, client: Arc<Client>,
_miner: Arc<MinerService>,
accounts: Arc<TestAccountProvider>, accounts: Arc<TestAccountProvider>,
handler: IoHandler, handler: IoHandler,
} }
@ -96,10 +96,10 @@ impl EthTester {
where F: Fn() -> Spec { where F: Fn() -> Spec {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), spec_provider(), dir.as_path(), IoChannel::disconnected()).unwrap();
let sync_provider = sync_provider();
let account_provider = account_provider(); let account_provider = account_provider();
let miner_service = miner_service(spec_provider(), account_provider.clone()); let miner_service = miner_service(spec_provider(), account_provider.clone());
let client = Client::new(ClientConfig::default(), spec_provider(), dir.as_path(), miner_service.clone(), IoChannel::disconnected()).unwrap();
let sync_provider = sync_provider();
let external_miner = Arc::new(ExternalMiner::default()); let external_miner = Arc::new(ExternalMiner::default());
let eth_client = EthClient::new( let eth_client = EthClient::new(
@ -109,10 +109,15 @@ impl EthTester {
&miner_service, &miner_service,
&external_miner &external_miner
); );
let eth_sign = EthSigningUnsafeClient::new(
&client,
&account_provider,
&miner_service
);
let handler = IoHandler::new(); let handler = IoHandler::new();
let delegate = eth_client.to_delegate(); handler.add_delegate(eth_client.to_delegate());
handler.add_delegate(delegate); handler.add_delegate(eth_sign.to_delegate());
EthTester { EthTester {
_miner: miner_service, _miner: miner_service,

View File

@ -19,11 +19,11 @@
use util::{Address, H256, Bytes, U256, FixedHash, Uint}; use util::{Address, H256, Bytes, U256, FixedHash, Uint};
use util::standard::*; use util::standard::*;
use ethcore::error::{Error, ExecutionError}; use ethcore::error::{Error, ExecutionError};
use ethcore::client::{BlockChainClient, Executed}; use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics};
use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use ethcore::receipt::Receipt; use ethcore::receipt::Receipt;
use ethminer::{MinerService, MinerStatus, AccountDetails, TransactionImportResult}; use ethcore::miner::{MinerService, MinerStatus, AccountDetails, TransactionImportResult};
/// Test miner service. /// Test miner service.
pub struct TestMinerService { pub struct TestMinerService {
@ -132,7 +132,7 @@ impl MinerService for TestMinerService {
} }
/// Imports transactions to transaction queue. /// Imports transactions to transaction queue.
fn import_own_transaction<T>(&self, chain: &BlockChainClient, transaction: SignedTransaction, _fetch_account: T) -> fn import_own_transaction<T>(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction, _fetch_account: T) ->
Result<TransactionImportResult, Error> Result<TransactionImportResult, Error>
where T: Fn(&Address) -> AccountDetails { where T: Fn(&Address) -> AccountDetails {
@ -154,21 +154,21 @@ impl MinerService for TestMinerService {
} }
/// Removes all transactions from the queue and restart mining operation. /// Removes all transactions from the queue and restart mining operation.
fn clear_and_reset(&self, _chain: &BlockChainClient) { fn clear_and_reset(&self, _chain: &MiningBlockChainClient) {
unimplemented!(); unimplemented!();
} }
/// Called when blocks are imported to chain, updates transactions queue. /// Called when blocks are imported to chain, updates transactions queue.
fn chain_new_blocks(&self, _chain: &BlockChainClient, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) { fn chain_new_blocks(&self, _chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) {
unimplemented!(); unimplemented!();
} }
/// New chain head event. Restart mining operation. /// New chain head event. Restart mining operation.
fn update_sealing(&self, _chain: &BlockChainClient) { fn update_sealing(&self, _chain: &MiningBlockChainClient) {
unimplemented!(); unimplemented!();
} }
fn map_sealing_work<F, T>(&self, _chain: &BlockChainClient, _f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T { fn map_sealing_work<F, T>(&self, _chain: &MiningBlockChainClient, _f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
unimplemented!(); unimplemented!();
} }
@ -194,29 +194,29 @@ impl MinerService for TestMinerService {
/// Submit `seal` as a valid solution for the header of `pow_hash`. /// Submit `seal` as a valid solution for the header of `pow_hash`.
/// Will check the seal, but not actually insert the block into the chain. /// Will check the seal, but not actually insert the block into the chain.
fn submit_seal(&self, _chain: &BlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> { fn submit_seal(&self, _chain: &MiningBlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
unimplemented!(); unimplemented!();
} }
fn balance(&self, _chain: &BlockChainClient, address: &Address) -> U256 { fn balance(&self, _chain: &MiningBlockChainClient, address: &Address) -> U256 {
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone()) self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone())
} }
fn call(&self, _chain: &BlockChainClient, _t: &SignedTransaction) -> Result<Executed, ExecutionError> { fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
unimplemented!(); unimplemented!();
} }
fn storage_at(&self, _chain: &BlockChainClient, address: &Address, position: &H256) -> H256 { fn storage_at(&self, _chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(H256::default, |b| b.block().fields().state.storage_at(address, position).clone()) self.latest_closed_block.lock().unwrap().as_ref().map_or_else(H256::default, |b| b.block().fields().state.storage_at(address, position).clone())
} }
fn nonce(&self, _chain: &BlockChainClient, address: &Address) -> U256 { fn nonce(&self, _chain: &MiningBlockChainClient, address: &Address) -> U256 {
// we assume all transactions are in a pending block, ignoring the // we assume all transactions are in a pending block, ignoring the
// reality of gas limits. // reality of gas limits.
self.last_nonce(address).unwrap_or(U256::zero()) self.last_nonce(address).unwrap_or(U256::zero())
} }
fn code(&self, _chain: &BlockChainClient, address: &Address) -> Option<Bytes> { fn code(&self, _chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
self.latest_closed_block.lock().unwrap().as_ref().map_or(None, |b| b.block().fields().state.code(address).clone()) self.latest_closed_block.lock().unwrap().as_ref().map_or(None, |b| b.block().fields().state.code(address).clone())
} }

View File

@ -25,9 +25,9 @@ use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, Transaction
use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
use ethcore::receipt::LocalizedReceipt; use ethcore::receipt::LocalizedReceipt;
use ethcore::transaction::{Transaction, Action}; use ethcore::transaction::{Transaction, Action};
use ethminer::{ExternalMiner, MinerService}; use ethcore::miner::{ExternalMiner, MinerService};
use ethsync::SyncState; use ethsync::SyncState;
use v1::{Eth, EthClient}; use v1::{Eth, EthClient, EthSigning, EthSigningUnsafeClient};
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
use rustc_serialize::hex::ToHex; use rustc_serialize::hex::ToHex;
@ -72,8 +72,11 @@ impl Default for EthTester {
let hashrates = Arc::new(RwLock::new(HashMap::new())); let hashrates = Arc::new(RwLock::new(HashMap::new()));
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone())); let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner).to_delegate(); let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner).to_delegate();
let sign = EthSigningUnsafeClient::new(&client, &ap, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(eth); io.add_delegate(eth);
io.add_delegate(sign);
EthTester { EthTester {
client: client, client: client,
sync: sync, sync: sync,
@ -362,7 +365,7 @@ fn rpc_eth_pending_transaction_by_hash() {
tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx); tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx);
} }
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#;
let request = r#"{ let request = r#"{
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "eth_getTransactionByHash", "method": "eth_getTransactionByHash",
@ -427,6 +430,8 @@ fn rpc_eth_call() {
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: None,
vm_trace: None,
state_diff: None,
}); });
let request = r#"{ let request = r#"{
@ -460,6 +465,8 @@ fn rpc_eth_call_default_block() {
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: None,
vm_trace: None,
state_diff: None,
}); });
let request = r#"{ let request = r#"{
@ -492,6 +499,8 @@ fn rpc_eth_estimate_gas() {
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: None,
vm_trace: None,
state_diff: None,
}); });
let request = r#"{ let request = r#"{
@ -525,6 +534,8 @@ fn rpc_eth_estimate_gas_default_block() {
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: None,
vm_trace: None,
state_diff: None,
}); });
let request = r#"{ let request = r#"{

View File

@ -0,0 +1,75 @@
// 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 <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use jsonrpc_core::IoHandler;
use v1::impls::EthSigningQueueClient;
use v1::traits::EthSigning;
use v1::helpers::{ConfirmationsQueue, SigningQueue};
use util::keys::TestAccount;
struct EthSigningTester {
pub queue: Arc<ConfirmationsQueue>,
pub io: IoHandler,
}
impl Default for EthSigningTester {
fn default() -> Self {
let queue = Arc::new(ConfirmationsQueue::default());
let io = IoHandler::new();
io.add_delegate(EthSigningQueueClient::new(&queue).to_delegate());
EthSigningTester {
queue: queue,
io: io,
}
}
}
fn eth_signing() -> EthSigningTester {
EthSigningTester::default()
}
#[test]
fn should_add_transaction_to_queue() {
// given
let tester = eth_signing();
let account = TestAccount::new("123");
let address = account.address();
assert_eq!(tester.queue.requests().len(), 0);
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
// then
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
assert_eq!(tester.queue.requests().len(), 1);
}

View File

@ -18,17 +18,23 @@ use std::sync::Arc;
use std::str::FromStr; use std::str::FromStr;
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use v1::{Ethcore, EthcoreClient}; use v1::{Ethcore, EthcoreClient};
use ethminer::MinerService; use ethcore::client::{TestBlockChainClient};
use ethcore::miner::MinerService;
use v1::tests::helpers::TestMinerService; use v1::tests::helpers::TestMinerService;
use util::numbers::*; use util::numbers::*;
use rustc_serialize::hex::FromHex; use rustc_serialize::hex::FromHex;
use util::log::RotatingLogger; use util::log::RotatingLogger;
use util::network_settings::NetworkSettings; use util::network_settings::NetworkSettings;
fn blockchain_client() -> Arc<TestBlockChainClient> {
let client = TestBlockChainClient::new();
Arc::new(client)
}
fn miner_service() -> Arc<TestMinerService> { fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default()) Arc::new(TestMinerService::default())
} }
fn logger() -> Arc<RotatingLogger> { fn logger() -> Arc<RotatingLogger> {
Arc::new(RotatingLogger::new("rpc=trace".to_owned())) Arc::new(RotatingLogger::new("rpc=trace".to_owned()))
} }
@ -45,14 +51,15 @@ fn settings() -> Arc<NetworkSettings> {
}) })
} }
fn ethcore_client(miner: &Arc<TestMinerService>) -> EthcoreClient<TestMinerService> { fn ethcore_client(client: &Arc<TestBlockChainClient>, miner: &Arc<TestMinerService>) -> EthcoreClient<TestBlockChainClient, TestMinerService> {
EthcoreClient::new(&miner, logger(), settings()) EthcoreClient::new(&client, &miner, logger(), settings())
} }
#[test] #[test]
fn rpc_ethcore_extra_data() { fn rpc_ethcore_extra_data() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -68,7 +75,8 @@ fn rpc_ethcore_default_extra_data() {
use util::ToPretty; use util::ToPretty;
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -81,7 +89,8 @@ fn rpc_ethcore_default_extra_data() {
#[test] #[test]
fn rpc_ethcore_gas_floor_target() { fn rpc_ethcore_gas_floor_target() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -94,7 +103,8 @@ fn rpc_ethcore_gas_floor_target() {
#[test] #[test]
fn rpc_ethcore_min_gas_price() { fn rpc_ethcore_min_gas_price() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -107,7 +117,8 @@ fn rpc_ethcore_min_gas_price() {
#[test] #[test]
fn rpc_ethcore_set_min_gas_price() { fn rpc_ethcore_set_min_gas_price() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -121,7 +132,8 @@ fn rpc_ethcore_set_min_gas_price() {
#[test] #[test]
fn rpc_ethcore_set_gas_floor_target() { fn rpc_ethcore_set_gas_floor_target() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -135,7 +147,8 @@ fn rpc_ethcore_set_gas_floor_target() {
#[test] #[test]
fn rpc_ethcore_set_extra_data() { fn rpc_ethcore_set_extra_data() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -149,7 +162,8 @@ fn rpc_ethcore_set_extra_data() {
#[test] #[test]
fn rpc_ethcore_set_author() { fn rpc_ethcore_set_author() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -163,10 +177,11 @@ fn rpc_ethcore_set_author() {
#[test] #[test]
fn rpc_ethcore_dev_logs() { fn rpc_ethcore_dev_logs() {
let miner = miner_service(); let miner = miner_service();
let client = blockchain_client();
let logger = logger(); let logger = logger();
logger.append("a".to_owned()); logger.append("a".to_owned());
logger.append("b".to_owned()); logger.append("b".to_owned());
let ethcore = EthcoreClient::new(&miner, logger.clone(), settings()).to_delegate(); let ethcore = EthcoreClient::new(&client, &miner, logger.clone(), settings()).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -179,7 +194,8 @@ fn rpc_ethcore_dev_logs() {
#[test] #[test]
fn rpc_ethcore_dev_logs_levels() { fn rpc_ethcore_dev_logs_levels() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -191,7 +207,8 @@ fn rpc_ethcore_dev_logs_levels() {
#[test] #[test]
fn rpc_ethcore_set_transactions_limit() { fn rpc_ethcore_set_transactions_limit() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -205,7 +222,8 @@ fn rpc_ethcore_set_transactions_limit() {
#[test] #[test]
fn rpc_ethcore_transactions_limit() { fn rpc_ethcore_transactions_limit() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -218,7 +236,8 @@ fn rpc_ethcore_transactions_limit() {
#[test] #[test]
fn rpc_ethcore_net_chain() { fn rpc_ethcore_net_chain() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -231,7 +250,8 @@ fn rpc_ethcore_net_chain() {
#[test] #[test]
fn rpc_ethcore_net_max_peers() { fn rpc_ethcore_net_max_peers() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -244,7 +264,8 @@ fn rpc_ethcore_net_max_peers() {
#[test] #[test]
fn rpc_ethcore_net_port() { fn rpc_ethcore_net_port() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -257,7 +278,8 @@ fn rpc_ethcore_net_port() {
#[test] #[test]
fn rpc_ethcore_rpc_settings() { fn rpc_ethcore_rpc_settings() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -270,7 +292,8 @@ fn rpc_ethcore_rpc_settings() {
#[test] #[test]
fn rpc_ethcore_node_name() { fn rpc_ethcore_node_name() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);

View File

@ -18,8 +18,10 @@
//! method calls properly. //! method calls properly.
mod eth; mod eth;
mod eth_signing;
mod net; mod net;
mod web3; mod web3;
mod personal; mod personal;
mod personal_signer;
mod ethcore; mod ethcore;
mod rpc; mod rpc;

View File

@ -0,0 +1,169 @@
// 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 <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use std::str::FromStr;
use std::collections::HashMap;
use jsonrpc_core::IoHandler;
use util::numbers::*;
use util::keys::{TestAccount, TestAccountProvider};
use ethcore::client::TestBlockChainClient;
use ethcore::transaction::{Transaction, Action};
use v1::{SignerClient, PersonalSigner};
use v1::tests::helpers::TestMinerService;
use v1::helpers::{SigningQueue, ConfirmationsQueue};
use v1::types::TransactionRequest;
struct PersonalSignerTester {
queue: Arc<ConfirmationsQueue>,
accounts: Arc<TestAccountProvider>,
io: IoHandler,
miner: Arc<TestMinerService>,
// these unused fields are necessary to keep the data alive
// as the handler has only weak pointers.
_client: Arc<TestBlockChainClient>,
}
fn blockchain_client() -> Arc<TestBlockChainClient> {
let client = TestBlockChainClient::new();
Arc::new(client)
}
fn accounts_provider() -> Arc<TestAccountProvider> {
let accounts = HashMap::new();
let ap = TestAccountProvider::new(accounts);
Arc::new(ap)
}
fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default())
}
fn signer_tester() -> PersonalSignerTester {
let queue = Arc::new(ConfirmationsQueue::default());
let accounts = accounts_provider();
let client = blockchain_client();
let miner = miner_service();
let io = IoHandler::new();
io.add_delegate(SignerClient::new(&accounts, &client, &miner, &queue).to_delegate());
PersonalSignerTester {
queue: queue,
accounts: accounts,
io: io,
miner: miner,
_client: client,
}
}
#[test]
fn should_return_list_of_transactions_in_queue() {
// given
let tester = signer_tester();
tester.queue.add_request(TransactionRequest {
from: Address::from(1),
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: Some(U256::from(10_000)),
gas: Some(U256::from(10_000_000)),
value: Some(U256::from(1)),
data: None,
nonce: None,
});
// when
let request = r#"{"jsonrpc":"2.0","method":"personal_transactionsToConfirm","params":[],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"id":"0x01","transaction":{"data":null,"from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x01"}}],"id":1}"#;
// then
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
}
#[test]
fn should_reject_transaction_from_queue_without_dispatching() {
// given
let tester = signer_tester();
tester.queue.add_request(TransactionRequest {
from: Address::from(1),
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: Some(U256::from(10_000)),
gas: Some(U256::from(10_000_000)),
value: Some(U256::from(1)),
data: None,
nonce: None,
});
assert_eq!(tester.queue.requests().len(), 1);
// when
let request = r#"{"jsonrpc":"2.0","method":"personal_rejectTransaction","params":["0x01"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
// then
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
assert_eq!(tester.queue.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 0);
}
#[test]
fn should_confirm_transaction_and_dispatch() {
// given
let tester = signer_tester();
let account = TestAccount::new("test");
let address = account.address();
let secret = account.secret.clone();
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
tester.accounts.accounts
.write()
.unwrap()
.insert(address, account);
tester.queue.add_request(TransactionRequest {
from: address,
to: Some(recipient),
gas_price: Some(U256::from(10_000)),
gas: Some(U256::from(10_000_000)),
value: Some(U256::from(1)),
data: None,
nonce: None,
});
let t = Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x1000),
gas: U256::from(10_000_000),
action: Action::Call(recipient),
value: U256::from(0x1),
data: vec![]
}.sign(&secret);
assert_eq!(tester.queue.requests().len(), 1);
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"personal_confirmTransaction",
"params":["0x01", {"gasPrice":"0x1000"}, "test"],
"id":1
}"#;
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
// then
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
assert_eq!(tester.queue.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 1);
}

View File

@ -74,12 +74,6 @@ pub trait Eth: Sized + Send + Sync + 'static {
/// Returns the code at given address at given time (block number). /// Returns the code at given address at given time (block number).
fn code_at(&self, _: Params) -> Result<Value, Error>; fn code_at(&self, _: Params) -> Result<Value, Error>;
/// Signs the data with given address signature.
fn sign(&self, _: Params) -> Result<Value, Error>;
/// Sends transaction.
fn send_transaction(&self, _: Params) -> Result<Value, Error>;
/// Sends signed transaction. /// Sends signed transaction.
fn send_raw_transaction(&self, _: Params) -> Result<Value, Error>; fn send_raw_transaction(&self, _: Params) -> Result<Value, Error>;
@ -150,8 +144,6 @@ pub trait Eth: Sized + Send + Sync + 'static {
delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count_by_hash); delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count_by_hash);
delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count_by_number); delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count_by_number);
delegate.add_method("eth_getCode", Eth::code_at); delegate.add_method("eth_getCode", Eth::code_at);
delegate.add_method("eth_sign", Eth::sign);
delegate.add_method("eth_sendTransaction", Eth::send_transaction);
delegate.add_method("eth_sendRawTransaction", Eth::send_raw_transaction); delegate.add_method("eth_sendRawTransaction", Eth::send_raw_transaction);
delegate.add_method("eth_call", Eth::call); delegate.add_method("eth_call", Eth::call);
delegate.add_method("eth_estimateGas", Eth::estimate_gas); delegate.add_method("eth_estimateGas", Eth::estimate_gas);
@ -208,3 +200,20 @@ pub trait EthFilter: Sized + Send + Sync + 'static {
delegate delegate
} }
} }
/// Signing methods implementation relying on unlocked accounts.
pub trait EthSigning: Sized + Send + Sync + 'static {
/// Signs the data with given address signature.
fn sign(&self, _: Params) -> Result<Value, Error>;
/// Sends transaction.
fn send_transaction(&self, _: Params) -> Result<Value, Error>;
/// Should be used to convert object to io delegate.
fn to_delegate(self) -> IoDelegate<Self> {
let mut delegate = IoDelegate::new(Arc::new(self));
delegate.add_method("eth_sign", EthSigning::sign);
delegate.add_method("eth_sendTransaction", EthSigning::send_transaction);
delegate
}
}

View File

@ -72,6 +72,11 @@ pub trait Ethcore: Sized + Send + Sync + 'static {
/// Returns default extra data /// Returns default extra data
fn default_extra_data(&self, _: Params) -> Result<Value, Error>; fn default_extra_data(&self, _: Params) -> Result<Value, Error>;
/// Executes the given call and returns the VM trace for it.
fn vm_trace_call(&self, _: Params) -> Result<Value, Error>;
/// Executes the given call and returns the diff for it.
fn state_diff_call(&self, params: Params) -> Result<Value, Error>;
/// Should be used to convert object to io delegate. /// Should be used to convert object to io delegate.
fn to_delegate(self) -> IoDelegate<Self> { fn to_delegate(self) -> IoDelegate<Self> {
@ -95,6 +100,9 @@ pub trait Ethcore: Sized + Send + Sync + 'static {
delegate.add_method("ethcore_nodeName", Ethcore::node_name); delegate.add_method("ethcore_nodeName", Ethcore::node_name);
delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data); delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data);
delegate.add_method("ethcore_vmTraceCall", Ethcore::vm_trace_call);
delegate.add_method("ethcore_stateDiffCall", Ethcore::state_diff_call);
delegate delegate
} }
} }

View File

@ -25,9 +25,11 @@ pub mod traces;
pub mod rpc; pub mod rpc;
pub use self::web3::Web3; pub use self::web3::Web3;
pub use self::eth::{Eth, EthFilter}; pub use self::eth::{Eth, EthFilter, EthSigning};
pub use self::net::Net; pub use self::net::Net;
pub use self::personal::Personal; pub use self::personal::{Personal, PersonalSigner};
pub use self::ethcore::Ethcore; pub use self::ethcore::Ethcore;
pub use self::traces::Traces; pub use self::traces::Traces;
pub use self::rpc::Rpc; pub use self::rpc::Rpc;

View File

@ -43,3 +43,26 @@ pub trait Personal: Sized + Send + Sync + 'static {
delegate delegate
} }
} }
/// Personal extension for transactions confirmations rpc interface.
pub trait PersonalSigner: Sized + Send + Sync + 'static {
/// Returns a list of transactions to confirm.
fn transactions_to_confirm(&self, _: Params) -> Result<Value, Error>;
/// Confirm and send a specific transaction.
fn confirm_transaction(&self, _: Params) -> Result<Value, Error>;
/// Reject the transaction request.
fn reject_transaction(&self, _: Params) -> Result<Value, Error>;
/// Should be used to convert object to io delegate.
fn to_delegate(self) -> IoDelegate<Self> {
let mut delegate = IoDelegate::new(Arc::new(self));
delegate.add_method("personal_transactionsToConfirm", PersonalSigner::transactions_to_confirm);
delegate.add_method("personal_confirmTransaction", PersonalSigner::confirm_transaction);
delegate.add_method("personal_rejectTransaction", PersonalSigner::reject_transaction);
delegate
}
}

View File

@ -103,7 +103,7 @@ mod tests {
fn test_serialize_block_transactions() { fn test_serialize_block_transactions() {
let t = BlockTransactions::Full(vec![Transaction::default()]); let t = BlockTransactions::Full(vec![Transaction::default()]);
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}]"#); assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#);
let t = BlockTransactions::Hashes(vec![H256::default()]); let t = BlockTransactions::Hashes(vec![H256::default()]);
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();

View File

@ -38,7 +38,7 @@ pub use self::log::Log;
pub use self::optionals::OptionalValue; pub use self::optionals::OptionalValue;
pub use self::sync::{SyncStatus, SyncInfo}; pub use self::sync::{SyncStatus, SyncInfo};
pub use self::transaction::Transaction; pub use self::transaction::Transaction;
pub use self::transaction_request::TransactionRequest; pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification};
pub use self::call_request::CallRequest; pub use self::call_request::CallRequest;
pub use self::receipt::Receipt; pub use self::receipt::Receipt;
pub use self::trace::Trace; pub use self::trace::Trace;

View File

@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::numbers::*; use util::numbers::*;
use ethcore::contract_address;
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
use v1::types::{Bytes, OptionalValue}; use v1::types::{Bytes, OptionalValue};
@ -46,7 +47,9 @@ pub struct Transaction {
/// Gas /// Gas
pub gas: U256, pub gas: U256,
/// Data /// Data
pub input: Bytes pub input: Bytes,
/// Creates contract
pub creates: OptionalValue<Address>,
} }
impl From<LocalizedTransaction> for Transaction { impl From<LocalizedTransaction> for Transaction {
@ -65,7 +68,11 @@ impl From<LocalizedTransaction> for Transaction {
value: t.value, value: t.value,
gas_price: t.gas_price, gas_price: t.gas_price,
gas: t.gas, gas: t.gas,
input: Bytes::new(t.data.clone()) input: Bytes::new(t.data.clone()),
creates: match t.action {
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
Action::Call(_) => OptionalValue::Null,
},
} }
} }
} }
@ -86,7 +93,11 @@ impl From<SignedTransaction> for Transaction {
value: t.value, value: t.value,
gas_price: t.gas_price, gas_price: t.gas_price,
gas: t.gas, gas: t.gas,
input: Bytes::new(t.data.clone()) input: Bytes::new(t.data.clone()),
creates: match t.action {
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
Action::Call(_) => OptionalValue::Null,
},
} }
} }
} }
@ -100,7 +111,7 @@ mod tests {
fn test_transaction_serialize() { fn test_transaction_serialize() {
let t = Transaction::default(); let t = Transaction::default();
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}"#); assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}"#);
} }
} }

View File

@ -21,7 +21,7 @@ use util::numbers::U256;
use v1::types::bytes::Bytes; use v1::types::bytes::Bytes;
/// Transaction request coming from RPC /// Transaction request coming from RPC
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Deserialize)] #[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct TransactionRequest { pub struct TransactionRequest {
/// Sender /// Sender
pub from: Address, pub from: Address,
@ -40,6 +40,24 @@ pub struct TransactionRequest {
pub nonce: Option<U256>, pub nonce: Option<U256>,
} }
/// Transaction confirmation waiting in a queue
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize)]
pub struct TransactionConfirmation {
/// Id of this confirmation
pub id: U256,
/// TransactionRequest
pub transaction: TransactionRequest,
}
/// Possible modifications to the confirmed transaction sent by SystemUI
#[derive(Debug, PartialEq, Deserialize)]
pub struct TransactionModification {
/// Modified gas price
#[serde(rename="gasPrice")]
pub gas_price: Option<U256>,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr; use std::str::FromStr;
@ -135,5 +153,26 @@ mod tests {
nonce: None, nonce: None,
}); });
} }
#[test]
fn should_deserialize_modification() {
// given
let s1 = r#"{
"gasPrice":"0x0ba43b7400"
}"#;
let s2 = r#"{}"#;
// when
let res1: TransactionModification = serde_json::from_str(s1).unwrap();
let res2: TransactionModification = serde_json::from_str(s2).unwrap();
// then
assert_eq!(res1, TransactionModification {
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
});
assert_eq!(res2, TransactionModification {
gas_price: None,
});
}
} }

View File

@ -30,12 +30,15 @@
//! //!
//! ``` //! ```
//! extern crate ethcore_signer; //! extern crate ethcore_signer;
//! extern crate ethcore_rpc;
//! //!
//! use std::sync::Arc;
//! use ethcore_signer::ServerBuilder; //! use ethcore_signer::ServerBuilder;
//! use ethcore_rpc::ConfirmationsQueue;
//! //!
//! fn main() { //! fn main() {
//! let builder = ServerBuilder::new(); //! let queue = Arc::new(ConfirmationsQueue::default());
//! let _server = builder.start("127.0.0.1:8084".parse().unwrap()).unwrap(); //! let _server = ServerBuilder::new(queue).start("127.0.0.1:8084".parse().unwrap());
//! } //! }
//! ``` //! ```
@ -48,9 +51,7 @@ extern crate ethcore_rpc as rpc;
extern crate jsonrpc_core; extern crate jsonrpc_core;
extern crate ws; extern crate ws;
mod signing_queue;
mod ws_server; mod ws_server;
pub use ws_server::*; pub use ws_server::*;
#[cfg(test)] #[cfg(test)]

Some files were not shown because too many files have changed in this diff Show More