Merge branch 'signer-wsnotification' into signer-ui

This commit is contained in:
Tomasz Drwięga 2016-06-02 13:09:11 +02:00
commit d40504caa5
70 changed files with 1478 additions and 925 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 ] &&

25
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.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -334,7 +334,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.4 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -392,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.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.0 (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)",
@ -411,20 +410,6 @@ dependencies = [
"syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.32.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"
@ -433,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)",
@ -1222,6 +1206,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.

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)
} }
} }

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

@ -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};
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);
} }
} }
@ -421,7 +436,7 @@ impl<V> Client<V> where V: Verifier {
} }
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, vm_tracing: bool) -> 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,17 @@ 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 // give the sender a decent balance
state.sub_balance(&sender, &balance); state.sub_balance(&sender, &balance);
state.add_balance(&sender, &U256::max_value()); state.add_balance(&sender, &(U256::from(1) << 200));
let options = TransactOptions { tracing: false, check_nonce: false }; let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false };
Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options) Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options)
} }
// TODO [todr] Should be moved to miner crate eventually.
fn try_seal(&self, block: LockedBlock, seal: Vec<Bytes>) -> Result<SealedBlock, LockedBlock> {
block.try_seal(self.engine.deref().deref(), seal)
}
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()))
} }
@ -774,6 +718,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,8 @@ 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;
/// 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 +156,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, vm_tracing: bool) -> Result<Executed, ExecutionError>;
/// Returns EvmFactory. /// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory; fn vm_factory(&self) -> &EvmFactory;
@ -183,5 +177,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};
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, _vm_tracing: bool) -> 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

@ -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

@ -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,8 +17,9 @@
///! 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};
@ -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 {
@ -293,10 +301,15 @@ 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, &mut mem, &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); mem.expand(mem_size);
current_gas = current_gas - gas_cost; //TODO: use operator -= current_gas = current_gas - gas_cost; //TODO: use operator -=
@ -311,11 +324,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 mem, &mut stack
)); ));
if trace_executed {
ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(mem[o..(o + s)]))), store_written);
}
// Advance // Advance
match result { match result {
InstructionResult::Ok => {}, InstructionResult::Ok => {},
@ -485,6 +507,31 @@ impl Interpreter {
} }
} }
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) -> Result<(U256, usize), evm::Error> { fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> Result<(U256, usize), evm::Error> {
let gas_for_mem = |mem_size: U256| { let gas_for_mem = |mem_size: U256| {
let s = mem_size >> 5; let s = mem_size >> 5;
@ -833,10 +880,12 @@ impl Interpreter {
} }
} }
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>
) -> Result<(), evm::Error> {
if !stack.has(info.args) { if !stack.has(info.args) {
Err(evm::Error::StackUnderflow { Err(evm::Error::StackUnderflow {
instruction: info.name, instruction: info.name,

View File

@ -16,6 +16,7 @@
//! 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;

View File

@ -33,3 +33,4 @@ pub use self::evm::{Evm, Error, 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

@ -21,9 +21,8 @@ use engine::*;
use evm::{self, Ext, Factory}; use evm::{self, Ext, Factory};
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,20 +190,26 @@ 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 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);
} }
@ -193,7 +219,7 @@ 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)
@ -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 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 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,
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,7 @@ impl<'a> Executive<'a> {
contracts_created: vec![], contracts_created: vec![],
output: output, output: output,
trace: trace, trace: trace,
vm_trace: vm_trace,
}) })
}, },
_ => { _ => {
@ -406,6 +462,7 @@ 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,
}) })
}, },
} }
@ -438,6 +495,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 +524,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 +583,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 +600,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 +636,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 +676,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 +743,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 +767,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 +832,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 +884,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 +945,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 +990,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 +1023,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 +1057,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 +1089,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 +1123,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 +1157,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 +1192,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
} }
@ -286,6 +288,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 +307,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 +359,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 +372,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 +395,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 +411,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 +437,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 +455,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

@ -25,6 +25,7 @@ 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)]
struct CallCreate { struct CallCreate {
@ -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)
} }
@ -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,6 +206,7 @@ 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 evm = vm_factory.create();
let res = evm.exec(params, &mut ex); let res = evm.exec(params, &mut ex);

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;
@ -139,3 +141,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,17 @@ 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, BlockID};
use ethcore::block::{ClosedBlock, IsBlock}; use block::{ClosedBlock, IsBlock};
use ethcore::error::*; use error::*;
use ethcore::client::{Executive, Executed, EnvInfo, TransactOptions}; use client::{Executive, Executed, EnvInfo, TransactOptions};
use ethcore::transaction::SignedTransaction; use transaction::SignedTransaction;
use ethcore::receipt::Receipt; use receipt::{Receipt};
use ethcore::spec::Spec; use spec::Spec;
use ethcore::engine::Engine; use engine::Engine;
use super::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; use miner::{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 +104,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 +205,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 +236,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,7 +251,7 @@ impl MinerService for Miner {
} }
} }
fn call(&self, chain: &BlockChainClient, t: &SignedTransaction) -> Result<Executed, ExecutionError> { fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, vm_tracing: bool) -> 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) => {
@ -277,17 +277,18 @@ impl MinerService for Miner {
// give the sender max balance // give the sender max balance
state.sub_balance(&sender, &balance); state.sub_balance(&sender, &balance);
state.add_balance(&sender, &U256::max_value()); state.add_balance(&sender, &U256::max_value());
let options = TransactOptions { tracing: false, check_nonce: false }; let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false };
// TODO: use vm_trace here.
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)
}, },
None => { None => {
chain.call(t) chain.call(t, vm_tracing)
} }
} }
} }
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 +296,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 +304,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 +376,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 +470,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 +490,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 +500,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 +523,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 +585,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};
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, vm_tracing: bool) -> 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

@ -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

@ -183,16 +183,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,7 +220,7 @@ 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.

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

@ -18,7 +18,7 @@
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 ipc::binary::BinaryConvertError; use ipc::binary::BinaryConvertError;
use std::fmt; use std::fmt;
@ -59,6 +59,8 @@ 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>,
} }
/// Result of executing the transaction. /// Result of executing the transaction.

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

@ -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

