Merge branch 'master' of github.com:ethcore/parity
This commit is contained in:
commit
401bdca153
@ -33,7 +33,7 @@ env:
|
||||
global:
|
||||
# GH_TOKEN
|
||||
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
|
||||
- TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer -p ethjson -p ethcore-dapps -p ethcore-signer"
|
||||
- TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer"
|
||||
- ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
||||
- KCOV_FEATURES=""
|
||||
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov"
|
||||
@ -72,7 +72,6 @@ after_success: |
|
||||
$KCOV_CMD target/debug/deps/ethcore_rpc-* &&
|
||||
$KCOV_CMD target/debug/deps/ethcore_dapps-* &&
|
||||
$KCOV_CMD target/debug/deps/ethcore_signer-* &&
|
||||
$KCOV_CMD target/debug/deps/ethminer-* &&
|
||||
$KCOV_CMD target/debug/deps/ethjson-* &&
|
||||
$KCOV_CMD target/debug/parity-* &&
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
|
93
Cargo.lock
generated
93
Cargo.lock
generated
@ -17,7 +17,6 @@ dependencies = [
|
||||
"ethcore-rpc 1.2.0",
|
||||
"ethcore-signer 1.2.0",
|
||||
"ethcore-util 1.2.0",
|
||||
"ethminer 1.2.0",
|
||||
"ethsync 1.2.0",
|
||||
"fdlimit 0.1.0",
|
||||
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -105,6 +104,11 @@ name = "bloomchain"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "0.3.0"
|
||||
@ -250,6 +254,7 @@ dependencies = [
|
||||
"lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rust-crypto 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -294,7 +299,7 @@ version = "1.2.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.2.0",
|
||||
"ethcore-util 1.2.0",
|
||||
"nanomsg 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -316,7 +321,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.2.0",
|
||||
"jsonrpc-core 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -326,8 +331,9 @@ dependencies = [
|
||||
"clippy 0.0.69 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethash 1.2.0",
|
||||
"ethcore 1.2.0",
|
||||
"ethcore-devtools 1.2.0",
|
||||
"ethcore-util 1.2.0",
|
||||
"ethminer 1.2.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethsync 1.2.0",
|
||||
"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)",
|
||||
@ -347,9 +353,12 @@ 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-rpc 1.2.0",
|
||||
"ethcore-util 1.2.0",
|
||||
"jsonrpc-core 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ws 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -375,13 +384,13 @@ dependencies = [
|
||||
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rocksdb 0.4.3 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||
"rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||
"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_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)",
|
||||
"sha3 0.1.0",
|
||||
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -400,20 +409,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "ethsync"
|
||||
version = "1.2.0"
|
||||
@ -422,7 +417,6 @@ dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.2.0",
|
||||
"ethcore-util 1.2.0",
|
||||
"ethminer 1.2.0",
|
||||
"heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -564,7 +558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "json-ipc-server"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ethcore/json-ipc-server.git#fdcba83d00b127c7419fe21406a5b81998f686eb"
|
||||
source = "git+https://github.com/ethcore/json-ipc-server.git#4f9226c4f84dcce2385a188374e3b5fc66b63e68"
|
||||
dependencies = [
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -645,14 +639,6 @@ name = "libc"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "librocksdb-sys"
|
||||
version = "0.2.3"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#6b6ce93e2828182691e00da57fdfb2926226f1f1"
|
||||
dependencies = [
|
||||
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.6"
|
||||
@ -728,8 +714,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nanomsg"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git#9c81fb3b0f71714b173d0abf14bfd30addf8c7b1"
|
||||
version = "0.5.1"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00"
|
||||
dependencies = [
|
||||
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
@ -738,7 +724,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nanomsg-sys"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git#9c81fb3b0f71714b173d0abf14bfd30addf8c7b1"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1059,11 +1045,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rocksdb"
|
||||
version = "0.4.3"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#6b6ce93e2828182691e00da57fdfb2926226f1f1"
|
||||
version = "0.4.5"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#9140e37ce0fdb748097f85653c01b0f7e3736ea9"
|
||||
dependencies = [
|
||||
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"librocksdb-sys 0.2.3 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocksdb-sys"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#9140e37ce0fdb748097f85653c01b0f7e3736ea9"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1161,6 +1156,14 @@ dependencies = [
|
||||
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.1.0"
|
||||
@ -1173,6 +1176,11 @@ name = "slab"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "solicit"
|
||||
version = "0.4.4"
|
||||
@ -1405,6 +1413,19 @@ name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ws"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha1 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
|
@ -27,7 +27,6 @@ clippy = { version = "0.0.69", optional = true}
|
||||
ethcore = { path = "ethcore" }
|
||||
ethcore-util = { path = "util" }
|
||||
ethsync = { path = "sync" }
|
||||
ethminer = { path = "miner" }
|
||||
ethcore-devtools = { path = "devtools" }
|
||||
ethcore-rpc = { path = "rpc", optional = true }
|
||||
ethcore-signer = { path = "signer", optional = true }
|
||||
@ -46,7 +45,7 @@ default-features = false
|
||||
default = ["rpc", "dapps", "ethcore-signer"]
|
||||
rpc = ["ethcore-rpc"]
|
||||
dapps = ["ethcore-dapps"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethminer/dev",
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev",
|
||||
"ethcore-dapps/dev", "ethcore-signer/dev"]
|
||||
travis-beta = ["ethcore/json-tests"]
|
||||
travis-nightly = ["ethcore/json-tests", "dev"]
|
||||
|
55
README.md
55
README.md
@ -1,4 +1,5 @@
|
||||
# ethcore
|
||||
# [Parity](https://ethcore.io/parity.html)
|
||||
### Fast, light, and robust Ethereum implementation
|
||||
|
||||
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/trogdoro/xiki][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
|
||||
|
||||
@ -11,30 +12,64 @@
|
||||
[license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
|
||||
[license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
[Documentation](http://ethcore.github.io/parity/ethcore/index.html)
|
||||
[Internal Documentation](http://ethcore.github.io/parity/ethcore/index.html)
|
||||
|
||||
### Building from source
|
||||
----
|
||||
|
||||
First (if you don't already have it) get multirust:
|
||||
## About Parity
|
||||
|
||||
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and
|
||||
cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
|
||||
|
||||
By default, Parity will run a JSONRPC server on `127.0.0.1:8545`. This is fully configurable and supports a number
|
||||
of RPC APIs.
|
||||
|
||||
Parity also runs a server for running decentralized apps, or "Dapps", on `http://127.0.0.1:8080`.
|
||||
This includes a few useful Dapps, including Ethereum Wallet, Maker OTC, and a node status page.
|
||||
In a near-future release, it will be easy to install Dapps and use them through this web interface.
|
||||
|
||||
If you run into an issue while using parity, feel free to file one in this repository
|
||||
or hop on our [gitter chat room]([gitter-url]) to ask a question. We are glad to help!
|
||||
|
||||
Parity's current release is 1.1. You can download it at https://ethcore.io/parity.html or follow the instructions
|
||||
below to build from source.
|
||||
|
||||
----
|
||||
|
||||
## Building from source
|
||||
|
||||
Parity is fully compatible with Stable Rust.
|
||||
|
||||
We recommend installing Rust through [multirust](https://github.com/brson/multirust). If you don't already have multirust, you can install it like this:
|
||||
|
||||
- Linux:
|
||||
```bash
|
||||
curl -sf https://raw.githubusercontent.com/brson/multirust/master/quick-install.sh | sh
|
||||
$ curl -sf https://raw.githubusercontent.com/brson/multirust/master/quick-install.sh | sh
|
||||
```
|
||||
|
||||
- OSX with Homebrew:
|
||||
```bash
|
||||
brew update && brew install multirust
|
||||
multirust default stable
|
||||
$ brew update && brew install multirust
|
||||
$ multirust default stable
|
||||
```
|
||||
|
||||
Then, download and build Parity:
|
||||
|
||||
```bash
|
||||
# download Parity code
|
||||
git clone https://github.com/ethcore/parity
|
||||
cd parity
|
||||
$ git clone https://github.com/ethcore/parity
|
||||
$ cd parity
|
||||
|
||||
# build in release mode
|
||||
cargo build --release
|
||||
$ cargo build --release
|
||||
```
|
||||
|
||||
This will produce an executable in the `target/release` subdirectory.
|
||||
Either run `cd target/release`, or copy `target/release/parity` to another location.
|
||||
|
||||
To get started, just run
|
||||
```bash
|
||||
$ parity
|
||||
```
|
||||
|
||||
and parity will begin syncing the Ethereum blockchain.
|
@ -38,11 +38,16 @@ pub fn utils() -> Box<Endpoint> {
|
||||
|
||||
pub fn all_endpoints() -> Endpoints {
|
||||
let mut pages = Endpoints::new();
|
||||
pages.insert("proxy".to_owned(), ProxyPac::boxed());
|
||||
pages.insert("proxy".into(), ProxyPac::boxed());
|
||||
|
||||
// Home page needs to be safe embed
|
||||
// because we use Cross-Origin LocalStorage.
|
||||
// TODO [ToDr] Account naming should be moved to parity.
|
||||
pages.insert("home".into(), Box::new(
|
||||
PageEndpoint::new_safe_to_embed(parity_dapps_builtins::App::default())
|
||||
));
|
||||
insert::<parity_dapps_status::App>(&mut pages, "status");
|
||||
insert::<parity_dapps_status::App>(&mut pages, "parity");
|
||||
insert::<parity_dapps_builtins::App>(&mut pages, "home");
|
||||
|
||||
wallet_page(&mut pages);
|
||||
daodapp_page(&mut pages);
|
||||
|
@ -21,7 +21,7 @@ use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
|
||||
use std::io::Write;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Clone)]
|
||||
pub struct EndpointPath {
|
||||
@ -45,7 +45,7 @@ pub trait Endpoint : Send + Sync {
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream>>;
|
||||
}
|
||||
|
||||
pub type Endpoints = HashMap<String, Box<Endpoint>>;
|
||||
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
|
||||
pub type Handler = server::Handler<HttpStream>;
|
||||
|
||||
pub struct ContentHandler {
|
||||
|
@ -34,9 +34,9 @@
|
||||
//! let io = IoHandler::new();
|
||||
//! io.add_method("say_hello", SayHello);
|
||||
//! let _server = Server::start_unsecure_http(
|
||||
//! &"127.0.0.1:3030".parse().unwrap(),
|
||||
//! Arc::new(io)
|
||||
//! );
|
||||
//! &"127.0.0.1:3030".parse().unwrap(),
|
||||
//! Arc::new(io)
|
||||
//! );
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
@ -52,6 +52,7 @@ extern crate serde_json;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_http_server;
|
||||
extern crate parity_dapps;
|
||||
extern crate ethcore_rpc;
|
||||
|
||||
mod endpoint;
|
||||
mod apps;
|
||||
@ -66,6 +67,7 @@ use std::net::SocketAddr;
|
||||
use std::collections::HashMap;
|
||||
use jsonrpc_core::{IoHandler, IoDelegate};
|
||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||
use ethcore_rpc::Extendable;
|
||||
|
||||
static DAPPS_DOMAIN : &'static str = ".parity";
|
||||
|
||||
@ -74,6 +76,12 @@ pub struct ServerBuilder {
|
||||
handler: Arc<IoHandler>,
|
||||
}
|
||||
|
||||
impl Extendable for ServerBuilder {
|
||||
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerBuilder {
|
||||
/// Construct new dapps server
|
||||
pub fn new() -> Self {
|
||||
@ -82,11 +90,6 @@ impl ServerBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add io delegate.
|
||||
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// returns result with `Server` handle on success or an error.
|
||||
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
||||
|
@ -30,6 +30,8 @@ pub struct PageEndpoint<T : WebApp + 'static> {
|
||||
pub app: Arc<T>,
|
||||
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
||||
pub prefix: Option<String>,
|
||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
||||
safe_to_embed: bool,
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
@ -37,6 +39,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: None,
|
||||
safe_to_embed: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +47,18 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: Some(prefix),
|
||||
safe_to_embed: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new `PageEndpoint` which can be safely used in iframe
|
||||
/// even from different origin. It might be dangerous (clickjacking).
|
||||
/// Use wisely!
|
||||
pub fn new_safe_to_embed(app: T) -> Self {
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: None,
|
||||
safe_to_embed: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,6 +76,7 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
|
||||
path: path,
|
||||
file: None,
|
||||
write_pos: 0,
|
||||
safe_to_embed: self.safe_to_embed,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -83,6 +99,7 @@ struct PageHandler<T: WebApp + 'static> {
|
||||
path: EndpointPath,
|
||||
file: Option<String>,
|
||||
write_pos: usize,
|
||||
safe_to_embed: bool,
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> PageHandler<T> {
|
||||
@ -128,6 +145,9 @@ impl<T: WebApp + 'static> server::Handler<HttpStream> for PageHandler<T> {
|
||||
if let Some(f) = self.file.as_ref().and_then(|f| self.app.file(f)) {
|
||||
res.set_status(StatusCode::Ok);
|
||||
res.headers_mut().set(header::ContentType(f.content_type.parse().unwrap()));
|
||||
if !self.safe_to_embed {
|
||||
res.headers_mut().set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||
}
|
||||
Next::write()
|
||||
} else {
|
||||
res.set_status(StatusCode::NotFound);
|
||||
@ -192,6 +212,7 @@ fn should_extract_path_with_appid() {
|
||||
},
|
||||
file: None,
|
||||
write_pos: 0,
|
||||
safe_to_embed: true,
|
||||
};
|
||||
|
||||
// when
|
||||
|
@ -8,17 +8,19 @@ authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
syntex = "*"
|
||||
syntex = "0.32"
|
||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
ethcore-util = { path = "../util" }
|
||||
clippy = { version = "0.0.67", optional = true}
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
|
||||
semver = "0.2"
|
||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" }
|
||||
crossbeam = "0.2"
|
||||
ethcore-util = { path = "../util" }
|
||||
|
||||
[features]
|
||||
dev = ["clippy"]
|
||||
|
@ -18,15 +18,13 @@
|
||||
|
||||
use traits::*;
|
||||
use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBIterator,
|
||||
IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::{RwLock};
|
||||
IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
|
||||
use std::sync::{RwLock, Arc};
|
||||
use std::convert::From;
|
||||
use ipc::IpcConfig;
|
||||
use std::ops::*;
|
||||
use std::mem;
|
||||
use ipc::binary::BinaryConvertError;
|
||||
use std::collections::VecDeque;
|
||||
use std::collections::{VecDeque, HashMap, BTreeMap};
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(s: String) -> Error {
|
||||
@ -34,20 +32,136 @@ impl From<String> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
enum WriteCacheEntry {
|
||||
Remove,
|
||||
Write(Vec<u8>),
|
||||
}
|
||||
|
||||
pub struct WriteCache {
|
||||
entries: HashMap<Vec<u8>, WriteCacheEntry>,
|
||||
preferred_len: usize,
|
||||
}
|
||||
|
||||
const FLUSH_BATCH_SIZE: usize = 4096;
|
||||
|
||||
impl WriteCache {
|
||||
fn new(cache_len: usize) -> WriteCache {
|
||||
WriteCache {
|
||||
entries: HashMap::new(),
|
||||
preferred_len: cache_len,
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, key: Vec<u8>, val: Vec<u8>) {
|
||||
self.entries.insert(key, WriteCacheEntry::Write(val));
|
||||
}
|
||||
|
||||
fn remove(&mut self, key: Vec<u8>) {
|
||||
self.entries.insert(key, WriteCacheEntry::Remove);
|
||||
}
|
||||
|
||||
fn get(&self, key: &Vec<u8>) -> Option<Vec<u8>> {
|
||||
self.entries.get(key).and_then(
|
||||
|vec_ref| match vec_ref {
|
||||
&WriteCacheEntry::Write(ref val) => Some(val.clone()),
|
||||
&WriteCacheEntry::Remove => None
|
||||
})
|
||||
}
|
||||
|
||||
/// WriteCache should be locked for this
|
||||
fn flush(&mut self, db: &DB, amount: usize) -> Result<(), Error> {
|
||||
let batch = WriteBatch::new();
|
||||
let mut removed_so_far = 0;
|
||||
while removed_so_far < amount {
|
||||
if self.entries.len() == 0 { break; }
|
||||
let removed_key = {
|
||||
let (key, cache_entry) = self.entries.iter().nth(0)
|
||||
.expect("if entries.len == 0, we should have break in the loop, still we got here somehow");
|
||||
|
||||
match *cache_entry {
|
||||
WriteCacheEntry::Write(ref val) => {
|
||||
try!(batch.put(&key, val));
|
||||
},
|
||||
WriteCacheEntry::Remove => {
|
||||
try!(batch.delete(&key));
|
||||
},
|
||||
}
|
||||
key.clone()
|
||||
};
|
||||
|
||||
self.entries.remove(&removed_key);
|
||||
|
||||
removed_so_far = removed_so_far + 1;
|
||||
}
|
||||
if removed_so_far > 0 {
|
||||
try!(db.write(batch));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// flushes until cache is empty
|
||||
fn flush_all(&mut self, db: &DB) -> Result<(), Error> {
|
||||
while !self.is_empty() { try!(self.flush(db, FLUSH_BATCH_SIZE)); }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.entries.is_empty()
|
||||
}
|
||||
|
||||
fn try_shrink(&mut self, db: &DB) -> Result<(), Error> {
|
||||
if self.entries.len() > self.preferred_len {
|
||||
try!(self.flush(db, FLUSH_BATCH_SIZE));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Database {
|
||||
db: RwLock<Option<DB>>,
|
||||
transactions: RwLock<BTreeMap<TransactionHandle, WriteBatch>>,
|
||||
/// Iterators - dont't use between threads!
|
||||
iterators: RwLock<BTreeMap<IteratorHandle, DBIterator>>,
|
||||
write_cache: RwLock<WriteCache>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Database {}
|
||||
unsafe impl Sync for Database {}
|
||||
|
||||
impl Database {
|
||||
pub fn new() -> Database {
|
||||
Database {
|
||||
db: RwLock::new(None),
|
||||
transactions: RwLock::new(BTreeMap::new()),
|
||||
iterators: RwLock::new(BTreeMap::new()),
|
||||
write_cache: RwLock::new(WriteCache::new(DEFAULT_CACHE_LEN)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write().unwrap();
|
||||
let db_lock = self.db.read().unwrap();
|
||||
if db_lock.is_none() { return Ok(()); }
|
||||
let db = db_lock.as_ref().unwrap();
|
||||
|
||||
try!(cache_lock.try_shrink(&db));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush_all(&self) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write().unwrap();
|
||||
let db_lock = self.db.read().unwrap();
|
||||
if db_lock.is_none() { return Ok(()); }
|
||||
let db = db_lock.as_ref().expect("we should have exited with Ok(()) on the previous step");
|
||||
|
||||
try!(cache_lock.flush_all(&db));
|
||||
Ok(())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Database {
|
||||
fn drop(&mut self) {
|
||||
self.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ipc)]
|
||||
@ -72,51 +186,64 @@ impl DatabaseService for Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Opens database in the specified path with the default config
|
||||
fn open_default(&self, path: String) -> Result<(), Error> {
|
||||
self.open(DatabaseConfig::default(), path)
|
||||
}
|
||||
|
||||
fn close(&self) -> Result<(), Error> {
|
||||
try!(self.flush_all());
|
||||
|
||||
let mut db = self.db.write().unwrap();
|
||||
if db.is_none() { return Err(Error::IsClosed); }
|
||||
|
||||
// TODO: wait for transactions to expire/close here?
|
||||
if self.transactions.read().unwrap().len() > 0 { return Err(Error::UncommitedTransactions); }
|
||||
|
||||
*db = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error> {
|
||||
let db_lock = self.db.read().unwrap();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
|
||||
try!(db.put(key, value));
|
||||
let mut cache_lock = self.write_cache.write().unwrap();
|
||||
cache_lock.write(key.to_vec(), value.to_vec());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete(&self, key: &[u8]) -> Result<(), Error> {
|
||||
let db_lock = self.db.read().unwrap();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
|
||||
try!(db.delete(key));
|
||||
let mut cache_lock = self.write_cache.write().unwrap();
|
||||
cache_lock.remove(key.to_vec());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, handle: TransactionHandle) -> Result<(), Error> {
|
||||
let db_lock = self.db.read().unwrap();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
fn write(&self, transaction: DBTransaction) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write().unwrap();
|
||||
|
||||
let mut transactions = self.transactions.write().unwrap();
|
||||
let batch = try!(
|
||||
transactions.remove(&handle).ok_or(Error::TransactionUnknown)
|
||||
);
|
||||
try!(db.write(batch));
|
||||
let mut writes = transaction.writes.borrow_mut();
|
||||
for kv in writes.drain(..) {
|
||||
cache_lock.write(kv.key, kv.value);
|
||||
}
|
||||
|
||||
let mut removes = transaction.removes.borrow_mut();
|
||||
for k in removes.drain(..) {
|
||||
cache_lock.remove(k);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
|
||||
{
|
||||
let key_vec = key.to_vec();
|
||||
let cache_hit = self.write_cache.read().unwrap().get(&key_vec);
|
||||
|
||||
if cache_hit.is_some() {
|
||||
return Ok(Some(cache_hit.expect("cache_hit.is_some() = true, still there is none somehow here")))
|
||||
}
|
||||
}
|
||||
let db_lock = self.db.read().unwrap();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
|
||||
match try!(db.get(key)) {
|
||||
Some(db_vec) => Ok(Some(db_vec.to_vec())),
|
||||
Some(db_vec) => {
|
||||
Ok(Some(db_vec.to_vec()))
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
@ -166,37 +293,35 @@ impl DatabaseService for Database {
|
||||
})
|
||||
}
|
||||
|
||||
fn transaction_put(&self, transaction: TransactionHandle, key: &[u8], value: &[u8]) -> Result<(), Error>
|
||||
{
|
||||
let mut transactions = self.transactions.write().unwrap();
|
||||
let batch = try!(
|
||||
transactions.get_mut(&transaction).ok_or(Error::TransactionUnknown)
|
||||
);
|
||||
try!(batch.put(&key, &value));
|
||||
fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error> {
|
||||
let mut iterators = self.iterators.write().unwrap();
|
||||
iterators.remove(&handle);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transaction_delete(&self, transaction: TransactionHandle, key: &[u8]) -> Result<(), Error> {
|
||||
let mut transactions = self.transactions.write().unwrap();
|
||||
let batch = try!(
|
||||
transactions.get_mut(&transaction).ok_or(Error::TransactionUnknown)
|
||||
);
|
||||
try!(batch.delete(&key));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_transaction(&self) -> TransactionHandle {
|
||||
let mut transactions = self.transactions.write().unwrap();
|
||||
let next_transaction = transactions.keys().last().unwrap_or(&0) + 1;
|
||||
transactions.insert(next_transaction, WriteBatch::new());
|
||||
|
||||
next_transaction
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : put proper at compile-time
|
||||
impl IpcConfig for Database {}
|
||||
|
||||
/// Database iterator
|
||||
pub struct DatabaseIterator {
|
||||
client: Arc<DatabaseClient<::nanomsg::Socket>>,
|
||||
handle: IteratorHandle,
|
||||
}
|
||||
|
||||
impl Iterator for DatabaseIterator {
|
||||
type Item = (Vec<u8>, Vec<u8>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.client.iter_next(self.handle).and_then(|kv| Some((kv.key, kv.value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DatabaseIterator {
|
||||
fn drop(&mut self) {
|
||||
self.client.dispose_iter(self.handle).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
@ -215,7 +340,7 @@ mod test {
|
||||
fn can_be_open_empty() {
|
||||
let db = Database::new();
|
||||
let path = RandomTempPath::create_dir();
|
||||
db.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
|
||||
assert!(db.is_empty().is_ok());
|
||||
}
|
||||
@ -224,9 +349,10 @@ mod test {
|
||||
fn can_store_key() {
|
||||
let db = Database::new();
|
||||
let path = RandomTempPath::create_dir();
|
||||
db.open(DatabaseConfig { prefix_size: None }, path.as_str().to_owned()).unwrap();
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
|
||||
db.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
db.flush_all().unwrap();
|
||||
assert!(!db.is_empty().unwrap());
|
||||
}
|
||||
|
||||
@ -234,15 +360,37 @@ mod test {
|
||||
fn can_retrieve() {
|
||||
let db = Database::new();
|
||||
let path = RandomTempPath::create_dir();
|
||||
db.open(DatabaseConfig { prefix_size: None }, path.as_str().to_owned()).unwrap();
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
db.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
db.close().unwrap();
|
||||
|
||||
db.open(DatabaseConfig { prefix_size: None }, path.as_str().to_owned()).unwrap();
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert_eq!(db.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod write_cache_tests {
|
||||
use super::Database;
|
||||
use traits::*;
|
||||
use devtools::*;
|
||||
|
||||
#[test]
|
||||
fn cache_write_flush() {
|
||||
let db = Database::new();
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
db.put("100500".as_bytes(), "1".as_bytes()).unwrap();
|
||||
db.delete("100500".as_bytes()).unwrap();
|
||||
db.flush_all().unwrap();
|
||||
|
||||
let val = db.get("100500".as_bytes()).unwrap();
|
||||
assert!(val.is_none());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod client_tests {
|
||||
use super::{DatabaseClient, Database};
|
||||
@ -251,6 +399,8 @@ mod client_tests {
|
||||
use nanoipc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{Ordering, AtomicBool};
|
||||
use crossbeam;
|
||||
use run_worker;
|
||||
|
||||
fn init_worker(addr: &str) -> nanoipc::Worker<Database> {
|
||||
let mut worker = nanoipc::Worker::<Database>::new(&Arc::new(Database::new()));
|
||||
@ -268,7 +418,7 @@ mod client_tests {
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
let mut worker = init_worker(url);
|
||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||
worker.poll();
|
||||
c_worker_is_ready.store(true, Ordering::Relaxed);
|
||||
}
|
||||
@ -295,7 +445,7 @@ mod client_tests {
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
let mut worker = init_worker(url);
|
||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||
worker.poll();
|
||||
c_worker_is_ready.store(true, Ordering::Relaxed);
|
||||
}
|
||||
@ -304,7 +454,7 @@ mod client_tests {
|
||||
while !worker_is_ready.load(Ordering::Relaxed) { }
|
||||
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert!(client.is_empty().unwrap());
|
||||
worker_should_exit.store(true, Ordering::Relaxed);
|
||||
}
|
||||
@ -314,27 +464,16 @@ mod client_tests {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-30.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
let worker_should_exit = Arc::new(AtomicBool::new(false));
|
||||
let worker_is_ready = Arc::new(AtomicBool::new(false));
|
||||
let c_worker_should_exit = worker_should_exit.clone();
|
||||
let c_worker_is_ready = worker_is_ready.clone();
|
||||
crossbeam::scope(move |scope| {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
run_worker(scope, stop.clone(), url);
|
||||
let client = nanoipc::init_client::<DatabaseClient<_>>(url).unwrap();
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
client.close().unwrap();
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
let mut worker = init_worker(url);
|
||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||
worker.poll();
|
||||
c_worker_is_ready.store(true, Ordering::Relaxed);
|
||||
}
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
});
|
||||
|
||||
while !worker_is_ready.load(Ordering::Relaxed) { }
|
||||
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
|
||||
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
client.close().unwrap();
|
||||
|
||||
worker_should_exit.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -342,29 +481,93 @@ mod client_tests {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-40.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
let worker_should_exit = Arc::new(AtomicBool::new(false));
|
||||
let worker_is_ready = Arc::new(AtomicBool::new(false));
|
||||
let c_worker_should_exit = worker_should_exit.clone();
|
||||
let c_worker_is_ready = worker_is_ready.clone();
|
||||
crossbeam::scope(move |scope| {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
run_worker(scope, stop.clone(), url);
|
||||
let client = nanoipc::init_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
let mut worker = init_worker(url);
|
||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||
worker.poll();
|
||||
c_worker_is_ready.store(true, Ordering::Relaxed);
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
client.close().unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
||||
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_read_empty() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-45.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
crossbeam::scope(move |scope| {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
run_worker(scope, stop.clone(), url);
|
||||
let client = nanoipc::init_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert!(client.get("xxx".as_bytes()).unwrap().is_none());
|
||||
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn can_commit_client_transaction() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-60.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
crossbeam::scope(move |scope| {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
run_worker(scope, stop.clone(), url);
|
||||
let client = nanoipc::init_client::<DatabaseClient<_>>(url).unwrap();
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
|
||||
let transaction = DBTransaction::new();
|
||||
transaction.put("xxx".as_bytes(), "1".as_bytes());
|
||||
client.write(transaction).unwrap();
|
||||
|
||||
client.close().unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
||||
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_write_read_ipc() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-70.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
crossbeam::scope(|scope| {
|
||||
let stop = StopGuard::new();
|
||||
run_worker(&scope, stop.share(), url);
|
||||
|
||||
let client = nanoipc::init_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
let mut batch = Vec::new();
|
||||
for _ in 0..100 {
|
||||
batch.push((random_str(256).as_bytes().to_vec(), random_str(256).as_bytes().to_vec()));
|
||||
batch.push((random_str(256).as_bytes().to_vec(), random_str(2048).as_bytes().to_vec()));
|
||||
batch.push((random_str(2048).as_bytes().to_vec(), random_str(2048).as_bytes().to_vec()));
|
||||
batch.push((random_str(2048).as_bytes().to_vec(), random_str(256).as_bytes().to_vec()));
|
||||
}
|
||||
|
||||
for &(ref k, ref v) in batch.iter() {
|
||||
client.put(k, v).unwrap();
|
||||
}
|
||||
client.close().unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
for &(ref k, ref v) in batch.iter() {
|
||||
assert_eq!(v, &client.get(k).unwrap().unwrap());
|
||||
}
|
||||
});
|
||||
|
||||
while !worker_is_ready.load(Ordering::Relaxed) { }
|
||||
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
|
||||
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
client.close().unwrap();
|
||||
|
||||
client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap();
|
||||
assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
||||
|
||||
worker_should_exit.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,71 @@ extern crate rocksdb;
|
||||
extern crate ethcore_devtools as devtools;
|
||||
extern crate semver;
|
||||
extern crate ethcore_ipc_nano as nanoipc;
|
||||
extern crate nanomsg;
|
||||
extern crate crossbeam;
|
||||
extern crate ethcore_util as util;
|
||||
|
||||
pub mod database;
|
||||
pub mod traits;
|
||||
|
||||
pub use traits::{DatabaseService, DBTransaction, Error};
|
||||
pub use database::{Database, DatabaseClient, DatabaseIterator};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub type DatabaseNanoClient = DatabaseClient<::nanomsg::Socket>;
|
||||
pub type DatabaseConnection = nanoipc::GuardedSocket<DatabaseNanoClient>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ServiceError {
|
||||
Io(std::io::Error),
|
||||
Socket(nanoipc::SocketError),
|
||||
}
|
||||
|
||||
impl std::convert::From<std::io::Error> for ServiceError {
|
||||
fn from(io_error: std::io::Error) -> ServiceError { ServiceError::Io(io_error) }
|
||||
}
|
||||
|
||||
impl std::convert::From<nanoipc::SocketError> for ServiceError {
|
||||
fn from(socket_error: nanoipc::SocketError) -> ServiceError { ServiceError::Socket(socket_error) }
|
||||
}
|
||||
|
||||
pub fn blocks_service_url(db_path: &str) -> Result<String, std::io::Error> {
|
||||
let mut path = PathBuf::from(db_path);
|
||||
try!(::std::fs::create_dir_all(db_path));
|
||||
path.push("blocks.ipc");
|
||||
Ok(format!("ipc://{}", path.to_str().unwrap()))
|
||||
}
|
||||
|
||||
pub fn extras_service_url(db_path: &str) -> Result<String, ::std::io::Error> {
|
||||
let mut path = PathBuf::from(db_path);
|
||||
try!(::std::fs::create_dir_all(db_path));
|
||||
path.push("extras.ipc");
|
||||
Ok(format!("ipc://{}", path.to_str().unwrap()))
|
||||
}
|
||||
|
||||
pub fn blocks_client(db_path: &str) -> Result<DatabaseConnection, ServiceError> {
|
||||
let url = try!(blocks_service_url(db_path));
|
||||
let client = try!(nanoipc::init_client::<DatabaseClient<_>>(&url));
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub fn extras_client(db_path: &str) -> Result<DatabaseConnection, ServiceError> {
|
||||
let url = try!(extras_service_url(db_path));
|
||||
let client = try!(nanoipc::init_client::<DatabaseClient<_>>(&url));
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
// for tests
|
||||
pub fn run_worker(scope: &crossbeam::Scope, stop: Arc<AtomicBool>, socket_path: &str) {
|
||||
let socket_path = socket_path.to_owned();
|
||||
scope.spawn(move || {
|
||||
let mut worker = nanoipc::Worker::new(&Arc::new(Database::new()));
|
||||
worker.add_reqrep(&socket_path).unwrap();
|
||||
while !stop.load(Ordering::Relaxed) {
|
||||
worker.poll();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,21 +1,38 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethcore database trait
|
||||
|
||||
use ipc::BinaryConvertable;
|
||||
use std::mem;
|
||||
use ipc::binary::BinaryConvertError;
|
||||
use std::collections::VecDeque;
|
||||
use std::cell::RefCell;
|
||||
|
||||
pub type TransactionHandle = u32;
|
||||
pub type IteratorHandle = u32;
|
||||
|
||||
pub const DEFAULT_CACHE_LEN: usize = 12288;
|
||||
|
||||
#[derive(Binary)]
|
||||
pub struct KeyValue {
|
||||
pub key: Vec<u8>,
|
||||
pub value: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Binary)]
|
||||
pub enum Error {
|
||||
#[derive(Debug, Binary)]
|
||||
pub enum Error {
|
||||
AlreadyOpen,
|
||||
IsClosed,
|
||||
RocksDb(String),
|
||||
@ -28,13 +45,36 @@ pub enum Error {
|
||||
#[derive(Binary)]
|
||||
pub struct DatabaseConfig {
|
||||
/// Optional prefix size in bytes. Allows lookup by partial key.
|
||||
pub prefix_size: Option<usize>
|
||||
pub prefix_size: Option<usize>,
|
||||
/// write cache length
|
||||
pub cache: usize,
|
||||
}
|
||||
|
||||
pub trait DatabaseService {
|
||||
impl Default for DatabaseConfig {
|
||||
fn default() -> DatabaseConfig {
|
||||
DatabaseConfig {
|
||||
prefix_size: None,
|
||||
cache: DEFAULT_CACHE_LEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseConfig {
|
||||
fn with_prefix(prefix: usize) -> DatabaseConfig {
|
||||
DatabaseConfig {
|
||||
prefix_size: Some(prefix),
|
||||
cache: DEFAULT_CACHE_LEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DatabaseService : Sized {
|
||||
/// Opens database in the specified path
|
||||
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error>;
|
||||
|
||||
/// Opens database in the specified path with the default config
|
||||
fn open_default(&self, path: String) -> Result<(), Error>;
|
||||
|
||||
/// Closes database
|
||||
fn close(&self) -> Result<(), Error>;
|
||||
|
||||
@ -44,18 +84,6 @@ pub trait DatabaseService {
|
||||
/// Delete value by key.
|
||||
fn delete(&self, key: &[u8]) -> Result<(), Error>;
|
||||
|
||||
/// Insert a key-value pair in the transaction. Any existing value value will be overwritten.
|
||||
fn transaction_put(&self, transaction: TransactionHandle, key: &[u8], value: &[u8]) -> Result<(), Error>;
|
||||
|
||||
/// Delete value by key using transaction
|
||||
fn transaction_delete(&self, transaction: TransactionHandle, key: &[u8]) -> Result<(), Error>;
|
||||
|
||||
/// Commit transaction to database.
|
||||
fn write(&self, tr: TransactionHandle) -> Result<(), Error>;
|
||||
|
||||
/// Initiate new transaction on database
|
||||
fn new_transaction(&self) -> TransactionHandle;
|
||||
|
||||
/// Get value by key.
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error>;
|
||||
|
||||
@ -70,4 +98,35 @@ pub trait DatabaseService {
|
||||
|
||||
/// Next key-value for the the given iterator
|
||||
fn iter_next(&self, iterator: IteratorHandle) -> Option<KeyValue>;
|
||||
|
||||
/// Dispose iteration that is no longer needed
|
||||
fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error>;
|
||||
|
||||
/// Write client transaction
|
||||
fn write(&self, transaction: DBTransaction) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
#[derive(Binary)]
|
||||
pub struct DBTransaction {
|
||||
pub writes: RefCell<Vec<KeyValue>>,
|
||||
pub removes: RefCell<Vec<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl DBTransaction {
|
||||
pub fn new() -> DBTransaction {
|
||||
DBTransaction {
|
||||
writes: RefCell::new(Vec::new()),
|
||||
removes: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put(&self, key: &[u8], value: &[u8]) {
|
||||
let mut brw = self.writes.borrow_mut();
|
||||
brw.push(KeyValue { key: key.to_vec(), value: value.to_vec() });
|
||||
}
|
||||
|
||||
pub fn delete(&self, key: &[u8]) {
|
||||
let mut brw = self.removes.borrow_mut();
|
||||
brw.push(key.to_vec());
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ extern crate rand;
|
||||
|
||||
pub mod random_path;
|
||||
pub mod test_socket;
|
||||
pub mod stop_guard;
|
||||
|
||||
pub use random_path::*;
|
||||
pub use test_socket::*;
|
||||
pub use stop_guard::*;
|
||||
|
@ -26,7 +26,11 @@ pub struct RandomTempPath {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -54,6 +58,12 @@ impl RandomTempPath {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.path.to_str().unwrap()
|
||||
}
|
||||
|
||||
pub fn new_in(&self, name: &str) -> String {
|
||||
let mut path = self.path.clone();
|
||||
path.push(name);
|
||||
path.to_str().unwrap().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RandomTempPath {
|
||||
|
@ -14,12 +14,32 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// 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() {
|
||||
if let Channel::Nightly = version_meta().channel {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
/// Stop guard that will set a stop flag on drop
|
||||
pub struct StopGuard {
|
||||
flag: Arc<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)
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ ethcore-devtools = { path = "../devtools" }
|
||||
ethjson = { path = "../json" }
|
||||
bloomchain = "0.1"
|
||||
"ethcore-ipc" = { path = "../ipc/rpc" }
|
||||
rayon = "0.3.1"
|
||||
|
||||
[features]
|
||||
jit = ["evmjit"]
|
||||
|
@ -34,17 +34,8 @@
|
||||
"gasLimit": "0x2fefd8"
|
||||
},
|
||||
"nodes": [
|
||||
"enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303",
|
||||
"enode://7ee7195bfac561ec938a72cd84cd1a5d2b334415263feddc325b20b5010446fc6c361297d13decab4039028fa659c1e27cca1396574b87cc7b29eea2985e97fe@108.61.197.28:30303",
|
||||
"enode://933c5d5470b77537e7d9c1ee686132b5032dd3e2a096d2f64d2004df4ce9fca4ad6da5e358edcc8f81e65f047e40045600181f5fb35066e771025f6cca8e7952@46.101.114.191:30303",
|
||||
"enode://ad4028ba28783d5bf58f512cb4e24a8ce980d768177c4974e1140b16b925132c947349db9ca3646752891b382dafc839a0c0716c3764c1ed9d424f09d13d01cf@148.251.220.116:30303",
|
||||
"enode://c54ddaacddc7029683c80edae91015520eb2712176fbe6fdb7a5a074659270638f1266cba1731681c7cb785bceb02ca8d8b23024e3ec736fc5579f2042be97ae@54.175.255.230:30303",
|
||||
"enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303",
|
||||
"enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303",
|
||||
"enode://e941c58fed2709d792f552f408d2162c3d0a5597d22d1da617a9c9e6181f3251056a96adb45ae22eba70119355227298dc7e6dff805b092bae7da2f8564de422@85.25.217.23:30303",
|
||||
"enode://f4b73c9d11a780293ff0ca7afa12c67797afdc33a4797a7c2ecc5b87e455b32a8b9e9804f2004072bac38350bf82d52521d1a09590d2079705fc8357aef2bf9c@71.202.223.50:56603",
|
||||
"enode://1173eea53e0cb2b8da92423e44cf4cbafbc8ea16c1558cf06e18dfc5a2fc9b140cc802a4362b4c773fb1442541e6f2a225b200bb4c1f6b347e7510a50fa4873f@104.41.138.167:30300",
|
||||
"enode://1aad341327808738ad34655611f1b13293c4155dde36c8e3788128829f15cc6db2da9435f29520553d4efc134aadc50115690194ac3af519aac7a388b524811e@109.188.125.2:30303"
|
||||
"enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
@ -261,7 +261,7 @@ mod tests {
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
let rlp = {
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
let mut a = Account::new_contract(69.into(), 0.into());
|
||||
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
|
||||
a.commit_storage(&mut db);
|
||||
a.init_code(vec![]);
|
||||
@ -281,7 +281,7 @@ mod tests {
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
|
||||
let rlp = {
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
let mut a = Account::new_contract(69.into(), 0.into());
|
||||
a.init_code(vec![0x55, 0x44, 0xffu8]);
|
||||
a.commit_code(&mut db);
|
||||
a.rlp()
|
||||
@ -296,10 +296,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn commit_storage() {
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
let mut a = Account::new_contract(69.into(), 0.into());
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.set_storage(x!(0), x!(0x1234));
|
||||
a.set_storage(0.into(), 0x1234.into());
|
||||
assert_eq!(a.storage_root(), None);
|
||||
a.commit_storage(&mut db);
|
||||
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
|
||||
@ -307,21 +307,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn commit_remove_commit_storage() {
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
let mut a = Account::new_contract(69.into(), 0.into());
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.set_storage(x!(0), x!(0x1234));
|
||||
a.set_storage(0.into(), 0x1234.into());
|
||||
a.commit_storage(&mut db);
|
||||
a.set_storage(x!(1), x!(0x1234));
|
||||
a.set_storage(1.into(), 0x1234.into());
|
||||
a.commit_storage(&mut db);
|
||||
a.set_storage(x!(1), x!(0));
|
||||
a.set_storage(1.into(), 0.into());
|
||||
a.commit_storage(&mut db);
|
||||
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commit_code() {
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
let mut a = Account::new_contract(69.into(), 0.into());
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.init_code(vec![0x55, 0x44, 0xffu8]);
|
||||
|
@ -20,7 +20,7 @@ impl<'db> AccountDB<'db> {
|
||||
pub fn new(db: &'db HashDB, address: &Address) -> AccountDB<'db> {
|
||||
AccountDB {
|
||||
db: db,
|
||||
address: x!(address),
|
||||
address: address.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,7 +67,7 @@ impl<'db> AccountDBMut<'db> {
|
||||
pub fn new(db: &'db mut HashDB, address: &Address) -> AccountDBMut<'db> {
|
||||
AccountDBMut {
|
||||
db: db,
|
||||
address: x!(address),
|
||||
address: address.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,9 +86,9 @@ impl Engine for BasicAuthority {
|
||||
let gas_limit = parent.gas_limit;
|
||||
let bound_divisor = self.our_params.gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1))
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||
} else {
|
||||
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1))
|
||||
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
||||
}
|
||||
};
|
||||
header.note_dirty();
|
||||
@ -211,12 +211,12 @@ mod tests {
|
||||
let engine = new_test_authority().engine;
|
||||
let schedule = engine.schedule(&EnvInfo {
|
||||
number: 10000000,
|
||||
author: x!(0),
|
||||
author: 0.into(),
|
||||
timestamp: 0,
|
||||
difficulty: x!(0),
|
||||
difficulty: 0.into(),
|
||||
last_hashes: vec![],
|
||||
gas_used: x!(0),
|
||||
gas_limit: x!(0)
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into()
|
||||
});
|
||||
|
||||
assert!(schedule.stack_limit > 0);
|
||||
@ -278,7 +278,7 @@ mod tests {
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, addr.clone(), x!(3141562), vec![]);
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, addr.clone(), 3141562.into(), vec![]);
|
||||
let b = b.close_and_lock();
|
||||
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
||||
|
||||
|
@ -469,7 +469,7 @@ pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Head
|
||||
}
|
||||
}
|
||||
|
||||
let mut b = OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, header.author().clone(), x!(3141562), header.extra_data().clone());
|
||||
let mut b = OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, header.author().clone(), 3141562.into(), header.extra_data().clone());
|
||||
b.set_difficulty(*header.difficulty());
|
||||
b.set_gas_limit(*header.gas_limit());
|
||||
b.set_timestamp(header.timestamp());
|
||||
@ -514,7 +514,7 @@ mod tests {
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), 3141562.into(), vec![]);
|
||||
let b = b.close_and_lock();
|
||||
let _ = b.seal(engine.deref(), vec![]);
|
||||
}
|
||||
@ -530,7 +530,7 @@ mod tests {
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]).close_and_lock().seal(engine.deref(), vec![]).unwrap();
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), 3141562.into(), vec![]).close_and_lock().seal(engine.deref(), vec![]).unwrap();
|
||||
let orig_bytes = b.rlp_bytes();
|
||||
let orig_db = b.drain();
|
||||
|
||||
@ -557,7 +557,7 @@ mod tests {
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let vm_factory = Default::default();
|
||||
let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]);
|
||||
let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), 3141562.into(), vec![]);
|
||||
let mut uncle1_header = Header::new();
|
||||
uncle1_header.extra_data = b"uncle1".to_vec();
|
||||
let mut uncle2_header = Header::new();
|
||||
|
@ -311,17 +311,17 @@ impl BlockQueue {
|
||||
let h = header.hash();
|
||||
{
|
||||
if self.processing.read().unwrap().contains(&h) {
|
||||
return Err(x!(ImportError::AlreadyQueued));
|
||||
return Err(ImportError::AlreadyQueued.into());
|
||||
}
|
||||
|
||||
let mut bad = self.verification.bad.lock().unwrap();
|
||||
if bad.contains(&h) {
|
||||
return Err(x!(ImportError::KnownBad));
|
||||
return Err(ImportError::KnownBad.into());
|
||||
}
|
||||
|
||||
if bad.contains(&header.parent_hash) {
|
||||
bad.insert(h.clone());
|
||||
return Err(x!(ImportError::KnownBad));
|
||||
return Err(ImportError::KnownBad.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
use bloomchain::group as bc;
|
||||
use util::HeapSizeOf;
|
||||
|
||||
/// Represents BloomGroup position in database.
|
||||
/// Represents `BloomGroup` position in database.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct GroupPosition {
|
||||
/// Bloom level.
|
||||
|
@ -37,7 +37,7 @@ use filter::Filter;
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use block_queue::{BlockQueue, BlockQueueInfo};
|
||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, TraceFilter};
|
||||
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter};
|
||||
use client::Error as ClientError;
|
||||
use env_info::EnvInfo;
|
||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||
@ -48,6 +48,7 @@ use trace;
|
||||
pub use types::blockchain_info::BlockChainInfo;
|
||||
pub use types::block_status::BlockStatus;
|
||||
use evm::Factory as EvmFactory;
|
||||
use miner::{Miner, MinerService, TransactionImportResult, AccountDetails};
|
||||
|
||||
impl fmt::Display for BlockChainInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@ -90,6 +91,7 @@ pub struct Client<V = CanonVerifier> where V: Verifier {
|
||||
panic_handler: Arc<PanicHandler>,
|
||||
verifier: PhantomData<V>,
|
||||
vm_factory: Arc<EvmFactory>,
|
||||
miner: Arc<Miner>,
|
||||
}
|
||||
|
||||
const HISTORY: u64 = 1200;
|
||||
@ -102,8 +104,8 @@ const CLIENT_DB_VER_STR: &'static str = "5.3";
|
||||
|
||||
impl Client<CanonVerifier> {
|
||||
/// 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> {
|
||||
Client::<CanonVerifier>::new_with_verifier(config, spec, path, message_channel)
|
||||
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, miner, message_channel)
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +128,14 @@ pub fn append_path(path: &Path, item: &str) -> String {
|
||||
|
||||
impl<V> Client<V> where V: Verifier {
|
||||
/// Create a new client with given spec and DB path and custom verifier.
|
||||
pub fn new_with_verifier(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<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 gb = spec.genesis_block();
|
||||
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,
|
||||
verifier: PhantomData,
|
||||
vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
|
||||
miner: miner,
|
||||
};
|
||||
|
||||
Ok(Arc::new(client))
|
||||
@ -254,7 +264,7 @@ impl<V> Client<V> where V: Verifier {
|
||||
|
||||
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
||||
pub fn import_verified_blocks(&self, io: &IoChannel<NetSyncMessage>) -> usize {
|
||||
let max_blocks_to_import = 128;
|
||||
let max_blocks_to_import = 64;
|
||||
|
||||
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
|
||||
let mut invalid_blocks = HashSet::new();
|
||||
@ -328,6 +338,11 @@ impl<V> Client<V> where V: Verifier {
|
||||
{
|
||||
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(import_results);
|
||||
|
||||
if self.queue_info().is_empty() {
|
||||
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
|
||||
}
|
||||
|
||||
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
|
||||
imported: imported_blocks,
|
||||
invalid: invalid_blocks,
|
||||
@ -339,7 +354,7 @@ impl<V> Client<V> where V: Verifier {
|
||||
|
||||
{
|
||||
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 {
|
||||
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 view = HeaderView::new(&header);
|
||||
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)
|
||||
}));
|
||||
let balance = state.balance(&sender);
|
||||
// give the sender max balance
|
||||
// give the sender a decent balance
|
||||
state.sub_balance(&sender, &balance);
|
||||
state.add_balance(&sender, &U256::max_value());
|
||||
let options = TransactOptions { tracing: false, check_nonce: false };
|
||||
state.add_balance(&sender, &(U256::from(1) << 200));
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
&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> {
|
||||
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()))
|
||||
}
|
||||
@ -655,10 +599,10 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
||||
{
|
||||
let header = BlockView::new(&bytes).header_view();
|
||||
if self.chain.is_known(&header.sha3()) {
|
||||
return Err(x!(ImportError::AlreadyInChain));
|
||||
return Err(ImportError::AlreadyInChain.into());
|
||||
}
|
||||
if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown {
|
||||
return Err(x!(BlockError::UnknownParent(header.parent_hash())));
|
||||
return Err(BlockError::UnknownParent(header.parent_hash()).into());
|
||||
}
|
||||
}
|
||||
self.block_queue.import_block(bytes)
|
||||
@ -774,6 +718,89 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
||||
fn last_hashes(&self) -> LastHashes {
|
||||
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 {
|
||||
|
@ -46,6 +46,8 @@ use error::{ImportResult, ExecutionError};
|
||||
use receipt::LocalizedReceipt;
|
||||
use trace::LocalizedTrace;
|
||||
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.
|
||||
pub trait BlockChainClient : Sync + Send {
|
||||
@ -154,17 +156,9 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// Returns logs matching given filter.
|
||||
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.
|
||||
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.
|
||||
fn vm_factory(&self) -> &EvmFactory;
|
||||
@ -183,5 +177,20 @@ pub trait BlockChainClient : Sync + Send {
|
||||
|
||||
/// Get last hashes starting from best block.
|
||||
fn last_hashes(&self) -> LastHashes;
|
||||
|
||||
/// import transactions from network/other 3rd party
|
||||
fn import_transactions(&self, transactions: Vec<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>);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
|
||||
use util::*;
|
||||
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
|
||||
use blockchain::TreeRoute;
|
||||
use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes};
|
||||
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes};
|
||||
use header::{Header as BlockHeader, BlockNumber};
|
||||
use filter::Filter;
|
||||
use log_entry::LocalizedLogEntry;
|
||||
@ -28,6 +28,7 @@ use receipt::{Receipt, LocalizedReceipt};
|
||||
use blockchain::extras::BlockReceipts;
|
||||
use error::{ImportResult};
|
||||
use evm::Factory as EvmFactory;
|
||||
use miner::{Miner, MinerService};
|
||||
|
||||
use block_queue::BlockQueueInfo;
|
||||
use block::{SealedBlock, ClosedBlock, LockedBlock};
|
||||
@ -35,6 +36,9 @@ use executive::Executed;
|
||||
use error::{ExecutionError};
|
||||
use trace::LocalizedTrace;
|
||||
|
||||
use miner::{TransactionImportResult, AccountDetails};
|
||||
use error::Error as EthError;
|
||||
|
||||
/// Test client.
|
||||
pub struct TestBlockChainClient {
|
||||
/// Blocks.
|
||||
@ -61,6 +65,8 @@ pub struct TestBlockChainClient {
|
||||
pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>,
|
||||
/// Block queue size.
|
||||
pub queue_size: AtomicUsize,
|
||||
/// Miner
|
||||
pub miner: Arc<Miner>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -99,6 +105,7 @@ impl TestBlockChainClient {
|
||||
execution_result: RwLock::new(None),
|
||||
receipts: RwLock::new(HashMap::new()),
|
||||
queue_size: AtomicUsize::new(0),
|
||||
miner: Arc::new(Miner::default()),
|
||||
};
|
||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||
client.genesis_hash = client.last_hash.read().unwrap().clone();
|
||||
@ -232,8 +239,19 @@ impl TestBlockChainClient {
|
||||
}
|
||||
}
|
||||
|
||||
impl MiningBlockChainClient for TestBlockChainClient {
|
||||
fn try_seal(&self, block: LockedBlock, _seal: Vec<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 {
|
||||
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())
|
||||
}
|
||||
|
||||
@ -296,14 +314,6 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
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> {
|
||||
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>> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -47,10 +47,10 @@ impl Default for EnvInfo {
|
||||
number: 0,
|
||||
author: Address::new(),
|
||||
timestamp: 0,
|
||||
difficulty: x!(0),
|
||||
gas_limit: x!(0),
|
||||
difficulty: 0.into(),
|
||||
gas_limit: 0.into(),
|
||||
last_hashes: vec![],
|
||||
gas_used: x!(0),
|
||||
gas_used: 0.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,15 +92,15 @@ mod tests {
|
||||
|
||||
assert_eq!(env_info.number, 1112339);
|
||||
assert_eq!(env_info.author, Address::from_str("000000f00000000f000000000000f00000000f00").unwrap());
|
||||
assert_eq!(env_info.gas_limit, x!(40000));
|
||||
assert_eq!(env_info.difficulty, x!(50000));
|
||||
assert_eq!(env_info.gas_used, x!(0));
|
||||
assert_eq!(env_info.gas_limit, 40000.into());
|
||||
assert_eq!(env_info.difficulty, 50000.into());
|
||||
assert_eq!(env_info.gas_used, 0.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_be_created_as_default() {
|
||||
let default_env_info = EnvInfo::default();
|
||||
|
||||
assert_eq!(default_env_info.difficulty, x!(0));
|
||||
assert_eq!(default_env_info.difficulty, 0.into());
|
||||
}
|
||||
}
|
||||
|
@ -111,9 +111,9 @@ impl Engine for Ethash {
|
||||
let gas_limit = parent.gas_limit;
|
||||
let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1))
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||
} else {
|
||||
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1) + (header.gas_used * x!(6) / x!(5)) / bound_divisor)
|
||||
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into() + (header.gas_used * 6.into() / 5.into()) / bound_divisor)
|
||||
}
|
||||
};
|
||||
header.note_dirty();
|
||||
@ -255,12 +255,12 @@ impl Ethash {
|
||||
|
||||
/// Convert an Ethash boundary to its original difficulty. Basically just `f(x) = 2^256 / x`.
|
||||
pub fn boundary_to_difficulty(boundary: &H256) -> U256 {
|
||||
U256::from((U512::one() << 256) / x!(U256::from(boundary.as_slice())))
|
||||
U256::from((U512::one() << 256) / U256::from(boundary.as_slice()).into())
|
||||
}
|
||||
|
||||
/// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`.
|
||||
pub fn difficulty_to_boundary(difficulty: &U256) -> H256 {
|
||||
x!(U256::from((U512::one() << 256) / x!(difficulty)))
|
||||
U256::from((U512::one() << 256) / difficulty.into()).into()
|
||||
}
|
||||
|
||||
fn to_ethash(hash: H256) -> EH256 {
|
||||
@ -308,7 +308,7 @@ mod tests {
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), 3141562.into(), vec![]);
|
||||
let b = b.close();
|
||||
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
|
||||
}
|
||||
@ -323,7 +323,7 @@ mod tests {
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let vm_factory = Default::default();
|
||||
let mut b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]);
|
||||
let mut b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), 3141562.into(), vec![]);
|
||||
let mut uncle = Header::new();
|
||||
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
|
||||
uncle.author = uncle_author.clone();
|
||||
@ -346,24 +346,24 @@ mod tests {
|
||||
let engine = new_morden().engine;
|
||||
let schedule = engine.schedule(&EnvInfo {
|
||||
number: 10000000,
|
||||
author: x!(0),
|
||||
author: 0.into(),
|
||||
timestamp: 0,
|
||||
difficulty: x!(0),
|
||||
difficulty: 0.into(),
|
||||
last_hashes: vec![],
|
||||
gas_used: x!(0),
|
||||
gas_limit: x!(0)
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into()
|
||||
});
|
||||
|
||||
assert!(schedule.stack_limit > 0);
|
||||
|
||||
let schedule = engine.schedule(&EnvInfo {
|
||||
number: 100,
|
||||
author: x!(0),
|
||||
author: 0.into(),
|
||||
timestamp: 0,
|
||||
difficulty: x!(0),
|
||||
difficulty: 0.into(),
|
||||
last_hashes: vec![],
|
||||
gas_used: x!(0),
|
||||
gas_limit: x!(0)
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into()
|
||||
});
|
||||
|
||||
assert!(!schedule.have_delegate_call);
|
||||
|
@ -105,4 +105,10 @@ pub trait Ext {
|
||||
|
||||
/// Increments sstore refunds count by 1.
|
||||
fn inc_sstore_clears(&mut self);
|
||||
|
||||
/// Prepare to trace an operation. Passthrough for the VM trace.
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
|
||||
|
||||
/// Trace the finalised execution of a single instruction.
|
||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||
}
|
||||
|
@ -124,6 +124,7 @@ pub struct InstructionInfo {
|
||||
pub side_effects: bool,
|
||||
pub tier: GasPriceTier
|
||||
}
|
||||
|
||||
impl InstructionInfo {
|
||||
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo {
|
||||
InstructionInfo {
|
||||
@ -139,7 +140,7 @@ impl InstructionInfo {
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
/// Return details about specific instruction
|
||||
pub fn get_info (instruction: Instruction) -> InstructionInfo {
|
||||
pub fn get_info(instruction: Instruction) -> InstructionInfo {
|
||||
match instruction {
|
||||
STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero),
|
||||
ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
|
@ -17,8 +17,9 @@
|
||||
///! Rust VM implementation
|
||||
|
||||
use common::*;
|
||||
use trace::VMTracer;
|
||||
use super::instructions as instructions;
|
||||
use super::instructions::Instruction;
|
||||
use super::instructions::{Instruction, get_info};
|
||||
use std::marker::Copy;
|
||||
use evm::{self, MessageCallResult, ContractCreateResult};
|
||||
|
||||
@ -69,6 +70,8 @@ trait Stack<T> {
|
||||
fn push(&mut self, elem: T);
|
||||
/// Get number of elements on Stack
|
||||
fn size(&self) -> usize;
|
||||
/// Returns all data on stack.
|
||||
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
|
||||
}
|
||||
|
||||
struct VecStack<S> {
|
||||
@ -131,6 +134,11 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
|
||||
fn size(&self) -> usize {
|
||||
self.stack.len()
|
||||
}
|
||||
|
||||
fn peek_top(&mut self, no_from_top: usize) -> &[S] {
|
||||
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
|
||||
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
|
||||
}
|
||||
}
|
||||
|
||||
trait Memory {
|
||||
@ -293,10 +301,15 @@ impl evm::Evm for Interpreter {
|
||||
|
||||
while reader.position < code.len() {
|
||||
let instruction = code[reader.position];
|
||||
reader.position += 1;
|
||||
|
||||
// Calculate gas cost
|
||||
let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack));
|
||||
|
||||
// TODO: make compile-time removable if too much of a performance hit.
|
||||
let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost);
|
||||
|
||||
reader.position += 1;
|
||||
|
||||
try!(self.verify_gas(¤t_gas, &gas_cost));
|
||||
mem.expand(mem_size);
|
||||
current_gas = current_gas - gas_cost; //TODO: use operator -=
|
||||
@ -311,10 +324,19 @@ impl evm::Evm for Interpreter {
|
||||
);
|
||||
});
|
||||
|
||||
let (mem_written, store_written) = match trace_executed {
|
||||
true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)),
|
||||
false => (None, None),
|
||||
};
|
||||
|
||||
// Execute instruction
|
||||
let result = try!(self.exec_instruction(
|
||||
current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack
|
||||
));
|
||||
));
|
||||
|
||||
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
|
||||
match result {
|
||||
@ -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> {
|
||||
let gas_for_mem = |mem_size: U256| {
|
||||
let s = mem_size >> 5;
|
||||
@ -833,10 +880,12 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_instructions_requirements(&self,
|
||||
info: &instructions::InstructionInfo,
|
||||
stack_limit: usize,
|
||||
stack: &Stack<U256>) -> Result<(), evm::Error> {
|
||||
fn verify_instructions_requirements(
|
||||
&self,
|
||||
info: &instructions::InstructionInfo,
|
||||
stack_limit: usize,
|
||||
stack: &Stack<U256>
|
||||
) -> Result<(), evm::Error> {
|
||||
if !stack.has(info.args) {
|
||||
Err(evm::Error::StackUnderflow {
|
||||
instruction: info.name,
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
//! Just in time compiler execution environment.
|
||||
use common::*;
|
||||
use trace::VMTracer;
|
||||
use evmjit;
|
||||
use evm;
|
||||
|
||||
|
@ -33,3 +33,4 @@ pub use self::evm::{Evm, Error, Result};
|
||||
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
|
||||
pub use self::factory::{Factory, VMType};
|
||||
pub use self::schedule::Schedule;
|
||||
pub use self::instructions::get_info;
|
||||
|
@ -21,9 +21,8 @@ use engine::*;
|
||||
use evm::{self, Ext, Factory};
|
||||
use externalities::*;
|
||||
use substate::*;
|
||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
|
||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
|
||||
use crossbeam;
|
||||
|
||||
pub use types::executed::{Executed, ExecutionResult};
|
||||
|
||||
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
|
||||
@ -43,6 +42,8 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
|
||||
pub struct TransactOptions {
|
||||
/// Enable call tracing.
|
||||
pub tracing: bool,
|
||||
/// Enable VM tracing.
|
||||
pub vm_tracing: bool,
|
||||
/// Check transaction nonce before execution.
|
||||
pub check_nonce: bool,
|
||||
}
|
||||
@ -80,21 +81,40 @@ impl<'a> Executive<'a> {
|
||||
}
|
||||
|
||||
/// Creates `Externalities` from `Executive`.
|
||||
pub fn as_externalities<'_, T>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_, '_>, tracer: &'_ mut T) -> Externalities<'_, T> where T: Tracer {
|
||||
Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer)
|
||||
pub fn as_externalities<'_, T, V>(
|
||||
&'_ mut self,
|
||||
origin_info: OriginInfo,
|
||||
substate: &'_ mut Substate,
|
||||
output: OutputPolicy<'_, '_>,
|
||||
tracer: &'_ mut T,
|
||||
vm_tracer: &'_ mut V
|
||||
) -> Externalities<'_, T, V> where T: Tracer, V: VMTracer {
|
||||
Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer, vm_tracer)
|
||||
}
|
||||
|
||||
/// This function should be used to execute transaction.
|
||||
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
|
||||
let check = options.check_nonce;
|
||||
match options.tracing {
|
||||
true => self.transact_with_tracer(t, check, ExecutiveTracer::default()),
|
||||
false => self.transact_with_tracer(t, check, NoopTracer),
|
||||
true => match options.vm_tracing {
|
||||
true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::default()),
|
||||
false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer),
|
||||
},
|
||||
false => match options.vm_tracing {
|
||||
true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::default()),
|
||||
false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute transaction/call with tracing enabled
|
||||
pub fn transact_with_tracer<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 message = format!("Transaction malformed: {:?}", e);
|
||||
ExecutionError::TransactionMalformed(message)
|
||||
@ -154,7 +174,7 @@ impl<'a> Executive<'a> {
|
||||
code: Some(t.data.clone()),
|
||||
data: None,
|
||||
};
|
||||
(self.create(params, &mut substate, &mut tracer), vec![])
|
||||
(self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
|
||||
},
|
||||
Action::Call(ref address) => {
|
||||
let params = ActionParams {
|
||||
@ -170,20 +190,26 @@ impl<'a> Executive<'a> {
|
||||
};
|
||||
// TODO: move output upstream
|
||||
let mut out = vec![];
|
||||
(self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer), out)
|
||||
(self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer, &mut vm_tracer), out)
|
||||
}
|
||||
};
|
||||
|
||||
// finalize here!
|
||||
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop())))
|
||||
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain())))
|
||||
}
|
||||
|
||||
fn exec_vm<T>(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy, tracer: &mut T)
|
||||
-> evm::Result where T: Tracer {
|
||||
fn exec_vm<T, V>(
|
||||
&mut self,
|
||||
params: ActionParams,
|
||||
unconfirmed_substate: &mut Substate,
|
||||
output_policy: OutputPolicy,
|
||||
tracer: &mut T,
|
||||
vm_tracer: &mut V
|
||||
) -> evm::Result where T: Tracer, V: VMTracer {
|
||||
// Ordinary execution - keep VM in same thread
|
||||
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
|
||||
let vm_factory = self.vm_factory;
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer);
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
||||
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
|
||||
return vm_factory.create().exec(params, &mut ext);
|
||||
}
|
||||
@ -193,7 +219,7 @@ impl<'a> Executive<'a> {
|
||||
// https://github.com/aturon/crossbeam/issues/16
|
||||
crossbeam::scope(|scope| {
|
||||
let vm_factory = self.vm_factory;
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer);
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
||||
|
||||
scope.spawn(move || {
|
||||
vm_factory.create().exec(params, &mut ext)
|
||||
@ -205,8 +231,14 @@ impl<'a> Executive<'a> {
|
||||
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||
/// Modifies the substate and the output.
|
||||
/// Returns either gas_left or `evm::Error`.
|
||||
pub fn call<T>(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef, tracer: &mut T)
|
||||
-> evm::Result where T: Tracer {
|
||||
pub fn call<T, V>(
|
||||
&mut self,
|
||||
params: ActionParams,
|
||||
substate: &mut Substate,
|
||||
mut output: BytesRef,
|
||||
tracer: &mut T,
|
||||
vm_tracer: &mut V
|
||||
) -> evm::Result where T: Tracer, V: VMTracer {
|
||||
// backup used in case of running out of gas
|
||||
self.state.snapshot();
|
||||
|
||||
@ -264,16 +296,22 @@ impl<'a> Executive<'a> {
|
||||
let trace_info = tracer.prepare_trace_call(¶ms);
|
||||
let mut trace_output = tracer.prepare_trace_output();
|
||||
let mut subtracer = tracer.subtracer();
|
||||
|
||||
let gas = params.gas;
|
||||
|
||||
if params.code.is_some() {
|
||||
// part of substate that may be reverted
|
||||
let mut unconfirmed_substate = Substate::new();
|
||||
|
||||
// TODO: make ActionParams pass by ref then avoid copy altogether.
|
||||
let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("scope is protected by params.code.is_some condition"));
|
||||
|
||||
let res = {
|
||||
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer)
|
||||
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
|
||||
};
|
||||
|
||||
vm_tracer.done_subtrace(subvmtracer);
|
||||
|
||||
trace!(target: "executive", "res={:?}", res);
|
||||
|
||||
let traces = subtracer.traces();
|
||||
@ -307,8 +345,13 @@ impl<'a> Executive<'a> {
|
||||
/// Creates contract with given contract params.
|
||||
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||
/// Modifies the substate.
|
||||
pub fn create<T>(&mut self, params: ActionParams, substate: &mut Substate, tracer: &mut T) -> evm::Result where T:
|
||||
Tracer {
|
||||
pub fn create<T, V>(
|
||||
&mut self,
|
||||
params: ActionParams,
|
||||
substate: &mut Substate,
|
||||
tracer: &mut T,
|
||||
vm_tracer: &mut V
|
||||
) -> evm::Result where T: Tracer, V: VMTracer {
|
||||
// backup used in case of running out of gas
|
||||
self.state.snapshot();
|
||||
|
||||
@ -330,10 +373,14 @@ impl<'a> Executive<'a> {
|
||||
let gas = params.gas;
|
||||
let created = params.address.clone();
|
||||
|
||||
let mut subvmtracer = vm_tracer.prepare_subtrace(¶ms.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed"));
|
||||
|
||||
let res = {
|
||||
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer)
|
||||
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
|
||||
};
|
||||
|
||||
vm_tracer.done_subtrace(subvmtracer);
|
||||
|
||||
match res {
|
||||
Ok(gas_left) => tracer.trace_create(
|
||||
trace_info,
|
||||
@ -351,7 +398,15 @@ impl<'a> Executive<'a> {
|
||||
}
|
||||
|
||||
/// Finalizes the transaction (does refunds and suicides).
|
||||
fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes, trace: Option<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);
|
||||
|
||||
// refunds from SSTORE nonzero -> zero
|
||||
@ -361,7 +416,7 @@ impl<'a> Executive<'a> {
|
||||
let refunds_bound = sstore_refunds + suicide_refunds;
|
||||
|
||||
// real ammount to refund
|
||||
let gas_left_prerefund = match result { Ok(x) => x, _ => x!(0) };
|
||||
let gas_left_prerefund = match result { Ok(x) => x, _ => 0.into() };
|
||||
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) / U256::from(2));
|
||||
let gas_left = gas_left_prerefund + refunded;
|
||||
|
||||
@ -394,6 +449,7 @@ impl<'a> Executive<'a> {
|
||||
contracts_created: vec![],
|
||||
output: output,
|
||||
trace: trace,
|
||||
vm_trace: vm_trace,
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
@ -406,6 +462,7 @@ impl<'a> Executive<'a> {
|
||||
contracts_created: substate.contracts_created,
|
||||
output: output,
|
||||
trace: trace,
|
||||
vm_trace: vm_trace,
|
||||
})
|
||||
},
|
||||
}
|
||||
@ -438,6 +495,7 @@ mod tests {
|
||||
use tests::helpers::*;
|
||||
use trace::trace;
|
||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
|
||||
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
|
||||
|
||||
#[test]
|
||||
fn test_contract_address() {
|
||||
@ -466,7 +524,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
ex.create(params, &mut substate, &mut NoopTracer).unwrap()
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(79_975));
|
||||
@ -525,7 +583,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
ex.create(params, &mut substate, &mut NoopTracer).unwrap()
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(62_976));
|
||||
@ -542,7 +600,7 @@ mod tests {
|
||||
// 52
|
||||
// 60 1d - push 29
|
||||
// 60 03 - push 3
|
||||
// 60 17 - push 17
|
||||
// 60 17 - push 23
|
||||
// f0 - create
|
||||
// 60 00 - push 0
|
||||
// 55 sstore
|
||||
@ -578,20 +636,23 @@ mod tests {
|
||||
let engine = TestEngine::new(5);
|
||||
let mut substate = Substate::new();
|
||||
let mut tracer = ExecutiveTracer::default();
|
||||
let mut vm_tracer = ExecutiveVMTracer::default();
|
||||
|
||||
let gas_left = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
let output = BytesRef::Fixed(&mut[0u8;0]);
|
||||
ex.call(params, &mut substate, output, &mut tracer).unwrap()
|
||||
ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(44_752));
|
||||
|
||||
let expected_trace = vec![ Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("cd1722f3947def4cf144679da39c4c32bdc35681"),
|
||||
to: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"),
|
||||
value: x!(100),
|
||||
gas: x!(100000),
|
||||
from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(),
|
||||
to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
|
||||
value: 100.into(),
|
||||
gas: 100000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -601,9 +662,9 @@ mod tests {
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: trace::Action::Create(trace::Create {
|
||||
from: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"),
|
||||
value: x!(23),
|
||||
gas: x!(67979),
|
||||
from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
|
||||
value: 23.into(),
|
||||
gas: 67979.into(),
|
||||
init: 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]
|
||||
}),
|
||||
result: trace::Res::Create(trace::CreateResult {
|
||||
@ -615,7 +676,39 @@ mod tests {
|
||||
}]
|
||||
}];
|
||||
assert_eq!(tracer.traces(), expected_trace);
|
||||
assert_eq!(gas_left, U256::from(44_752));
|
||||
|
||||
let expected_vm_trace = VMTrace {
|
||||
parent_step: 0,
|
||||
code: vec![124, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85, 96, 0, 82, 96, 29, 96, 3, 96, 23, 240, 96, 0, 85],
|
||||
operations: vec![
|
||||
VMOperation { pc: 0, instruction: 124, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 30, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 32, instruction: 82, gas_cost: 6.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![0, 0, 0, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] }), store_diff: None }) },
|
||||
VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec_into![29], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec_into![3], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![23], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec_into![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) }
|
||||
],
|
||||
subs: vec![
|
||||
VMTrace {
|
||||
parent_step: 7,
|
||||
code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85],
|
||||
operations: vec![
|
||||
VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67973.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67970.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67967.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 67958.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) },
|
||||
VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) }
|
||||
],
|
||||
subs: vec![]
|
||||
}
|
||||
]
|
||||
};
|
||||
assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace);
|
||||
}
|
||||
|
||||
evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int}
|
||||
@ -642,7 +735,7 @@ mod tests {
|
||||
params.origin = sender.clone();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(code.clone());
|
||||
params.value = ActionValue::Transfer(x!(100));
|
||||
params.value = ActionValue::Transfer(100.into());
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
@ -650,17 +743,20 @@ mod tests {
|
||||
let engine = TestEngine::new(5);
|
||||
let mut substate = Substate::new();
|
||||
let mut tracer = ExecutiveTracer::default();
|
||||
let mut vm_tracer = ExecutiveVMTracer::default();
|
||||
|
||||
let gas_left = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
ex.create(params.clone(), &mut substate, &mut tracer).unwrap()
|
||||
ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(96_776));
|
||||
|
||||
let expected_trace = vec![Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Create(trace::Create {
|
||||
from: params.sender,
|
||||
value: x!(100),
|
||||
value: 100.into(),
|
||||
gas: params.gas,
|
||||
init: 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],
|
||||
}),
|
||||
@ -671,9 +767,23 @@ mod tests {
|
||||
}),
|
||||
subs: vec![]
|
||||
}];
|
||||
|
||||
assert_eq!(tracer.traces(), expected_trace);
|
||||
assert_eq!(gas_left, U256::from(96_776));
|
||||
|
||||
let expected_vm_trace = VMTrace {
|
||||
parent_step: 0,
|
||||
code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85],
|
||||
operations: vec![
|
||||
VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) },
|
||||
VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) }
|
||||
],
|
||||
subs: vec![]
|
||||
};
|
||||
assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace);
|
||||
}
|
||||
|
||||
evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int}
|
||||
@ -722,7 +832,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
ex.create(params, &mut substate, &mut NoopTracer).unwrap()
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(62_976));
|
||||
@ -774,7 +884,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
ex.create(params, &mut substate, &mut NoopTracer).unwrap();
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(substate.contracts_created.len(), 1);
|
||||
@ -835,7 +945,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap()
|
||||
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(73_237));
|
||||
@ -880,7 +990,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap()
|
||||
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(59_870));
|
||||
@ -913,7 +1023,7 @@ mod tests {
|
||||
|
||||
let executed = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false };
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
||||
ex.transact(&t, opts).unwrap()
|
||||
};
|
||||
|
||||
@ -947,7 +1057,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false };
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
||||
ex.transact(&t, opts)
|
||||
};
|
||||
|
||||
@ -979,7 +1089,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false };
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
||||
ex.transact(&t, opts)
|
||||
};
|
||||
|
||||
@ -1013,7 +1123,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false };
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
||||
ex.transact(&t, opts)
|
||||
};
|
||||
|
||||
@ -1047,7 +1157,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false };
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
||||
ex.transact(&t, opts)
|
||||
};
|
||||
|
||||
@ -1082,7 +1192,7 @@ mod tests {
|
||||
|
||||
let result = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
ex.create(params, &mut substate, &mut NoopTracer)
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer)
|
||||
};
|
||||
|
||||
match result {
|
||||
|
@ -21,7 +21,7 @@ use engine::*;
|
||||
use executive::*;
|
||||
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
|
||||
use substate::*;
|
||||
use trace::Tracer;
|
||||
use trace::{Tracer, VMTracer};
|
||||
|
||||
/// Policy for handling output data on `RETURN` opcode.
|
||||
pub enum OutputPolicy<'a, 'b> {
|
||||
@ -55,7 +55,7 @@ impl OriginInfo {
|
||||
}
|
||||
|
||||
/// Implementation of evm Externalities.
|
||||
pub struct Externalities<'a, T> where T: 'a + Tracer {
|
||||
pub struct Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
state: &'a mut State,
|
||||
env_info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer {
|
||||
schedule: Schedule,
|
||||
output: OutputPolicy<'a, 'a>,
|
||||
tracer: &'a mut T,
|
||||
vm_tracer: &'a mut V,
|
||||
}
|
||||
|
||||
impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
|
||||
|
||||
impl<'a, T, V> Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||
/// Basic `Externalities` constructor.
|
||||
pub fn new(state: &'a mut State,
|
||||
@ -81,6 +81,7 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
|
||||
substate: &'a mut Substate,
|
||||
output: OutputPolicy<'a, 'a>,
|
||||
tracer: &'a mut T,
|
||||
vm_tracer: &'a mut V,
|
||||
) -> Self {
|
||||
Externalities {
|
||||
state: state,
|
||||
@ -93,11 +94,12 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
|
||||
schedule: engine.schedule(env_info),
|
||||
output: output,
|
||||
tracer: tracer,
|
||||
vm_tracer: vm_tracer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
|
||||
impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
fn storage_at(&self, key: &H256) -> H256 {
|
||||
self.state.storage_at(&self.origin_info.address, key)
|
||||
}
|
||||
@ -152,7 +154,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
|
||||
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth);
|
||||
|
||||
// TODO: handle internal error separately
|
||||
match ex.create(params, self.substate, self.tracer) {
|
||||
match ex.create(params, self.substate, self.tracer, self.vm_tracer) {
|
||||
Ok(gas_left) => {
|
||||
self.substate.contracts_created.push(address.clone());
|
||||
ContractCreateResult::Created(address, gas_left)
|
||||
@ -190,7 +192,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
|
||||
|
||||
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth);
|
||||
|
||||
match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer) {
|
||||
match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) {
|
||||
Ok(gas_left) => MessageCallResult::Success(gas_left),
|
||||
_ => MessageCallResult::Failed
|
||||
}
|
||||
@ -286,6 +288,14 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
|
||||
fn inc_sstore_clears(&mut self) {
|
||||
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
|
||||
}
|
||||
|
||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
|
||||
self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost)
|
||||
}
|
||||
|
||||
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
|
||||
self.vm_tracer.trace_executed(gas_used, stack_push, mem_diff, store_diff)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -297,7 +307,7 @@ mod tests {
|
||||
use substate::*;
|
||||
use tests::helpers::*;
|
||||
use super::*;
|
||||
use trace::{NoopTracer};
|
||||
use trace::{NoopTracer, NoopVMTracer};
|
||||
|
||||
fn get_test_origin() -> OriginInfo {
|
||||
OriginInfo {
|
||||
@ -311,12 +321,12 @@ mod tests {
|
||||
fn get_test_env_info() -> EnvInfo {
|
||||
EnvInfo {
|
||||
number: 100,
|
||||
author: x!(0),
|
||||
author: 0.into(),
|
||||
timestamp: 0,
|
||||
difficulty: x!(0),
|
||||
difficulty: 0.into(),
|
||||
last_hashes: vec![],
|
||||
gas_used: x!(0),
|
||||
gas_limit: x!(0)
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,9 +359,10 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
let vm_factory = Default::default();
|
||||
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
|
||||
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
||||
|
||||
assert_eq!(ext.env_info().number, 100);
|
||||
}
|
||||
@ -361,9 +372,10 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
let vm_factory = Default::default();
|
||||
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
|
||||
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
||||
|
||||
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
|
||||
|
||||
@ -383,9 +395,10 @@ mod tests {
|
||||
}
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
let vm_factory = Default::default();
|
||||
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
|
||||
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
||||
|
||||
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
|
||||
|
||||
@ -398,9 +411,10 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
let vm_factory = Default::default();
|
||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
|
||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
||||
|
||||
let mut output = vec![];
|
||||
|
||||
@ -423,10 +437,11 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
{
|
||||
let vm_factory = Default::default();
|
||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
|
||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
||||
ext.log(log_topics, &log_data);
|
||||
}
|
||||
|
||||
@ -440,10 +455,11 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
{
|
||||
let vm_factory = Default::default();
|
||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer);
|
||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
||||
ext.suicide(&refund_account);
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ use tests::helpers::*;
|
||||
use devtools::*;
|
||||
use spec::Genesis;
|
||||
use ethjson;
|
||||
use miner::Miner;
|
||||
|
||||
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
init_log();
|
||||
@ -53,7 +54,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
|
||||
let temp = RandomTempPath::new();
|
||||
{
|
||||
let client = Client::new(ClientConfig::default(), spec, temp.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let client = Client::new(ClientConfig::default(), spec, temp.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap();
|
||||
for b in &blockchain.blocks_rlp() {
|
||||
if Block::is_good(&b) {
|
||||
let _ = client.import_block(b.clone());
|
||||
|
@ -25,6 +25,7 @@ use substate::*;
|
||||
use tests::helpers::*;
|
||||
use ethjson;
|
||||
use trace::{Tracer, NoopTracer};
|
||||
use trace::{VMTracer, NoopVMTracer};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct CallCreate {
|
||||
@ -48,32 +49,35 @@ impl From<ethjson::vm::Call> for CallCreate {
|
||||
|
||||
/// Tiny wrapper around executive externalities.
|
||||
/// Stores callcreates.
|
||||
struct TestExt<'a, T> where T: 'a + Tracer {
|
||||
ext: Externalities<'a, T>,
|
||||
struct TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
ext: Externalities<'a, T, V>,
|
||||
callcreates: Vec<CallCreate>,
|
||||
contract_address: Address
|
||||
}
|
||||
|
||||
impl<'a, T> TestExt<'a, T> where T: 'a + Tracer {
|
||||
fn new(state: &'a mut State,
|
||||
info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
vm_factory: &'a Factory,
|
||||
depth: usize,
|
||||
origin_info: OriginInfo,
|
||||
substate: &'a mut Substate,
|
||||
output: OutputPolicy<'a, 'a>,
|
||||
address: Address,
|
||||
tracer: &'a mut T) -> Self {
|
||||
impl<'a, T, V> TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
fn new(
|
||||
state: &'a mut State,
|
||||
info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
vm_factory: &'a Factory,
|
||||
depth: usize,
|
||||
origin_info: OriginInfo,
|
||||
substate: &'a mut Substate,
|
||||
output: OutputPolicy<'a, 'a>,
|
||||
address: Address,
|
||||
tracer: &'a mut T,
|
||||
vm_tracer: &'a mut V,
|
||||
) -> Self {
|
||||
TestExt {
|
||||
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![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
let mut output = vec![];
|
||||
|
||||
// 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),
|
||||
params.address.clone(),
|
||||
&mut tracer,
|
||||
&mut vm_tracer,
|
||||
);
|
||||
let evm = vm_factory.create();
|
||||
let res = evm.exec(params, &mut ex);
|
||||
|
@ -90,6 +90,7 @@ extern crate crossbeam;
|
||||
extern crate ethjson;
|
||||
extern crate bloomchain;
|
||||
#[macro_use] extern crate ethcore_ipc as ipc;
|
||||
extern crate rayon;
|
||||
|
||||
#[cfg(test)] extern crate ethcore_devtools as devtools;
|
||||
#[cfg(feature = "jit" )] extern crate evmjit;
|
||||
@ -109,6 +110,7 @@ pub mod views;
|
||||
pub mod pod_state;
|
||||
pub mod engine;
|
||||
pub mod migrations;
|
||||
pub mod miner;
|
||||
|
||||
mod blooms;
|
||||
mod db;
|
||||
@ -139,3 +141,5 @@ mod tests;
|
||||
mod json_tests;
|
||||
|
||||
pub use types::*;
|
||||
pub use evm::get_info;
|
||||
pub use executive::contract_address;
|
@ -18,17 +18,17 @@ use rayon::prelude::*;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use util::*;
|
||||
use util::keys::store::{AccountService, AccountProvider};
|
||||
use ethcore::views::{BlockView, HeaderView};
|
||||
use ethcore::client::{BlockChainClient, BlockID};
|
||||
use ethcore::block::{ClosedBlock, IsBlock};
|
||||
use ethcore::error::*;
|
||||
use ethcore::client::{Executive, Executed, EnvInfo, TransactOptions};
|
||||
use ethcore::transaction::SignedTransaction;
|
||||
use ethcore::receipt::{Receipt};
|
||||
use ethcore::spec::Spec;
|
||||
use ethcore::engine::Engine;
|
||||
use super::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
|
||||
use util::keys::store::{AccountProvider};
|
||||
use views::{BlockView, HeaderView};
|
||||
use client::{MiningBlockChainClient, BlockID};
|
||||
use block::{ClosedBlock, IsBlock};
|
||||
use error::*;
|
||||
use client::{Executive, Executed, EnvInfo, TransactOptions};
|
||||
use transaction::SignedTransaction;
|
||||
use receipt::{Receipt};
|
||||
use spec::Spec;
|
||||
use engine::Engine;
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
|
||||
|
||||
/// Keeps track of transactions using priority queue and holds currently mined block.
|
||||
pub struct Miner {
|
||||
@ -44,7 +44,7 @@ pub struct Miner {
|
||||
extra_data: RwLock<Bytes>,
|
||||
spec: Spec,
|
||||
|
||||
accounts: RwLock<Option<Arc<AccountService>>>, // TODO: this is horrible since AccountService already contains a single RwLock field. refactor.
|
||||
accounts: Option<Arc<AccountProvider>>,
|
||||
}
|
||||
|
||||
impl Default for Miner {
|
||||
@ -58,7 +58,7 @@ impl Default for Miner {
|
||||
gas_floor_target: RwLock::new(U256::zero()),
|
||||
author: RwLock::new(Address::default()),
|
||||
extra_data: RwLock::new(Vec::new()),
|
||||
accounts: RwLock::new(None),
|
||||
accounts: None,
|
||||
spec: Spec::new_test(),
|
||||
}
|
||||
}
|
||||
@ -76,13 +76,13 @@ impl Miner {
|
||||
gas_floor_target: RwLock::new(U256::zero()),
|
||||
author: RwLock::new(Address::default()),
|
||||
extra_data: RwLock::new(Vec::new()),
|
||||
accounts: RwLock::new(None),
|
||||
accounts: None,
|
||||
spec: spec,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates new instance of miner
|
||||
pub fn with_accounts(force_sealing: bool, spec: Spec, accounts: Arc<AccountService>) -> Arc<Miner> {
|
||||
pub fn with_accounts(force_sealing: bool, spec: Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
|
||||
Arc::new(Miner {
|
||||
transaction_queue: Mutex::new(TransactionQueue::new()),
|
||||
force_sealing: force_sealing,
|
||||
@ -92,7 +92,7 @@ impl Miner {
|
||||
gas_floor_target: RwLock::new(U256::zero()),
|
||||
author: RwLock::new(Address::default()),
|
||||
extra_data: RwLock::new(Vec::new()),
|
||||
accounts: RwLock::new(Some(accounts)),
|
||||
accounts: Some(accounts),
|
||||
spec: spec,
|
||||
})
|
||||
}
|
||||
@ -104,7 +104,7 @@ impl Miner {
|
||||
/// Prepares new block for sealing including top transactions from queue.
|
||||
#[cfg_attr(feature="dev", allow(match_same_arms))]
|
||||
#[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");
|
||||
let transactions = self.transaction_queue.lock().unwrap().top_transactions();
|
||||
let mut sealing_work = self.sealing_work.lock().unwrap();
|
||||
@ -137,7 +137,7 @@ impl Miner {
|
||||
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
|
||||
let min_tx_gas: U256 = x!(21000); // TODO: figure this out properly.
|
||||
let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly.
|
||||
if gas_limit - gas_used < min_tx_gas {
|
||||
break;
|
||||
}
|
||||
@ -177,9 +177,8 @@ impl Miner {
|
||||
if !block.transactions().is_empty() {
|
||||
trace!(target: "miner", "prepare_sealing: block has transaction - attempting internal seal.");
|
||||
// block with transactions - see if we can seal immediately.
|
||||
let a = self.accounts.read().unwrap();
|
||||
let s = self.engine().generate_seal(block.block(), match *a.deref() {
|
||||
Some(ref x) => Some(x.deref() as &AccountProvider),
|
||||
let s = self.engine().generate_seal(block.block(), match self.accounts {
|
||||
Some(ref x) => Some(&**x),
|
||||
None => None,
|
||||
});
|
||||
if let Some(seal) = s {
|
||||
@ -206,14 +205,14 @@ impl Miner {
|
||||
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 mut queue = self.transaction_queue.lock().unwrap();
|
||||
queue.set_gas_limit(gas_limit);
|
||||
}
|
||||
|
||||
/// 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");
|
||||
let have_work = self.sealing_work.lock().unwrap().peek_last_ref().is_some();
|
||||
trace!(target: "miner", "enable_and_prepare_sealing: have_work={}", have_work);
|
||||
@ -237,7 +236,7 @@ const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5;
|
||||
|
||||
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.update_sealing(chain);
|
||||
}
|
||||
@ -252,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();
|
||||
match sealing_work.peek_last_ref() {
|
||||
Some(work) => {
|
||||
@ -278,17 +277,18 @@ impl MinerService for Miner {
|
||||
// give the sender max balance
|
||||
state.sub_balance(&sender, &balance);
|
||||
state.add_balance(&sender, &U256::max_value());
|
||||
let options = TransactOptions { tracing: false, check_nonce: false };
|
||||
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)
|
||||
},
|
||||
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();
|
||||
sealing_work.peek_last_ref().map_or_else(
|
||||
|| chain.latest_balance(address),
|
||||
@ -296,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();
|
||||
sealing_work.peek_last_ref().map_or_else(
|
||||
|| chain.latest_storage_at(address, position),
|
||||
@ -304,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();
|
||||
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();
|
||||
sealing_work.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address))
|
||||
}
|
||||
@ -337,11 +337,11 @@ impl MinerService for Miner {
|
||||
|
||||
fn sensible_gas_price(&self) -> U256 {
|
||||
// 10% above our minimum.
|
||||
*self.transaction_queue.lock().unwrap().minimal_gas_price() * x!(110) / x!(100)
|
||||
*self.transaction_queue.lock().unwrap().minimal_gas_price() * 110.into() / 100.into()
|
||||
}
|
||||
|
||||
fn sensible_gas_limit(&self) -> U256 {
|
||||
*self.gas_floor_target.read().unwrap() / x!(5)
|
||||
*self.gas_floor_target.read().unwrap() / 5.into()
|
||||
}
|
||||
|
||||
fn transactions_limit(&self) -> usize {
|
||||
@ -376,7 +376,7 @@ impl MinerService for Miner {
|
||||
.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>
|
||||
where T: Fn(&Address) -> AccountDetails {
|
||||
let hash = transaction.hash();
|
||||
@ -395,6 +395,7 @@ impl MinerService for Miner {
|
||||
Err(ref e) => {
|
||||
trace!(target: "own_tx", "Failed to import transaction {:?} (hash: {:?})", e, hash);
|
||||
trace!(target: "own_tx", "Status: {:?}", transaction_queue.status());
|
||||
warn!(target: "own_tx", "Error importing transaction: {:?}", e);
|
||||
},
|
||||
}
|
||||
import
|
||||
@ -469,7 +470,7 @@ impl MinerService for Miner {
|
||||
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) {
|
||||
let current_no = chain.chain_info().best_block_number;
|
||||
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");
|
||||
self.enable_and_prepare_sealing(chain);
|
||||
trace!(target: "miner", "map_sealing_work: sealing prepared");
|
||||
@ -499,7 +500,7 @@ impl MinerService for Miner {
|
||||
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) {
|
||||
match chain.try_seal(b.lock(), seal) {
|
||||
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 fetch_transactions(chain: &BlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
|
||||
fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
|
||||
fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
|
||||
let block = chain
|
||||
.block(BlockID::Hash(*hash))
|
||||
// Client should send message after commit to db and inserting to chain.
|
||||
@ -584,13 +585,13 @@ impl MinerService for Miner {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use MinerService;
|
||||
use super::{Miner};
|
||||
use super::super::MinerService;
|
||||
use super::Miner;
|
||||
use util::*;
|
||||
use ethcore::client::{TestBlockChainClient, EachBlockWith};
|
||||
use ethcore::block::*;
|
||||
use client::{TestBlockChainClient, EachBlockWith};
|
||||
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]
|
||||
#[test]
|
||||
fn should_prepare_block_to_seal() {
|
@ -26,12 +26,10 @@
|
||||
//! ```rust
|
||||
//! extern crate ethcore_util as util;
|
||||
//! extern crate ethcore;
|
||||
//! extern crate ethminer;
|
||||
//! use std::env;
|
||||
//! use util::network::{NetworkService, NetworkConfiguration};
|
||||
//! use ethcore::client::{Client, ClientConfig};
|
||||
//! use ethcore::ethereum;
|
||||
//! use ethminer::{Miner, MinerService};
|
||||
//! use ethcore::miner::{Miner, MinerService};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! 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 external;
|
||||
mod transaction_queue;
|
||||
|
||||
pub use transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
|
||||
pub use miner::{Miner};
|
||||
pub use external::{ExternalMiner, ExternalMinerService};
|
||||
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
|
||||
pub use self::miner::{Miner};
|
||||
pub use self::external::{ExternalMiner, ExternalMinerService};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use util::{H256, U256, Address, Bytes};
|
||||
use ethcore::client::{BlockChainClient, Executed};
|
||||
use ethcore::block::ClosedBlock;
|
||||
use ethcore::receipt::Receipt;
|
||||
use ethcore::error::{Error, ExecutionError};
|
||||
use ethcore::transaction::SignedTransaction;
|
||||
use client::{MiningBlockChainClient, Executed};
|
||||
use block::ClosedBlock;
|
||||
use receipt::Receipt;
|
||||
use error::{Error, ExecutionError};
|
||||
use transaction::SignedTransaction;
|
||||
|
||||
/// Miner client API
|
||||
pub trait MinerService : Send + Sync {
|
||||
@ -107,31 +96,32 @@ pub trait MinerService : Send + Sync {
|
||||
/// Imports transactions to transaction queue.
|
||||
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) ->
|
||||
Vec<Result<TransactionImportResult, Error>>
|
||||
where T: Fn(&Address) -> AccountDetails;
|
||||
where T: Fn(&Address) -> AccountDetails, Self: Sized;
|
||||
|
||||
/// 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>
|
||||
where T: Fn(&Address) -> AccountDetails;
|
||||
where T: Fn(&Address) -> AccountDetails, Self: Sized;
|
||||
|
||||
/// Returns hashes of transactions currently in pending
|
||||
fn pending_transactions_hashes(&self) -> Vec<H256>;
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
fn update_sealing(&self, chain: &BlockChainClient);
|
||||
fn update_sealing(&self, chain: &MiningBlockChainClient);
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
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, Self: Sized;
|
||||
|
||||
/// Query pending transactions for hash.
|
||||
fn transaction(&self, hash: &H256) -> Option<SignedTransaction>;
|
||||
@ -149,25 +139,25 @@ pub trait MinerService : Send + Sync {
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||
|
||||
/// Suggested gas price.
|
||||
fn sensible_gas_price(&self) -> U256 { x!(20000000000u64) }
|
||||
fn sensible_gas_price(&self) -> U256 { 20000000000u64.into() }
|
||||
|
||||
/// Suggested gas limit.
|
||||
fn sensible_gas_limit(&self) -> U256 { x!(21000) }
|
||||
fn sensible_gas_limit(&self) -> U256 { 21000.into() }
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
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.
|
||||
fn nonce(&self, chain: &BlockChainClient, address: &Address) -> U256;
|
||||
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256;
|
||||
|
||||
/// 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
|
@ -26,13 +26,12 @@
|
||||
//! ```rust
|
||||
//! extern crate ethcore_util as util;
|
||||
//! extern crate ethcore;
|
||||
//! extern crate ethminer;
|
||||
//! extern crate rustc_serialize;
|
||||
//!
|
||||
//! use util::crypto::KeyPair;
|
||||
//! use util::hash::Address;
|
||||
//! use util::numbers::{Uint, U256};
|
||||
//! use ethminer::{TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
//! use ethcore::miner::{TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
//! use ethcore::transaction::*;
|
||||
//! use rustc_serialize::hex::FromHex;
|
||||
//!
|
||||
@ -89,8 +88,8 @@ use std::collections::{HashMap, BTreeSet};
|
||||
use util::numbers::{Uint, U256};
|
||||
use util::hash::{Address, H256};
|
||||
use util::table::*;
|
||||
use ethcore::transaction::*;
|
||||
use ethcore::error::{Error, TransactionError};
|
||||
use transaction::*;
|
||||
use error::{Error, TransactionError};
|
||||
|
||||
/// Transaction origin
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@ -778,8 +777,8 @@ mod test {
|
||||
extern crate rustc_serialize;
|
||||
use util::table::*;
|
||||
use util::*;
|
||||
use ethcore::transaction::*;
|
||||
use ethcore::error::{Error, TransactionError};
|
||||
use transaction::*;
|
||||
use error::{Error, TransactionError};
|
||||
use super::*;
|
||||
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
|
||||
|
@ -114,11 +114,11 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn existence() {
|
||||
let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]};
|
||||
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]};
|
||||
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&a)), None);
|
||||
assert_eq!(AccountDiff::diff_pod(None, Some(&a)), Some(AccountDiff{
|
||||
balance: Diff::Born(x!(69)),
|
||||
nonce: Diff::Born(x!(0)),
|
||||
balance: Diff::Born(69.into()),
|
||||
nonce: Diff::Born(0.into()),
|
||||
code: Diff::Born(vec![]),
|
||||
storage: map![],
|
||||
}));
|
||||
@ -126,11 +126,11 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]};
|
||||
let b = PodAccount{balance: x!(42), nonce: x!(1), code: vec![], storage: map![]};
|
||||
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]};
|
||||
let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: vec![], storage: map![]};
|
||||
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
||||
balance: Diff::Changed(x!(69), x!(42)),
|
||||
nonce: Diff::Changed(x!(0), x!(1)),
|
||||
balance: Diff::Changed(69.into(), 42.into()),
|
||||
nonce: Diff::Changed(0.into(), 1.into()),
|
||||
code: Diff::Same,
|
||||
storage: map![],
|
||||
}));
|
||||
@ -138,11 +138,11 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn code() {
|
||||
let a = PodAccount{balance: x!(0), nonce: x!(0), code: vec![], storage: map![]};
|
||||
let b = PodAccount{balance: x!(0), nonce: x!(1), code: vec![0], storage: map![]};
|
||||
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: vec![], storage: map![]};
|
||||
let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: vec![0], storage: map![]};
|
||||
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
||||
balance: Diff::Same,
|
||||
nonce: Diff::Changed(x!(0), x!(1)),
|
||||
nonce: Diff::Changed(0.into(), 1.into()),
|
||||
code: Diff::Changed(vec![], vec![0]),
|
||||
storage: map![],
|
||||
}));
|
||||
@ -151,27 +151,27 @@ mod test {
|
||||
#[test]
|
||||
fn storage() {
|
||||
let a = PodAccount {
|
||||
balance: x!(0),
|
||||
nonce: x!(0),
|
||||
balance: 0.into(),
|
||||
nonce: 0.into(),
|
||||
code: vec![],
|
||||
storage: mapx![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0]
|
||||
storage: map_into![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0]
|
||||
};
|
||||
let b = PodAccount {
|
||||
balance: x!(0),
|
||||
nonce: x!(0),
|
||||
balance: 0.into(),
|
||||
nonce: 0.into(),
|
||||
code: vec![],
|
||||
storage: mapx![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9]
|
||||
storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9]
|
||||
};
|
||||
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
||||
balance: Diff::Same,
|
||||
nonce: Diff::Same,
|
||||
code: Diff::Same,
|
||||
storage: map![
|
||||
x!(2) => Diff::new(x!(2), x!(3)),
|
||||
x!(3) => Diff::new(x!(3), x!(0)),
|
||||
x!(4) => Diff::new(x!(4), x!(0)),
|
||||
x!(7) => Diff::new(x!(0), x!(7)),
|
||||
x!(9) => Diff::new(x!(0), x!(9))
|
||||
2.into() => Diff::new(2.into(), 3.into()),
|
||||
3.into() => Diff::new(3.into(), 0.into()),
|
||||
4.into() => Diff::new(4.into(), 0.into()),
|
||||
7.into() => Diff::new(0.into(), 7.into()),
|
||||
9.into() => Diff::new(0.into(), 9.into())
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ use util::panics::*;
|
||||
use spec::Spec;
|
||||
use error::*;
|
||||
use client::{Client, ClientConfig};
|
||||
use miner::Miner;
|
||||
|
||||
/// Message type for external and internal events
|
||||
#[derive(Clone)]
|
||||
@ -54,14 +55,14 @@ pub struct ClientService {
|
||||
|
||||
impl ClientService {
|
||||
/// 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 mut net_service = try!(NetworkService::start(net_config));
|
||||
panic_handler.forward_from(&net_service);
|
||||
|
||||
info!("Starting {}", net_service.host_info());
|
||||
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());
|
||||
let client_io = Arc::new(ClientIoHandler {
|
||||
client: client.clone()
|
||||
@ -141,12 +142,14 @@ mod tests {
|
||||
use util::network::*;
|
||||
use devtools::*;
|
||||
use client::ClientConfig;
|
||||
use std::sync::Arc;
|
||||
use miner::Miner;
|
||||
|
||||
#[test]
|
||||
fn it_can_be_started() {
|
||||
let spec = get_test_spec();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -183,16 +183,14 @@ impl State {
|
||||
|
||||
/// Add `incr` to the balance of account `a`.
|
||||
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);
|
||||
trace!("state: add_balance({}, {}): {} -> {}\n", a, incr, old, self.balance(a));
|
||||
}
|
||||
|
||||
/// Subtract `decr` from the balance of account `a`.
|
||||
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);
|
||||
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`.
|
||||
@ -214,7 +212,7 @@ impl State {
|
||||
/// Initialise the code of account `a` so that it is `value` for `key`.
|
||||
/// NOTE: Account should have been created with `new_contract`.
|
||||
pub fn init_code(&mut self, a: &Address, code: Bytes) {
|
||||
self.require_or_from(a, true, || Account::new_contract(x!(0), self.account_start_nonce), |_|{}).init_code(code);
|
||||
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).init_code(code);
|
||||
}
|
||||
|
||||
/// Execute a given transaction.
|
||||
@ -222,7 +220,7 @@ impl State {
|
||||
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
||||
// 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));
|
||||
|
||||
// TODO uncomment once to_pod() works correctly.
|
||||
@ -377,27 +375,27 @@ fn should_apply_create_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Create,
|
||||
value: x!(100),
|
||||
value: 100.into(),
|
||||
data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Create(trace::Create {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
value: x!(100),
|
||||
gas: x!(77412),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
value: 100.into(),
|
||||
gas: 77412.into(),
|
||||
init: 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],
|
||||
}),
|
||||
result: trace::Res::Create(trace::CreateResult {
|
||||
@ -438,27 +436,27 @@ fn should_trace_failed_create_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Create,
|
||||
value: x!(100),
|
||||
value: 100.into(),
|
||||
data: FromHex::from_hex("5b600056").unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Create(trace::Create {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
value: x!(100),
|
||||
gas: x!(78792),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
value: 100.into(),
|
||||
gas: 78792.into(),
|
||||
init: vec![91, 96, 0, 86],
|
||||
}),
|
||||
result: trace::Res::FailedCreate,
|
||||
@ -476,29 +474,29 @@ fn should_trace_call_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(100),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 100.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -519,28 +517,28 @@ fn should_trace_basic_call_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(100),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 100.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -561,15 +559,15 @@ fn should_trace_call_transaction_to_builtin() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0x1)),
|
||||
value: x!(0),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0x1.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
@ -579,10 +577,10 @@ fn should_trace_call_transaction_to_builtin() {
|
||||
assert_eq!(result.trace, Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!("0000000000000000000000000000000000000001"),
|
||||
value: x!(0),
|
||||
gas: x!(79_000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: "0000000000000000000000000000000000000001".into(),
|
||||
value: 0.into(),
|
||||
gas: 79_000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -601,29 +599,29 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(0),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
||||
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(0),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 0.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -643,30 +641,30 @@ fn should_not_trace_callcode() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(0),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
|
||||
state.init_code(&x!(0xb), FromHex::from_hex("6000").unwrap());
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
||||
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(0),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 0.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -686,33 +684,33 @@ fn should_not_trace_delegatecall() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
info.number = 0x789b0;
|
||||
let engine = Spec::new_test().engine;
|
||||
|
||||
println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(0),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
|
||||
state.init_code(&x!(0xb), FromHex::from_hex("6000").unwrap());
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
||||
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(0),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 0.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -732,29 +730,29 @@ fn should_trace_failed_call_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(100),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 100.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::FailedCall,
|
||||
@ -774,30 +772,30 @@ fn should_trace_call_with_subcall_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(100),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&x!(0xb), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 100.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -807,10 +805,10 @@ fn should_trace_call_with_subcall_transaction() {
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(0),
|
||||
gas: x!(78934),
|
||||
from: 0xa.into(),
|
||||
to: 0xb.into(),
|
||||
value: 0.into(),
|
||||
gas: 78934.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -832,29 +830,29 @@ fn should_trace_call_with_basic_subcall_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(100),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006045600b6000f1").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 100.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -864,10 +862,10 @@ fn should_trace_call_with_basic_subcall_transaction() {
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(69),
|
||||
gas: x!(2300),
|
||||
from: 0xa.into(),
|
||||
to: 0xb.into(),
|
||||
value: 69.into(),
|
||||
gas: 2300.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult::default()),
|
||||
@ -886,29 +884,29 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(100),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds.
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds.
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 100.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -929,30 +927,30 @@ fn should_trace_failed_subcall_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(100),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],//600480600b6000396000f35b600056
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&x!(0xb), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 100.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -962,10 +960,10 @@ fn should_trace_failed_subcall_transaction() {
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(0),
|
||||
gas: x!(78934),
|
||||
from: 0xa.into(),
|
||||
to: 0xb.into(),
|
||||
value: 0.into(),
|
||||
gas: 78934.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::FailedCall,
|
||||
@ -984,31 +982,31 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(100),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&x!(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap());
|
||||
state.init_code(&x!(0xc), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap());
|
||||
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 100.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -1018,10 +1016,10 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(0),
|
||||
gas: x!(78934),
|
||||
from: 0xa.into(),
|
||||
to: 0xb.into(),
|
||||
value: 0.into(),
|
||||
gas: 78934.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -1031,10 +1029,10 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
|
||||
subs: vec![Trace {
|
||||
depth: 2,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xb),
|
||||
to: x!(0xc),
|
||||
value: x!(0),
|
||||
gas: x!(78868),
|
||||
from: 0xb.into(),
|
||||
to: 0xc.into(),
|
||||
value: 0.into(),
|
||||
gas: 78868.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -1057,31 +1055,31 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = x!(1_000_000);
|
||||
info.gas_limit = 1_000_000.into();
|
||||
let engine = TestEngine::new(5);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: x!(0),
|
||||
gas_price: x!(0),
|
||||
gas: x!(100_000),
|
||||
action: Action::Call(x!(0xa)),
|
||||
value: x!(100),
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],//600480600b6000396000f35b600056
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&x!(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap());
|
||||
state.init_code(&x!(0xc), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap());
|
||||
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||
to: 0xa.into(),
|
||||
value: 100.into(),
|
||||
gas: 79000.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
@ -1091,21 +1089,21 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(0),
|
||||
gas: x!(78934),
|
||||
from: 0xa.into(),
|
||||
to: 0xb.into(),
|
||||
value: 0.into(),
|
||||
gas: 78934.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::FailedCall,
|
||||
subs: vec![Trace {
|
||||
depth: 2,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xb),
|
||||
to: x!(0xc),
|
||||
value: x!(0),
|
||||
gas: x!(78868),
|
||||
input: vec![],
|
||||
from: 0xb.into(),
|
||||
to: 0xc.into(),
|
||||
value: 0.into(),
|
||||
gas: 78868.into(),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(3),
|
||||
@ -1125,7 +1123,7 @@ fn code_from_database() {
|
||||
let temp = RandomTempPath::new();
|
||||
let (root, db) = {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
state.require_or_from(&a, false, ||Account::new_contract(x!(42), x!(0)), |_|{});
|
||||
state.require_or_from(&a, false, ||Account::new_contract(42.into(), 0.into()), |_|{});
|
||||
state.init_code(&a, vec![1, 2, 3]);
|
||||
assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec()));
|
||||
state.commit();
|
||||
|
@ -59,19 +59,19 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn create_delete() {
|
||||
let a = PodState::from(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]);
|
||||
let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]);
|
||||
assert_eq!(StateDiff::diff_pod(&a, &PodState::new()), StateDiff(map![
|
||||
x!(1) => AccountDiff{
|
||||
balance: Diff::Died(x!(69)),
|
||||
nonce: Diff::Died(x!(0)),
|
||||
1.into() => AccountDiff{
|
||||
balance: Diff::Died(69.into()),
|
||||
nonce: Diff::Died(0.into()),
|
||||
code: Diff::Died(vec![]),
|
||||
storage: map![],
|
||||
}
|
||||
]));
|
||||
assert_eq!(StateDiff::diff_pod(&PodState::new(), &a), StateDiff(map![
|
||||
x!(1) => AccountDiff{
|
||||
balance: Diff::Born(x!(69)),
|
||||
nonce: Diff::Born(x!(0)),
|
||||
1.into() => AccountDiff{
|
||||
balance: Diff::Born(69.into()),
|
||||
nonce: Diff::Born(0.into()),
|
||||
code: Diff::Born(vec![]),
|
||||
storage: map![],
|
||||
}
|
||||
@ -80,23 +80,23 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn create_delete_with_unchanged() {
|
||||
let a = PodState::from(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]);
|
||||
let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]);
|
||||
let b = PodState::from(map![
|
||||
x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]),
|
||||
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
|
||||
1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]),
|
||||
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
|
||||
]);
|
||||
assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![
|
||||
x!(2) => AccountDiff{
|
||||
balance: Diff::Born(x!(69)),
|
||||
nonce: Diff::Born(x!(0)),
|
||||
2.into() => AccountDiff{
|
||||
balance: Diff::Born(69.into()),
|
||||
nonce: Diff::Born(0.into()),
|
||||
code: Diff::Born(vec![]),
|
||||
storage: map![],
|
||||
}
|
||||
]));
|
||||
assert_eq!(StateDiff::diff_pod(&b, &a), StateDiff(map![
|
||||
x!(2) => AccountDiff{
|
||||
balance: Diff::Died(x!(69)),
|
||||
nonce: Diff::Died(x!(0)),
|
||||
2.into() => AccountDiff{
|
||||
balance: Diff::Died(69.into()),
|
||||
nonce: Diff::Died(0.into()),
|
||||
code: Diff::Died(vec![]),
|
||||
storage: map![],
|
||||
}
|
||||
@ -106,17 +106,17 @@ mod test {
|
||||
#[test]
|
||||
fn change_with_unchanged() {
|
||||
let a = PodState::from(map![
|
||||
x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]),
|
||||
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
|
||||
1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]),
|
||||
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
|
||||
]);
|
||||
let b = PodState::from(map![
|
||||
x!(1) => PodAccount::new(x!(69), x!(1), vec![], map![]),
|
||||
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
|
||||
1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]),
|
||||
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
|
||||
]);
|
||||
assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![
|
||||
x!(1) => AccountDiff{
|
||||
1.into() => AccountDiff{
|
||||
balance: Diff::Same,
|
||||
nonce: Diff::Changed(x!(0), x!(1)),
|
||||
nonce: Diff::Changed(0.into(), 1.into()),
|
||||
code: Diff::Same,
|
||||
storage: map![],
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ mod tests {
|
||||
topics: vec![],
|
||||
data: vec![]
|
||||
});
|
||||
sub_state.sstore_clears_count = x!(5);
|
||||
sub_state.sstore_clears_count = 5.into();
|
||||
sub_state.suicides.insert(address_from_u64(10u64));
|
||||
|
||||
let mut sub_state_2 = Substate::new();
|
||||
@ -84,11 +84,11 @@ mod tests {
|
||||
topics: vec![],
|
||||
data: vec![]
|
||||
});
|
||||
sub_state_2.sstore_clears_count = x!(7);
|
||||
sub_state_2.sstore_clears_count = 7.into();
|
||||
|
||||
sub_state.accrue(sub_state_2);
|
||||
assert_eq!(sub_state.contracts_created.len(), 2);
|
||||
assert_eq!(sub_state.sstore_clears_count, x!(12));
|
||||
assert_eq!(sub_state.sstore_clears_count, 12.into());
|
||||
assert_eq!(sub_state.suicides.len(), 1);
|
||||
}
|
||||
}
|
||||
|
@ -14,16 +14,17 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// 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 tests::helpers::*;
|
||||
use common::*;
|
||||
use devtools::*;
|
||||
use miner::Miner;
|
||||
|
||||
#[test]
|
||||
fn imports_from_empty() {
|
||||
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.flush_queue();
|
||||
}
|
||||
@ -41,7 +42,7 @@ fn returns_state_root_basic() {
|
||||
#[test]
|
||||
fn imports_good_block() {
|
||||
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();
|
||||
if let Err(_) = client.import_block(good_block) {
|
||||
panic!("error importing block being good by definition");
|
||||
@ -56,7 +57,7 @@ fn imports_good_block() {
|
||||
#[test]
|
||||
fn query_none_block() {
|
||||
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));
|
||||
assert!(non_existant.is_none());
|
||||
@ -115,7 +116,7 @@ fn can_collect_garbage() {
|
||||
fn can_handle_long_fork() {
|
||||
let client_result = generate_dummy_client(1200);
|
||||
let client = client_result.reference();
|
||||
for _ in 0..10 {
|
||||
for _ in 0..20 {
|
||||
client.import_verified_blocks(&IoChannel::disconnected());
|
||||
}
|
||||
assert_eq!(1200, client.chain_info().best_block_number);
|
||||
@ -124,7 +125,7 @@ fn can_handle_long_fork() {
|
||||
push_blocks_to_client(client, 49, 1201, 800);
|
||||
push_blocks_to_client(client, 53, 1201, 600);
|
||||
|
||||
for _ in 0..20 {
|
||||
for _ in 0..40 {
|
||||
client.import_verified_blocks(&IoChannel::disconnected());
|
||||
}
|
||||
assert_eq!(2000, client.chain_info().best_block_number);
|
||||
@ -136,7 +137,7 @@ fn can_mine() {
|
||||
let client_result = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]);
|
||||
let client = client_result.reference();
|
||||
|
||||
let b = client.prepare_sealing(Address::default(), x!(31415926), vec![], vec![]).0.unwrap();
|
||||
let b = client.prepare_sealing(Address::default(), 31415926.into(), vec![], vec![]).0.unwrap();
|
||||
|
||||
assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3());
|
||||
assert!(client.try_seal(b.lock(), vec![]).is_ok());
|
||||
|
@ -23,6 +23,7 @@ use evm::Schedule;
|
||||
use engine::*;
|
||||
use ethereum;
|
||||
use devtools::*;
|
||||
use miner::Miner;
|
||||
|
||||
#[cfg(feature = "json-tests")]
|
||||
pub enum ChainEra {
|
||||
@ -98,8 +99,8 @@ pub fn create_test_block(header: &Header) -> Bytes {
|
||||
|
||||
fn create_unverifiable_block_header(order: u32, parent_hash: H256) -> Header {
|
||||
let mut header = Header::new();
|
||||
header.gas_limit = x!(0);
|
||||
header.difficulty = x!(order * 100);
|
||||
header.gas_limit = 0.into();
|
||||
header.difficulty = (order * 100).into();
|
||||
header.timestamp = (order * 10) as u64;
|
||||
header.number = order as u64;
|
||||
header.parent_hash = parent_hash;
|
||||
@ -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>> {
|
||||
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_engine = &test_spec.engine;
|
||||
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>> {
|
||||
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 {
|
||||
if let Err(_) = client.import_block(block.clone()) {
|
||||
panic!("panic importing block which is well-formed");
|
||||
@ -335,7 +336,7 @@ pub fn get_bad_state_dummy_block() -> Bytes {
|
||||
block_header.timestamp = 40;
|
||||
block_header.number = 1;
|
||||
block_header.parent_hash = test_spec.genesis_header().hash();
|
||||
block_header.state_root = x!(0xbad);
|
||||
block_header.state_root = 0xbad.into();
|
||||
|
||||
create_test_block(&block_header)
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ impl Key<BlockTraces> for H256 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around blooms::GroupPosition so it could be
|
||||
/// Wrapper around `blooms::GroupPosition` so it could be
|
||||
/// uniquely identified in the database.
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
struct TraceGroupPosition(blooms::GroupPosition);
|
||||
|
@ -18,13 +18,13 @@
|
||||
|
||||
use util::{Bytes, Address, U256};
|
||||
use action_params::ActionParams;
|
||||
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult};
|
||||
use trace::Tracer;
|
||||
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
|
||||
use trace::{Tracer, VMTracer};
|
||||
|
||||
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
||||
#[derive(Default)]
|
||||
pub struct ExecutiveTracer {
|
||||
traces: Vec<Trace>
|
||||
traces: Vec<Trace>,
|
||||
}
|
||||
|
||||
impl Tracer for ExecutiveTracer {
|
||||
@ -40,8 +40,7 @@ impl Tracer for ExecutiveTracer {
|
||||
Some(vec![])
|
||||
}
|
||||
|
||||
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs:
|
||||
Vec<Trace>, delegate_call: bool) {
|
||||
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>, delegate_call: bool) {
|
||||
// don't trace if it's DELEGATECALL or CALLCODE.
|
||||
if delegate_call {
|
||||
return;
|
||||
@ -106,3 +105,46 @@ impl Tracer for ExecutiveTracer {
|
||||
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() }
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ pub use self::block::BlockTraces;
|
||||
pub use self::config::{Config, Switch};
|
||||
pub use self::db::TraceDB;
|
||||
pub use self::error::Error;
|
||||
pub use types::trace_types::trace::Trace;
|
||||
pub use self::noop_tracer::NoopTracer;
|
||||
pub use self::executive_tracer::ExecutiveTracer;
|
||||
pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
|
||||
pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
|
||||
pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
|
||||
pub use types::trace_types::filter::{Filter, AddressesFilter};
|
||||
pub use self::import::ImportRequest;
|
||||
pub use self::localized::LocalizedTrace;
|
||||
@ -81,13 +81,32 @@ pub trait Tracer: Send {
|
||||
/// Stores failed create 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;
|
||||
|
||||
/// Consumes self and returns all traces.
|
||||
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,
|
||||
/// but necessary to work correctly.
|
||||
pub trait DatabaseExtras {
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
use util::{Bytes, Address, U256};
|
||||
use action_params::ActionParams;
|
||||
use trace::Tracer;
|
||||
use trace::trace::{Trace, Call, Create};
|
||||
use trace::{Tracer, VMTracer};
|
||||
use trace::trace::{Trace, Call, Create, VMTrace};
|
||||
|
||||
/// Nonoperative tracer. Does not trace anything.
|
||||
pub struct NoopTracer;
|
||||
@ -63,3 +63,23 @@ impl Tracer for NoopTracer {
|
||||
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 }
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
use util::numbers::*;
|
||||
use util::Bytes;
|
||||
use trace::Trace;
|
||||
use trace::{Trace, VMTrace};
|
||||
use types::log_entry::LogEntry;
|
||||
use ipc::binary::BinaryConvertError;
|
||||
use std::fmt;
|
||||
@ -59,6 +59,8 @@ pub struct Executed {
|
||||
pub output: Bytes,
|
||||
/// The trace of this transaction.
|
||||
pub trace: Option<Trace>,
|
||||
/// The VM trace of this transaction.
|
||||
pub vm_trace: Option<VMTrace>,
|
||||
}
|
||||
|
||||
/// Result of executing the transaction.
|
||||
|
@ -105,10 +105,10 @@ pub struct LocalizedReceipt {
|
||||
fn test_basic() {
|
||||
let expected = ::rustc_serialize::hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let r = Receipt::new(
|
||||
x!("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee"),
|
||||
x!(0x40cae),
|
||||
"2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(),
|
||||
0x40cae.into(),
|
||||
vec![LogEntry {
|
||||
address: x!("dcf421d093428b096ca501a7cd1a740855a7976f"),
|
||||
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
|
||||
topics: vec![],
|
||||
data: vec![0u8; 32]
|
||||
}]
|
||||
|
@ -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)]
|
||||
mod tests {
|
||||
use util::{Address, U256, FixedHash};
|
||||
|
@ -36,6 +36,7 @@ pub enum Action {
|
||||
/// Create creates new contract.
|
||||
Create,
|
||||
/// Calls contract at given address.
|
||||
/// In the case of a transfer, this is the receiver's address.'
|
||||
Call(Address),
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use serde::{Deserialize, Deserializer, Error};
|
||||
use serde::de::Visitor;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Lenient bytes json deserialization for test json files.
|
||||
#[derive(Default, Debug, PartialEq, Clone)]
|
||||
@ -30,6 +31,14 @@ impl Into<Vec<u8>> for Bytes {
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Bytes {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Vec<u8> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Bytes {
|
||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||
where D: Deserializer {
|
||||
|
@ -40,7 +40,7 @@ pub struct Transaction {
|
||||
/// To.
|
||||
pub to: MaybeEmpty<Address>,
|
||||
/// Value.
|
||||
pub value: Uint
|
||||
pub value: Uint,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -39,16 +39,47 @@ pub struct Call {
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use vm::Call;
|
||||
use util::numbers::U256;
|
||||
use uint::Uint;
|
||||
use util::hash::Address as Hash160;
|
||||
use hash::Address;
|
||||
use maybe::MaybeEmpty;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn call_deserialization() {
|
||||
fn call_deserialization_empty_dest() {
|
||||
let s = r#"{
|
||||
"data" : "0x1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff",
|
||||
"destination" : "",
|
||||
"gasLimit" : "0x1748766aa5",
|
||||
"value" : "0x00"
|
||||
}"#;
|
||||
let _deserialized: Call = serde_json::from_str(s).unwrap();
|
||||
// TODO: validate all fields
|
||||
let call: Call = serde_json::from_str(s).unwrap();
|
||||
|
||||
assert_eq!(&call.data[..],
|
||||
&[0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77,
|
||||
0x88, 0x88, 0x99, 0x99, 0x00, 0x00, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd,
|
||||
0xee, 0xee, 0xff, 0xff]);
|
||||
|
||||
assert_eq!(call.destination, MaybeEmpty::None);
|
||||
assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64)));
|
||||
assert_eq!(call.value, Uint(U256::from(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_deserialization_full_dest() {
|
||||
let s = r#"{
|
||||
"data" : "0x1234",
|
||||
"destination" : "5a39ed1020c04d4d84539975b893a4e7c53eab6c",
|
||||
"gasLimit" : "0x1748766aa5",
|
||||
"value" : "0x00"
|
||||
}"#;
|
||||
|
||||
let call: Call = serde_json::from_str(s).unwrap();
|
||||
|
||||
assert_eq!(&call.data[..], &[0x12, 0x34]);
|
||||
assert_eq!(call.destination, MaybeEmpty::Some(Address(Hash160::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c").unwrap())));
|
||||
assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64)));
|
||||
assert_eq!(call.value, Uint(U256::from(0)));
|
||||
}
|
||||
}
|
||||
|
@ -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"]
|
@ -76,13 +76,13 @@ API and Console Options:
|
||||
interface. APIS is a comma-delimited list of API
|
||||
name. Possible name are web3, eth, net, personal,
|
||||
ethcore, traces.
|
||||
[default: web3,eth,net,personal,ethcore,traces].
|
||||
[default: web3,eth,net,personal,traces].
|
||||
|
||||
--ipc-off Disable JSON-RPC over IPC service.
|
||||
--ipc-path PATH Specify custom path for JSON-RPC over IPC service
|
||||
[default: $HOME/.parity/jsonrpc.ipc].
|
||||
--ipc-apis APIS Specify custom API set available via JSON-RPC over
|
||||
IPC [default: web3,eth,net,personal,ethcore].
|
||||
IPC [default: web3,eth,net,personal,traces].
|
||||
|
||||
--dapps-off Disable the Dapps server (e.g. status page).
|
||||
--dapps-port PORT Specify the port portion of the Dapps server
|
||||
@ -97,6 +97,11 @@ API and Console Options:
|
||||
--dapps-pass PASSWORD Specify password for Dapps server. Use only in
|
||||
conjunction with --dapps-user.
|
||||
|
||||
--signer Enable Trusted Signer WebSocket endpoint used by
|
||||
System UIs.
|
||||
--signer-port PORT Specify the port of Trusted Signer server
|
||||
[default: 8180].
|
||||
|
||||
Sealing/Mining Options:
|
||||
--force-sealing Force the node to author new blocks as if it were
|
||||
always sealing/mining.
|
||||
@ -234,6 +239,8 @@ pub struct Args {
|
||||
pub flag_dapps_interface: String,
|
||||
pub flag_dapps_user: Option<String>,
|
||||
pub flag_dapps_pass: Option<String>,
|
||||
pub flag_signer: bool,
|
||||
pub flag_signer_port: u16,
|
||||
pub flag_force_sealing: bool,
|
||||
pub flag_author: String,
|
||||
pub flag_usd_per_tx: String,
|
||||
|
@ -17,14 +17,9 @@
|
||||
use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
use std::net::SocketAddr;
|
||||
use ethcore::client::Client;
|
||||
use ethsync::EthSync;
|
||||
use ethminer::{Miner, ExternalMiner};
|
||||
use util::RotatingLogger;
|
||||
use util::panics::PanicHandler;
|
||||
use util::keys::store::AccountService;
|
||||
use util::network_settings::NetworkSettings;
|
||||
use die::*;
|
||||
use rpc_apis;
|
||||
|
||||
#[cfg(feature = "dapps")]
|
||||
pub use ethcore_dapps::Server as WebappServer;
|
||||
@ -41,13 +36,7 @@ pub struct Configuration {
|
||||
|
||||
pub struct Dependencies {
|
||||
pub panic_handler: Arc<PanicHandler>,
|
||||
pub client: Arc<Client>,
|
||||
pub sync: Arc<EthSync>,
|
||||
pub secret_store: Arc<AccountService>,
|
||||
pub miner: Arc<Miner>,
|
||||
pub external_miner: Arc<ExternalMiner>,
|
||||
pub logger: Arc<RotatingLogger>,
|
||||
pub settings: Arc<NetworkSettings>,
|
||||
pub apis: Arc<rpc_apis::Dependencies>,
|
||||
}
|
||||
|
||||
pub fn new(configuration: Configuration, deps: Dependencies) -> Option<WebappServer> {
|
||||
@ -92,17 +81,10 @@ pub fn setup_dapps_server(
|
||||
url: &SocketAddr,
|
||||
auth: Option<(String, String)>
|
||||
) -> WebappServer {
|
||||
use ethcore_rpc::v1::*;
|
||||
use ethcore_dapps as dapps;
|
||||
|
||||
let server = dapps::ServerBuilder::new();
|
||||
server.add_delegate(Web3Client::new().to_delegate());
|
||||
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
|
||||
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
|
||||
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
|
||||
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate());
|
||||
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate());
|
||||
|
||||
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
||||
let start_result = match auth {
|
||||
None => {
|
||||
server.start_unsecure_http(url)
|
||||
|
@ -27,7 +27,6 @@ extern crate rustc_serialize;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcore;
|
||||
extern crate ethsync;
|
||||
extern crate ethminer;
|
||||
#[macro_use]
|
||||
extern crate log as rlog;
|
||||
extern crate env_logger;
|
||||
@ -50,6 +49,9 @@ extern crate ethcore_rpc;
|
||||
#[cfg(feature = "dapps")]
|
||||
extern crate ethcore_dapps;
|
||||
|
||||
#[cfg(feature = "ethcore-signer")]
|
||||
extern crate ethcore_signer;
|
||||
|
||||
#[macro_use]
|
||||
mod die;
|
||||
mod price_info;
|
||||
@ -63,6 +65,8 @@ mod io_handler;
|
||||
mod cli;
|
||||
mod configuration;
|
||||
mod migration;
|
||||
mod signer;
|
||||
mod rpc_apis;
|
||||
|
||||
use std::io::{Write, Read, BufReader, BufRead};
|
||||
use std::ops::Deref;
|
||||
@ -81,7 +85,7 @@ use ethcore::error::{Error, ImportError};
|
||||
use ethcore::service::ClientService;
|
||||
use ethcore::spec::Spec;
|
||||
use ethsync::EthSync;
|
||||
use ethminer::{Miner, MinerService, ExternalMiner};
|
||||
use ethcore::miner::{Miner, MinerService, ExternalMiner};
|
||||
use daemonize::Daemonize;
|
||||
use migration::migrate;
|
||||
use informant::Informant;
|
||||
@ -89,6 +93,7 @@ use informant::Informant;
|
||||
use die::*;
|
||||
use cli::print_version;
|
||||
use rpc::RpcServer;
|
||||
use signer::SignerServer;
|
||||
use dapps::WebappServer;
|
||||
use io_handler::ClientIoHandler;
|
||||
use configuration::Configuration;
|
||||
@ -168,14 +173,6 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
|
||||
// Secret Store
|
||||
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
|
||||
let miner = Miner::with_accounts(conf.args.flag_force_sealing, conf.spec(), account_service.clone());
|
||||
miner.set_author(conf.author());
|
||||
@ -184,14 +181,23 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
|
||||
miner.set_minimal_gas_price(conf.gas_price());
|
||||
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 network_settings = Arc::new(conf.network_settings());
|
||||
|
||||
// Sync
|
||||
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
|
||||
let sync = EthSync::register(service.network(), sync_config, client.clone());
|
||||
|
||||
let dependencies = Arc::new(rpc::Dependencies {
|
||||
panic_handler: panic_handler.clone(),
|
||||
let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies {
|
||||
signer_enabled: conf.args.flag_signer,
|
||||
signer_queue: Arc::new(rpc_apis::ConfirmationsQueue::default()),
|
||||
client: client.clone(),
|
||||
sync: sync.clone(),
|
||||
secret_store: account_service.clone(),
|
||||
@ -201,6 +207,11 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
|
||||
settings: network_settings.clone(),
|
||||
});
|
||||
|
||||
let dependencies = rpc::Dependencies {
|
||||
panic_handler: panic_handler.clone(),
|
||||
apis: deps_for_rpc_apis.clone(),
|
||||
};
|
||||
|
||||
// Setup http rpc
|
||||
let rpc_server = rpc::new_http(rpc::HttpConfiguration {
|
||||
enabled: network_settings.rpc_enabled,
|
||||
@ -222,13 +233,16 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
|
||||
pass: conf.args.flag_dapps_pass.clone(),
|
||||
}, dapps::Dependencies {
|
||||
panic_handler: panic_handler.clone(),
|
||||
client: client.clone(),
|
||||
sync: sync.clone(),
|
||||
secret_store: account_service.clone(),
|
||||
miner: miner.clone(),
|
||||
external_miner: external_miner.clone(),
|
||||
logger: logger.clone(),
|
||||
settings: network_settings.clone(),
|
||||
apis: deps_for_rpc_apis.clone(),
|
||||
});
|
||||
|
||||
// Set up a signer
|
||||
let signer_server = signer::start(signer::Configuration {
|
||||
enabled: deps_for_rpc_apis.signer_enabled,
|
||||
port: conf.args.flag_signer_port,
|
||||
}, signer::Dependencies {
|
||||
panic_handler: panic_handler.clone(),
|
||||
apis: deps_for_rpc_apis.clone(),
|
||||
});
|
||||
|
||||
// Register IO handler
|
||||
@ -241,7 +255,7 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
|
||||
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
||||
|
||||
// Handle exit
|
||||
wait_for_exit(panic_handler, rpc_server, dapps_server);
|
||||
wait_for_exit(panic_handler, rpc_server, dapps_server, signer_server);
|
||||
}
|
||||
|
||||
fn flush_stdout() {
|
||||
@ -277,7 +291,7 @@ fn execute_export(conf: Configuration) {
|
||||
|
||||
// Build client
|
||||
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));
|
||||
|
||||
panic_handler.forward_from(&service);
|
||||
@ -348,7 +362,7 @@ fn execute_import(conf: Configuration) {
|
||||
|
||||
// Build client
|
||||
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));
|
||||
|
||||
panic_handler.forward_from(&service);
|
||||
@ -453,7 +467,12 @@ fn execute_account_cli(conf: Configuration) {
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_for_exit(panic_handler: Arc<PanicHandler>, _rpc_server: Option<RpcServer>, _dapps_server: Option<WebappServer>) {
|
||||
fn wait_for_exit(
|
||||
panic_handler: Arc<PanicHandler>,
|
||||
_rpc_server: Option<RpcServer>,
|
||||
_dapps_server: Option<WebappServer>,
|
||||
_signer_server: Option<SignerServer>
|
||||
) {
|
||||
let exit = Arc::new(Condvar::new());
|
||||
|
||||
// Handle possible exits
|
||||
|
@ -73,7 +73,7 @@ fn version_file_path(path: &PathBuf) -> PathBuf {
|
||||
}
|
||||
|
||||
/// Reads current database version from the file at given path.
|
||||
/// If the file does not exist returns DEFAULT_VERSION.
|
||||
/// If the file does not exist returns `DEFAULT_VERSION`.
|
||||
fn current_version(path: &PathBuf) -> Result<u32, Error> {
|
||||
match File::open(version_file_path(path)) {
|
||||
Err(ref err) if err.kind() == ErrorKind::NotFound => Ok(DEFAULT_VERSION),
|
||||
|
@ -15,19 +15,13 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::net::SocketAddr;
|
||||
use ethcore::client::Client;
|
||||
use ethsync::EthSync;
|
||||
use ethminer::{Miner, ExternalMiner};
|
||||
use util::RotatingLogger;
|
||||
use util::panics::PanicHandler;
|
||||
use util::keys::store::AccountService;
|
||||
use util::network_settings::NetworkSettings;
|
||||
use die::*;
|
||||
use jsonipc;
|
||||
use rpc_apis;
|
||||
|
||||
#[cfg(feature = "rpc")]
|
||||
pub use ethcore_rpc::Server as RpcServer;
|
||||
@ -52,16 +46,10 @@ pub struct IpcConfiguration {
|
||||
|
||||
pub struct Dependencies {
|
||||
pub panic_handler: Arc<PanicHandler>,
|
||||
pub client: Arc<Client>,
|
||||
pub sync: Arc<EthSync>,
|
||||
pub secret_store: Arc<AccountService>,
|
||||
pub miner: Arc<Miner>,
|
||||
pub external_miner: Arc<ExternalMiner>,
|
||||
pub logger: Arc<RotatingLogger>,
|
||||
pub settings: Arc<NetworkSettings>,
|
||||
pub apis: Arc<rpc_apis::Dependencies>,
|
||||
}
|
||||
|
||||
pub fn new_http(conf: HttpConfiguration, deps: &Arc<Dependencies>) -> Option<RpcServer> {
|
||||
pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Option<RpcServer> {
|
||||
if !conf.enabled {
|
||||
return None;
|
||||
}
|
||||
@ -78,58 +66,23 @@ pub fn new_http(conf: HttpConfiguration, deps: &Arc<Dependencies>) -> Option<Rpc
|
||||
Some(setup_http_rpc_server(deps, &addr, conf.cors, apis))
|
||||
}
|
||||
|
||||
pub fn new_ipc(conf: IpcConfiguration, deps: &Arc<Dependencies>) -> Option<jsonipc::Server> {
|
||||
pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Option<jsonipc::Server> {
|
||||
if !conf.enabled { return None; }
|
||||
let apis = conf.apis.split(',').collect();
|
||||
Some(setup_ipc_rpc_server(deps, &conf.socket_addr, apis))
|
||||
}
|
||||
|
||||
fn setup_rpc_server(apis: Vec<&str>, deps: &Arc<Dependencies>) -> Server {
|
||||
use ethcore_rpc::v1::*;
|
||||
|
||||
fn setup_rpc_server(apis: Vec<&str>, deps: &Dependencies) -> Server {
|
||||
let apis = rpc_apis::from_str(apis);
|
||||
let server = Server::new();
|
||||
let mut modules = BTreeMap::new();
|
||||
for api in apis.into_iter() {
|
||||
match api {
|
||||
"web3" => {
|
||||
modules.insert("web3".to_owned(), "1.0".to_owned());
|
||||
server.add_delegate(Web3Client::new().to_delegate());
|
||||
},
|
||||
"net" => {
|
||||
modules.insert("net".to_owned(), "1.0".to_owned());
|
||||
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
|
||||
},
|
||||
"eth" => {
|
||||
modules.insert("eth".to_owned(), "1.0".to_owned());
|
||||
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
|
||||
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
|
||||
},
|
||||
"personal" => {
|
||||
modules.insert("personal".to_owned(), "1.0".to_owned());
|
||||
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate())
|
||||
},
|
||||
"ethcore" => {
|
||||
modules.insert("ethcore".to_owned(), "1.0".to_owned());
|
||||
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
|
||||
},
|
||||
"traces" => {
|
||||
modules.insert("traces".to_owned(), "1.0".to_owned());
|
||||
server.add_delegate(TracesClient::new(&deps.client).to_delegate())
|
||||
},
|
||||
_ => {
|
||||
die!("{}: Invalid API name to be enabled.", api);
|
||||
},
|
||||
}
|
||||
}
|
||||
server.add_delegate(RpcClient::new(modules).to_delegate());
|
||||
server
|
||||
rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::List(apis))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rpc"))]
|
||||
pub fn setup_http_rpc_server(
|
||||
_deps: Dependencies,
|
||||
_deps: &Dependencies,
|
||||
_url: &SocketAddr,
|
||||
_cors_domain: Option<String>,
|
||||
_cors_domain: Vec<String>,
|
||||
_apis: Vec<&str>,
|
||||
) -> ! {
|
||||
die!("Your Parity version has been compiled without JSON-RPC support.")
|
||||
@ -137,27 +90,31 @@ pub fn setup_http_rpc_server(
|
||||
|
||||
#[cfg(feature = "rpc")]
|
||||
pub fn setup_http_rpc_server(
|
||||
dependencies: &Arc<Dependencies>,
|
||||
dependencies: &Dependencies,
|
||||
url: &SocketAddr,
|
||||
cors_domains: Vec<String>,
|
||||
apis: Vec<&str>,
|
||||
) -> RpcServer {
|
||||
let server = setup_rpc_server(apis, dependencies);
|
||||
let start_result = server.start_http(url, cors_domains);
|
||||
let deps = dependencies.clone();
|
||||
let ph = dependencies.panic_handler.clone();
|
||||
match start_result {
|
||||
Err(RpcServerError::IoError(err)) => die_with_io_error("RPC", err),
|
||||
Err(e) => die!("RPC: {:?}", e),
|
||||
Ok(server) => {
|
||||
server.set_panic_handler(move || {
|
||||
deps.panic_handler.notify_all("Panic in RPC thread.".to_owned());
|
||||
ph.notify_all("Panic in RPC thread.".to_owned());
|
||||
});
|
||||
server
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_ipc_rpc_server(dependencies: &Arc<Dependencies>, addr: &str, apis: Vec<&str>) -> jsonipc::Server {
|
||||
#[cfg(not(feature = "rpc"))]
|
||||
pub fn setup_ipc_rpc_server(_dependencies: &Dependencies, _addr: &str, _apis: Vec<&str>) -> ! {
|
||||
die!("Your Parity version has been compiled without JSON-RPC support.")
|
||||
}
|
||||
#[cfg(feature = "rpc")]
|
||||
pub fn setup_ipc_rpc_server(dependencies: &Dependencies, addr: &str, apis: Vec<&str>) -> jsonipc::Server {
|
||||
let server = setup_rpc_server(apis, dependencies);
|
||||
match server.start_ipc(addr) {
|
||||
Err(jsonipc::Error::Io(io_error)) => die_with_io_error("RPC", io_error),
|
||||
|
168
parity/rpc_apis.rs
Normal file
168
parity/rpc_apis.rs
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use die::*;
|
||||
use ethsync::EthSync;
|
||||
use ethcore::miner::{Miner, ExternalMiner};
|
||||
use ethcore::client::Client;
|
||||
use util::RotatingLogger;
|
||||
use util::keys::store::AccountService;
|
||||
use util::network_settings::NetworkSettings;
|
||||
|
||||
#[cfg(feature="rpc")]
|
||||
pub use ethcore_rpc::ConfirmationsQueue;
|
||||
#[cfg(not(feature="rpc"))]
|
||||
#[derive(Default)]
|
||||
pub struct ConfirmationsQueue;
|
||||
|
||||
#[cfg(feature="rpc")]
|
||||
use ethcore_rpc::Extendable;
|
||||
|
||||
pub enum Api {
|
||||
Web3,
|
||||
Net,
|
||||
Eth,
|
||||
Personal,
|
||||
Ethcore,
|
||||
Traces,
|
||||
Rpc,
|
||||
}
|
||||
|
||||
pub enum ApiError {
|
||||
UnknownApi(String)
|
||||
}
|
||||
|
||||
pub enum ApiSet {
|
||||
SafeContext,
|
||||
UnsafeContext,
|
||||
List(Vec<Api>),
|
||||
}
|
||||
|
||||
impl FromStr for Api {
|
||||
type Err = ApiError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
use self::Api::*;
|
||||
|
||||
match s {
|
||||
"web3" => Ok(Web3),
|
||||
"net" => Ok(Net),
|
||||
"eth" => Ok(Eth),
|
||||
"personal" => Ok(Personal),
|
||||
"ethcore" => Ok(Ethcore),
|
||||
"traces" => Ok(Traces),
|
||||
"rpc" => Ok(Rpc),
|
||||
e => Err(ApiError::UnknownApi(e.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dependencies {
|
||||
pub signer_enabled: bool,
|
||||
pub signer_queue: Arc<ConfirmationsQueue>,
|
||||
pub client: Arc<Client>,
|
||||
pub sync: Arc<EthSync>,
|
||||
pub secret_store: Arc<AccountService>,
|
||||
pub miner: Arc<Miner>,
|
||||
pub external_miner: Arc<ExternalMiner>,
|
||||
pub logger: Arc<RotatingLogger>,
|
||||
pub settings: Arc<NetworkSettings>,
|
||||
}
|
||||
|
||||
fn to_modules(apis: &[Api]) -> BTreeMap<String, String> {
|
||||
let mut modules = BTreeMap::new();
|
||||
for api in apis {
|
||||
let (name, version) = match *api {
|
||||
Api::Web3 => ("web3", "1.0"),
|
||||
Api::Net => ("net", "1.0"),
|
||||
Api::Eth => ("eth", "1.0"),
|
||||
Api::Personal => ("personal", "1.0"),
|
||||
Api::Ethcore => ("ethcore", "1.0"),
|
||||
Api::Traces => ("traces", "1.0"),
|
||||
Api::Rpc => ("rpc", "1.0"),
|
||||
};
|
||||
modules.insert(name.into(), version.into());
|
||||
}
|
||||
modules
|
||||
}
|
||||
|
||||
pub fn from_str(apis: Vec<&str>) -> Vec<Api> {
|
||||
apis.into_iter()
|
||||
.map(Api::from_str)
|
||||
.collect::<Result<Vec<Api>, ApiError>>()
|
||||
.unwrap_or_else(|e| match e {
|
||||
ApiError::UnknownApi(s) => die!("Unknown RPC API specified: {}", s),
|
||||
})
|
||||
}
|
||||
|
||||
fn list_apis(apis: ApiSet, signer_enabled: bool) -> Vec<Api> {
|
||||
match apis {
|
||||
ApiSet::List(apis) => apis,
|
||||
ApiSet::UnsafeContext if signer_enabled => {
|
||||
vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc]
|
||||
}
|
||||
_ => {
|
||||
vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Ethcore, Api::Traces, Api::Rpc]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet) -> T {
|
||||
use ethcore_rpc::v1::*;
|
||||
|
||||
let apis = list_apis(apis, deps.signer_enabled);
|
||||
for api in &apis {
|
||||
match *api {
|
||||
Api::Web3 => {
|
||||
server.add_delegate(Web3Client::new().to_delegate());
|
||||
},
|
||||
Api::Net => {
|
||||
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
|
||||
},
|
||||
Api::Eth => {
|
||||
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate());
|
||||
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
|
||||
|
||||
if deps.signer_enabled {
|
||||
server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue).to_delegate());
|
||||
} else {
|
||||
server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate());
|
||||
}
|
||||
},
|
||||
Api::Personal => {
|
||||
server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate());
|
||||
if deps.signer_enabled {
|
||||
server.add_delegate(SignerClient::new(&deps.secret_store, &deps.client, &deps.miner, &deps.signer_queue).to_delegate());
|
||||
}
|
||||
},
|
||||
Api::Ethcore => {
|
||||
server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
|
||||
},
|
||||
Api::Traces => {
|
||||
server.add_delegate(TracesClient::new(&deps.client).to_delegate())
|
||||
},
|
||||
Api::Rpc => {
|
||||
let modules = to_modules(&apis);
|
||||
server.add_delegate(RpcClient::new(modules).to_delegate());
|
||||
}
|
||||
}
|
||||
}
|
||||
server
|
||||
}
|
@ -27,6 +27,8 @@ pub fn setup_log(init: &Option<String>) -> Arc<RotatingLogger> {
|
||||
|
||||
let mut levels = String::new();
|
||||
let mut builder = LogBuilder::new();
|
||||
// Disable ws info logging by default.
|
||||
builder.filter(Some("ws"), LogLevelFilter::Warn);
|
||||
builder.filter(None, LogLevelFilter::Info);
|
||||
|
||||
if env::var("RUST_LOG").is_ok() {
|
||||
|
74
parity/signer.rs
Normal file
74
parity/signer.rs
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use util::panics::{PanicHandler, ForwardPanic};
|
||||
use die::*;
|
||||
use rpc_apis;
|
||||
|
||||
#[cfg(feature = "ethcore-signer")]
|
||||
use ethcore_signer as signer;
|
||||
#[cfg(feature = "ethcore-signer")]
|
||||
pub use ethcore_signer::Server as SignerServer;
|
||||
#[cfg(not(feature = "ethcore-signer"))]
|
||||
pub struct SignerServer;
|
||||
|
||||
pub struct Configuration {
|
||||
pub enabled: bool,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
pub struct Dependencies {
|
||||
pub panic_handler: Arc<PanicHandler>,
|
||||
pub apis: Arc<rpc_apis::Dependencies>,
|
||||
}
|
||||
|
||||
pub fn start(conf: Configuration, deps: Dependencies) -> Option<SignerServer> {
|
||||
if !conf.enabled {
|
||||
None
|
||||
} else {
|
||||
Some(do_start(conf, deps))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ethcore-signer")]
|
||||
fn do_start(conf: Configuration, deps: Dependencies) -> SignerServer {
|
||||
let addr = format!("127.0.0.1:{}", conf.port).parse().unwrap_or_else(|_| {
|
||||
die!("Invalid port specified: {}", conf.port)
|
||||
});
|
||||
|
||||
let start_result = {
|
||||
let server = signer::ServerBuilder::new();
|
||||
let server = rpc_apis::setup_rpc(server, deps.apis, rpc_apis::ApiSet::SafeContext);
|
||||
server.start(addr)
|
||||
};
|
||||
|
||||
match start_result {
|
||||
Err(signer::ServerError::IoError(err)) => die_with_io_error("Trusted Signer", err),
|
||||
Err(e) => die!("Trusted Signer: {:?}", e),
|
||||
Ok(server) => {
|
||||
deps.panic_handler.forward_from(&server);
|
||||
server
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ethcore-signer"))]
|
||||
fn do_start(conf: Configuration) -> ! {
|
||||
die!("Your Parity version has been compiled without Trusted Signer support.")
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,8 @@ ethcore-util = { path = "../util" }
|
||||
ethcore = { path = "../ethcore" }
|
||||
ethash = { path = "../ethash" }
|
||||
ethsync = { path = "../sync" }
|
||||
ethminer = { path = "../miner" }
|
||||
ethjson = { path = "../json" }
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
rustc-serialize = "0.3"
|
||||
transient-hashmap = "0.1"
|
||||
serde_macros = { version = "0.7.0", optional = true }
|
||||
@ -32,4 +33,4 @@ syntex = "^0.32.0"
|
||||
[features]
|
||||
default = ["serde_codegen"]
|
||||
nightly = ["serde_macros"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethminer/dev"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev"]
|
||||
|
@ -26,25 +26,44 @@ extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_http_server;
|
||||
#[macro_use]
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcore;
|
||||
extern crate ethsync;
|
||||
extern crate ethminer;
|
||||
extern crate transient_hashmap;
|
||||
extern crate json_ipc_server as ipc;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate ethjson;
|
||||
#[cfg(test)]
|
||||
extern crate ethcore_devtools as devtools;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::net::SocketAddr;
|
||||
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||
|
||||
pub use jsonrpc_http_server::{Server, RpcServerError};
|
||||
pub mod v1;
|
||||
pub use v1::{SigningQueue, ConfirmationsQueue};
|
||||
|
||||
/// An object that can be extended with `IoDelegates`
|
||||
pub trait Extendable {
|
||||
/// Add `Delegate` to this object.
|
||||
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>);
|
||||
}
|
||||
|
||||
/// Http server.
|
||||
pub struct RpcServer {
|
||||
handler: Arc<jsonrpc_core::io::IoHandler>,
|
||||
}
|
||||
|
||||
impl Extendable for RpcServer {
|
||||
/// Add io delegate.
|
||||
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
impl RpcServer {
|
||||
/// Construct new http server object.
|
||||
pub fn new() -> RpcServer {
|
||||
@ -53,11 +72,6 @@ impl RpcServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add io delegate.
|
||||
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
|
||||
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
||||
pub fn start_http(&self, addr: &SocketAddr, cors_domains: Vec<String>) -> Result<Server, RpcServerError> {
|
||||
let cors_domains = cors_domains.into_iter()
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
mod poll_manager;
|
||||
mod poll_filter;
|
||||
mod signing_queue;
|
||||
|
||||
pub use self::poll_manager::PollManager;
|
||||
pub use self::poll_filter::PollFilter;
|
||||
pub use self::signing_queue::{ConfirmationsQueue, SigningQueue};
|
||||
|
108
rpc/src/v1/helpers/signing_queue.rs
Normal file
108
rpc/src/v1/helpers/signing_queue.rs
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use v1::types::{TransactionRequest, TransactionConfirmation};
|
||||
use util::U256;
|
||||
|
||||
/// A queue of transactions awaiting to be confirmed and signed.
|
||||
pub trait SigningQueue: Send + Sync {
|
||||
/// Add new request to the queue.
|
||||
fn add_request(&self, transaction: TransactionRequest) -> U256;
|
||||
|
||||
/// Remove request from the queue.
|
||||
fn remove_request(&self, id: U256) -> Option<TransactionConfirmation>;
|
||||
|
||||
/// Return copy of all the requests in the queue.
|
||||
fn requests(&self) -> Vec<TransactionConfirmation>;
|
||||
}
|
||||
|
||||
/// Queue for all unconfirmed transactions.
|
||||
pub struct ConfirmationsQueue {
|
||||
id: Mutex<U256>,
|
||||
queue: Mutex<HashMap<U256, TransactionConfirmation>>,
|
||||
}
|
||||
|
||||
impl Default for ConfirmationsQueue {
|
||||
fn default() -> Self {
|
||||
ConfirmationsQueue {
|
||||
id: Mutex::new(U256::from(0)),
|
||||
queue: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningQueue for ConfirmationsQueue {
|
||||
fn add_request(&self, transaction: TransactionRequest) -> U256 {
|
||||
// Increment id
|
||||
let id = {
|
||||
let mut last_id = self.id.lock().unwrap();
|
||||
*last_id = *last_id + U256::from(1);
|
||||
*last_id
|
||||
};
|
||||
let mut queue = self.queue.lock().unwrap();
|
||||
queue.insert(id, TransactionConfirmation {
|
||||
id: id,
|
||||
transaction: transaction,
|
||||
});
|
||||
id
|
||||
}
|
||||
|
||||
fn remove_request(&self, id: U256) -> Option<TransactionConfirmation> {
|
||||
self.queue.lock().unwrap().remove(&id)
|
||||
}
|
||||
|
||||
fn requests(&self) -> Vec<TransactionConfirmation> {
|
||||
let queue = self.queue.lock().unwrap();
|
||||
queue.values().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use util::hash::Address;
|
||||
use util::numbers::U256;
|
||||
use v1::types::TransactionRequest;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_work_for_hashset() {
|
||||
// given
|
||||
let queue = ConfirmationsQueue::default();
|
||||
|
||||
let request = TransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from(2)),
|
||||
gas_price: None,
|
||||
gas: None,
|
||||
value: Some(U256::from(10_000_000)),
|
||||
data: None,
|
||||
nonce: None,
|
||||
};
|
||||
|
||||
// when
|
||||
queue.add_request(request.clone());
|
||||
let all = queue.requests();
|
||||
|
||||
// then
|
||||
assert_eq!(all.len(), 1);
|
||||
let el = all.get(0).unwrap();
|
||||
assert_eq!(el.id, U256::from(1));
|
||||
assert_eq!(el.transaction, request);
|
||||
}
|
||||
}
|
@ -18,16 +18,16 @@
|
||||
|
||||
extern crate ethash;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::{Arc, Weak, Mutex};
|
||||
use std::ops::Deref;
|
||||
use ethsync::{SyncProvider, SyncState};
|
||||
use ethminer::{MinerService, ExternalMinerService};
|
||||
use ethcore::miner::{MinerService, ExternalMinerService};
|
||||
use jsonrpc_core::*;
|
||||
use util::numbers::*;
|
||||
use util::sha3::*;
|
||||
use util::rlp::{encode, decode, UntrustedRlp, View};
|
||||
use ethcore::client::{BlockChainClient, BlockID, TransactionID, UncleID};
|
||||
use util::keys::store::AccountProvider;
|
||||
use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID};
|
||||
use ethcore::block::IsBlock;
|
||||
use ethcore::views::*;
|
||||
use ethcore::ethereum::Ethash;
|
||||
@ -35,16 +35,14 @@ use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Act
|
||||
use ethcore::log_entry::LogEntry;
|
||||
use ethcore::filter::Filter as EthcoreFilter;
|
||||
use self::ethash::SeedHashCompute;
|
||||
use v1::traits::{Eth, EthFilter};
|
||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
|
||||
use v1::helpers::{PollFilter, PollManager};
|
||||
use v1::impls::{dispatch_transaction, sign_and_dispatch};
|
||||
use util::keys::store::AccountProvider;
|
||||
use v1::traits::Eth;
|
||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
|
||||
use v1::impls::dispatch_transaction;
|
||||
use serde;
|
||||
|
||||
/// Eth rpc implementation.
|
||||
pub struct EthClient<C, S, A, M, EM> where
|
||||
C: BlockChainClient,
|
||||
C: MiningBlockChainClient,
|
||||
S: SyncProvider,
|
||||
A: AccountProvider,
|
||||
M: MinerService,
|
||||
@ -59,7 +57,7 @@ pub struct EthClient<C, S, A, M, EM> where
|
||||
}
|
||||
|
||||
impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> where
|
||||
C: BlockChainClient,
|
||||
C: MiningBlockChainClient,
|
||||
S: SyncProvider,
|
||||
A: AccountProvider,
|
||||
M: MinerService,
|
||||
@ -170,6 +168,25 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
||||
let receipts = miner.pending_receipts();
|
||||
|
||||
let pending_logs = receipts.into_iter()
|
||||
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
|
||||
.collect::<Vec<(H256, LogEntry)>>();
|
||||
|
||||
let result = pending_logs.into_iter()
|
||||
.filter(|pair| filter.matches(&pair.1))
|
||||
.map(|pair| {
|
||||
let mut log = Log::from(pair.1);
|
||||
log.transaction_hash = Some(pair.0);
|
||||
log
|
||||
})
|
||||
.collect();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
|
||||
|
||||
fn params_len(params: &Params) -> usize {
|
||||
@ -193,25 +210,6 @@ fn from_params_default_third<F1, F2>(params: Params) -> Result<(F1, F2, BlockNum
|
||||
}
|
||||
}
|
||||
|
||||
fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
||||
let receipts = miner.pending_receipts();
|
||||
|
||||
let pending_logs = receipts.into_iter()
|
||||
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
|
||||
.collect::<Vec<(H256, LogEntry)>>();
|
||||
|
||||
let result = pending_logs.into_iter()
|
||||
.filter(|pair| filter.matches(&pair.1))
|
||||
.map(|pair| {
|
||||
let mut log = Log::from(pair.1);
|
||||
log.transaction_hash = Some(pair.0);
|
||||
log
|
||||
})
|
||||
.collect();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// must be in range [-32099, -32000]
|
||||
const UNSUPPORTED_REQUEST_CODE: i64 = -32000;
|
||||
|
||||
@ -224,7 +222,7 @@ fn make_unsupported_err() -> Error {
|
||||
}
|
||||
|
||||
impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
||||
C: BlockChainClient + 'static,
|
||||
C: MiningBlockChainClient + 'static,
|
||||
S: SyncProvider + 'static,
|
||||
A: AccountProvider + 'static,
|
||||
M: MinerService + 'static,
|
||||
@ -244,12 +242,14 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
||||
let res = match status.state {
|
||||
SyncState::Idle => SyncStatus::None,
|
||||
SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead => {
|
||||
let current_block = U256::from(take_weak!(self.client).chain_info().best_block_number);
|
||||
|
||||
let info = SyncInfo {
|
||||
starting_block: U256::from(status.start_block_number),
|
||||
current_block: U256::from(take_weak!(self.client).chain_info().best_block_number),
|
||||
current_block: current_block,
|
||||
highest_block: U256::from(status.highest_block_number.unwrap_or(status.start_block_number))
|
||||
};
|
||||
match info.highest_block > info.starting_block + U256::from(6) {
|
||||
match info.highest_block > info.current_block + U256::from(6) {
|
||||
true => SyncStatus::Info(info),
|
||||
false => SyncStatus::None,
|
||||
}
|
||||
@ -494,17 +494,6 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
||||
})
|
||||
}
|
||||
|
||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(TransactionRequest, )>(params)
|
||||
.and_then(|(request, )| {
|
||||
let accounts = take_weak!(self.accounts);
|
||||
match accounts.account_secret(&request.from) {
|
||||
Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret),
|
||||
Err(_) => to_value(&H256::zero())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Bytes, )>(params)
|
||||
.and_then(|(raw_transaction, )| {
|
||||
@ -522,8 +511,8 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
||||
.and_then(|(request, block_number,)| {
|
||||
let signed = try!(self.sign_call(request));
|
||||
let r = match block_number {
|
||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed),
|
||||
BlockNumber::Latest => take_weak!(self.client).call(&signed),
|
||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false),
|
||||
BlockNumber::Latest => take_weak!(self.client).call(&signed, false),
|
||||
_ => panic!("{:?}", block_number),
|
||||
};
|
||||
to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![])))
|
||||
@ -535,194 +524,23 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
||||
.and_then(|(request, block_number,)| {
|
||||
let signed = try!(self.sign_call(request));
|
||||
let r = match block_number {
|
||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed),
|
||||
BlockNumber::Latest => take_weak!(self.client).call(&signed),
|
||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false),
|
||||
BlockNumber::Latest => take_weak!(self.client).call(&signed, false),
|
||||
_ => return Err(Error::invalid_params()),
|
||||
};
|
||||
to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Eth filter rpc implementation.
|
||||
pub struct EthFilterClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
fn compile_lll(&self, _: Params) -> Result<Value, Error> {
|
||||
rpc_unimplemented!()
|
||||
}
|
||||
|
||||
client: Weak<C>,
|
||||
miner: Weak<M>,
|
||||
polls: Mutex<PollManager<PollFilter>>,
|
||||
}
|
||||
fn compile_serpent(&self, _: Params) -> Result<Value, Error> {
|
||||
rpc_unimplemented!()
|
||||
}
|
||||
|
||||
impl<C, M> EthFilterClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
|
||||
/// Creates new Eth filter client.
|
||||
pub fn new(client: &Arc<C>, miner: &Arc<M>) -> Self {
|
||||
EthFilterClient {
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
polls: Mutex::new(PollManager::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||
C: BlockChainClient + 'static,
|
||||
M: MinerService + 'static {
|
||||
|
||||
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Filter,)>(params)
|
||||
.and_then(|(filter,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let block_number = take_weak!(self.client).chain_info().best_block_number;
|
||||
let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter));
|
||||
to_value(&U256::from(id))
|
||||
})
|
||||
}
|
||||
|
||||
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
match params {
|
||||
Params::None => {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
|
||||
to_value(&U256::from(id))
|
||||
},
|
||||
_ => Err(Error::invalid_params())
|
||||
}
|
||||
}
|
||||
|
||||
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
match params {
|
||||
Params::None => {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
||||
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
||||
|
||||
to_value(&U256::from(id))
|
||||
},
|
||||
_ => Err(Error::invalid_params())
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
|
||||
let client = take_weak!(self.client);
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
match polls.poll_mut(&index.value()) {
|
||||
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||
Some(filter) => match *filter {
|
||||
PollFilter::Block(ref mut block_number) => {
|
||||
// + 1, cause we want to return hashes including current block hash.
|
||||
let current_number = client.chain_info().best_block_number + 1;
|
||||
let hashes = (*block_number..current_number).into_iter()
|
||||
.map(BlockID::Number)
|
||||
.filter_map(|id| client.block_hash(id))
|
||||
.collect::<Vec<H256>>();
|
||||
|
||||
*block_number = current_number;
|
||||
|
||||
to_value(&hashes)
|
||||
},
|
||||
PollFilter::PendingTransaction(ref mut previous_hashes) => {
|
||||
// get hashes of pending transactions
|
||||
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
|
||||
|
||||
let new_hashes =
|
||||
{
|
||||
let previous_hashes_set = previous_hashes.iter().collect::<HashSet<_>>();
|
||||
|
||||
// find all new hashes
|
||||
current_hashes
|
||||
.iter()
|
||||
.filter(|hash| !previous_hashes_set.contains(hash))
|
||||
.cloned()
|
||||
.collect::<Vec<H256>>()
|
||||
};
|
||||
|
||||
// save all hashes of pending transactions
|
||||
*previous_hashes = current_hashes;
|
||||
|
||||
// return new hashes
|
||||
to_value(&new_hashes)
|
||||
},
|
||||
PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => {
|
||||
// retrive the current block number
|
||||
let current_number = client.chain_info().best_block_number;
|
||||
|
||||
// check if we need to check pending hashes
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
|
||||
// build appropriate filter
|
||||
let mut filter: EthcoreFilter = filter.clone().into();
|
||||
filter.from_block = BlockID::Number(*block_number);
|
||||
filter.to_block = BlockID::Latest;
|
||||
|
||||
// retrieve logs in range from_block..min(BlockID::Latest..to_block)
|
||||
let mut logs = client.logs(filter.clone())
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
// additionally retrieve pending logs
|
||||
if include_pending {
|
||||
let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter);
|
||||
|
||||
// remove logs about which client was already notified about
|
||||
let new_pending_logs: Vec<_> = pending_logs.iter()
|
||||
.filter(|p| !previous_logs.contains(p))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// save all logs retrieved by client
|
||||
*previous_logs = pending_logs.into_iter().collect();
|
||||
|
||||
// append logs array with new pending logs
|
||||
logs.extend(new_pending_logs);
|
||||
}
|
||||
|
||||
// save current block number as next from block number
|
||||
*block_number = current_number;
|
||||
|
||||
to_value(&logs)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
match polls.poll(&index.value()) {
|
||||
Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => {
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
let filter: EthcoreFilter = filter.clone().into();
|
||||
let mut logs = take_weak!(self.client).logs(filter.clone())
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
if include_pending {
|
||||
logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter));
|
||||
}
|
||||
|
||||
to_value(&logs)
|
||||
},
|
||||
// just empty array
|
||||
_ => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
self.polls.lock().unwrap().remove_poll(&index.value());
|
||||
to_value(&true)
|
||||
})
|
||||
fn compile_solidity(&self, _: Params) -> Result<Value, Error> {
|
||||
rpc_unimplemented!()
|
||||
}
|
||||
}
|
||||
|
214
rpc/src/v1/impls/eth_filter.rs
Normal file
214
rpc/src/v1/impls/eth_filter.rs
Normal file
@ -0,0 +1,214 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Eth Filter RPC implementation
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Weak, Mutex};
|
||||
use std::collections::HashSet;
|
||||
use jsonrpc_core::*;
|
||||
use util::numbers::*;
|
||||
use ethcore::miner::MinerService;
|
||||
use ethcore::filter::Filter as EthcoreFilter;
|
||||
use ethcore::client::{BlockChainClient, BlockID};
|
||||
use v1::traits::EthFilter;
|
||||
use v1::types::{BlockNumber, Index, Filter, Log};
|
||||
use v1::helpers::{PollFilter, PollManager};
|
||||
use v1::impls::eth::pending_logs;
|
||||
|
||||
|
||||
/// Eth filter rpc implementation.
|
||||
pub struct EthFilterClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
|
||||
client: Weak<C>,
|
||||
miner: Weak<M>,
|
||||
polls: Mutex<PollManager<PollFilter>>,
|
||||
}
|
||||
|
||||
impl<C, M> EthFilterClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
|
||||
/// Creates new Eth filter client.
|
||||
pub fn new(client: &Arc<C>, miner: &Arc<M>) -> Self {
|
||||
EthFilterClient {
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
polls: Mutex::new(PollManager::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||
C: BlockChainClient + 'static,
|
||||
M: MinerService + 'static {
|
||||
|
||||
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Filter,)>(params)
|
||||
.and_then(|(filter,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let block_number = take_weak!(self.client).chain_info().best_block_number;
|
||||
let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter));
|
||||
to_value(&U256::from(id))
|
||||
})
|
||||
}
|
||||
|
||||
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
match params {
|
||||
Params::None => {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
|
||||
to_value(&U256::from(id))
|
||||
},
|
||||
_ => Err(Error::invalid_params())
|
||||
}
|
||||
}
|
||||
|
||||
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
match params {
|
||||
Params::None => {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
||||
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
||||
|
||||
to_value(&U256::from(id))
|
||||
},
|
||||
_ => Err(Error::invalid_params())
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
|
||||
let client = take_weak!(self.client);
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
match polls.poll_mut(&index.value()) {
|
||||
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||
Some(filter) => match *filter {
|
||||
PollFilter::Block(ref mut block_number) => {
|
||||
// + 1, cause we want to return hashes including current block hash.
|
||||
let current_number = client.chain_info().best_block_number + 1;
|
||||
let hashes = (*block_number..current_number).into_iter()
|
||||
.map(BlockID::Number)
|
||||
.filter_map(|id| client.block_hash(id))
|
||||
.collect::<Vec<H256>>();
|
||||
|
||||
*block_number = current_number;
|
||||
|
||||
to_value(&hashes)
|
||||
},
|
||||
PollFilter::PendingTransaction(ref mut previous_hashes) => {
|
||||
// get hashes of pending transactions
|
||||
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
|
||||
|
||||
let new_hashes =
|
||||
{
|
||||
let previous_hashes_set = previous_hashes.iter().collect::<HashSet<_>>();
|
||||
|
||||
// find all new hashes
|
||||
current_hashes
|
||||
.iter()
|
||||
.filter(|hash| !previous_hashes_set.contains(hash))
|
||||
.cloned()
|
||||
.collect::<Vec<H256>>()
|
||||
};
|
||||
|
||||
// save all hashes of pending transactions
|
||||
*previous_hashes = current_hashes;
|
||||
|
||||
// return new hashes
|
||||
to_value(&new_hashes)
|
||||
},
|
||||
PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => {
|
||||
// retrive the current block number
|
||||
let current_number = client.chain_info().best_block_number;
|
||||
|
||||
// check if we need to check pending hashes
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
|
||||
// build appropriate filter
|
||||
let mut filter: EthcoreFilter = filter.clone().into();
|
||||
filter.from_block = BlockID::Number(*block_number);
|
||||
filter.to_block = BlockID::Latest;
|
||||
|
||||
// retrieve logs in range from_block..min(BlockID::Latest..to_block)
|
||||
let mut logs = client.logs(filter.clone())
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
// additionally retrieve pending logs
|
||||
if include_pending {
|
||||
let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter);
|
||||
|
||||
// remove logs about which client was already notified about
|
||||
let new_pending_logs: Vec<_> = pending_logs.iter()
|
||||
.filter(|p| !previous_logs.contains(p))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// save all logs retrieved by client
|
||||
*previous_logs = pending_logs.into_iter().collect();
|
||||
|
||||
// append logs array with new pending logs
|
||||
logs.extend(new_pending_logs);
|
||||
}
|
||||
|
||||
// save current block number as next from block number
|
||||
*block_number = current_number;
|
||||
|
||||
to_value(&logs)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
match polls.poll(&index.value()) {
|
||||
Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => {
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
let filter: EthcoreFilter = filter.clone().into();
|
||||
let mut logs = take_weak!(self.client).logs(filter.clone())
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
if include_pending {
|
||||
logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter));
|
||||
}
|
||||
|
||||
to_value(&logs)
|
||||
},
|
||||
// just empty array
|
||||
_ => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
self.polls.lock().unwrap().remove_poll(&index.value());
|
||||
to_value(&true)
|
||||
})
|
||||
}
|
||||
}
|
111
rpc/src/v1/impls/eth_signing.rs
Normal file
111
rpc/src/v1/impls/eth_signing.rs
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Eth Signing RPC implementation.
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
use jsonrpc_core::*;
|
||||
use ethcore::miner::MinerService;
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use util::numbers::*;
|
||||
use util::keys::store::AccountProvider;
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
use v1::traits::EthSigning;
|
||||
use v1::types::TransactionRequest;
|
||||
use v1::impls::sign_and_dispatch;
|
||||
|
||||
|
||||
/// Implementation of functions that require signing when no trusted signer is used.
|
||||
pub struct EthSigningQueueClient {
|
||||
queue: Weak<ConfirmationsQueue>,
|
||||
}
|
||||
|
||||
impl EthSigningQueueClient {
|
||||
/// Creates a new signing queue client given shared signing queue.
|
||||
pub fn new(queue: &Arc<ConfirmationsQueue>) -> Self {
|
||||
EthSigningQueueClient {
|
||||
queue: Arc::downgrade(queue),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EthSigning for EthSigningQueueClient {
|
||||
|
||||
fn sign(&self, _params: Params) -> Result<Value, Error> {
|
||||
// TODO [ToDr] Implement sign when rest of the signing queue is ready.
|
||||
rpc_unimplemented!()
|
||||
}
|
||||
|
||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(TransactionRequest, )>(params)
|
||||
.and_then(|(request, )| {
|
||||
let queue = take_weak!(self.queue);
|
||||
queue.add_request(request);
|
||||
// TODO [ToDr] Block and wait for confirmation?
|
||||
to_value(&H256::zero())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of functions that require signing when no trusted signer is used.
|
||||
pub struct EthSigningUnsafeClient<C, A, M> where
|
||||
C: MiningBlockChainClient,
|
||||
A: AccountProvider,
|
||||
M: MinerService {
|
||||
client: Weak<C>,
|
||||
accounts: Weak<A>,
|
||||
miner: Weak<M>,
|
||||
}
|
||||
|
||||
impl<C, A, M> EthSigningUnsafeClient<C, A, M> where
|
||||
C: MiningBlockChainClient,
|
||||
A: AccountProvider,
|
||||
M: MinerService {
|
||||
|
||||
/// Creates new EthClient.
|
||||
pub fn new(client: &Arc<C>, accounts: &Arc<A>, miner: &Arc<M>)
|
||||
-> Self {
|
||||
EthSigningUnsafeClient {
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
accounts: Arc::downgrade(accounts),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, A, M> EthSigning for EthSigningUnsafeClient<C, A, M> where
|
||||
C: MiningBlockChainClient + 'static,
|
||||
A: AccountProvider + 'static,
|
||||
M: MinerService + 'static {
|
||||
|
||||
fn sign(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Address, H256)>(params).and_then(|(addr, msg)| {
|
||||
to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero()))
|
||||
})
|
||||
}
|
||||
|
||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(TransactionRequest, )>(params)
|
||||
.and_then(|(request, )| {
|
||||
let accounts = take_weak!(self.accounts);
|
||||
match accounts.account_secret(&request.from) {
|
||||
Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret),
|
||||
Err(_) => to_value(&H256::zero())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
@ -15,37 +15,107 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethcore-specific rpc implementation.
|
||||
use util::{U256, Address, RotatingLogger};
|
||||
use util::{U256, Address, RotatingLogger, FixedHash, Uint};
|
||||
use util::network_settings::NetworkSettings;
|
||||
use util::misc::version_data;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::ops::Deref;
|
||||
use std::collections::BTreeMap;
|
||||
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::types::Bytes;
|
||||
use v1::types::{Bytes, CallRequest};
|
||||
|
||||
/// Ethcore implementation.
|
||||
pub struct EthcoreClient<M>
|
||||
where M: MinerService {
|
||||
pub struct EthcoreClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
|
||||
client: Weak<C>,
|
||||
miner: Weak<M>,
|
||||
logger: Arc<RotatingLogger>,
|
||||
settings: Arc<NetworkSettings>,
|
||||
}
|
||||
|
||||
impl<M> EthcoreClient<M> where M: MinerService {
|
||||
impl<C, M> EthcoreClient<C, M> where C: BlockChainClient, M: MinerService {
|
||||
/// 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 {
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
logger: logger,
|
||||
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> {
|
||||
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();
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -25,26 +25,36 @@ macro_rules! take_weak {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! rpc_unimplemented {
|
||||
() => (Err(Error::internal_error()))
|
||||
}
|
||||
|
||||
mod web3;
|
||||
mod eth;
|
||||
mod eth_filter;
|
||||
mod eth_signing;
|
||||
mod net;
|
||||
mod personal;
|
||||
mod personal_signer;
|
||||
mod ethcore;
|
||||
mod traces;
|
||||
mod rpc;
|
||||
|
||||
pub use self::web3::Web3Client;
|
||||
pub use self::eth::{EthClient, EthFilterClient};
|
||||
pub use self::eth::EthClient;
|
||||
pub use self::eth_filter::EthFilterClient;
|
||||
pub use self::eth_signing::{EthSigningUnsafeClient, EthSigningQueueClient};
|
||||
pub use self::net::NetClient;
|
||||
pub use self::personal::PersonalClient;
|
||||
pub use self::personal_signer::SignerClient;
|
||||
pub use self::ethcore::EthcoreClient;
|
||||
pub use self::traces::TracesClient;
|
||||
pub use self::rpc::RpcClient;
|
||||
|
||||
use v1::types::TransactionRequest;
|
||||
use std::sync::Weak;
|
||||
use ethminer::{AccountDetails, MinerService};
|
||||
use ethcore::client::BlockChainClient;
|
||||
use ethcore::miner::{AccountDetails, MinerService};
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use ethcore::transaction::{Action, SignedTransaction, Transaction};
|
||||
use util::numbers::*;
|
||||
use util::rlp::encode;
|
||||
@ -52,29 +62,21 @@ use util::bytes::ToPretty;
|
||||
use jsonrpc_core::{Error, to_value, Value};
|
||||
|
||||
fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error>
|
||||
where C: BlockChainClient, M: MinerService {
|
||||
where C: MiningBlockChainClient, M: MinerService {
|
||||
let hash = signed_transaction.hash();
|
||||
|
||||
let import = {
|
||||
miner.import_own_transaction(client, signed_transaction, |a: &Address| {
|
||||
AccountDetails {
|
||||
nonce: client.latest_nonce(&a),
|
||||
balance: client.latest_balance(&a),
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
match import {
|
||||
Ok(_) => to_value(&hash),
|
||||
Err(e) => {
|
||||
warn!("Error sending transaction: {:?}", e);
|
||||
to_value(&H256::zero())
|
||||
let import = miner.import_own_transaction(client, signed_transaction, |a: &Address| {
|
||||
AccountDetails {
|
||||
nonce: client.latest_nonce(&a),
|
||||
balance: client.latest_balance(&a),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
to_value(&import.map(|_| hash).unwrap_or(H256::zero()))
|
||||
}
|
||||
|
||||
fn sign_and_dispatch<C, M>(client: &Weak<C>, miner: &Weak<M>, request: TransactionRequest, secret: H256) -> Result<Value, Error>
|
||||
where C: BlockChainClient, M: MinerService {
|
||||
where C: MiningBlockChainClient, M: MinerService {
|
||||
let client = take_weak!(client);
|
||||
let miner = take_weak!(miner);
|
||||
|
||||
|
@ -20,21 +20,21 @@ use jsonrpc_core::*;
|
||||
use v1::traits::Personal;
|
||||
use v1::types::TransactionRequest;
|
||||
use v1::impls::sign_and_dispatch;
|
||||
use util::keys::store::*;
|
||||
use util::keys::store::AccountProvider;
|
||||
use util::numbers::*;
|
||||
use ethcore::client::BlockChainClient;
|
||||
use ethminer::MinerService;
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use ethcore::miner::MinerService;
|
||||
|
||||
/// Account management (personal) rpc implementation.
|
||||
pub struct PersonalClient<A, C, M>
|
||||
where A: AccountProvider, C: BlockChainClient, M: MinerService {
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
accounts: Weak<A>,
|
||||
client: Weak<C>,
|
||||
miner: Weak<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
|
||||
pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>) -> Self {
|
||||
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>
|
||||
where A: AccountProvider, C: BlockChainClient, M: MinerService {
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
fn accounts(&self, _: Params) -> Result<Value, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
match store.accounts() {
|
||||
|
93
rpc/src/v1/impls/personal_signer.rs
Normal file
93
rpc/src/v1/impls/personal_signer.rs
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transactions Confirmations (personal) rpc implementation
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
use jsonrpc_core::*;
|
||||
use v1::traits::PersonalSigner;
|
||||
use v1::types::TransactionModification;
|
||||
use v1::impls::sign_and_dispatch;
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
use util::keys::store::AccountProvider;
|
||||
use util::numbers::*;
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use ethcore::miner::MinerService;
|
||||
|
||||
/// Transactions confirmation (personal) rpc implementation.
|
||||
pub struct SignerClient<A, C, M>
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
queue: Weak<ConfirmationsQueue>,
|
||||
accounts: Weak<A>,
|
||||
client: Weak<C>,
|
||||
miner: Weak<M>,
|
||||
}
|
||||
|
||||
impl<A: 'static, C: 'static, M: 'static> SignerClient<A, C, M>
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
|
||||
/// Create new instance of signer client.
|
||||
pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>, queue: &Arc<ConfirmationsQueue>) -> Self {
|
||||
SignerClient {
|
||||
queue: Arc::downgrade(queue),
|
||||
accounts: Arc::downgrade(store),
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: 'static, C: 'static, M: 'static> PersonalSigner for SignerClient<A, C, M>
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
|
||||
fn transactions_to_confirm(&self, _params: Params) -> Result<Value, Error> {
|
||||
let queue = take_weak!(self.queue);
|
||||
to_value(&queue.requests())
|
||||
}
|
||||
|
||||
fn confirm_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(U256, TransactionModification, String)>(params).and_then(
|
||||
|(id, modification, pass)| {
|
||||
let accounts = take_weak!(self.accounts);
|
||||
let queue = take_weak!(self.queue);
|
||||
queue.remove_request(id)
|
||||
.and_then(|confirmation| {
|
||||
let mut request = confirmation.transaction;
|
||||
// apply modification
|
||||
if let Some(gas_price) = modification.gas_price {
|
||||
request.gas_price = Some(gas_price);
|
||||
}
|
||||
match accounts.locked_account_secret(&request.from, &pass) {
|
||||
Ok(secret) => Some(sign_and_dispatch(&self.client, &self.miner, request, secret)),
|
||||
Err(_) => None
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| to_value(&H256::zero()))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn reject_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(U256, )>(params).and_then(
|
||||
|(id, )| {
|
||||
let queue = take_weak!(self.queue);
|
||||
let res = queue.remove_request(id);
|
||||
to_value(&res.is_some())
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,13 @@
|
||||
//!
|
||||
//! Compliant with ethereum rpc.
|
||||
|
||||
pub mod traits;
|
||||
mod impls;
|
||||
mod types;
|
||||
mod helpers;
|
||||
|
||||
pub mod traits;
|
||||
pub mod tests;
|
||||
pub mod types;
|
||||
|
||||
pub use self::traits::{Web3, Eth, EthFilter, Personal, Net, Ethcore, Traces, Rpc};
|
||||
pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Personal, PersonalSigner, Net, Ethcore, Traces, Rpc};
|
||||
pub use self::impls::*;
|
||||
pub use self::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
|
360
rpc/src/v1/tests/eth.rs
Normal file
360
rpc/src/v1/tests/eth.rs
Normal file
@ -0,0 +1,360 @@
|
||||
// Copyright 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! rpc integration tests.
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use ethcore::client::{BlockChainClient, Client, ClientConfig};
|
||||
use ethcore::ids::BlockID;
|
||||
use ethcore::spec::{Genesis, Spec};
|
||||
use ethcore::block::Block;
|
||||
use ethcore::views::BlockView;
|
||||
use ethcore::ethereum;
|
||||
use ethcore::miner::{MinerService, ExternalMiner, Miner};
|
||||
use devtools::RandomTempPath;
|
||||
use util::Hashable;
|
||||
use util::io::IoChannel;
|
||||
use util::hash::{Address, H256};
|
||||
use util::numbers::U256;
|
||||
use util::keys::{AccountProvider, TestAccount, TestAccountProvider};
|
||||
use jsonrpc_core::IoHandler;
|
||||
use ethjson::blockchain::BlockChain;
|
||||
|
||||
use v1::traits::eth::{Eth, EthSigning};
|
||||
use v1::impls::{EthClient, EthSigningUnsafeClient};
|
||||
use v1::tests::helpers::{TestSyncProvider, Config};
|
||||
|
||||
fn account_provider() -> Arc<TestAccountProvider> {
|
||||
let mut accounts = HashMap::new();
|
||||
accounts.insert(Address::from(1), TestAccount::new("test"));
|
||||
let ap = TestAccountProvider::new(accounts);
|
||||
Arc::new(ap)
|
||||
}
|
||||
|
||||
fn sync_provider() -> Arc<TestSyncProvider> {
|
||||
Arc::new(TestSyncProvider::new(Config {
|
||||
network_id: U256::from(3),
|
||||
num_peers: 120,
|
||||
}))
|
||||
}
|
||||
|
||||
fn miner_service(spec: Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
|
||||
Miner::with_accounts(true, spec, accounts)
|
||||
}
|
||||
|
||||
fn make_spec(chain: &BlockChain) -> Spec {
|
||||
let genesis = Genesis::from(chain.genesis());
|
||||
let mut spec = ethereum::new_frontier_test();
|
||||
let state = chain.pre_state.clone().into();
|
||||
spec.set_genesis_state(state);
|
||||
spec.overwrite_genesis_params(genesis);
|
||||
assert!(spec.is_state_root_valid());
|
||||
spec
|
||||
}
|
||||
|
||||
struct EthTester {
|
||||
client: Arc<Client>,
|
||||
_miner: Arc<MinerService>,
|
||||
accounts: Arc<TestAccountProvider>,
|
||||
handler: IoHandler,
|
||||
}
|
||||
|
||||
impl EthTester {
|
||||
fn from_chain(chain: &BlockChain) -> Self {
|
||||
let tester = Self::from_spec_provider(|| make_spec(chain));
|
||||
|
||||
for b in &chain.blocks_rlp() {
|
||||
if Block::is_good(&b) {
|
||||
let _ = tester.client.import_block(b.clone());
|
||||
tester.client.flush_queue();
|
||||
tester.client.import_verified_blocks(&IoChannel::disconnected());
|
||||
}
|
||||
}
|
||||
|
||||
tester.client.flush_queue();
|
||||
|
||||
assert!(tester.client.chain_info().best_block_hash == chain.best_block.clone().into());
|
||||
tester
|
||||
}
|
||||
|
||||
fn from_spec_provider<F>(spec_provider: F) -> Self
|
||||
where F: Fn() -> Spec {
|
||||
|
||||
let dir = RandomTempPath::new();
|
||||
let account_provider = account_provider();
|
||||
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 eth_client = EthClient::new(
|
||||
&client,
|
||||
&sync_provider,
|
||||
&account_provider,
|
||||
&miner_service,
|
||||
&external_miner
|
||||
);
|
||||
let eth_sign = EthSigningUnsafeClient::new(
|
||||
&client,
|
||||
&account_provider,
|
||||
&miner_service
|
||||
);
|
||||
|
||||
let handler = IoHandler::new();
|
||||
handler.add_delegate(eth_client.to_delegate());
|
||||
handler.add_delegate(eth_sign.to_delegate());
|
||||
|
||||
EthTester {
|
||||
_miner: miner_service,
|
||||
client: client,
|
||||
accounts: account_provider,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn harness_works() {
|
||||
let chain: BlockChain = extract_chain!("BlockchainTests/bcUncleTest");
|
||||
let _ = EthTester::from_chain(&chain);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eth_get_balance() {
|
||||
let chain = extract_chain!("BlockchainTests/bcWalletTest", "wallet2outOf3txs");
|
||||
let tester = EthTester::from_chain(&chain);
|
||||
// final account state
|
||||
let req_latest = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": ["0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa", "latest"],
|
||||
"id": 1
|
||||
}"#;
|
||||
let res_latest = r#"{"jsonrpc":"2.0","result":"0x09","id":1}"#.to_owned();
|
||||
assert_eq!(tester.handler.handle_request(req_latest).unwrap(), res_latest);
|
||||
|
||||
// non-existant account
|
||||
let req_new_acc = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
|
||||
"id": 3
|
||||
}"#;
|
||||
|
||||
let res_new_acc = r#"{"jsonrpc":"2.0","result":"0x00","id":3}"#.to_owned();
|
||||
assert_eq!(tester.handler.handle_request(req_new_acc).unwrap(), res_new_acc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eth_block_number() {
|
||||
let chain = extract_chain!("BlockchainTests/bcRPC_API_Test");
|
||||
let tester = EthTester::from_chain(&chain);
|
||||
let req_number = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_blockNumber",
|
||||
"params": [],
|
||||
"id": 1
|
||||
}"#;
|
||||
|
||||
let res_number = r#"{"jsonrpc":"2.0","result":"0x20","id":1}"#.to_owned();
|
||||
assert_eq!(tester.handler.handle_request(req_number).unwrap(), res_number);
|
||||
}
|
||||
|
||||
// a frontier-like test with an expanded gas limit and balance on known account.
|
||||
const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{
|
||||
"name": "Frontier (Test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x50000",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x50000"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"faa34835af5c2ea724333018a515fbb7d5bc0b33": { "balance": "10000000000000", "nonce": "0" }
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn eth_transaction_count() {
|
||||
use util::crypto::Secret;
|
||||
|
||||
let address = Address::from_str("faa34835af5c2ea724333018a515fbb7d5bc0b33").unwrap();
|
||||
let secret = Secret::from_str("8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2").unwrap();
|
||||
|
||||
let tester = EthTester::from_spec_provider(|| Spec::load(TRANSACTION_COUNT_SPEC));
|
||||
tester.accounts.accounts.write().unwrap().insert(address, TestAccount {
|
||||
unlocked: false,
|
||||
password: "123".into(),
|
||||
secret: secret
|
||||
});
|
||||
|
||||
let req_before = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionCount",
|
||||
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
|
||||
"id": 15
|
||||
}"#;
|
||||
|
||||
let res_before = r#"{"jsonrpc":"2.0","result":"0x00","id":15}"#;
|
||||
|
||||
assert_eq!(tester.handler.handle_request(&req_before).unwrap(), res_before);
|
||||
|
||||
let req_send_trans = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||
"gas": "0x30000",
|
||||
"gasPrice": "0x01",
|
||||
"value": "0x9184e72a"
|
||||
}],
|
||||
"id": 16
|
||||
}"#;
|
||||
|
||||
// dispatch the transaction.
|
||||
tester.handler.handle_request(&req_send_trans).unwrap();
|
||||
|
||||
// we have submitted the transaction -- but this shouldn't be reflected in a "latest" query.
|
||||
let req_after_latest = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionCount",
|
||||
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
|
||||
"id": 17
|
||||
}"#;
|
||||
|
||||
let res_after_latest = r#"{"jsonrpc":"2.0","result":"0x00","id":17}"#;
|
||||
|
||||
assert_eq!(&tester.handler.handle_request(&req_after_latest).unwrap(), res_after_latest);
|
||||
|
||||
// the pending transactions should have been updated.
|
||||
let req_after_pending = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionCount",
|
||||
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "pending"],
|
||||
"id": 18
|
||||
}"#;
|
||||
|
||||
let res_after_pending = r#"{"jsonrpc":"2.0","result":"0x01","id":18}"#;
|
||||
|
||||
assert_eq!(&tester.handler.handle_request(&req_after_pending).unwrap(), res_after_pending);
|
||||
}
|
||||
|
||||
fn verify_transaction_counts(name: String, chain: BlockChain) {
|
||||
struct PanicHandler(String);
|
||||
impl Drop for PanicHandler {
|
||||
fn drop(&mut self) {
|
||||
if ::std::thread::panicking() {
|
||||
println!("Test failed: {}", self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _panic = PanicHandler(name);
|
||||
|
||||
fn by_hash(hash: H256, count: usize, id: &mut usize) -> (String, String) {
|
||||
let req = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBlockTransactionCountByHash",
|
||||
"params": [
|
||||
""#.to_owned() + format!("0x{:?}", hash).as_ref() + r#""
|
||||
],
|
||||
"id": "# + format!("{}", *id).as_ref() + r#"
|
||||
}"#;
|
||||
|
||||
let res = r#"{"jsonrpc":"2.0","result":""#.to_owned()
|
||||
+ format!("0x{:02x}", count).as_ref()
|
||||
+ r#"","id":"#
|
||||
+ format!("{}", *id).as_ref() + r#"}"#;
|
||||
*id += 1;
|
||||
(req, res)
|
||||
}
|
||||
|
||||
fn by_number(num: u64, count: usize, id: &mut usize) -> (String, String) {
|
||||
let req = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBlockTransactionCountByNumber",
|
||||
"params": [
|
||||
"#.to_owned() + &::serde_json::to_string(&U256::from(num)).unwrap() + r#"
|
||||
],
|
||||
"id": "# + format!("{}", *id).as_ref() + r#"
|
||||
}"#;
|
||||
|
||||
let res = r#"{"jsonrpc":"2.0","result":""#.to_owned()
|
||||
+ format!("0x{:02x}", count).as_ref()
|
||||
+ r#"","id":"#
|
||||
+ format!("{}", *id).as_ref() + r#"}"#;
|
||||
*id += 1;
|
||||
(req, res)
|
||||
}
|
||||
|
||||
let tester = EthTester::from_chain(&chain);
|
||||
|
||||
let mut id = 1;
|
||||
for b in chain.blocks_rlp().iter().filter(|b| Block::is_good(b)).map(|b| BlockView::new(b)) {
|
||||
let count = b.transactions_count();
|
||||
|
||||
let hash = b.sha3();
|
||||
let number = b.header_view().number();
|
||||
|
||||
let (req, res) = by_hash(hash, count, &mut id);
|
||||
assert_eq!(tester.handler.handle_request(&req), Some(res));
|
||||
|
||||
// uncles can share block numbers, so skip them.
|
||||
if tester.client.block_hash(BlockID::Number(number)) == Some(hash) {
|
||||
let (req, res) = by_number(number, count, &mut id);
|
||||
assert_eq!(tester.handler.handle_request(&req), Some(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest");
|
||||
register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest");
|
||||
register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest");
|
@ -19,11 +19,11 @@
|
||||
use util::{Address, H256, Bytes, U256, FixedHash, Uint};
|
||||
use util::standard::*;
|
||||
use ethcore::error::{Error, ExecutionError};
|
||||
use ethcore::client::{BlockChainClient, Executed};
|
||||
use ethcore::client::{MiningBlockChainClient, Executed};
|
||||
use ethcore::block::{ClosedBlock, IsBlock};
|
||||
use ethcore::transaction::SignedTransaction;
|
||||
use ethcore::receipt::Receipt;
|
||||
use ethminer::{MinerService, MinerStatus, AccountDetails, TransactionImportResult};
|
||||
use ethcore::miner::{MinerService, MinerStatus, AccountDetails, TransactionImportResult};
|
||||
|
||||
/// Test miner service.
|
||||
pub struct TestMinerService {
|
||||
@ -115,12 +115,16 @@ impl MinerService for TestMinerService {
|
||||
}
|
||||
|
||||
/// Imports transactions to transaction queue.
|
||||
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, _fetch_account: T) ->
|
||||
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) ->
|
||||
Vec<Result<TransactionImportResult, Error>>
|
||||
where T: Fn(&Address) -> AccountDetails {
|
||||
// lets assume that all txs are valid
|
||||
self.imported_transactions.lock().unwrap().extend_from_slice(&transactions);
|
||||
|
||||
for sender in transactions.iter().filter_map(|t| t.sender().ok()) {
|
||||
let nonce = self.last_nonce(&sender).unwrap_or(fetch_account(&sender).nonce);
|
||||
self.last_nonces.write().unwrap().insert(sender, nonce + U256::from(1));
|
||||
}
|
||||
transactions
|
||||
.iter()
|
||||
.map(|_| Ok(TransactionImportResult::Current))
|
||||
@ -128,9 +132,16 @@ impl MinerService for TestMinerService {
|
||||
}
|
||||
|
||||
/// 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>
|
||||
where T: Fn(&Address) -> AccountDetails {
|
||||
|
||||
// keep the pending nonces up to date
|
||||
if let Ok(ref sender) = transaction.sender() {
|
||||
let nonce = self.last_nonce(sender).unwrap_or(chain.latest_nonce(sender));
|
||||
self.last_nonces.write().unwrap().insert(sender.clone(), nonce + U256::from(1));
|
||||
}
|
||||
|
||||
// lets assume that all txs are valid
|
||||
self.imported_transactions.lock().unwrap().push(transaction);
|
||||
|
||||
@ -143,21 +154,21 @@ impl MinerService for TestMinerService {
|
||||
}
|
||||
|
||||
/// 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!();
|
||||
}
|
||||
|
||||
/// 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!();
|
||||
}
|
||||
|
||||
/// New chain head event. Restart mining operation.
|
||||
fn update_sealing(&self, _chain: &BlockChainClient) {
|
||||
fn update_sealing(&self, _chain: &MiningBlockChainClient) {
|
||||
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!();
|
||||
}
|
||||
|
||||
@ -183,27 +194,29 @@ impl MinerService for TestMinerService {
|
||||
|
||||
/// 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.
|
||||
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!();
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
fn call(&self, _chain: &BlockChainClient, _t: &SignedTransaction) -> Result<Executed, ExecutionError> {
|
||||
fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _vm_tracing: bool) -> Result<Executed, ExecutionError> {
|
||||
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())
|
||||
}
|
||||
|
||||
fn nonce(&self, _chain: &BlockChainClient, address: &Address) -> U256 {
|
||||
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.nonce(address).clone())
|
||||
fn nonce(&self, _chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||
// we assume all transactions are in a pending block, ignoring the
|
||||
// reality of gas limits.
|
||||
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())
|
||||
}
|
||||
|
||||
|
@ -20,4 +20,4 @@ mod sync_provider;
|
||||
mod miner_service;
|
||||
|
||||
pub use self::sync_provider::{Config, TestSyncProvider};
|
||||
pub use self::miner_service::{TestMinerService};
|
||||
pub use self::miner_service::TestMinerService;
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
//! Test implementation of SyncProvider.
|
||||
|
||||
use util::{U256};
|
||||
use util::U256;
|
||||
use ethsync::{SyncProvider, SyncStatus, SyncState};
|
||||
use std::sync::{RwLock};
|
||||
use std::sync::RwLock;
|
||||
|
||||
/// TestSyncProvider config.
|
||||
pub struct Config {
|
||||
|
@ -20,14 +20,16 @@ use std::sync::{Arc, RwLock};
|
||||
use jsonrpc_core::IoHandler;
|
||||
use util::hash::{Address, H256, FixedHash};
|
||||
use util::numbers::{Uint, U256};
|
||||
use util::keys::{TestAccount, TestAccountProvider};
|
||||
use util::keys::{AccountProvider, TestAccount, TestAccountProvider};
|
||||
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID};
|
||||
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
|
||||
use ethcore::receipt::LocalizedReceipt;
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use ethminer::ExternalMiner;
|
||||
use v1::{Eth, EthClient};
|
||||
use ethcore::miner::{ExternalMiner, MinerService};
|
||||
use ethsync::SyncState;
|
||||
use v1::{Eth, EthClient, EthSigning, EthSigningUnsafeClient};
|
||||
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
||||
use rustc_serialize::hex::ToHex;
|
||||
|
||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||
let client = TestBlockChainClient::new();
|
||||
@ -70,8 +72,11 @@ impl Default for EthTester {
|
||||
let hashrates = Arc::new(RwLock::new(HashMap::new()));
|
||||
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
|
||||
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner).to_delegate();
|
||||
let sign = EthSigningUnsafeClient::new(&client, &ap, &miner).to_delegate();
|
||||
let io = IoHandler::new();
|
||||
io.add_delegate(eth);
|
||||
io.add_delegate(sign);
|
||||
|
||||
EthTester {
|
||||
client: client,
|
||||
sync: sync,
|
||||
@ -92,9 +97,39 @@ fn rpc_eth_protocol_version() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn rpc_eth_syncing() {
|
||||
unimplemented!()
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1}"#;
|
||||
|
||||
let tester = EthTester::default();
|
||||
|
||||
let false_res = r#"{"jsonrpc":"2.0","result":false,"id":1}"#;
|
||||
assert_eq!(tester.io.handle_request(request), Some(false_res.to_owned()));
|
||||
|
||||
{
|
||||
let mut status = tester.sync.status.write().unwrap();
|
||||
status.state = SyncState::Blocks;
|
||||
status.highest_block_number = Some(2500);
|
||||
|
||||
// "sync" to 1000 blocks.
|
||||
// causes TestBlockChainClient to return 1000 for its best block number.
|
||||
let mut blocks = tester.client.blocks.write().unwrap();
|
||||
for i in 0..1000 {
|
||||
blocks.insert(H256::from(i), Vec::new());
|
||||
}
|
||||
}
|
||||
|
||||
let true_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x03e8","highestBlock":"0x09c4","startingBlock":"0x00"},"id":1}"#;
|
||||
assert_eq!(tester.io.handle_request(request), Some(true_res.to_owned()));
|
||||
|
||||
{
|
||||
// finish "syncing"
|
||||
let mut blocks = tester.client.blocks.write().unwrap();
|
||||
for i in 0..1500 {
|
||||
blocks.insert(H256::from(i + 1000), Vec::new());
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(tester.io.handle_request(request), Some(false_res.to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -130,9 +165,47 @@ fn rpc_eth_submit_hashrate() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn rpc_eth_sign() {
|
||||
let tester = EthTester::default();
|
||||
|
||||
let account = tester.accounts_provider.new_account("abcd").unwrap();
|
||||
let message = H256::from("0x0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f");
|
||||
let signed = tester.accounts_provider.sign(&account, &message).unwrap();
|
||||
|
||||
let req = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sign",
|
||||
"params": [
|
||||
""#.to_owned() + &format!("0x{:?}", account) + r#"",
|
||||
"0x0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f"
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:?}", signed) + r#"","id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request(&req), Some(res));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rpc_eth_author() {
|
||||
unimplemented!()
|
||||
let make_res = |addr| r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:?}", addr) + r#"","id":1}"#;
|
||||
let tester = EthTester::default();
|
||||
|
||||
let req = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_coinbase",
|
||||
"params": [],
|
||||
"id": 1
|
||||
}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request(req), Some(make_res(Address::zero())));
|
||||
|
||||
for i in 0..20 {
|
||||
let addr = tester.accounts_provider.new_account(&format!("{}", i)).unwrap();
|
||||
tester.miner.set_author(addr.clone());
|
||||
|
||||
assert_eq!(tester.io.handle_request(req), Some(make_res(addr)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -193,18 +266,22 @@ fn rpc_eth_balance() {
|
||||
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
#[ignore] //TODO: propert test
|
||||
#[test]
|
||||
fn rpc_eth_balance_pending() {
|
||||
let tester = EthTester::default();
|
||||
tester.client.set_balance(Address::from(1), U256::from(5));
|
||||
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": ["0x0000000000000000000000000000000000000001", "latest"],
|
||||
"params": ["0x0000000000000000000000000000000000000001", "pending"],
|
||||
"id": 1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":"0x","id":1}"#;
|
||||
|
||||
// the TestMinerService doesn't communicate with the the TestBlockChainClient in any way.
|
||||
// if this returns zero, we know that the "pending" call is being properly forwarded to the
|
||||
// miner.
|
||||
let response = r#"{"jsonrpc":"2.0","result":"0x00","id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
||||
}
|
||||
@ -288,7 +365,7 @@ fn rpc_eth_pending_transaction_by_hash() {
|
||||
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#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionByHash",
|
||||
@ -353,6 +430,7 @@ fn rpc_eth_call() {
|
||||
contracts_created: vec![],
|
||||
output: vec![0x12, 0x34, 0xff],
|
||||
trace: None,
|
||||
vm_trace: None,
|
||||
});
|
||||
|
||||
let request = r#"{
|
||||
@ -386,6 +464,7 @@ fn rpc_eth_call_default_block() {
|
||||
contracts_created: vec![],
|
||||
output: vec![0x12, 0x34, 0xff],
|
||||
trace: None,
|
||||
vm_trace: None,
|
||||
});
|
||||
|
||||
let request = r#"{
|
||||
@ -418,6 +497,7 @@ fn rpc_eth_estimate_gas() {
|
||||
contracts_created: vec![],
|
||||
output: vec![0x12, 0x34, 0xff],
|
||||
trace: None,
|
||||
vm_trace: None,
|
||||
});
|
||||
|
||||
let request = r#"{
|
||||
@ -451,6 +531,7 @@ fn rpc_eth_estimate_gas_default_block() {
|
||||
contracts_created: vec![],
|
||||
output: vec![0x12, 0x34, 0xff],
|
||||
trace: None,
|
||||
vm_trace: None,
|
||||
});
|
||||
|
||||
let request = r#"{
|
||||
@ -503,7 +584,7 @@ fn rpc_eth_send_transaction() {
|
||||
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request(request.as_ref()), Some(response));
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response));
|
||||
|
||||
tester.miner.last_nonces.write().unwrap().insert(address.clone(), U256::zero());
|
||||
|
||||
@ -518,19 +599,38 @@ fn rpc_eth_send_transaction() {
|
||||
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request(request.as_ref()), Some(response));
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn rpc_eth_send_raw_transaction() {
|
||||
unimplemented!()
|
||||
}
|
||||
let tester = EthTester::default();
|
||||
let address = tester.accounts_provider.new_account("abcd").unwrap();
|
||||
let secret = tester.accounts_provider.account_secret(&address).unwrap();
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn rpc_eth_sign() {
|
||||
unimplemented!()
|
||||
let t = Transaction {
|
||||
nonce: U256::zero(),
|
||||
gas_price: U256::from(0x9184e72a000u64),
|
||||
gas: U256::from(0x76c0),
|
||||
action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||
value: U256::from(0x9184e72au64),
|
||||
data: vec![]
|
||||
}.sign(&secret);
|
||||
|
||||
let rlp = ::util::rlp::encode(&t).to_vec().to_hex();
|
||||
|
||||
let req = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sendRawTransaction",
|
||||
"params": [
|
||||
"0x"#.to_owned() + &rlp + r#""
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
|
||||
let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:?}", t.hash()) + r#"","id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request(&req), Some(res));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
75
rpc/src/v1/tests/mocked/eth_signing.rs
Normal file
75
rpc/src/v1/tests/mocked/eth_signing.rs
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use v1::impls::EthSigningQueueClient;
|
||||
use v1::traits::EthSigning;
|
||||
use v1::helpers::{ConfirmationsQueue, SigningQueue};
|
||||
use util::keys::TestAccount;
|
||||
|
||||
struct EthSigningTester {
|
||||
pub queue: Arc<ConfirmationsQueue>,
|
||||
pub io: IoHandler,
|
||||
}
|
||||
|
||||
impl Default for EthSigningTester {
|
||||
fn default() -> Self {
|
||||
let queue = Arc::new(ConfirmationsQueue::default());
|
||||
let io = IoHandler::new();
|
||||
io.add_delegate(EthSigningQueueClient::new(&queue).to_delegate());
|
||||
|
||||
EthSigningTester {
|
||||
queue: queue,
|
||||
io: io,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eth_signing() -> EthSigningTester {
|
||||
EthSigningTester::default()
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_add_transaction_to_queue() {
|
||||
// given
|
||||
let tester = eth_signing();
|
||||
let account = TestAccount::new("123");
|
||||
let address = account.address();
|
||||
assert_eq!(tester.queue.requests().len(), 0);
|
||||
|
||||
// when
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||
"gas": "0x76c0",
|
||||
"gasPrice": "0x9184e72a000",
|
||||
"value": "0x9184e72a"
|
||||
}],
|
||||
"id": 1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
|
||||
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
}
|
@ -18,17 +18,23 @@ use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use v1::{Ethcore, EthcoreClient};
|
||||
use ethminer::MinerService;
|
||||
use ethcore::client::{TestBlockChainClient};
|
||||
use ethcore::miner::MinerService;
|
||||
use v1::tests::helpers::TestMinerService;
|
||||
use util::numbers::*;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use util::log::RotatingLogger;
|
||||
use util::network_settings::NetworkSettings;
|
||||
|
||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||
let client = TestBlockChainClient::new();
|
||||
Arc::new(client)
|
||||
}
|
||||
|
||||
fn miner_service() -> Arc<TestMinerService> {
|
||||
Arc::new(TestMinerService::default())
|
||||
}
|
||||
|
||||
fn logger() -> Arc<RotatingLogger> {
|
||||
Arc::new(RotatingLogger::new("rpc=trace".to_owned()))
|
||||
}
|
||||
@ -45,14 +51,15 @@ fn settings() -> Arc<NetworkSettings> {
|
||||
})
|
||||
}
|
||||
|
||||
fn ethcore_client(miner: &Arc<TestMinerService>) -> EthcoreClient<TestMinerService> {
|
||||
EthcoreClient::new(&miner, logger(), settings())
|
||||
fn ethcore_client(client: &Arc<TestBlockChainClient>, miner: &Arc<TestMinerService>) -> EthcoreClient<TestBlockChainClient, TestMinerService> {
|
||||
EthcoreClient::new(&client, &miner, logger(), settings())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rpc_ethcore_extra_data() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -68,7 +75,8 @@ fn rpc_ethcore_default_extra_data() {
|
||||
use util::ToPretty;
|
||||
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -81,7 +89,8 @@ fn rpc_ethcore_default_extra_data() {
|
||||
#[test]
|
||||
fn rpc_ethcore_gas_floor_target() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -94,7 +103,8 @@ fn rpc_ethcore_gas_floor_target() {
|
||||
#[test]
|
||||
fn rpc_ethcore_min_gas_price() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -107,7 +117,8 @@ fn rpc_ethcore_min_gas_price() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_min_gas_price() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -121,7 +132,8 @@ fn rpc_ethcore_set_min_gas_price() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_gas_floor_target() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -135,7 +147,8 @@ fn rpc_ethcore_set_gas_floor_target() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_extra_data() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -149,7 +162,8 @@ fn rpc_ethcore_set_extra_data() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_author() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -163,10 +177,11 @@ fn rpc_ethcore_set_author() {
|
||||
#[test]
|
||||
fn rpc_ethcore_dev_logs() {
|
||||
let miner = miner_service();
|
||||
let client = blockchain_client();
|
||||
let logger = logger();
|
||||
logger.append("a".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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -179,7 +194,8 @@ fn rpc_ethcore_dev_logs() {
|
||||
#[test]
|
||||
fn rpc_ethcore_dev_logs_levels() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -191,7 +207,8 @@ fn rpc_ethcore_dev_logs_levels() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_transactions_limit() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -205,7 +222,8 @@ fn rpc_ethcore_set_transactions_limit() {
|
||||
#[test]
|
||||
fn rpc_ethcore_transactions_limit() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -218,7 +236,8 @@ fn rpc_ethcore_transactions_limit() {
|
||||
#[test]
|
||||
fn rpc_ethcore_net_chain() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -231,7 +250,8 @@ fn rpc_ethcore_net_chain() {
|
||||
#[test]
|
||||
fn rpc_ethcore_net_max_peers() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -244,7 +264,8 @@ fn rpc_ethcore_net_max_peers() {
|
||||
#[test]
|
||||
fn rpc_ethcore_net_port() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -257,7 +278,8 @@ fn rpc_ethcore_net_port() {
|
||||
#[test]
|
||||
fn rpc_ethcore_rpc_settings() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -270,7 +292,8 @@ fn rpc_ethcore_rpc_settings() {
|
||||
#[test]
|
||||
fn rpc_ethcore_node_name() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
|
@ -14,11 +14,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! RPC serialization tests.
|
||||
//! RPC mocked tests. Most of these test that the RPC server is serializing and forwarding
|
||||
//! method calls properly.
|
||||
|
||||
mod eth;
|
||||
mod eth_signing;
|
||||
mod net;
|
||||
mod web3;
|
||||
mod personal;
|
||||
mod personal_signer;
|
||||
mod ethcore;
|
||||
mod rpc;
|
||||
|
169
rpc/src/v1/tests/mocked/personal_signer.rs
Normal file
169
rpc/src/v1/tests/mocked/personal_signer.rs
Normal file
@ -0,0 +1,169 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
use std::collections::HashMap;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use util::numbers::*;
|
||||
use util::keys::{TestAccount, TestAccountProvider};
|
||||
use ethcore::client::TestBlockChainClient;
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use v1::{SignerClient, PersonalSigner};
|
||||
use v1::tests::helpers::TestMinerService;
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
use v1::types::TransactionRequest;
|
||||
|
||||
|
||||
struct PersonalSignerTester {
|
||||
queue: Arc<ConfirmationsQueue>,
|
||||
accounts: Arc<TestAccountProvider>,
|
||||
io: IoHandler,
|
||||
miner: Arc<TestMinerService>,
|
||||
// these unused fields are necessary to keep the data alive
|
||||
// as the handler has only weak pointers.
|
||||
_client: Arc<TestBlockChainClient>,
|
||||
}
|
||||
|
||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||
let client = TestBlockChainClient::new();
|
||||
Arc::new(client)
|
||||
}
|
||||
|
||||
fn accounts_provider() -> Arc<TestAccountProvider> {
|
||||
let accounts = HashMap::new();
|
||||
let ap = TestAccountProvider::new(accounts);
|
||||
Arc::new(ap)
|
||||
}
|
||||
|
||||
fn miner_service() -> Arc<TestMinerService> {
|
||||
Arc::new(TestMinerService::default())
|
||||
}
|
||||
|
||||
fn signer_tester() -> PersonalSignerTester {
|
||||
let queue = Arc::new(ConfirmationsQueue::default());
|
||||
let accounts = accounts_provider();
|
||||
let client = blockchain_client();
|
||||
let miner = miner_service();
|
||||
|
||||
let io = IoHandler::new();
|
||||
io.add_delegate(SignerClient::new(&accounts, &client, &miner, &queue).to_delegate());
|
||||
|
||||
PersonalSignerTester {
|
||||
queue: queue,
|
||||
accounts: accounts,
|
||||
io: io,
|
||||
miner: miner,
|
||||
_client: client,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_return_list_of_transactions_in_queue() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
nonce: None,
|
||||
});
|
||||
|
||||
// when
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_transactionsToConfirm","params":[],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":[{"id":"0x01","transaction":{"data":null,"from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x01"}}],"id":1}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_reject_transaction_from_queue_without_dispatching() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
nonce: None,
|
||||
});
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
// when
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_rejectTransaction","params":["0x01"],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 0);
|
||||
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_confirm_transaction_and_dispatch() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
let account = TestAccount::new("test");
|
||||
let address = account.address();
|
||||
let secret = account.secret.clone();
|
||||
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
|
||||
tester.accounts.accounts
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(address, account);
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
from: address,
|
||||
to: Some(recipient),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
nonce: None,
|
||||
});
|
||||
let t = Transaction {
|
||||
nonce: U256::zero(),
|
||||
gas_price: U256::from(0x1000),
|
||||
gas: U256::from(10_000_000),
|
||||
action: Action::Call(recipient),
|
||||
value: U256::from(0x1),
|
||||
data: vec![]
|
||||
}.sign(&secret);
|
||||
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
// when
|
||||
let request = r#"{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"personal_confirmTransaction",
|
||||
"params":["0x01", {"gasPrice":"0x1000"}, "test"],
|
||||
"id":1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 0);
|
||||
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 1);
|
||||
}
|
||||
|
@ -2,5 +2,71 @@
|
||||
|
||||
pub mod helpers;
|
||||
|
||||
// extract a chain from the given JSON file,
|
||||
// stored in ethcore/res/ethereum/tests/.
|
||||
//
|
||||
// usage:
|
||||
// `extract_chain!("Folder/File")` will load Folder/File.json and extract
|
||||
// the first block chain stored within.
|
||||
//
|
||||
// `extract_chain!("Folder/File", "with_name")` will load Folder/File.json and
|
||||
// extract the chain with that name. This will panic if no chain by that name
|
||||
// is found.
|
||||
macro_rules! extract_chain {
|
||||
(iter $file:expr) => {{
|
||||
const RAW_DATA: &'static [u8] =
|
||||
include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/", $file, ".json"));
|
||||
::ethjson::blockchain::Test::load(RAW_DATA).unwrap().into_iter()
|
||||
}};
|
||||
|
||||
($file:expr, $name:expr) => {{
|
||||
let mut chain = None;
|
||||
for (name, c) in extract_chain!(iter $file) {
|
||||
if name == $name {
|
||||
chain = Some(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
chain.unwrap()
|
||||
}};
|
||||
|
||||
($file:expr) => {{
|
||||
extract_chain!(iter $file).next().unwrap().1
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! register_test {
|
||||
($name:ident, $cb:expr, $file:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
for (name, chain) in extract_chain!(iter $file) {
|
||||
$cb(name, chain);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(heavy $name:ident, $cb:expr, $file:expr) => {
|
||||
#[test]
|
||||
#[cfg(feature = "test-heavy")]
|
||||
fn $name() {
|
||||
for (name, chain) in extract_chain!(iter $file) {
|
||||
$cb(name, chain);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(ignore $name:ident, $cb:expr, $file:expr) => {
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn $name() {
|
||||
for (name, chain) in extract_chain!(iter $file) {
|
||||
$cb(name, chain);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mocked;
|
||||
#[cfg(test)]
|
||||
mod eth;
|
||||
|
@ -21,115 +21,109 @@ use jsonrpc_core::*;
|
||||
/// Eth rpc interface.
|
||||
pub trait Eth: Sized + Send + Sync + 'static {
|
||||
/// Returns protocol version.
|
||||
fn protocol_version(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn protocol_version(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns an object with data about the sync status or false. (wtf?)
|
||||
fn syncing(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn syncing(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns the number of hashes per second that the node is mining with.
|
||||
fn hashrate(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn hashrate(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns block author.
|
||||
fn author(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn author(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns true if client is actively mining new blocks.
|
||||
fn is_mining(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn is_mining(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns current gas_price.
|
||||
fn gas_price(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn gas_price(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns accounts list.
|
||||
fn accounts(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn accounts(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns highest block number.
|
||||
fn block_number(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn block_number(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns balance of the given account.
|
||||
fn balance(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn balance(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns content of the storage at given address.
|
||||
fn storage_at(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn storage_at(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns block with given hash.
|
||||
fn block_by_hash(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn block_by_hash(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns block with given number.
|
||||
fn block_by_number(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn block_by_number(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns the number of transactions sent from given address at given time (block number).
|
||||
fn transaction_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn transaction_count(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns the number of transactions in a block with given hash.
|
||||
fn block_transaction_count_by_hash(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn block_transaction_count_by_hash(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns the number of transactions in a block with given block number.
|
||||
fn block_transaction_count_by_number(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn block_transaction_count_by_number(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns the number of uncles in a block with given hash.
|
||||
fn block_uncles_count_by_hash(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn block_uncles_count_by_hash(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns the number of uncles in a block with given block number.
|
||||
fn block_uncles_count_by_number(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn block_uncles_count_by_number(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns the code at given address at given time (block number).
|
||||
fn code_at(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
|
||||
/// Signs the data with given address signature.
|
||||
fn sign(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
|
||||
/// Sends transaction.
|
||||
fn send_transaction(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn code_at(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sends signed transaction.
|
||||
fn send_raw_transaction(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn send_raw_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Call contract.
|
||||
fn call(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn call(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Estimate gas needed for execution of given contract.
|
||||
fn estimate_gas(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn estimate_gas(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Get transaction by it's hash.
|
||||
fn transaction_by_hash(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn transaction_by_hash(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns transaction at given block hash and index.
|
||||
fn transaction_by_block_hash_and_index(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn transaction_by_block_hash_and_index(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns transaction by given block number and index.
|
||||
fn transaction_by_block_number_and_index(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn transaction_by_block_number_and_index(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns transaction receipt.
|
||||
fn transaction_receipt(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn transaction_receipt(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns an uncles at given block and index.
|
||||
fn uncle_by_block_hash_and_index(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn uncle_by_block_hash_and_index(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns an uncles at given block and index.
|
||||
fn uncle_by_block_number_and_index(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn uncle_by_block_number_and_index(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns available compilers.
|
||||
fn compilers(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn compilers(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Compiles lll code.
|
||||
fn compile_lll(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn compile_lll(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Compiles solidity.
|
||||
fn compile_solidity(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn compile_solidity(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Compiles serpent.
|
||||
fn compile_serpent(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn compile_serpent(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns logs matching given filter object.
|
||||
fn logs(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn logs(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns the hash of the current block, the seedHash, and the boundary condition to be met.
|
||||
fn work(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn work(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Used for submitting a proof-of-work solution.
|
||||
fn submit_work(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn submit_work(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Used for submitting mining hashrate.
|
||||
fn submit_hashrate(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn submit_hashrate(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
@ -150,8 +144,6 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
||||
delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count_by_hash);
|
||||
delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count_by_number);
|
||||
delegate.add_method("eth_getCode", Eth::code_at);
|
||||
delegate.add_method("eth_sign", Eth::sign);
|
||||
delegate.add_method("eth_sendTransaction", Eth::send_transaction);
|
||||
delegate.add_method("eth_sendRawTransaction", Eth::send_raw_transaction);
|
||||
delegate.add_method("eth_call", Eth::call);
|
||||
delegate.add_method("eth_estimateGas", Eth::estimate_gas);
|
||||
@ -179,22 +171,22 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
||||
// TODO: do filters api properly
|
||||
pub trait EthFilter: Sized + Send + Sync + 'static {
|
||||
/// Returns id of new filter.
|
||||
fn new_filter(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn new_filter(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns id of new block filter.
|
||||
fn new_block_filter(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn new_block_filter(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns id of new block filter.
|
||||
fn new_pending_transaction_filter(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn new_pending_transaction_filter(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns filter changes since last poll.
|
||||
fn filter_changes(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn filter_changes(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns all logs matching given filter (in a range 'from' - 'to').
|
||||
fn filter_logs(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn filter_logs(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Uninstalls filter.
|
||||
fn uninstall_filter(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn uninstall_filter(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
@ -208,3 +200,20 @@ pub trait EthFilter: Sized + Send + Sync + 'static {
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
||||
/// Signing methods implementation relying on unlocked accounts.
|
||||
pub trait EthSigning: Sized + Send + Sync + 'static {
|
||||
/// Signs the data with given address signature.
|
||||
fn sign(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sends transaction.
|
||||
fn send_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||
delegate.add_method("eth_sign", EthSigning::sign);
|
||||
delegate.add_method("eth_sendTransaction", EthSigning::send_transaction);
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
@ -22,56 +22,58 @@ use jsonrpc_core::*;
|
||||
pub trait Ethcore: Sized + Send + Sync + 'static {
|
||||
|
||||
/// Sets new minimal gas price for mined blocks.
|
||||
fn set_min_gas_price(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn set_min_gas_price(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sets new gas floor target for mined blocks.
|
||||
fn set_gas_floor_target(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn set_gas_floor_target(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sets new extra data for mined blocks.
|
||||
fn set_extra_data(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn set_extra_data(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sets new author for mined block.
|
||||
fn set_author(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn set_author(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sets the limits for transaction queue.
|
||||
fn set_transactions_limit(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn set_transactions_limit(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns current transactions limit.
|
||||
fn transactions_limit(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn transactions_limit(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns mining extra data.
|
||||
fn extra_data(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn extra_data(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns mining gas floor target.
|
||||
fn gas_floor_target(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn gas_floor_target(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns minimal gas price for transaction to be included in queue.
|
||||
fn min_gas_price(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn min_gas_price(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns latest logs
|
||||
fn dev_logs(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn dev_logs(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns logs levels
|
||||
fn dev_logs_levels(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn dev_logs_levels(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns chain name
|
||||
fn net_chain(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn net_chain(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns max peers
|
||||
fn net_max_peers(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn net_max_peers(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns network port
|
||||
fn net_port(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn net_port(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns rpc settings
|
||||
fn rpc_settings(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn rpc_settings(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns node name
|
||||
fn node_name(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn node_name(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns default extra data
|
||||
fn default_extra_data(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
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.
|
||||
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_defaultExtraData", Ethcore::default_extra_data);
|
||||
|
||||
delegate.add_method("ethcore_vmTraceCall", Ethcore::vm_trace_call);
|
||||
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,6 @@
|
||||
|
||||
//! Ethereum rpc interfaces.
|
||||
|
||||
macro_rules! rpc_unimplemented {
|
||||
() => (Err(Error::internal_error()))
|
||||
}
|
||||
|
||||
pub mod web3;
|
||||
pub mod eth;
|
||||
pub mod net;
|
||||
@ -29,9 +25,11 @@ pub mod traces;
|
||||
pub mod rpc;
|
||||
|
||||
pub use self::web3::Web3;
|
||||
pub use self::eth::{Eth, EthFilter};
|
||||
pub use self::eth::{Eth, EthFilter, EthSigning};
|
||||
pub use self::net::Net;
|
||||
pub use self::personal::Personal;
|
||||
pub use self::personal::{Personal, PersonalSigner};
|
||||
pub use self::ethcore::Ethcore;
|
||||
pub use self::traces::Traces;
|
||||
pub use self::rpc::Rpc;
|
||||
|
||||
|
||||
|
@ -21,14 +21,14 @@ use jsonrpc_core::*;
|
||||
/// Net rpc interface.
|
||||
pub trait Net: Sized + Send + Sync + 'static {
|
||||
/// Returns protocol version.
|
||||
fn version(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn version(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns number of peers connected to node.
|
||||
fn peer_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn peer_count(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns true if client is actively listening for network connections.
|
||||
/// Otherwise false.
|
||||
fn is_listening(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn is_listening(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
|
@ -22,16 +22,16 @@ use jsonrpc_core::*;
|
||||
pub trait Personal: Sized + Send + Sync + 'static {
|
||||
|
||||
/// Lists all stored accounts
|
||||
fn accounts(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn accounts(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Creates new account (it becomes new current unlocked account)
|
||||
fn new_account(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn new_account(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Unlocks specified account for use (can only be one unlocked account at one moment)
|
||||
fn unlock_account(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn unlock_account(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sends transaction and signs it in single call. The account is not unlocked in such case.
|
||||
fn sign_and_send_transaction(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn sign_and_send_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
@ -43,3 +43,26 @@ pub trait Personal: Sized + Send + Sync + 'static {
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
||||
/// Personal extension for transactions confirmations rpc interface.
|
||||
pub trait PersonalSigner: Sized + Send + Sync + 'static {
|
||||
|
||||
/// Returns a list of transactions to confirm.
|
||||
fn transactions_to_confirm(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Confirm and send a specific transaction.
|
||||
fn confirm_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Reject the transaction request.
|
||||
fn reject_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||
delegate.add_method("personal_transactionsToConfirm", PersonalSigner::transactions_to_confirm);
|
||||
delegate.add_method("personal_confirmTransaction", PersonalSigner::confirm_transaction);
|
||||
delegate.add_method("personal_rejectTransaction", PersonalSigner::reject_transaction);
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,10 +23,10 @@ use jsonrpc_core::*;
|
||||
pub trait Rpc: Sized + Send + Sync + 'static {
|
||||
|
||||
/// Returns supported modules for Geth 1.3.6
|
||||
fn modules(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn modules(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns supported modules for Geth 1.4.0
|
||||
fn rpc_modules(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn rpc_modules(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
|
@ -21,16 +21,16 @@ use jsonrpc_core::*;
|
||||
/// Traces specific rpc interface.
|
||||
pub trait Traces: Sized + Send + Sync + 'static {
|
||||
/// Returns traces matching given filter.
|
||||
fn filter(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn filter(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns transaction trace at given index.
|
||||
fn trace(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn trace(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns all traces of given transaction.
|
||||
fn transaction_traces(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn transaction_traces(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns all traces produced at given block.
|
||||
fn block_traces(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn block_traces(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
|
@ -21,10 +21,10 @@ use jsonrpc_core::*;
|
||||
/// Web3 rpc interface.
|
||||
pub trait Web3: Sized + Send + Sync + 'static {
|
||||
/// Returns current client version.
|
||||
fn client_version(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn client_version(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Returns sha3 of the given data
|
||||
fn sha3(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||
fn sha3(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user