@ -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;
@ -86,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;
@ -174,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());
@ -190,11 +181,19 @@ 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 deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies { let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies {
signer_enabled: conf.args.flag_signer, signer_enabled: conf.args.flag_signer,
@ -292,7 +291,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);
@ -363,7 +362,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

@ -20,7 +20,7 @@ use std::sync::Arc;
use die::*; use die::*;
use ethsync::EthSync; use ethsync::EthSync;
use ethminer::{Miner, ExternalMiner}; use ethcore::miner::{Miner, ExternalMiner};
use ethcore::client::Client; use ethcore::client::Client;
use util::RotatingLogger; use util::RotatingLogger;
use util::keys::store::AccountService; use util::keys::store::AccountService;
@ -153,7 +153,7 @@ pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet
} }
}, },
Api::Ethcore => { Api::Ethcore => {
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
}, },
Api::Traces => { Api::Traces => {
server.add_delegate(TracesClient::new(&deps.client).to_delegate()) server.add_delegate(TracesClient::new(&deps.client).to_delegate())

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 = "^0.32.0"
[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;

View File

@ -16,19 +16,23 @@
use std::thread; use std::thread;
use std::time::{Instant, Duration}; use std::time::{Instant, Duration};
use std::sync::{mpsc, Mutex, RwLock}; use std::sync::{mpsc, Mutex, RwLock, Arc};
use std::collections::HashMap; use std::collections::HashMap;
use v1::types::{TransactionRequest, TransactionConfirmation}; use v1::types::{TransactionRequest, TransactionConfirmation};
use util::{U256, H256}; use util::{U256, H256};
/// Messages that queue informs about /// Possible events happening in the queue that can be listened to.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum QueueMessage { pub enum QueueEvent {
/// Receiver should stop work upon receiving `Finish` message. /// Receiver should stop work upon receiving `Finish` message.
Finish, Finish,
/// Informs about new transaction request. /// Informs about new request.
NewRequest(U256), NewRequest(U256),
/// Request rejected.
RequestRejected(U256),
/// Request resolved.
RequestConfirmed(U256),
} }
/// Defines possible errors returned from queue receiving method. /// Defines possible errors returned from queue receiving method.
@ -41,19 +45,20 @@ pub enum QueueError {
} }
/// Message Receiver type /// Message Receiver type
pub type QueueMessageReceiver = mpsc::Receiver<QueueMessage>; pub type QueueEventReceiver = mpsc::Receiver<QueueEvent>;
/// A queue of transactions awaiting to be confirmed and signed. /// A queue of transactions awaiting to be confirmed and signed.
pub trait SigningQueue: Send + Sync { pub trait SigningQueue: Send + Sync {
/// Add new request to the queue. /// Add new request to the queue.
fn add_request(&self, transaction: TransactionRequest) -> U256; /// Returns a `ConfirmationPromise` that can be used to await for resolution of given request.
fn add_request(&self, transaction: TransactionRequest) -> ConfirmationPromise;
/// Remove request from the queue. /// Removes a request from the queue.
/// Notify possible waiters that transaction was rejected. /// Notifies possible token holders that transaction was rejected.
fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation>; fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation>;
/// Remove request from the queue. /// Removes a request from the queue.
/// Notify possible waiters that transaction was confirmed and got given hash. /// Notifies possible token holders that transaction was confirmed and given hash was assigned.
fn request_confirmed(&self, id: U256, hash: H256) -> Option<TransactionConfirmation>; fn request_confirmed(&self, id: U256, hash: H256) -> Option<TransactionConfirmation>;
/// Returns a request if it is contained in the queue. /// Returns a request if it is contained in the queue.
@ -61,32 +66,89 @@ pub trait SigningQueue: Send + Sync {
/// Return copy of all the requests in the queue. /// Return copy of all the requests in the queue.
fn requests(&self) -> Vec<TransactionConfirmation>; fn requests(&self) -> Vec<TransactionConfirmation>;
}
/// Blocks for some time waiting for confirmation. #[derive(Debug, PartialEq)]
/// Returns `None` when timeout reached or transaction was rejected. enum ConfirmationResult {
/// Returns transaction hash when transaction was confirmed. /// The transaction has not yet been confirmed nor rejected.
fn wait_with_timeout(&self, id: U256) -> Option<H256>; Waiting,
/// The transaction has been rejected.
Rejected,
/// The transaction has been confirmed.
Confirmed(H256),
} }
/// Time you need to confirm the transaction in UI. /// 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 /// Unless we have a multi-threaded RPC this will lock
/// any other incoming call! /// any other incoming call!
const QUEUE_TIMEOUT_DURATION_SEC : u64 = 20; const QUEUE_TIMEOUT_DURATION_SEC : u64 = 20;
#[derive(Debug, Clone)] /// A handle to submitted request.
enum QueueStatus { /// Allows to block and wait for a resolution of that request.
Waiting, pub struct ConfirmationToken {
Rejected, result: Arc<Mutex<ConfirmationResult>>,
Confirmed(H256), 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);
while Instant::now() < deadline {
// Park thread
thread::park_timeout(timeout);
// 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` and make sure to remove waiting.
trace!(target: "own_tx", "Signer: Confirmation timeout reached... ({:?}).", self.id);
None
}
} }
/// Queue for all unconfirmed transactions. /// Queue for all unconfirmed transactions.
pub struct ConfirmationsQueue { pub struct ConfirmationsQueue {
id: Mutex<U256>, id: Mutex<U256>,
waiters: RwLock<HashMap<U256, QueueStatus>>, queue: RwLock<HashMap<U256, ConfirmationToken>>,
queue: RwLock<HashMap<U256, TransactionConfirmation>>, sender: Mutex<mpsc::Sender<QueueEvent>>,
sender: Mutex<mpsc::Sender<QueueMessage>>, receiver: Mutex<Option<mpsc::Receiver<QueueEvent>>>,
receiver: Mutex<Option<mpsc::Receiver<QueueMessage>>>,
} }
impl Default for ConfirmationsQueue { impl Default for ConfirmationsQueue {
@ -95,7 +157,6 @@ impl Default for ConfirmationsQueue {
ConfirmationsQueue { ConfirmationsQueue {
id: Mutex::new(U256::from(0)), id: Mutex::new(U256::from(0)),
waiters: RwLock::new(HashMap::new()),
queue: RwLock::new(HashMap::new()), queue: RwLock::new(HashMap::new()),
sender: Mutex::new(send), sender: Mutex::new(send),
receiver: Mutex::new(Some(recv)), receiver: Mutex::new(Some(recv)),
@ -104,11 +165,11 @@ impl Default for ConfirmationsQueue {
} }
impl ConfirmationsQueue { impl ConfirmationsQueue {
/// Blocks the thread and starts listening for notifications. /// Blocks the thread and starts listening for notifications regarding all actions in the queue.
/// For each event `listener` callback function will be invoked. /// For each event, `listener` callback will be invoked.
/// This method can be used only once. /// This method can be used only once (only single consumer of events can exist).
pub fn start_listening<F>(&self, listener: F) -> Result<(), QueueError> pub fn start_listening<F>(&self, listener: F) -> Result<(), QueueError>
where F: Fn(QueueMessage) -> () { where F: Fn(QueueEvent) -> () {
let recv = self.receiver.lock().unwrap().take(); let recv = self.receiver.lock().unwrap().take();
if let None = recv { if let None = recv {
return Err(QueueError::AlreadyUsed); return Err(QueueError::AlreadyUsed);
@ -117,7 +178,7 @@ impl ConfirmationsQueue {
loop { loop {
let message = try!(recv.recv().map_err(|e| QueueError::ReceiverError(e))); let message = try!(recv.recv().map_err(|e| QueueError::ReceiverError(e)));
if let QueueMessage::Finish = message { if let QueueEvent::Finish = message {
return Ok(()); return Ok(());
} }
@ -125,23 +186,35 @@ impl ConfirmationsQueue {
} }
} }
/// Notifies receiver that the communcation is over. /// Notifies consumer that the communcation is over.
/// No more events will be sent after this function is invoked.
pub fn finish(&self) { pub fn finish(&self) {
self.notify(QueueMessage::Finish); self.notify(QueueEvent::Finish);
} }
fn notify(&self, message: QueueMessage) { /// Notifies receiver about the event happening in this queue.
fn notify(&self, message: QueueEvent) {
// We don't really care about the result // We don't really care about the result
let _ = self.sender.lock().unwrap().send(message); let _ = self.sender.lock().unwrap().send(message);
} }
fn remove(&self, id: U256) -> Option<TransactionConfirmation> { /// Removes transaction from this queue and notifies `ConfirmationPromise` holders about the result.
self.queue.write().unwrap().remove(&id) /// 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);
fn update_status(&self, id: U256, status: QueueStatus) { if let Some(token) = token {
let mut waiters = self.waiters.write().unwrap(); // notify receiver about the event
waiters.insert(id, status); 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
} }
} }
@ -152,7 +225,7 @@ impl Drop for ConfirmationsQueue {
} }
impl SigningQueue for ConfirmationsQueue { impl SigningQueue for ConfirmationsQueue {
fn add_request(&self, transaction: TransactionRequest) -> U256 { fn add_request(&self, transaction: TransactionRequest) -> ConfirmationPromise {
// Increment id // Increment id
let id = { let id = {
let mut last_id = self.id.lock().unwrap(); let mut last_id = self.id.lock().unwrap();
@ -160,87 +233,42 @@ impl SigningQueue for ConfirmationsQueue {
*last_id *last_id
}; };
// Add request to queue // Add request to queue
{ let res = {
let mut queue = self.queue.write().unwrap(); let mut queue = self.queue.write().unwrap();
queue.insert(id, TransactionConfirmation { queue.insert(id, ConfirmationToken {
result: Arc::new(Mutex::new(ConfirmationResult::Waiting)),
handle: thread::current(),
request: TransactionConfirmation {
id: id, id: id,
transaction: transaction, transaction: transaction,
},
}); });
debug!(target: "own_tx", "Signer: New transaction ({:?}) in confirmation queue.", id); 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 // Notify listeners
self.notify(QueueMessage::NewRequest(id)); self.notify(QueueEvent::NewRequest(id));
id res
} }
fn peek(&self, id: &U256) -> Option<TransactionConfirmation> { fn peek(&self, id: &U256) -> Option<TransactionConfirmation> {
self.queue.read().unwrap().get(id).cloned() self.queue.read().unwrap().get(id).map(|token| token.request.clone())
} }
fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation> { fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation> {
debug!(target: "own_tx", "Signer: Transaction rejected ({:?}).", id); debug!(target: "own_tx", "Signer: Transaction rejected ({:?}).", id);
let o = self.remove(id); self.remove(id, None)
self.update_status(id, QueueStatus::Rejected);
o
} }
fn request_confirmed(&self, id: U256, hash: H256) -> Option<TransactionConfirmation> { fn request_confirmed(&self, id: U256, hash: H256) -> Option<TransactionConfirmation> {
debug!(target: "own_tx", "Signer: Transaction confirmed ({:?}).", id); debug!(target: "own_tx", "Signer: Transaction confirmed ({:?}).", id);
let o = self.remove(id); self.remove(id, Some(hash))
self.update_status(id, QueueStatus::Confirmed(hash));
o
} }
fn requests(&self) -> Vec<TransactionConfirmation> { fn requests(&self) -> Vec<TransactionConfirmation> {
let queue = self.queue.read().unwrap(); let queue = self.queue.read().unwrap();
queue.values().cloned().collect() queue.values().map(|token| token.request.clone()).collect()
}
fn wait_with_timeout(&self, id: U256) -> Option<H256> {
{
let mut waiters = self.waiters.write().unwrap();
let r = waiters.insert(id, QueueStatus::Waiting);
match r {
// This is ok, we can have many waiters
Some(QueueStatus::Waiting) | None => {},
// There already was a response for someone.
// The one waiting for it will cleanup, so...
Some(v) => {
// ... insert old status back
waiters.insert(id, v.clone());
if let QueueStatus::Confirmed(h) = v {
return Some(h);
}
return None;
},
}
}
info!(target: "own_tx", "Signer: Awaiting transaction confirmation... ({:?}).", id);
// Now wait for a response
let deadline = Instant::now() + Duration::from_secs(QUEUE_TIMEOUT_DURATION_SEC);
while Instant::now() < deadline {
let status = {
let waiters = self.waiters.read().unwrap();
waiters.get(&id).expect("Only the waiting thread can remove any message.").clone()
};
match status {
QueueStatus::Waiting => thread::sleep(Duration::from_millis(50)),
QueueStatus::Confirmed(h) => {
self.waiters.write().unwrap().remove(&id);
return Some(h);
},
QueueStatus::Rejected => {
self.waiters.write().unwrap().remove(&id);
return None;
},
}
}
// We reached the timeout. Just return `None` and make sure to remove waiting.
trace!(target: "own_tx", "Signer: Confirmation timeout reached... ({:?}).", id);
self.waiters.write().unwrap().remove(&id);
None
} }
} }
@ -277,7 +305,7 @@ mod test {
let q = queue.clone(); let q = queue.clone();
let handle = thread::spawn(move || { let handle = thread::spawn(move || {
let v = q.add_request(request); let v = q.add_request(request);
q.wait_with_timeout(v).expect("Should return hash") v.wait_with_timeout().expect("Should return hash")
}); });
let id = U256::from(1); let id = U256::from(1);
@ -307,13 +335,13 @@ mod test {
*v = Some(notification); *v = Some(notification);
}).expect("Should be closed nicely.") }).expect("Should be closed nicely.")
}); });
let v = queue.add_request(request); queue.add_request(request);
queue.finish(); queue.finish();
// then // then
handle.join().expect("Thread should finish nicely"); handle.join().expect("Thread should finish nicely");
let r = received.lock().unwrap().take(); let r = received.lock().unwrap().take();
assert_eq!(r, Some(QueueMessage::NewRequest(v))); assert_eq!(r, Some(QueueEvent::NewRequest(U256::from(1))));
} }
#[test] #[test]

View File

@ -21,13 +21,13 @@ extern crate ethash;
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;
@ -42,7 +42,7 @@ 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,
@ -57,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,
@ -222,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,
@ -511,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, false),
BlockNumber::Latest => take_weak!(self.client).call(&signed), BlockNumber::Latest => take_weak!(self.client).call(&signed, false),
_ => 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![])))
@ -524,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, false),
BlockNumber::Latest => take_weak!(self.client).call(&signed), BlockNumber::Latest => take_weak!(self.client).call(&signed, false),
_ => 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)))

View File

@ -21,7 +21,7 @@ use std::sync::{Arc, Weak, Mutex};
use std::collections::HashSet; use std::collections::HashSet;
use jsonrpc_core::*; use jsonrpc_core::*;
use util::numbers::*; use util::numbers::*;
use ethminer::MinerService; use ethcore::miner::MinerService;
use ethcore::filter::Filter as EthcoreFilter; use ethcore::filter::Filter as EthcoreFilter;
use ethcore::client::{BlockChainClient, BlockID}; use ethcore::client::{BlockChainClient, BlockID};
use v1::traits::EthFilter; use v1::traits::EthFilter;

View File

@ -18,8 +18,8 @@
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use jsonrpc_core::*; use jsonrpc_core::*;
use ethminer::MinerService; use ethcore::miner::MinerService;
use ethcore::client::BlockChainClient; use ethcore::client::MiningBlockChainClient;
use util::numbers::*; use util::numbers::*;
use util::keys::store::AccountProvider; use util::keys::store::AccountProvider;
use v1::helpers::{SigningQueue, ConfirmationsQueue}; use v1::helpers::{SigningQueue, ConfirmationsQueue};
@ -54,7 +54,7 @@ impl EthSigning for EthSigningQueueClient {
.and_then(|(request, )| { .and_then(|(request, )| {
let queue = take_weak!(self.queue); let queue = take_weak!(self.queue);
let id = queue.add_request(request); let id = queue.add_request(request);
let result = queue.wait_with_timeout(id); let result = id.wait_with_timeout();
to_value(&result.unwrap_or_else(H256::new)) to_value(&result.unwrap_or_else(H256::new))
}) })
} }
@ -62,7 +62,7 @@ impl EthSigning for EthSigningQueueClient {
/// Implementation of functions that require signing when no trusted signer is used. /// Implementation of functions that require signing when no trusted signer is used.
pub struct EthSigningUnsafeClient<C, A, M> where pub struct EthSigningUnsafeClient<C, A, M> where
C: BlockChainClient, C: MiningBlockChainClient,
A: AccountProvider, A: AccountProvider,
M: MinerService { M: MinerService {
client: Weak<C>, client: Weak<C>,
@ -71,7 +71,7 @@ pub struct EthSigningUnsafeClient<C, A, M> where
} }
impl<C, A, M> EthSigningUnsafeClient<C, A, M> where impl<C, A, M> EthSigningUnsafeClient<C, A, M> where
C: BlockChainClient, C: MiningBlockChainClient,
A: AccountProvider, A: AccountProvider,
M: MinerService { M: MinerService {
@ -87,7 +87,7 @@ impl<C, A, M> EthSigningUnsafeClient<C, A, M> where
} }
impl<C, A, M> EthSigning for EthSigningUnsafeClient<C, A, M> where impl<C, A, M> EthSigning for EthSigningUnsafeClient<C, A, M> where
C: BlockChainClient + 'static, C: MiningBlockChainClient + 'static,
A: AccountProvider + 'static, A: AccountProvider + 'static,
M: MinerService + 'static { M: MinerService + 'static {

View File

@ -15,37 +15,107 @@
// 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 ethcore::miner::MinerService;
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
use ethcore::client::BlockChainClient;
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)
}
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 +205,19 @@ 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, true);
if let Ok(executed) = r {
if let Some(vm_trace) = executed.vm_trace {
return Ok(vm_trace_to_object(&vm_trace));
}
}
Ok(Value::Null)
})
}
} }

View File

@ -52,15 +52,15 @@ pub use self::traces::TracesClient;
pub use self::rpc::RpcClient; pub use self::rpc::RpcClient;
use v1::types::TransactionRequest; use v1::types::TransactionRequest;
use ethminer::{AccountDetails, MinerService}; use ethcore::miner::{AccountDetails, MinerService};
use ethcore::client::BlockChainClient; use ethcore::client::MiningBlockChainClient;
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;
fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> H256 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| {
@ -74,7 +74,7 @@ fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedT
} }
fn sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, secret: H256) -> H256 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 signed_transaction = { let signed_transaction = {
Transaction { Transaction {

View File

@ -22,19 +22,19 @@ use v1::types::TransactionRequest;
use v1::impls::sign_and_dispatch; use v1::impls::sign_and_dispatch;
use util::keys::store::AccountProvider; 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() {

View File

@ -24,12 +24,12 @@ use v1::impls::sign_and_dispatch;
use v1::helpers::{SigningQueue, ConfirmationsQueue}; use v1::helpers::{SigningQueue, ConfirmationsQueue};
use util::keys::store::AccountProvider; 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;
/// Transactions confirmation (personal) rpc implementation. /// Transactions confirmation (personal) rpc implementation.
pub struct SignerClient<A, C, M> pub struct SignerClient<A, C, M>
where A: AccountProvider, C: BlockChainClient, M: MinerService { where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
queue: Weak<ConfirmationsQueue>, queue: Weak<ConfirmationsQueue>,
accounts: Weak<A>, accounts: Weak<A>,
client: Weak<C>, client: Weak<C>,
@ -37,7 +37,7 @@ pub struct SignerClient<A, C, M>
} }
impl<A: 'static, C: 'static, M: 'static> SignerClient<A, C, M> impl<A: 'static, C: 'static, M: 'static> SignerClient<A, C, M>
where A: AccountProvider, C: BlockChainClient, M: MinerService { where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
/// Create new instance of signer client. /// Create new instance of signer client.
pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>, queue: &Arc<ConfirmationsQueue>) -> Self { pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>, queue: &Arc<ConfirmationsQueue>) -> Self {
@ -51,7 +51,7 @@ impl<A: 'static, C: 'static, M: 'static> SignerClient<A, C, M>
} }
impl<A: 'static, C: 'static, M: 'static> PersonalSigner for SignerClient<A, C, M> impl<A: 'static, C: 'static, M: 'static> PersonalSigner for SignerClient<A, C, M>
where A: AccountProvider, C: BlockChainClient, M: MinerService { where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
fn transactions_to_confirm(&self, _params: Params) -> Result<Value, Error> { fn transactions_to_confirm(&self, _params: Params) -> Result<Value, Error> {
let queue = take_weak!(self.queue); let queue = take_weak!(self.queue);

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;
@ -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(

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};
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, _vm_tracing: bool) -> 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,7 +25,7 @@ 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, EthSigning, EthSigningUnsafeClient}; use v1::{Eth, EthClient, EthSigning, EthSigningUnsafeClient};
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
@ -365,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",
@ -430,6 +430,7 @@ 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,
}); });
let request = r#"{ let request = r#"{
@ -463,6 +464,7 @@ 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,
}); });
let request = r#"{ let request = r#"{
@ -495,6 +497,7 @@ 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,
}); });
let request = r#"{ let request = r#"{
@ -528,6 +531,7 @@ 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,
}); });
let request = r#"{ let request = r#"{

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

@ -72,6 +72,8 @@ 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>;
/// 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 +97,8 @@ 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 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

@ -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

@ -11,7 +11,6 @@ authors = ["Ethcore <admin@ethcore.io"]
ethcore-util = { path = "../util" } ethcore-util = { path = "../util" }
ethcore = { path = "../ethcore" } ethcore = { path = "../ethcore" }
clippy = { version = "0.0.69", optional = true} clippy = { version = "0.0.69", optional = true}
ethminer = { path = "../miner" }
log = "0.3" log = "0.3"
env_logger = "0.3" env_logger = "0.3"
time = "0.1.34" time = "0.1.34"
@ -20,4 +19,4 @@ heapsize = "0.3"
[features] [features]
default = [] default = []
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethminer/dev"] dev = ["clippy", "ethcore/dev", "ethcore-util/dev"]

View File

@ -97,7 +97,6 @@ use ethcore::client::{BlockChainClient, BlockStatus, BlockID, BlockChainInfo};
use ethcore::error::*; use ethcore::error::*;
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use ethcore::block::Block; use ethcore::block::Block;
use ethminer::{Miner, MinerService, AccountDetails};
use io::SyncIo; use io::SyncIo;
use time; use time;
use super::SyncConfig; use super::SyncConfig;
@ -241,15 +240,13 @@ pub struct ChainSync {
imported_this_round: Option<usize>, imported_this_round: Option<usize>,
/// Network ID /// Network ID
network_id: U256, network_id: U256,
/// Miner
miner: Arc<Miner>,
} }
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>; type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
impl ChainSync { impl ChainSync {
/// Create a new instance of syncing strategy. /// Create a new instance of syncing strategy.
pub fn new(config: SyncConfig, miner: Arc<Miner>, chain: &BlockChainClient) -> ChainSync { pub fn new(config: SyncConfig, chain: &BlockChainClient) -> ChainSync {
let chain = chain.chain_info(); let chain = chain.chain_info();
let mut sync = ChainSync { let mut sync = ChainSync {
state: SyncState::ChainHead, state: SyncState::ChainHead,
@ -265,7 +262,6 @@ impl ChainSync {
imported_this_round: None, imported_this_round: None,
_max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks), _max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
network_id: config.network_id, network_id: config.network_id,
miner: miner,
}; };
sync.reset(); sync.reset();
sync sync
@ -898,12 +894,7 @@ impl ChainSync {
let tx: SignedTransaction = try!(r.val_at(i)); let tx: SignedTransaction = try!(r.val_at(i));
transactions.push(tx); transactions.push(tx);
} }
let chain = io.chain(); let _ = io.chain().import_transactions(transactions);
let fetch_account = |a: &Address| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
};
let _ = self.miner.import_transactions(transactions, fetch_account);
Ok(()) Ok(())
} }
@ -1226,7 +1217,7 @@ impl ChainSync {
return 0; return 0;
} }
let mut transactions = self.miner.all_transactions(); let mut transactions = io.chain().all_transactions();
if transactions.is_empty() { if transactions.is_empty() {
return 0; return 0;
} }
@ -1277,10 +1268,8 @@ impl ChainSync {
} }
/// called when block is imported to chain, updates transactions queue and propagates the blocks /// called when block is imported to chain, updates transactions queue and propagates the blocks
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]) { pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) {
if io.is_chain_queue_empty() { if io.is_chain_queue_empty() {
// Notify miner
self.miner.chain_new_blocks(io.chain(), imported, invalid, enacted, retracted);
// Propagate latests blocks // Propagate latests blocks
self.propagate_latest_blocks(io); self.propagate_latest_blocks(io);
} }
@ -1289,10 +1278,6 @@ impl ChainSync {
self.restart_on_bad_block(io); self.restart_on_bad_block(io);
} }
} }
pub fn chain_new_head(&mut self, io: &mut SyncIo) {
self.miner.update_sealing(io.chain());
}
} }
#[cfg(test)] #[cfg(test)]
@ -1305,8 +1290,7 @@ mod tests {
use ethcore::views::BlockView; use ethcore::views::BlockView;
use ethcore::header::*; use ethcore::header::*;
use ethcore::client::*; use ethcore::client::*;
use ethcore::spec::Spec; use ethcore::miner::MinerService;
use ethminer::{Miner, MinerService};
fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes { fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes {
let mut header = Header::new(); let mut header = Header::new();
@ -1480,7 +1464,7 @@ mod tests {
} }
fn dummy_sync_with_peer(peer_latest_hash: H256, client: &BlockChainClient) -> ChainSync { fn dummy_sync_with_peer(peer_latest_hash: H256, client: &BlockChainClient) -> ChainSync {
let mut sync = ChainSync::new(SyncConfig::default(), Miner::new(false, Spec::new_test()), client); let mut sync = ChainSync::new(SyncConfig::default(), client);
sync.peers.insert(0, sync.peers.insert(0,
PeerInfo { PeerInfo {
protocol_version: 0, protocol_version: 0,
@ -1711,9 +1695,10 @@ mod tests {
{ {
let mut queue = VecDeque::new(); let mut queue = VecDeque::new();
let mut io = TestIo::new(&mut client, &mut queue, None); let mut io = TestIo::new(&mut client, &mut queue, None);
io.chain.miner.chain_new_blocks(io.chain, &[], &[], &[], &good_blocks);
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks); sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks);
assert_eq!(sync.miner.status().transactions_in_future_queue, 0); assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0);
assert_eq!(sync.miner.status().transactions_in_pending_queue, 1); assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 1);
} }
// We need to update nonce status (because we say that the block has been imported) // We need to update nonce status (because we say that the block has been imported)
for h in &[good_blocks[0]] { for h in &[good_blocks[0]] {
@ -1724,11 +1709,12 @@ mod tests {
{ {
let mut queue = VecDeque::new(); let mut queue = VecDeque::new();
let mut io = TestIo::new(&mut client, &mut queue, None); let mut io = TestIo::new(&mut client, &mut queue, None);
io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks);
sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks); sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks);
} }
// then // then
let status = sync.miner.status(); let status = client.miner.status();
assert_eq!(status.transactions_in_pending_queue, 1); assert_eq!(status.transactions_in_pending_queue, 1);
assert_eq!(status.transactions_in_future_queue, 0); assert_eq!(status.transactions_in_future_queue, 0);
} }
@ -1750,12 +1736,12 @@ mod tests {
// when // when
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks); sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks);
assert_eq!(sync.miner.status().transactions_in_future_queue, 0); assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0);
assert_eq!(sync.miner.status().transactions_in_pending_queue, 0); assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 0);
sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks); sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks);
// then // then
let status = sync.miner.status(); let status = io.chain.miner.status();
assert_eq!(status.transactions_in_pending_queue, 0); assert_eq!(status.transactions_in_pending_queue, 0);
assert_eq!(status.transactions_in_future_queue, 0); assert_eq!(status.transactions_in_future_queue, 0);
} }

View File

@ -14,10 +14,10 @@
// 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 ethcore::client::BlockChainClient;
use util::{NetworkContext, PeerId, PacketId,}; use util::{NetworkContext, PeerId, PacketId,};
use util::error::UtilError; use util::error::UtilError;
use ethcore::service::SyncMessage; use ethcore::service::SyncMessage;
use ethcore::client::BlockChainClient;
/// IO interface for the syning handler. /// IO interface for the syning handler.
/// Provides peer connection management and an interface to the blockchain client. /// Provides peer connection management and an interface to the blockchain client.

View File

@ -32,21 +32,20 @@
//! 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;
//! use std::env; //! use std::env;
//! use std::sync::Arc; //! use std::sync::Arc;
//! use util::network::{NetworkService, NetworkConfiguration}; //! use util::network::{NetworkService, NetworkConfiguration};
//! use ethcore::client::{Client, ClientConfig}; //! use ethcore::client::{Client, ClientConfig};
//! use ethsync::{EthSync, SyncConfig}; //! use ethsync::{EthSync, SyncConfig};
//! use ethminer::Miner;
//! use ethcore::ethereum; //! use ethcore::ethereum;
//! use ethcore::miner::Miner;
//! //!
//! fn main() { //! fn main() {
//! let mut service = NetworkService::start(NetworkConfiguration::new()).unwrap(); //! let mut service = NetworkService::start(NetworkConfiguration::new()).unwrap();
//! let dir = env::temp_dir(); //! let dir = env::temp_dir();
//! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(), &dir, service.io().channel()).unwrap(); //! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(), &dir, Arc::new(Miner::default()), service.io().channel()).unwrap();
//! let miner = Miner::new(false, ethereum::new_frontier()); //! let miner = Miner::new(false, ethereum::new_frontier());
//! EthSync::register(&mut service, SyncConfig::default(), client, miner); //! EthSync::register(&mut service, SyncConfig::default(), client);
//! } //! }
//! ``` //! ```
@ -55,7 +54,6 @@ extern crate log;
#[macro_use] #[macro_use]
extern crate ethcore_util as util; extern crate ethcore_util as util;
extern crate ethcore; extern crate ethcore;
extern crate ethminer;
extern crate env_logger; extern crate env_logger;
extern crate time; extern crate time;
extern crate rand; extern crate rand;
@ -69,7 +67,6 @@ use util::TimerToken;
use util::{U256, ONE_U256}; use util::{U256, ONE_U256};
use ethcore::client::Client; use ethcore::client::Client;
use ethcore::service::SyncMessage; use ethcore::service::SyncMessage;
use ethminer::Miner;
use io::NetSyncIo; use io::NetSyncIo;
use chain::ChainSync; use chain::ChainSync;
@ -115,8 +112,8 @@ pub use self::chain::{SyncStatus, SyncState};
impl EthSync { impl EthSync {
/// Creates and register protocol with the network service /// Creates and register protocol with the network service
pub fn register(service: &mut NetworkService<SyncMessage>, config: SyncConfig, chain: Arc<Client>, miner: Arc<Miner>) -> Arc<EthSync> { pub fn register(service: &mut NetworkService<SyncMessage>, config: SyncConfig, chain: Arc<Client>) -> Arc<EthSync> {
let sync = ChainSync::new(config, miner, chain.deref()); let sync = ChainSync::new(config, chain.deref());
let sync = Arc::new(EthSync { let sync = Arc::new(EthSync {
chain: chain, chain: chain,
sync: RwLock::new(sync), sync: RwLock::new(sync),
@ -171,10 +168,6 @@ impl NetworkProtocolHandler<SyncMessage> for EthSync {
let mut sync_io = NetSyncIo::new(io, self.chain.deref()); let mut sync_io = NetSyncIo::new(io, self.chain.deref());
self.sync.write().unwrap().chain_new_blocks(&mut sync_io, imported, invalid, enacted, retracted); self.sync.write().unwrap().chain_new_blocks(&mut sync_io, imported, invalid, enacted, retracted);
}, },
SyncMessage::NewChainHead => {
let mut sync_io = NetSyncIo::new(io, self.chain.deref());
self.sync.write().unwrap().chain_new_head(&mut sync_io);
},
_ => {/* Ignore other messages */}, _ => {/* Ignore other messages */},
} }
} }

View File

@ -16,10 +16,8 @@
use util::*; use util::*;
use ethcore::client::{TestBlockChainClient, BlockChainClient}; use ethcore::client::{TestBlockChainClient, BlockChainClient};
use ethcore::spec::Spec;
use io::SyncIo; use io::SyncIo;
use chain::ChainSync; use chain::ChainSync;
use ethminer::Miner;
use ::SyncConfig; use ::SyncConfig;
pub struct TestIo<'p> { pub struct TestIo<'p> {
@ -93,7 +91,7 @@ impl TestNet {
}; };
for _ in 0..n { for _ in 0..n {
let chain = TestBlockChainClient::new(); let chain = TestBlockChainClient::new();
let sync = ChainSync::new(SyncConfig::default(), Miner::new(false, Spec::new_test()), &chain); let sync = ChainSync::new(SyncConfig::default(), &chain);
net.peers.push(TestPeer { net.peers.push(TestPeer {
sync: sync, sync: sync,
chain: chain, chain: chain,

View File

@ -10,5 +10,4 @@ cargo test --features ethcore/json-tests $1 \
-p ethcore-signer \ -p ethcore-signer \
-p ethcore-dapps \ -p ethcore-dapps \
-p parity \ -p parity \
-p ethminer \
-p bigint -p bigint

View File

@ -25,7 +25,7 @@ elastic-array = "0.4"
heapsize = "0.3" heapsize = "0.3"
itertools = "0.4" itertools = "0.4"
crossbeam = "0.2" crossbeam = "0.2"
slab = "0.1" slab = "0.2"
sha3 = { path = "sha3" } sha3 = { path = "sha3" }
serde = "0.7.0" serde = "0.7.0"
clippy = { version = "0.0.69", optional = true} clippy = { version = "0.0.69", optional = true}

View File

@ -24,6 +24,13 @@ pub use vector::*;
pub use numbers::*; pub use numbers::*;
pub use sha3::*; pub use sha3::*;
#[macro_export]
macro_rules! vec_into {
( $( $x:expr ),* ) => {
vec![ $( $x.into() ),* ]
}
}
#[macro_export] #[macro_export]
macro_rules! hash_map { macro_rules! hash_map {
() => { HashMap::new() }; () => { HashMap::new() };

View File

@ -170,16 +170,16 @@ impl Connection {
self.token self.token
} }
/// Replace socket token
pub fn set_token(&mut self, token: StreamToken) {
self.token = token;
}
/// Get remote peer address /// Get remote peer address
pub fn remote_addr(&self) -> io::Result<SocketAddr> { pub fn remote_addr(&self) -> io::Result<SocketAddr> {
self.socket.peer_addr() self.socket.peer_addr()
} }
/// Get remote peer address string
pub fn remote_addr_str(&self) -> String {
self.socket.peer_addr().map(|a| a.to_string()).unwrap_or_else(|_| "Unknown".to_owned())
}
/// Clone this connection. Clears the receiving buffer of the returned connection. /// Clone this connection. Clears the receiving buffer of the returned connection.
pub fn try_clone(&self) -> io::Result<Self> { pub fn try_clone(&self) -> io::Result<Self> {
Ok(Connection { Ok(Connection {
@ -196,7 +196,7 @@ impl Connection {
/// Register this connection with the IO event loop. /// Register this connection with the IO event loop.
pub fn register_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> { pub fn register_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
trace!(target: "network", "connection register; token={:?}", reg); trace!(target: "network", "connection register; token={:?}", reg);
if let Err(e) = event_loop.register(&self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()) { if let Err(e) = event_loop.register(&self.socket, reg, self.interest, PollOpt::edge() /* | PollOpt::oneshot() */) { // TODO: oneshot is broken on windows
trace!(target: "network", "Failed to register {:?}, {:?}", reg, e); trace!(target: "network", "Failed to register {:?}, {:?}", reg, e);
} }
Ok(()) Ok(())
@ -205,7 +205,7 @@ impl Connection {
/// Update connection registration. Should be called at the end of the IO handler. /// Update connection registration. Should be called at the end of the IO handler.
pub fn update_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> { pub fn update_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
trace!(target: "network", "connection reregister; token={:?}", reg); trace!(target: "network", "connection reregister; token={:?}", reg);
event_loop.reregister( &self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()).or_else(|e| { event_loop.reregister( &self.socket, reg, self.interest, PollOpt::edge() /* | PollOpt::oneshot() */ ).or_else(|e| { // TODO: oneshot is broken on windows
trace!(target: "network", "Failed to reregister {:?}, {:?}", reg, e); trace!(target: "network", "Failed to reregister {:?}, {:?}", reg, e);
Ok(()) Ok(())
}) })
@ -246,7 +246,7 @@ enum EncryptedConnectionState {
/// https://github.com/ethereum/devp2p/blob/master/rlpx.md#framing /// https://github.com/ethereum/devp2p/blob/master/rlpx.md#framing
pub struct EncryptedConnection { pub struct EncryptedConnection {
/// Underlying tcp connection /// Underlying tcp connection
connection: Connection, pub connection: Connection,
/// Egress data encryptor /// Egress data encryptor
encoder: CtrMode<AesSafe256Encryptor>, encoder: CtrMode<AesSafe256Encryptor>,
/// Ingress data decryptor /// Ingress data decryptor
@ -266,27 +266,6 @@ pub struct EncryptedConnection {
} }
impl EncryptedConnection { impl EncryptedConnection {
/// Get socket token
pub fn token(&self) -> StreamToken {
self.connection.token
}
/// Replace socket token
pub fn set_token(&mut self, token: StreamToken) {
self.connection.set_token(token);
}
/// Get remote peer address
pub fn remote_addr(&self) -> io::Result<SocketAddr> {
self.connection.remote_addr()
}
/// Check if this connection has data to be sent.
pub fn is_sending(&self) -> bool {
self.connection.is_sending()
}
/// Create an encrypted connection out of the handshake. Consumes a handshake object. /// Create an encrypted connection out of the handshake. Consumes a handshake object.
pub fn new(handshake: &mut Handshake) -> Result<EncryptedConnection, UtilError> { pub fn new(handshake: &mut Handshake) -> Result<EncryptedConnection, UtilError> {
let shared = try!(crypto::ecdh::agree(handshake.ecdhe.secret(), &handshake.remote_ephemeral)); let shared = try!(crypto::ecdh::agree(handshake.ecdhe.secret(), &handshake.remote_ephemeral));
@ -323,8 +302,10 @@ impl EncryptedConnection {
ingress_mac.update(&mac_material); ingress_mac.update(&mac_material);
ingress_mac.update(if handshake.originated { &handshake.ack_cipher } else { &handshake.auth_cipher }); ingress_mac.update(if handshake.originated { &handshake.ack_cipher } else { &handshake.auth_cipher });
let old_connection = try!(handshake.connection.try_clone());
let connection = ::std::mem::replace(&mut handshake.connection, old_connection);
let mut enc = EncryptedConnection { let mut enc = EncryptedConnection {
connection: try!(handshake.connection.try_clone()), connection: connection,
encoder: encoder, encoder: encoder,
decoder: decoder, decoder: decoder,
mac_encoder: mac_encoder, mac_encoder: mac_encoder,
@ -463,24 +444,6 @@ impl EncryptedConnection {
try!(self.connection.writable()); try!(self.connection.writable());
Ok(()) Ok(())
} }
/// Register socket with the event lpop. This should be called at the end of the event loop.
pub fn register_socket<Host:Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> {
try!(self.connection.register_socket(reg, event_loop));
Ok(())
}
/// Update connection registration. This should be called at the end of the event loop.
pub fn update_socket<Host:Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> {
try!(self.connection.update_socket(reg, event_loop));
Ok(())
}
/// Delete connection registration. This should be called at the end of the event loop.
pub fn deregister_socket<Host:Handler>(&self, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> {
try!(self.connection.deregister_socket(event_loop));
Ok(())
}
} }
#[test] #[test]

View File

@ -16,7 +16,6 @@
use std::sync::Arc; use std::sync::Arc;
use rand::random; use rand::random;
use mio::*;
use mio::tcp::*; use mio::tcp::*;
use hash::*; use hash::*;
use rlp::*; use rlp::*;
@ -102,21 +101,6 @@ impl Handshake {
}) })
} }
/// Get id of the remote node if known
pub fn id(&self) -> &NodeId {
&self.id
}
/// Get stream token id
pub fn token(&self) -> StreamToken {
self.connection.token()
}
/// Mark this handshake as inactive to be deleted lated.
pub fn set_expired(&mut self) {
self.expired = true;
}
/// Check if this handshake is expired. /// Check if this handshake is expired.
pub fn expired(&self) -> bool { pub fn expired(&self) -> bool {
self.expired self.expired
@ -177,7 +161,7 @@ impl Handshake {
} }
/// Writabe IO handler. /// Writabe IO handler.
pub fn writable<Message>(&mut self, io: &IoContext<Message>, _host: &HostInfo) -> Result<(), UtilError> where Message: Send + Clone { pub fn writable<Message>(&mut self, io: &IoContext<Message>) -> Result<(), UtilError> where Message: Send + Clone {
if !self.expired() { if !self.expired() {
io.clear_timer(self.connection.token).unwrap(); io.clear_timer(self.connection.token).unwrap();
try!(self.connection.writable()); try!(self.connection.writable());
@ -188,28 +172,6 @@ impl Handshake {
Ok(()) Ok(())
} }
/// Register the socket with the event loop
pub fn register_socket<Host:Handler<Timeout=Token>>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> {
if !self.expired() {
try!(self.connection.register_socket(reg, event_loop));
}
Ok(())
}
/// Update socket registration with the event loop.
pub fn update_socket<Host:Handler<Timeout=Token>>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> {
if !self.expired() {
try!(self.connection.update_socket(reg, event_loop));
}
Ok(())
}
/// Delete registration
pub fn deregister_socket<Host:Handler>(&self, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> {
try!(self.connection.deregister_socket(event_loop));
Ok(())
}
fn set_auth(&mut self, host_secret: &Secret, sig: &[u8], remote_public: &[u8], remote_nonce: &[u8], remote_version: u64) -> Result<(), UtilError> { fn set_auth(&mut self, host_secret: &Secret, sig: &[u8], remote_public: &[u8], remote_nonce: &[u8], remote_version: u64) -> Result<(), UtilError> {
self.id.clone_from_slice(remote_public); self.id.clone_from_slice(remote_public);
self.remote_nonce.clone_from_slice(remote_nonce); self.remote_nonce.clone_from_slice(remote_nonce);
@ -222,7 +184,7 @@ impl Handshake {
/// Parse, validate and confirm auth message /// Parse, validate and confirm auth message
fn read_auth(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> { fn read_auth(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
trace!(target:"network", "Received handshake auth from {:?}", self.connection.socket.peer_addr()); trace!(target:"network", "Received handshake auth from {:?}", self.connection.remote_addr_str());
if data.len() != V4_AUTH_PACKET_SIZE { if data.len() != V4_AUTH_PACKET_SIZE {
debug!(target:"net", "Wrong auth packet size"); debug!(target:"net", "Wrong auth packet size");
return Err(From::from(NetworkError::BadProtocol)); return Err(From::from(NetworkError::BadProtocol));
@ -253,7 +215,7 @@ impl Handshake {
} }
fn read_auth_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> { fn read_auth_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr()); trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str());
self.auth_cipher.extend_from_slice(data); self.auth_cipher.extend_from_slice(data);
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..])); let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
let rlp = UntrustedRlp::new(&auth); let rlp = UntrustedRlp::new(&auth);
@ -268,7 +230,7 @@ impl Handshake {
/// Parse and validate ack message /// Parse and validate ack message
fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> { fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
trace!(target:"network", "Received handshake auth to {:?}", self.connection.socket.peer_addr()); trace!(target:"network", "Received handshake auth to {:?}", self.connection.remote_addr_str());
if data.len() != V4_ACK_PACKET_SIZE { if data.len() != V4_ACK_PACKET_SIZE {
debug!(target:"net", "Wrong ack packet size"); debug!(target:"net", "Wrong ack packet size");
return Err(From::from(NetworkError::BadProtocol)); return Err(From::from(NetworkError::BadProtocol));
@ -296,7 +258,7 @@ impl Handshake {
} }
fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> { fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr()); trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str());
self.ack_cipher.extend_from_slice(data); self.ack_cipher.extend_from_slice(data);
let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..])); let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..]));
let rlp = UntrustedRlp::new(&ack); let rlp = UntrustedRlp::new(&ack);
@ -309,7 +271,7 @@ impl Handshake {
/// Sends auth message /// Sends auth message
fn write_auth(&mut self, secret: &Secret, public: &Public) -> Result<(), UtilError> { fn write_auth(&mut self, secret: &Secret, public: &Public) -> Result<(), UtilError> {
trace!(target:"network", "Sending handshake auth to {:?}", self.connection.socket.peer_addr()); trace!(target:"network", "Sending handshake auth to {:?}", self.connection.remote_addr_str());
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
let len = data.len(); let len = data.len();
{ {
@ -336,7 +298,7 @@ impl Handshake {
/// Sends ack message /// Sends ack message
fn write_ack(&mut self) -> Result<(), UtilError> { fn write_ack(&mut self) -> Result<(), UtilError> {
trace!(target:"network", "Sending handshake ack to {:?}", self.connection.socket.peer_addr()); trace!(target:"network", "Sending handshake ack to {:?}", self.connection.remote_addr_str());
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
let len = data.len(); let len = data.len();
{ {
@ -355,7 +317,7 @@ impl Handshake {
/// Sends EIP8 ack message /// Sends EIP8 ack message
fn write_ack_eip8(&mut self) -> Result<(), UtilError> { fn write_ack_eip8(&mut self) -> Result<(), UtilError> {
trace!(target:"network", "Sending EIP8 handshake ack to {:?}", self.connection.socket.peer_addr()); trace!(target:"network", "Sending EIP8 handshake ack to {:?}", self.connection.remote_addr_str());
let mut rlp = RlpStream::new_list(3); let mut rlp = RlpStream::new_list(3);
rlp.append(self.ecdhe.public()); rlp.append(self.ecdhe.public());
rlp.append(&self.nonce); rlp.append(&self.nonce);

View File

@ -18,6 +18,7 @@ use std::net::{SocketAddr};
use std::collections::{HashMap}; use std::collections::{HashMap};
use std::str::{FromStr}; use std::str::{FromStr};
use std::sync::*; use std::sync::*;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use std::ops::*; use std::ops::*;
use std::cmp::min; use std::cmp::min;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -31,7 +32,6 @@ use misc::version;
use crypto::*; use crypto::*;
use sha3::Hashable; use sha3::Hashable;
use rlp::*; use rlp::*;
use network::handshake::Handshake;
use network::session::{Session, SessionData}; use network::session::{Session, SessionData};
use error::*; use error::*;
use io::*; use io::*;
@ -44,8 +44,7 @@ use network::ip_utils::{map_external_address, select_public_address};
type Slab<T> = ::slab::Slab<T, usize>; type Slab<T> = ::slab::Slab<T, usize>;
const _DEFAULT_PORT: u16 = 30304; const MAX_SESSIONS: usize = 1024 + MAX_HANDSHAKES;
const MAX_SESSIONS: usize = 1024;
const MAX_HANDSHAKES: usize = 80; const MAX_HANDSHAKES: usize = 80;
const MAX_HANDSHAKES_PER_ROUND: usize = 32; const MAX_HANDSHAKES_PER_ROUND: usize = 32;
const MAINTENANCE_TIMEOUT: u64 = 1000; const MAINTENANCE_TIMEOUT: u64 = 1000;
@ -115,18 +114,17 @@ impl NetworkConfiguration {
} }
// Tokens // Tokens
const TCP_ACCEPT: usize = LAST_HANDSHAKE + 1; const TCP_ACCEPT: usize = SYS_TIMER + 1;
const IDLE: usize = LAST_HANDSHAKE + 2; const IDLE: usize = SYS_TIMER + 2;
const DISCOVERY: usize = LAST_HANDSHAKE + 3; const DISCOVERY: usize = SYS_TIMER + 3;
const DISCOVERY_REFRESH: usize = LAST_HANDSHAKE + 4; const DISCOVERY_REFRESH: usize = SYS_TIMER + 4;
const DISCOVERY_ROUND: usize = LAST_HANDSHAKE + 5; const DISCOVERY_ROUND: usize = SYS_TIMER + 5;
const INIT_PUBLIC: usize = LAST_HANDSHAKE + 6; const INIT_PUBLIC: usize = SYS_TIMER + 6;
const NODE_TABLE: usize = LAST_HANDSHAKE + 7; const NODE_TABLE: usize = SYS_TIMER + 7;
const FIRST_SESSION: usize = 0; const FIRST_SESSION: usize = 0;
const LAST_SESSION: usize = FIRST_SESSION + MAX_SESSIONS - 1; const LAST_SESSION: usize = FIRST_SESSION + MAX_SESSIONS - 1;
const FIRST_HANDSHAKE: usize = LAST_SESSION + 1; const USER_TIMER: usize = LAST_SESSION + 256;
const LAST_HANDSHAKE: usize = FIRST_HANDSHAKE + MAX_HANDSHAKES - 1; const SYS_TIMER: usize = LAST_SESSION + 1;
const USER_TIMER: usize = LAST_HANDSHAKE + 256;
/// Protocol handler level packet id /// Protocol handler level packet id
pub type PacketId = u8; pub type PacketId = u8;
@ -306,7 +304,6 @@ impl HostInfo {
} }
type SharedSession = Arc<Mutex<Session>>; type SharedSession = Arc<Mutex<Session>>;
type SharedHandshake = Arc<Mutex<Handshake>>;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct ProtocolTimer { struct ProtocolTimer {
@ -318,7 +315,6 @@ struct ProtocolTimer {
pub struct Host<Message> where Message: Send + Sync + Clone { pub struct Host<Message> where Message: Send + Sync + Clone {
pub info: RwLock<HostInfo>, pub info: RwLock<HostInfo>,
tcp_listener: Mutex<TcpListener>, tcp_listener: Mutex<TcpListener>,
handshakes: Arc<RwLock<Slab<SharedHandshake>>>,
sessions: Arc<RwLock<Slab<SharedSession>>>, sessions: Arc<RwLock<Slab<SharedSession>>>,
discovery: Mutex<Option<Discovery>>, discovery: Mutex<Option<Discovery>>,
nodes: RwLock<NodeTable>, nodes: RwLock<NodeTable>,
@ -327,6 +323,7 @@ pub struct Host<Message> where Message: Send + Sync + Clone {
timer_counter: RwLock<usize>, timer_counter: RwLock<usize>,
stats: Arc<NetworkStats>, stats: Arc<NetworkStats>,
pinned_nodes: Vec<NodeId>, pinned_nodes: Vec<NodeId>,
num_sessions: AtomicUsize,
} }
impl<Message> Host<Message> where Message: Send + Sync + Clone { impl<Message> Host<Message> where Message: Send + Sync + Clone {
@ -370,7 +367,6 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
}), }),
discovery: Mutex::new(None), discovery: Mutex::new(None),
tcp_listener: Mutex::new(tcp_listener), tcp_listener: Mutex::new(tcp_listener),
handshakes: Arc::new(RwLock::new(Slab::new_starting_at(FIRST_HANDSHAKE, MAX_HANDSHAKES))),
sessions: Arc::new(RwLock::new(Slab::new_starting_at(FIRST_SESSION, MAX_SESSIONS))), sessions: Arc::new(RwLock::new(Slab::new_starting_at(FIRST_SESSION, MAX_SESSIONS))),
nodes: RwLock::new(NodeTable::new(path)), nodes: RwLock::new(NodeTable::new(path)),
handlers: RwLock::new(HashMap::new()), handlers: RwLock::new(HashMap::new()),
@ -378,6 +374,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
timer_counter: RwLock::new(USER_TIMER), timer_counter: RwLock::new(USER_TIMER),
stats: Arc::new(NetworkStats::default()), stats: Arc::new(NetworkStats::default()),
pinned_nodes: Vec::new(), pinned_nodes: Vec::new(),
num_sessions: AtomicUsize::new(0),
}; };
let boot_nodes = host.info.read().unwrap().config.boot_nodes.clone(); let boot_nodes = host.info.read().unwrap().config.boot_nodes.clone();
@ -477,19 +474,19 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
} }
fn have_session(&self, id: &NodeId) -> bool { fn have_session(&self, id: &NodeId) -> bool {
self.sessions.read().unwrap().iter().any(|e| e.lock().unwrap().info.id.eq(&id)) self.sessions.read().unwrap().iter().any(|e| e.lock().unwrap().info.id == Some(id.clone()))
} }
fn session_count(&self) -> usize { fn session_count(&self) -> usize {
self.sessions.read().unwrap().count() self.num_sessions.load(AtomicOrdering::Relaxed)
} }
fn connecting_to(&self, id: &NodeId) -> bool { fn connecting_to(&self, id: &NodeId) -> bool {
self.handshakes.read().unwrap().iter().any(|e| e.lock().unwrap().id.eq(&id)) self.sessions.read().unwrap().iter().any(|e| e.lock().unwrap().id() == Some(id))
} }
fn handshake_count(&self) -> usize { fn handshake_count(&self) -> usize {
self.handshakes.read().unwrap().count() self.sessions.read().unwrap().count() - self.session_count()
} }
fn keep_alive(&self, io: &IoContext<NetworkIoMessage<Message>>) { fn keep_alive(&self, io: &IoContext<NetworkIoMessage<Message>>) {
@ -565,21 +562,31 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
} }
} }
}; };
self.create_connection(socket, Some(id), io); if let Err(e) = self.create_connection(socket, Some(id), io) {
debug!(target: "network", "Can't create connection: {:?}", e);
}
} }
#[cfg_attr(feature="dev", allow(block_in_if_condition_stmt))] #[cfg_attr(feature="dev", allow(block_in_if_condition_stmt))]
fn create_connection(&self, socket: TcpStream, id: Option<&NodeId>, io: &IoContext<NetworkIoMessage<Message>>) { fn create_connection(&self, socket: TcpStream, id: Option<&NodeId>, io: &IoContext<NetworkIoMessage<Message>>) -> Result<(), UtilError> {
let nonce = self.info.write().unwrap().next_nonce(); let nonce = self.info.write().unwrap().next_nonce();
let mut handshakes = self.handshakes.write().unwrap(); let mut sessions = self.sessions.write().unwrap();
if handshakes.insert_with(|token| { let token = sessions.insert_with_opt(|token| {
let mut handshake = Handshake::new(token, id, socket, &nonce, self.stats.clone()).expect("Can't create handshake"); match Session::new(io, socket, token, id, &nonce, self.stats.clone(), &self.info.read().unwrap()) {
handshake.start(io, &self.info.read().unwrap(), id.is_some()).and_then(|_| io.register_stream(token)).unwrap_or_else (|e| { Ok(s) => Some(Arc::new(Mutex::new(s))),
debug!(target: "network", "Handshake create error: {:?}", e); Err(e) => {
debug!(target: "network", "Session create error: {:?}", e);
None
}
}
}); });
Arc::new(Mutex::new(handshake))
}).is_none() { match token {
debug!(target: "network", "Max handshakes reached"); Some(t) => io.register_stream(t),
None => {
debug!(target: "network", "Max sessions reached");
Ok(())
}
} }
} }
@ -594,21 +601,13 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
break break
}, },
}; };
self.create_connection(socket, None, io); if let Err(e) = self.create_connection(socket, None, io) {
debug!(target: "network", "Can't accept connection: {:?}", e);
}
} }
io.update_registration(TCP_ACCEPT).expect("Error registering TCP listener"); io.update_registration(TCP_ACCEPT).expect("Error registering TCP listener");
} }
fn handshake_writable(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) {
let handshake = { self.handshakes.read().unwrap().get(token).cloned() };
if let Some(handshake) = handshake {
let mut h = handshake.lock().unwrap();
if let Err(e) = h.writable(io, &self.info.read().unwrap()) {
trace!(target: "network", "Handshake write error: {}: {:?}", token, e);
}
}
}
fn session_writable(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) { fn session_writable(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) {
let session = { self.sessions.read().unwrap().get(token).cloned() }; let session = { self.sessions.read().unwrap().get(token).cloned() };
if let Some(session) = session { if let Some(session) = session {
@ -629,30 +628,6 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
self.kill_connection(token, io, true); self.kill_connection(token, io, true);
} }
fn handshake_readable(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) {
let mut create_session = false;
let mut kill = false;
let handshake = { self.handshakes.read().unwrap().get(token).cloned() };
if let Some(handshake) = handshake {
let mut h = handshake.lock().unwrap();
if let Err(e) = h.readable(io, &self.info.read().unwrap()) {
debug!(target: "network", "Handshake read error: {}: {:?}", token, e);
kill = true;
}
if h.done() {
create_session = true;
}
}
if kill {
self.kill_connection(token, io, true);
return;
} else if create_session {
self.start_session(token, io);
return;
}
io.update_registration(token).unwrap_or_else(|e| debug!(target: "network", "Token registration error: {:?}", e));
}
fn session_readable(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) { fn session_readable(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) {
let mut ready_data: Vec<ProtocolId> = Vec::new(); let mut ready_data: Vec<ProtocolId> = Vec::new();
let mut packet_data: Option<(ProtocolId, PacketId, Vec<u8>)> = None; let mut packet_data: Option<(ProtocolId, PacketId, Vec<u8>)> = None;
@ -662,17 +637,37 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
let mut s = session.lock().unwrap(); let mut s = session.lock().unwrap();
match s.readable(io, &self.info.read().unwrap()) { match s.readable(io, &self.info.read().unwrap()) {
Err(e) => { Err(e) => {
trace!(target: "network", "Session read error: {}:{} ({:?}) {:?}", token, s.id(), s.remote_addr(), e); trace!(target: "network", "Session read error: {}:{:?} ({:?}) {:?}", token, s.id(), s.remote_addr(), e);
match e { match e {
UtilError::Network(NetworkError::Disconnect(DisconnectReason::UselessPeer)) | UtilError::Network(NetworkError::Disconnect(DisconnectReason::UselessPeer)) |
UtilError::Network(NetworkError::Disconnect(DisconnectReason::IncompatibleProtocol)) => { UtilError::Network(NetworkError::Disconnect(DisconnectReason::IncompatibleProtocol)) => {
self.nodes.write().unwrap().mark_as_useless(s.id()); if let Some(id) = s.id() {
self.nodes.write().unwrap().mark_as_useless(id);
}
} }
_ => (), _ => (),
} }
kill = true; kill = true;
}, },
Ok(SessionData::Ready) => { Ok(SessionData::Ready) => {
if !s.info.originated {
let session_count = self.session_count();
let ideal_peers = { self.info.read().unwrap().deref().config.ideal_peers };
if session_count >= ideal_peers as usize {
s.disconnect(DisconnectReason::TooManyPeers);
return;
}
// Add it no node table
if let Ok(address) = s.remote_addr() {
let entry = NodeEntry { id: s.id().unwrap().clone(), endpoint: NodeEndpoint { address: address, udp_port: address.port() } };
self.nodes.write().unwrap().add_node(Node::new(entry.id.clone(), entry.endpoint.clone()));
let mut discovery = self.discovery.lock().unwrap();
if let Some(ref mut discovery) = *discovery.deref_mut() {
discovery.add_node(entry);
}
}
}
self.num_sessions.fetch_add(1, AtomicOrdering::SeqCst);
for (p, _) in self.handlers.read().unwrap().iter() { for (p, _) in self.handlers.read().unwrap().iter() {
if s.have_capability(p) { if s.have_capability(p) {
ready_data.push(p); ready_data.push(p);
@ -697,6 +692,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
} }
for p in ready_data { for p in ready_data {
let h = self.handlers.read().unwrap().get(p).unwrap().clone(); let h = self.handlers.read().unwrap().get(p).unwrap().clone();
self.stats.inc_sessions();
h.connected(&NetworkContext::new(io, p, session.clone(), self.sessions.clone()), &token); h.connected(&NetworkContext::new(io, p, session.clone(), self.sessions.clone()), &token);
} }
if let Some((p, packet_id, data)) = packet_data { if let Some((p, packet_id, data)) = packet_data {
@ -706,59 +702,6 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
io.update_registration(token).unwrap_or_else(|e| debug!(target: "network", "Token registration error: {:?}", e)); io.update_registration(token).unwrap_or_else(|e| debug!(target: "network", "Token registration error: {:?}", e));
} }
fn start_session(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) {
let mut handshakes = self.handshakes.write().unwrap();
if handshakes.get(token).is_none() {
return;
}
// turn a handshake into a session
let mut sessions = self.sessions.write().unwrap();
let mut h = handshakes.get_mut(token).unwrap().lock().unwrap();
if h.expired {
return;
}
io.deregister_stream(token).expect("Error deleting handshake registration");
h.set_expired();
let originated = h.originated;
let mut session = match Session::new(&mut h, &self.info.read().unwrap()) {
Ok(s) => s,
Err(e) => {
debug!(target: "network", "Session creation error: {:?}", e);
return;
}
};
if !originated {
let session_count = sessions.count();
let ideal_peers = { self.info.read().unwrap().deref().config.ideal_peers };
if session_count >= ideal_peers as usize {
session.disconnect(DisconnectReason::TooManyPeers);
return;
}
}
let result = sessions.insert_with(move |session_token| {
session.set_token(session_token);
io.register_stream(session_token).expect("Error creating session registration");
self.stats.inc_sessions();
trace!(target: "network", "Creating session {} -> {}:{} ({:?})", token, session_token, session.id(), session.remote_addr());
if !originated {
// Add it no node table
if let Ok(address) = session.remote_addr() {
let entry = NodeEntry { id: session.id().clone(), endpoint: NodeEndpoint { address: address, udp_port: address.port() } };
self.nodes.write().unwrap().add_node(Node::new(entry.id.clone(), entry.endpoint.clone()));
let mut discovery = self.discovery.lock().unwrap();
if let Some(ref mut discovery) = *discovery.deref_mut() {
discovery.add_node(entry);
}
}
}
Arc::new(Mutex::new(session))
});
if result.is_none() {
warn!("Max sessions reached");
}
}
fn connection_timeout(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) { fn connection_timeout(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) {
trace!(target: "network", "Connection timeout: {}", token); trace!(target: "network", "Connection timeout: {}", token);
self.kill_connection(token, io, true) self.kill_connection(token, io, true)
@ -770,17 +713,6 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
let mut deregister = false; let mut deregister = false;
let mut expired_session = None; let mut expired_session = None;
match token { match token {
FIRST_HANDSHAKE ... LAST_HANDSHAKE => {
let handshakes = self.handshakes.write().unwrap();
if let Some(handshake) = handshakes.get(token).cloned() {
let mut handshake = handshake.lock().unwrap();
if !handshake.expired() {
handshake.set_expired();
failure_id = Some(handshake.id().clone());
deregister = true;
}
}
},
FIRST_SESSION ... LAST_SESSION => { FIRST_SESSION ... LAST_SESSION => {
let sessions = self.sessions.write().unwrap(); let sessions = self.sessions.write().unwrap();
if let Some(session) = sessions.get(token).cloned() { if let Some(session) = sessions.get(token).cloned() {
@ -790,12 +722,13 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
if s.is_ready() { if s.is_ready() {
for (p, _) in self.handlers.read().unwrap().iter() { for (p, _) in self.handlers.read().unwrap().iter() {
if s.have_capability(p) { if s.have_capability(p) {
self.num_sessions.fetch_sub(1, AtomicOrdering::SeqCst);
to_disconnect.push(p); to_disconnect.push(p);
} }
} }
} }
s.set_expired(); s.set_expired();
failure_id = Some(s.id().clone()); failure_id = s.id().cloned();
} }
deregister = remote || s.done(); deregister = remote || s.done();
} }
@ -820,21 +753,12 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
fn update_nodes(&self, io: &IoContext<NetworkIoMessage<Message>>, node_changes: TableUpdates) { fn update_nodes(&self, io: &IoContext<NetworkIoMessage<Message>>, node_changes: TableUpdates) {
let mut to_remove: Vec<PeerId> = Vec::new(); let mut to_remove: Vec<PeerId> = Vec::new();
{
{
let handshakes = self.handshakes.write().unwrap();
for c in handshakes.iter() {
let h = c.lock().unwrap();
if node_changes.removed.contains(&h.id()) {
to_remove.push(h.token());
}
}
}
{ {
let sessions = self.sessions.write().unwrap(); let sessions = self.sessions.write().unwrap();
for c in sessions.iter() { for c in sessions.iter() {
let s = c.lock().unwrap(); let s = c.lock().unwrap();
if node_changes.removed.contains(&s.id()) { if let Some(id) = s.id() {
if node_changes.removed.contains(id) {
to_remove.push(s.token()); to_remove.push(s.token());
} }
} }
@ -860,7 +784,6 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
trace!(target: "network", "Hup: {}", stream); trace!(target: "network", "Hup: {}", stream);
match stream { match stream {
FIRST_SESSION ... LAST_SESSION => self.connection_closed(stream, io), FIRST_SESSION ... LAST_SESSION => self.connection_closed(stream, io),
FIRST_HANDSHAKE ... LAST_HANDSHAKE => self.connection_closed(stream, io),
_ => warn!(target: "network", "Unexpected hup"), _ => warn!(target: "network", "Unexpected hup"),
}; };
} }
@ -868,7 +791,6 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
fn stream_readable(&self, io: &IoContext<NetworkIoMessage<Message>>, stream: StreamToken) { fn stream_readable(&self, io: &IoContext<NetworkIoMessage<Message>>, stream: StreamToken) {
match stream { match stream {
FIRST_SESSION ... LAST_SESSION => self.session_readable(stream, io), FIRST_SESSION ... LAST_SESSION => self.session_readable(stream, io),
FIRST_HANDSHAKE ... LAST_HANDSHAKE => self.handshake_readable(stream, io),
DISCOVERY => { DISCOVERY => {
let node_changes = { self.discovery.lock().unwrap().as_mut().unwrap().readable() }; let node_changes = { self.discovery.lock().unwrap().as_mut().unwrap().readable() };
if let Some(node_changes) = node_changes { if let Some(node_changes) = node_changes {
@ -884,7 +806,6 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
fn stream_writable(&self, io: &IoContext<NetworkIoMessage<Message>>, stream: StreamToken) { fn stream_writable(&self, io: &IoContext<NetworkIoMessage<Message>>, stream: StreamToken) {
match stream { match stream {
FIRST_SESSION ... LAST_SESSION => self.session_writable(stream, io), FIRST_SESSION ... LAST_SESSION => self.session_writable(stream, io),
FIRST_HANDSHAKE ... LAST_HANDSHAKE => self.handshake_writable(stream, io),
DISCOVERY => { DISCOVERY => {
self.discovery.lock().unwrap().as_mut().unwrap().writable(); self.discovery.lock().unwrap().as_mut().unwrap().writable();
io.update_registration(DISCOVERY).expect("Error updating discovery registration"); io.update_registration(DISCOVERY).expect("Error updating discovery registration");
@ -899,7 +820,6 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
INIT_PUBLIC => self.init_public_interface(io).unwrap_or_else(|e| INIT_PUBLIC => self.init_public_interface(io).unwrap_or_else(|e|
warn!("Error initializing public interface: {:?}", e)), warn!("Error initializing public interface: {:?}", e)),
FIRST_SESSION ... LAST_SESSION => self.connection_timeout(token, io), FIRST_SESSION ... LAST_SESSION => self.connection_timeout(token, io),
FIRST_HANDSHAKE ... LAST_HANDSHAKE => self.connection_timeout(token, io),
DISCOVERY_REFRESH => { DISCOVERY_REFRESH => {
self.discovery.lock().unwrap().as_mut().unwrap().refresh(); self.discovery.lock().unwrap().as_mut().unwrap().refresh();
io.update_registration(DISCOVERY).expect("Error updating discovery registration"); io.update_registration(DISCOVERY).expect("Error updating discovery registration");
@ -966,7 +886,9 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
let session = { self.sessions.read().unwrap().get(*peer).cloned() }; let session = { self.sessions.read().unwrap().get(*peer).cloned() };
if let Some(session) = session { if let Some(session) = session {
session.lock().unwrap().disconnect(DisconnectReason::DisconnectRequested); session.lock().unwrap().disconnect(DisconnectReason::DisconnectRequested);
self.nodes.write().unwrap().mark_as_useless(session.lock().unwrap().id()); if let Some(id) = session.lock().unwrap().id() {
self.nodes.write().unwrap().mark_as_useless(id)
}
} }
trace!(target: "network", "Disabling peer {}", peer); trace!(target: "network", "Disabling peer {}", peer);
self.kill_connection(*peer, io, false); self.kill_connection(*peer, io, false);
@ -987,12 +909,6 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
session.lock().unwrap().register_socket(reg, event_loop).expect("Error registering socket"); session.lock().unwrap().register_socket(reg, event_loop).expect("Error registering socket");
} }
} }
FIRST_HANDSHAKE ... LAST_HANDSHAKE => {
let connection = { self.handshakes.read().unwrap().get(stream).cloned() };
if let Some(connection) = connection {
connection.lock().unwrap().register_socket(reg, event_loop).expect("Error registering socket");
}
}
DISCOVERY => self.discovery.lock().unwrap().as_ref().unwrap().register_socket(event_loop).expect("Error registering discovery socket"), DISCOVERY => self.discovery.lock().unwrap().as_ref().unwrap().register_socket(event_loop).expect("Error registering discovery socket"),
TCP_ACCEPT => event_loop.register(self.tcp_listener.lock().unwrap().deref(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error registering stream"), TCP_ACCEPT => event_loop.register(self.tcp_listener.lock().unwrap().deref(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error registering stream"),
_ => warn!("Unexpected stream registration") _ => warn!("Unexpected stream registration")
@ -1008,13 +924,6 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
connections.remove(stream); connections.remove(stream);
} }
} }
FIRST_HANDSHAKE ... LAST_HANDSHAKE => {
let mut connections = self.handshakes.write().unwrap();
if let Some(connection) = connections.get(stream).cloned() {
connection.lock().unwrap().deregister_socket(event_loop).expect("Error deregistering socket");
connections.remove(stream);
}
}
DISCOVERY => (), DISCOVERY => (),
_ => warn!("Unexpected stream deregistration") _ => warn!("Unexpected stream deregistration")
} }
@ -1028,12 +937,6 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
connection.lock().unwrap().update_socket(reg, event_loop).expect("Error updating socket"); connection.lock().unwrap().update_socket(reg, event_loop).expect("Error updating socket");
} }
} }
FIRST_HANDSHAKE ... LAST_HANDSHAKE => {
let connection = { self.handshakes.read().unwrap().get(stream).cloned() };
if let Some(connection) = connection {
connection.lock().unwrap().update_socket(reg, event_loop).expect("Error updating socket");
}
}
DISCOVERY => self.discovery.lock().unwrap().as_ref().unwrap().update_registration(event_loop).expect("Error reregistering discovery socket"), DISCOVERY => self.discovery.lock().unwrap().as_ref().unwrap().update_registration(event_loop).expect("Error reregistering discovery socket"),
TCP_ACCEPT => event_loop.reregister(self.tcp_listener.lock().unwrap().deref(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error reregistering stream"), TCP_ACCEPT => event_loop.reregister(self.tcp_listener.lock().unwrap().deref(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error reregistering stream"),
_ => warn!("Unexpected stream update") _ => warn!("Unexpected stream update")

View File

@ -16,15 +16,19 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use std::io; use std::io;
use std::sync::*;
use mio::*; use mio::*;
use mio::tcp::*;
use rlp::*; use rlp::*;
use network::connection::{EncryptedConnection, Packet}; use hash::*;
use network::connection::{EncryptedConnection, Packet, Connection};
use network::handshake::Handshake; use network::handshake::Handshake;
use error::*; use error::*;
use io::{IoContext, StreamToken}; use io::{IoContext, StreamToken};
use network::error::{NetworkError, DisconnectReason}; use network::error::{NetworkError, DisconnectReason};
use network::host::*; use network::host::*;
use network::node_table::NodeId; use network::node_table::NodeId;
use network::stats::NetworkStats;
use time; use time;
const PING_TIMEOUT_SEC: u64 = 30; const PING_TIMEOUT_SEC: u64 = 30;
@ -36,14 +40,18 @@ const PING_INTERVAL_SEC: u64 = 30;
pub struct Session { pub struct Session {
/// Shared session information /// Shared session information
pub info: SessionInfo, pub info: SessionInfo,
/// Underlying connection
connection: EncryptedConnection,
/// Session ready flag. Set after successfull Hello packet exchange /// Session ready flag. Set after successfull Hello packet exchange
had_hello: bool, had_hello: bool,
/// Session is no longer active flag. /// Session is no longer active flag.
expired: bool, expired: bool,
ping_time_ns: u64, ping_time_ns: u64,
pong_time_ns: Option<u64>, pong_time_ns: Option<u64>,
state: State,
}
enum State {
Handshake(Handshake),
Session(EncryptedConnection),
} }
/// Structure used to report various session events. /// Structure used to report various session events.
@ -65,7 +73,7 @@ pub enum SessionData {
/// Shared session information /// Shared session information
pub struct SessionInfo { pub struct SessionInfo {
/// Peer public key /// Peer public key
pub id: NodeId, pub id: Option<NodeId>,
/// Peer client ID /// Peer client ID
pub client_version: String, pub client_version: String,
/// Peer RLPx protocol version /// Peer RLPx protocol version
@ -74,6 +82,8 @@ pub struct SessionInfo {
capabilities: Vec<SessionCapabilityInfo>, capabilities: Vec<SessionCapabilityInfo>,
/// Peer ping delay in milliseconds /// Peer ping delay in milliseconds
pub ping_ms: Option<u64>, pub ping_ms: Option<u64>,
/// True if this session was originated by us.
pub originated: bool,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@ -112,31 +122,52 @@ const PACKET_LAST: u8 = 0x7f;
impl Session { impl Session {
/// Create a new session out of comepleted handshake. This clones the handshake connection object /// Create a new session out of comepleted handshake. This clones the handshake connection object
/// and leaves the handhsake in limbo to be deregistered from the event loop. /// and leaves the handhsake in limbo to be deregistered from the event loop.
pub fn new(h: &mut Handshake, host: &HostInfo) -> Result<Session, UtilError> { pub fn new<Message>(io: &IoContext<Message>, socket: TcpStream, token: StreamToken, id: Option<&NodeId>,
let id = h.id.clone(); nonce: &H256, stats: Arc<NetworkStats>, host: &HostInfo) -> Result<Session, UtilError>
let connection = try!(EncryptedConnection::new(h)); where Message: Send + Clone {
let mut session = Session { let originated = id.is_some();
connection: connection, let mut handshake = Handshake::new(token, id, socket, &nonce, stats).expect("Can't create handshake");
try!(handshake.start(io, host, originated));
Ok(Session {
state: State::Handshake(handshake),
had_hello: false, had_hello: false,
info: SessionInfo { info: SessionInfo {
id: id, id: id.cloned(),
client_version: String::new(), client_version: String::new(),
protocol_version: 0, protocol_version: 0,
capabilities: Vec::new(), capabilities: Vec::new(),
ping_ms: None, ping_ms: None,
originated: originated,
}, },
ping_time_ns: 0, ping_time_ns: 0,
pong_time_ns: None, pong_time_ns: None,
expired: false, expired: false,
})
}
fn complete_handshake(&mut self, host: &HostInfo) -> Result<(), UtilError> {
let connection = if let State::Handshake(ref mut h) = self.state {
self.info.id = Some(h.id.clone());
try!(EncryptedConnection::new(h))
} else {
panic!("Unexpected state");
}; };
try!(session.write_hello(host)); self.state = State::Session(connection);
try!(session.send_ping()); try!(self.write_hello(host));
Ok(session) try!(self.send_ping());
Ok(())
}
fn connection(&self) -> &Connection {
match self.state {
State::Handshake(ref h) => &h.connection,
State::Session(ref s) => &s.connection,
}
} }
/// Get id of the remote peer /// Get id of the remote peer
pub fn id(&self) -> &NodeId { pub fn id(&self) -> Option<&NodeId> {
&self.info.id self.info.id.as_ref()
} }
/// Check if session is ready to send/receive data /// Check if session is ready to send/receive data
@ -151,21 +182,20 @@ impl Session {
/// Check if this session is expired. /// Check if this session is expired.
pub fn expired(&self) -> bool { pub fn expired(&self) -> bool {
self.expired match self.state {
State::Handshake(ref h) => h.expired(),
_ => self.expired,
}
} }
/// Check if this session is over and there is nothing to be sent. /// Check if this session is over and there is nothing to be sent.
pub fn done(&self) -> bool { pub fn done(&self) -> bool {
self.expired() && !self.connection.is_sending() self.expired() && !self.connection().is_sending()
}
/// Replace socket token
pub fn set_token(&mut self, token: StreamToken) {
self.connection.set_token(token);
} }
/// Get remote peer address /// Get remote peer address
pub fn remote_addr(&self) -> io::Result<SocketAddr> { pub fn remote_addr(&self) -> io::Result<SocketAddr> {
self.connection.remote_addr() self.connection().remote_addr()
} }
/// Readable IO handler. Returns packet data if available. /// Readable IO handler. Returns packet data if available.
@ -173,15 +203,37 @@ impl Session {
if self.expired() { if self.expired() {
return Ok(SessionData::None) return Ok(SessionData::None)
} }
match try!(self.connection.readable(io)) { let mut create_session = false;
Some(data) => Ok(try!(self.read_packet(data, host))), let mut packet_data = None;
None => Ok(SessionData::None) match self.state {
State::Handshake(ref mut h) => {
try!(h.readable(io, host));
if h.done() {
create_session = true;
} }
} }
State::Session(ref mut c) => {
match try!(c.readable(io)) {
data @ Some(_) => packet_data = data,
None => return Ok(SessionData::None)
}
}
}
if let Some(data) = packet_data {
return Ok(try!(self.read_packet(data, host)));
}
if create_session {
try!(self.complete_handshake(host));
}
Ok(SessionData::None)
}
/// Writable IO handler. Sends pending packets. /// Writable IO handler. Sends pending packets.
pub fn writable<Message>(&mut self, io: &IoContext<Message>, _host: &HostInfo) -> Result<(), UtilError> where Message: Send + Sync + Clone { pub fn writable<Message>(&mut self, io: &IoContext<Message>, _host: &HostInfo) -> Result<(), UtilError> where Message: Send + Sync + Clone {
self.connection.writable(io) match self.state {
State::Handshake(ref mut h) => h.writable(io),
State::Session(ref mut s) => s.writable(io),
}
} }
/// Checks if peer supports given capability /// Checks if peer supports given capability
@ -194,18 +246,20 @@ impl Session {
if self.expired() { if self.expired() {
return Ok(()); return Ok(());
} }
try!(self.connection.register_socket(reg, event_loop)); try!(self.connection().register_socket(reg, event_loop));
Ok(()) Ok(())
} }
/// Update registration with the event loop. Should be called at the end of the IO handler. /// Update registration with the event loop. Should be called at the end of the IO handler.
pub fn update_socket<Host:Handler>(&self, reg:Token, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> { pub fn update_socket<Host:Handler>(&self, reg:Token, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> {
self.connection.update_socket(reg, event_loop) try!(self.connection().update_socket(reg, event_loop));
Ok(())
} }
/// Delete registration /// Delete registration
pub fn deregister_socket<Host:Handler>(&self, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> { pub fn deregister_socket<Host:Handler>(&self, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> {
self.connection.deregister_socket(event_loop) try!(self.connection().deregister_socket(event_loop));
Ok(())
} }
/// Send a protocol packet to peer. /// Send a protocol packet to peer.
@ -221,7 +275,7 @@ impl Session {
while protocol != self.info.capabilities[i].protocol { while protocol != self.info.capabilities[i].protocol {
i += 1; i += 1;
if i == self.info.capabilities.len() { if i == self.info.capabilities.len() {
debug!(target: "net", "Unknown protocol: {:?}", protocol); debug!(target: "network", "Unknown protocol: {:?}", protocol);
return Ok(()) return Ok(())
} }
} }
@ -229,11 +283,14 @@ impl Session {
let mut rlp = RlpStream::new(); let mut rlp = RlpStream::new();
rlp.append(&(pid as u32)); rlp.append(&(pid as u32));
rlp.append_raw(data, 1); rlp.append_raw(data, 1);
self.connection.send_packet(&rlp.out()) self.send(rlp)
} }
/// Keep this session alive. Returns false if ping timeout happened /// Keep this session alive. Returns false if ping timeout happened
pub fn keep_alive<Message>(&mut self, io: &IoContext<Message>) -> bool where Message: Send + Sync + Clone { pub fn keep_alive<Message>(&mut self, io: &IoContext<Message>) -> bool where Message: Send + Sync + Clone {
if let State::Handshake(_) = self.state {
return true;
}
let timed_out = if let Some(pong) = self.pong_time_ns { let timed_out = if let Some(pong) = self.pong_time_ns {
pong - self.ping_time_ns > PING_TIMEOUT_SEC * 1000_000_000 pong - self.ping_time_ns > PING_TIMEOUT_SEC * 1000_000_000
} else { } else {
@ -244,13 +301,13 @@ impl Session {
if let Err(e) = self.send_ping() { if let Err(e) = self.send_ping() {
debug!("Error sending ping message: {:?}", e); debug!("Error sending ping message: {:?}", e);
} }
io.update_registration(self.token()).unwrap_or_else(|e| debug!(target: "net", "Session registration error: {:?}", e)); io.update_registration(self.token()).unwrap_or_else(|e| debug!(target: "network", "Session registration error: {:?}", e));
} }
!timed_out !timed_out
} }
pub fn token(&self) -> StreamToken { pub fn token(&self) -> StreamToken {
self.connection.token() self.connection().token()
} }
fn read_packet(&mut self, packet: Packet, host: &HostInfo) -> Result<SessionData, UtilError> { fn read_packet(&mut self, packet: Packet, host: &HostInfo) -> Result<SessionData, UtilError> {
@ -288,7 +345,7 @@ impl Session {
while packet_id < self.info.capabilities[i].id_offset { while packet_id < self.info.capabilities[i].id_offset {
i += 1; i += 1;
if i == self.info.capabilities.len() { if i == self.info.capabilities.len() {
debug!(target: "net", "Unknown packet: {:?}", packet_id); debug!(target: "network", "Unknown packet: {:?}", packet_id);
return Ok(SessionData::None) return Ok(SessionData::None)
} }
} }
@ -299,7 +356,7 @@ impl Session {
Ok(SessionData::Packet { data: packet.data, protocol: protocol, packet_id: pid } ) Ok(SessionData::Packet { data: packet.data, protocol: protocol, packet_id: pid } )
}, },
_ => { _ => {
debug!(target: "net", "Unknown packet: {:?}", packet_id); debug!(target: "network", "Unknown packet: {:?}", packet_id);
Ok(SessionData::None) Ok(SessionData::None)
} }
} }
@ -314,7 +371,7 @@ impl Session {
.append(&host.capabilities) .append(&host.capabilities)
.append(&host.local_endpoint.address.port()) .append(&host.local_endpoint.address.port())
.append(host.id()); .append(host.id());
self.connection.send_packet(&rlp.out()) self.send(rlp)
} }
fn read_hello(&mut self, rlp: &UntrustedRlp, host: &HostInfo) -> Result<(), UtilError> { fn read_hello(&mut self, rlp: &UntrustedRlp, host: &HostInfo) -> Result<(), UtilError> {
@ -384,11 +441,13 @@ impl Session {
/// Disconnect this session /// Disconnect this session
pub fn disconnect(&mut self, reason: DisconnectReason) -> NetworkError { pub fn disconnect(&mut self, reason: DisconnectReason) -> NetworkError {
if let State::Session(_) = self.state {
let mut rlp = RlpStream::new(); let mut rlp = RlpStream::new();
rlp.append(&(PACKET_DISCONNECT as u32)); rlp.append(&(PACKET_DISCONNECT as u32));
rlp.begin_list(1); rlp.begin_list(1);
rlp.append(&(reason as u32)); rlp.append(&(reason as u32));
self.connection.send_packet(&rlp.out()).ok(); self.send(rlp).ok();
}
NetworkError::Disconnect(reason) NetworkError::Disconnect(reason)
} }
@ -400,7 +459,15 @@ impl Session {
} }
fn send(&mut self, rlp: RlpStream) -> Result<(), UtilError> { fn send(&mut self, rlp: RlpStream) -> Result<(), UtilError> {
self.connection.send_packet(&rlp.out()) match self.state {
State::Handshake(_) => {
warn!(target:"network", "Unexpected send request");
},
State::Session(ref mut s) => {
try!(s.send_packet(&rlp.out()))
},
}
Ok(())
} }
} }

View File

@ -338,6 +338,18 @@ impl<T> Encodable for Vec<T> where T: Encodable {
} }
} }
impl<T> Encodable for Option<T> where T: Encodable {
fn rlp_append(&self, s: &mut RlpStream) {
match *self {
None => { s.begin_list(0); },
Some(ref x) => {
s.begin_list(1);
s.append_internal(x);
}
}
}
}
impl<T> RlpEncodable for T where T: Encodable { impl<T> RlpEncodable for T where T: Encodable {
fn rlp_append(&self, s: &mut RlpStream) { fn rlp_append(&self, s: &mut RlpStream) {
Encodable::rlp_append(self, s) Encodable::rlp_append(self, s)

View File

@ -408,6 +408,12 @@ impl<T> Decodable for Vec<T> where T: Decodable {
} }
} }
impl<T> Decodable for Option<T> where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect::<Result<Vec<_>, DecoderError>>().map(|mut a| a.pop())
}
}
impl Decodable for Vec<u8> { impl Decodable for Vec<u8> {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.read_value(| bytes | { decoder.read_value(| bytes | {
@ -418,18 +424,6 @@ impl Decodable for Vec<u8> {
} }
} }
impl<T> Decodable for Option<T> where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.read_value(| bytes | {
let res = match bytes.len() {
0 => None,
_ => Some(try!(T::decode(decoder)))
};
Ok(res)
})
}
}
macro_rules! impl_array_decodable { macro_rules! impl_array_decodable {
($index_type:ty, $len:expr ) => ( ($index_type:ty, $len:expr ) => (
impl<T> Decodable for [T; $len] where T: Decodable { impl<T> Decodable for [T; $len] where T: Decodable {