merge accountdb migration

This commit is contained in:
Robert Habermeier 2016-07-09 17:33:14 +02:00
commit 36dfa4743e
234 changed files with 5015 additions and 30967 deletions

View File

@ -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 ethkey -p ethstore -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer"
- TARGETS="-p ethkey -p ethstore -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer -p bigint"
- 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,ethstore/tests target/kcov"

127
Cargo.lock generated
View File

@ -3,7 +3,7 @@ name = "parity"
version = "1.3.0"
dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)",
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
@ -70,7 +70,6 @@ dependencies = [
"heapsize 0.3.5 (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.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -129,15 +128,15 @@ dependencies = [
[[package]]
name = "clippy"
version = "0.0.77"
version = "0.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clippy_lints 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy_lints 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clippy_lints"
version = "0.0.77"
version = "0.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -149,15 +148,6 @@ dependencies = [
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cookie"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cookie"
version = "0.2.4"
@ -225,15 +215,13 @@ dependencies = [
[[package]]
name = "eth-secp256k1"
version = "0.5.4"
source = "git+https://github.com/ethcore/rust-secp256k1#b6fdd43bbcf6d46adb72a92dd1632a0fc834cbf5"
source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8e55e205a753ff25c"
dependencies = [
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -250,13 +238,14 @@ name = "ethcore"
version = "1.3.0"
dependencies = [
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethash 1.3.0",
"ethcore-devtools 1.3.0",
"ethcore-ipc 1.3.0",
"ethcore-ipc-codegen 1.3.0",
"ethcore-ipc-nano 1.3.0",
"ethcore-util 1.3.0",
"ethjson 0.1.0",
"ethstore 0.1.0",
@ -268,6 +257,7 @@ dependencies = [
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -276,7 +266,7 @@ dependencies = [
name = "ethcore-dapps"
version = "1.3.0"
dependencies = [
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-rpc 1.3.0",
"ethcore-util 1.3.0",
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
@ -293,6 +283,7 @@ dependencies = [
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -338,10 +329,11 @@ dependencies = [
name = "ethcore-rpc"
version = "1.3.0"
dependencies = [
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"ethash 1.3.0",
"ethcore 1.3.0",
"ethcore-devtools 1.3.0",
"ethcore-ipc 1.3.0",
"ethcore-util 1.3.0",
"ethjson 0.1.0",
"ethsync 1.3.0",
@ -361,7 +353,7 @@ dependencies = [
name = "ethcore-signer"
version = "1.3.0"
dependencies = [
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.78 (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.3.0",
"ethcore-util 1.3.0",
@ -381,16 +373,15 @@ dependencies = [
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"bigint 0.1.0",
"chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
"ethcore-devtools 1.3.0",
"heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"igd 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"igd 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
"json-tests 0.1.0",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -401,12 +392,13 @@ dependencies = [
"rust-crypto 0.2.36 (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.9 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.1.0",
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"table 0.1.0",
"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)",
"using_queue 0.1.0",
"vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -453,7 +445,7 @@ dependencies = [
name = "ethsync"
version = "1.3.0"
dependencies = [
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.78 (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.3.0",
"ethcore-util 1.3.0",
@ -506,26 +498,6 @@ name = "httparse"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cookie 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hyper"
version = "0.8.1"
@ -579,10 +551,10 @@ dependencies = [
[[package]]
name = "igd"
version = "0.4.2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hyper 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
"xml-rs 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
@ -609,14 +581,6 @@ dependencies = [
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "json-tests"
version = "0.1.0"
dependencies = [
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-core"
version = "2.0.7"
@ -647,11 +611,6 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "language-tags"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "language-tags"
version = "0.2.2"
@ -685,15 +644,6 @@ dependencies = [
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mime"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mime"
version = "0.2.0"
@ -898,7 +848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "parity-dapps"
version = "0.3.0"
source = "git+https://github.com/ethcore/parity-dapps-rs.git#8cc812c26c903cf5764ce0f4cc3f2a7c3ddb0dc2"
source = "git+https://github.com/ethcore/parity-dapps-rs.git#8ce18c014d8b69fa31fb203b68ff240091d77a23"
dependencies = [
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1154,14 +1104,6 @@ dependencies = [
"nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde"
version = "0.7.9"
@ -1253,6 +1195,10 @@ dependencies = [
"unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "table"
version = "0.1.0"
[[package]]
name = "target_info"
version = "0.1.0"
@ -1359,16 +1305,6 @@ name = "unicode-xid"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
version = "0.2.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "url"
version = "0.5.9"
@ -1390,20 +1326,15 @@ dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "using_queue"
version = "0.1.0"
[[package]]
name = "utf8-ranges"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "uuid"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "uuid"
version = "0.2.1"

View File

@ -22,7 +22,7 @@ fdlimit = { path = "util/fdlimit" }
num_cpus = "0.2"
number_prefix = "0.2"
rpassword = "0.2.1"
clippy = { version = "0.0.77", optional = true}
clippy = { version = "0.0.78", optional = true}
ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
ethsync = { path = "sync" }

View File

@ -1,7 +1,11 @@
# [Parity](https://ethcore.io/parity.html)
### Fast, light, and robust Ethereum implementation
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/trogdoro/xiki][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
[Internal Documentation][doc-url]
Be sure to check out [our wiki][wiki-url] for more information.
[travis-image]: https://travis-ci.org/ethcore/parity.svg?branch=master
[travis-url]: https://travis-ci.org/ethcore/parity
@ -11,8 +15,8 @@
[gitter-url]: https://gitter.im/ethcore/parity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
[license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html
[Internal Documentation](http://ethcore.github.io/parity/ethcore/index.html)
[doc-url]: http://ethcore.github.io/parity/ethcore/index.html
[wiki-url]: https://github.com/ethcore/parity/wiki
----
@ -29,7 +33,7 @@ This includes a few useful Dapps, including Ethereum Wallet, Maker OTC, and a no
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!
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
Parity's current release is 1.2. You can download it at https://ethcore.io/parity.html or follow the instructions
below to build from source.

View File

@ -13,6 +13,7 @@ log = "0.3"
jsonrpc-core = "2.0"
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
unicase = "1.3"
url = "1.0"
rustc-serialize = "0.3"
serde = "0.7.0"
@ -28,7 +29,7 @@ parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs
parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.4.0", optional = true }
parity-dapps-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.3.0", optional = true }
mime_guess = { version = "1.6.1" }
clippy = { version = "0.0.77", optional = true}
clippy = { version = "0.0.78", optional = true}
[build-dependencies]
serde_codegen = { version = "0.7.0", optional = true }

View File

@ -25,8 +25,8 @@ mod inner {
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let src = Path::new("./src/api/mod.rs.in");
let dst = Path::new(&out_dir).join("mod.rs");
let src = Path::new("./src/api/types.rs.in");
let dst = Path::new(&out_dir).join("types.rs");
let mut registry = syntex::Registry::new();

View File

@ -15,42 +15,23 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath};
use api::response::as_json;
use hyper::{server, net, Decoder, Encoder, Next};
use api::types::{App, ApiError};
use api::response::{as_json, as_json_error, ping_response};
use handlers::extract_url;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
#[derive(Clone)]
pub struct RestApi {
local_domain: String,
endpoints: Arc<Endpoints>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct App {
pub id: String,
pub name: String,
pub description: String,
pub version: String,
pub author: String,
#[serde(rename="iconUrl")]
pub icon_url: String,
}
impl App {
fn from_info(id: &str, info: &EndpointInfo) -> Self {
App {
id: id.to_owned(),
name: info.name.to_owned(),
description: info.description.to_owned(),
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
}
}
}
impl RestApi {
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> {
pub fn new(local_domain: String, endpoints: Arc<Endpoints>) -> Box<Endpoint> {
Box::new(RestApi {
endpoints: endpoints
local_domain: local_domain,
endpoints: endpoints,
})
}
@ -63,7 +44,58 @@ impl RestApi {
impl Endpoint for RestApi {
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
as_json(&self.list_apps())
Box::new(RestApiRouter {
api: self.clone(),
handler: as_json_error(&ApiError {
code: "404".into(),
title: "Not Found".into(),
detail: "Resource you requested has not been found.".into(),
}),
})
}
}
struct RestApiRouter {
api: RestApi,
handler: Box<Handler>,
}
impl server::Handler<net::HttpStream> for RestApiRouter {
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
let url = extract_url(&request);
if url.is_none() {
// Just return 404 if we can't parse URL
return Next::write();
}
let url = url.expect("Check for None is above; qed");
let endpoint = url.path.get(1).map(|v| v.as_str());
let handler = endpoint.and_then(|v| match v {
"apps" => Some(as_json(&self.api.list_apps())),
"ping" => Some(ping_response(&self.api.local_domain)),
_ => None,
});
// Overwrite default
if let Some(h) = handler {
self.handler = h;
}
self.handler.on_request(request)
}
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
self.handler.on_request_readable(decoder)
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
self.handler.on_response(res)
}
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
self.handler.on_response_writable(encoder)
}
}

View File

@ -16,13 +16,12 @@
//! REST API
#![warn(missing_docs)]
#![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))]
#![cfg_attr(feature="nightly", plugin(serde_macros, clippy))]
#[cfg(feature = "serde_macros")]
include!("mod.rs.in");
#[cfg(not(feature = "serde_macros"))]
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
mod api;
mod response;
mod types;
pub use self::api::RestApi;
pub use self::types::App;

View File

@ -16,8 +16,21 @@
use serde::Serialize;
use serde_json;
use endpoint::{ContentHandler, Handler};
use endpoint::Handler;
use handlers::{ContentHandler, EchoHandler};
pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
Box::new(ContentHandler::new(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
}
pub fn as_json_error<T : Serialize>(val: &T) -> Box<Handler> {
Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
}
pub fn ping_response(local_domain: &str) -> Box<Handler> {
Box::new(EchoHandler::cors(vec![
format!("http://{}", local_domain),
// Allow CORS calls also for localhost
format!("http://{}", local_domain.replace("127.0.0.1", "localhost")),
]))
}

View File

@ -14,11 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use rustc_serialize::hex::FromHex;
#[cfg(feature = "serde_macros")]
include!("types.rs.in");
pub fn hex_or_string(s: &str) -> Vec<u8> {
match s.starts_with("0x") {
true => s[2..].from_hex().unwrap(),
false => From::from(s)
}
}
#[cfg(not(feature = "serde_macros"))]
include!(concat!(env!("OUT_DIR"), "/types.rs"));

50
dapps/src/api/types.rs.in Normal file
View File

@ -0,0 +1,50 @@
// 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 endpoint::EndpointInfo;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct App {
pub id: String,
pub name: String,
pub description: String,
pub version: String,
pub author: String,
#[serde(rename="iconUrl")]
pub icon_url: String,
}
impl App {
/// Creates `App` instance from `EndpointInfo` and `id`.
pub fn from_info(id: &str, info: &EndpointInfo) -> Self {
App {
id: id.to_owned(),
name: info.name.to_owned(),
description: info.description.to_owned(),
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
}
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ApiError {
pub code: String,
pub title: String,
pub detail: String,
}

View File

@ -16,11 +16,7 @@
//! URL Endpoint traits
use hyper::status::StatusCode;
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use std::io::Write;
use hyper::{server, net};
use std::collections::BTreeMap;
#[derive(Debug, PartialEq, Default, Clone)]
@ -42,58 +38,8 @@ pub struct EndpointInfo {
pub trait Endpoint : Send + Sync {
fn info(&self) -> Option<&EndpointInfo> { None }
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream> + Send>;
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<net::HttpStream> + Send>;
}
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
pub type Handler = server::Handler<HttpStream> + Send;
pub struct ContentHandler {
content: String,
mimetype: String,
write_pos: usize,
}
impl ContentHandler {
pub fn new(content: String, mimetype: String) -> Self {
ContentHandler {
content: content,
mimetype: mimetype,
write_pos: 0
}
}
}
impl server::Handler<HttpStream> for ContentHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(StatusCode::Ok);
res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
let bytes = self.content.as_bytes();
if self.write_pos == bytes.len() {
return Next::end();
}
match encoder.write(&bytes[self.write_pos..]) {
Ok(bytes) => {
self.write_pos += bytes;
Next::write()
},
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::write(),
_ => Next::end()
},
}
}
}
pub type Handler = server::Handler<net::HttpStream> + Send;

View File

@ -0,0 +1,44 @@
// 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/>.
//! Authorization Handlers
use hyper::{server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::status::StatusCode;
pub struct AuthRequiredHandler;
impl server::Handler<HttpStream> for AuthRequiredHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(StatusCode::Unauthorized);
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
Next::write()
}
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
Next::end()
}
}

View File

@ -0,0 +1,92 @@
// 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/>.
//! Simple Content Handler
use std::io::Write;
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::status::StatusCode;
pub struct ContentHandler {
code: StatusCode,
content: String,
mimetype: String,
write_pos: usize,
}
impl ContentHandler {
pub fn ok(content: String, mimetype: String) -> Self {
ContentHandler {
code: StatusCode::Ok,
content: content,
mimetype: mimetype,
write_pos: 0
}
}
pub fn not_found(content: String, mimetype: String) -> Self {
ContentHandler {
code: StatusCode::NotFound,
content: content,
mimetype: mimetype,
write_pos: 0
}
}
pub fn new(code: StatusCode, content: String, mimetype: String) -> Self {
ContentHandler {
code: code,
content: content,
mimetype: mimetype,
write_pos: 0,
}
}
}
impl server::Handler<HttpStream> for ContentHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(self.code);
res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
let bytes = self.content.as_bytes();
if self.write_pos == bytes.len() {
return Next::end();
}
match encoder.write(&bytes[self.write_pos..]) {
Ok(bytes) => {
self.write_pos += bytes;
Next::write()
},
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::write(),
_ => Next::end()
},
}
}
}

148
dapps/src/handlers/echo.rs Normal file
View File

@ -0,0 +1,148 @@
// 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/>.
//! Echo Handler
use std::io::Read;
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::method::Method;
use hyper::net::HttpStream;
use unicase::UniCase;
use super::ContentHandler;
#[derive(Debug, PartialEq)]
/// Type of Cross-Origin request
enum Cors {
/// Not a Cross-Origin request - no headers needed
No,
/// Cross-Origin request with valid Origin
Allowed(String),
/// Cross-Origin request with invalid Origin
Forbidden,
}
pub struct EchoHandler {
safe_origins: Vec<String>,
content: String,
cors: Cors,
handler: Option<ContentHandler>,
}
impl EchoHandler {
pub fn cors(safe_origins: Vec<String>) -> Self {
EchoHandler {
safe_origins: safe_origins,
content: String::new(),
cors: Cors::Forbidden,
handler: None,
}
}
fn cors_header(&self, origin: Option<String>) -> Cors {
fn origin_is_allowed(origin: &str, safe_origins: &[String]) -> bool {
for safe in safe_origins {
if origin.starts_with(safe) {
return true;
}
}
false
}
match origin {
Some(ref origin) if origin_is_allowed(origin, &self.safe_origins) => {
Cors::Allowed(origin.clone())
},
None => Cors::No,
_ => Cors::Forbidden,
}
}
}
impl server::Handler<HttpStream> for EchoHandler {
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
let origin = request.headers().get_raw("origin")
.and_then(|list| list.get(0))
.and_then(|origin| String::from_utf8(origin.clone()).ok());
self.cors = self.cors_header(origin);
// Don't even read the payload if origin is forbidden!
if let Cors::Forbidden = self.cors {
self.handler = Some(ContentHandler::ok(String::new(), "text/plain".into()));
Next::write()
} else {
Next::read()
}
}
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
match decoder.read_to_string(&mut self.content) {
Ok(0) => {
self.handler = Some(ContentHandler::ok(self.content.clone(), "application/json".into()));
Next::write()
},
Ok(_) => Next::read(),
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::read(),
_ => Next::end(),
}
}
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
if let Cors::Allowed(ref domain) = self.cors {
let mut headers = res.headers_mut();
headers.set(header::Allow(vec![Method::Options, Method::Post, Method::Get]));
headers.set(header::AccessControlAllowHeaders(vec![
UniCase("origin".to_owned()),
UniCase("content-type".to_owned()),
UniCase("accept".to_owned()),
]));
headers.set(header::AccessControlAllowOrigin::Value(domain.clone()));
}
self.handler.as_mut().unwrap().on_response(res)
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
self.handler.as_mut().unwrap().on_response_writable(encoder)
}
}
#[test]
fn should_return_correct_cors_value() {
// given
let safe_origins = vec!["chrome-extension://".to_owned(), "http://localhost:8080".to_owned()];
let cut = EchoHandler {
safe_origins: safe_origins,
content: String::new(),
cors: Cors::No,
handler: None,
};
// when
let res1 = cut.cors_header(Some("http://ethcore.io".into()));
let res2 = cut.cors_header(Some("http://localhost:8080".into()));
let res3 = cut.cors_header(Some("chrome-extension://deadbeefcafe".into()));
let res4 = cut.cors_header(None);
// then
assert_eq!(res1, Cors::Forbidden);
assert_eq!(res2, Cors::Allowed("http://localhost:8080".into()));
assert_eq!(res3, Cors::Allowed("chrome-extension://deadbeefcafe".into()));
assert_eq!(res4, Cors::No);
}

57
dapps/src/handlers/mod.rs Normal file
View File

@ -0,0 +1,57 @@
// 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/>.
//! Hyper handlers implementations.
mod auth;
mod echo;
mod content;
mod redirect;
pub use self::auth::AuthRequiredHandler;
pub use self::echo::EchoHandler;
pub use self::content::ContentHandler;
pub use self::redirect::Redirection;
use url::Url;
use hyper::{server, header, net, uri};
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
match *req.uri() {
uri::RequestUri::AbsoluteUri(ref url) => {
match Url::from_generic_url(url.clone()) {
Ok(url) => Some(url),
_ => None,
}
},
uri::RequestUri::AbsolutePath(ref path) => {
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
let url_string = match req.headers().get::<header::Host>() {
Some(ref host) => {
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
},
None => return None,
};
match Url::parse(&url_string) {
Ok(url) => Some(url),
_ => None,
}
},
_ => None,
}
}

View File

@ -45,8 +45,9 @@
#[macro_use]
extern crate log;
extern crate url;
extern crate url as url_lib;
extern crate hyper;
extern crate unicase;
extern crate serde;
extern crate serde_json;
extern crate jsonrpc_core;
@ -59,9 +60,11 @@ mod endpoint;
mod apps;
mod page;
mod router;
mod handlers;
mod rpc;
mod api;
mod proxypac;
mod url;
use std::sync::{Arc, Mutex};
use std::net::SocketAddr;
@ -120,7 +123,7 @@ impl Server {
let special = Arc::new({
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(endpoints.clone()));
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(format!("{}", addr), endpoints.clone()));
special.insert(router::SpecialEndpoint::Utils, apps::utils());
special
});

View File

@ -16,7 +16,8 @@
//! Serving ProxyPac file
use endpoint::{Endpoint, Handler, ContentHandler, EndpointPath};
use endpoint::{Endpoint, Handler, EndpointPath};
use handlers::ContentHandler;
use apps::DAPPS_DOMAIN;
pub struct ProxyPac;
@ -41,7 +42,7 @@ function FindProxyForURL(url, host) {{
}}
"#,
DAPPS_DOMAIN, path.host, path.port);
Box::new(ContentHandler::new(content, "application/javascript".to_owned()))
Box::new(ContentHandler::ok(content, "application/javascript".to_owned()))
}
}

View File

@ -16,24 +16,23 @@
//! HTTP Authorization implementations
use std::io::Write;
use std::collections::HashMap;
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::status::StatusCode;
use hyper::{server, net, header, status};
use endpoint::Handler;
use handlers::{AuthRequiredHandler, ContentHandler};
/// Authorization result
pub enum Authorized {
/// Authorization was successful.
Yes,
/// Unsuccessful authorization. Handler for further work is returned.
No(Box<server::Handler<HttpStream> + Send>),
No(Box<Handler>),
}
/// Authorization interface
pub trait Authorization : Send + Sync {
/// Checks if authorization is valid.
fn is_authorized(&self, req: &server::Request<HttpStream>)-> Authorized;
fn is_authorized(&self, req: &server::Request<net::HttpStream>)-> Authorized;
}
/// HTTP Basic Authorization handler
@ -45,18 +44,22 @@ pub struct HttpBasicAuth {
pub struct NoAuth;
impl Authorization for NoAuth {
fn is_authorized(&self, _req: &server::Request<HttpStream>)-> Authorized {
fn is_authorized(&self, _req: &server::Request<net::HttpStream>)-> Authorized {
Authorized::Yes
}
}
impl Authorization for HttpBasicAuth {
fn is_authorized(&self, req: &server::Request<HttpStream>) -> Authorized {
fn is_authorized(&self, req: &server::Request<net::HttpStream>) -> Authorized {
let auth = self.check_auth(&req);
match auth {
Access::Denied => {
Authorized::No(Box::new(UnauthorizedHandler { write_pos: 0 }))
Authorized::No(Box::new(ContentHandler::new(
status::StatusCode::Unauthorized,
"<h1>Unauthorized</h1>".into(),
"text/html".into(),
)))
},
Access::AuthRequired => {
Authorized::No(Box::new(AuthRequiredHandler))
@ -89,7 +92,7 @@ impl HttpBasicAuth {
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
}
fn check_auth(&self, req: &server::Request<HttpStream>) -> Access {
fn check_auth(&self, req: &server::Request<net::HttpStream>) -> Access {
match req.headers().get::<header::Authorization<header::Basic>>() {
Some(&header::Authorization(
header::Basic { ref username, password: Some(ref password) }
@ -99,63 +102,3 @@ impl HttpBasicAuth {
}
}
}
pub struct UnauthorizedHandler {
write_pos: usize,
}
impl server::Handler<HttpStream> for UnauthorizedHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(StatusCode::Unauthorized);
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
let response = "Unauthorized".as_bytes();
if self.write_pos == response.len() {
return Next::end();
}
match encoder.write(&response[self.write_pos..]) {
Ok(bytes) => {
self.write_pos += bytes;
Next::write()
},
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::write(),
_ => Next::end()
},
}
}
}
pub struct AuthRequiredHandler;
impl server::Handler<HttpStream> for AuthRequiredHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(StatusCode::Unauthorized);
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
Next::write()
}
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
Next::end()
}
}

View File

@ -17,23 +17,18 @@
//! Router implementation
//! Processes request handling authorization and dispatching it to proper application.
mod url;
mod redirect;
pub mod auth;
use DAPPS_DOMAIN;
use std::sync::Arc;
use std::collections::HashMap;
use url::Host;
use hyper;
use hyper::{server, uri, header};
use hyper::{Next, Encoder, Decoder};
use url::{Url, Host};
use hyper::{self, server, Next, Encoder, Decoder};
use hyper::net::HttpStream;
use apps;
use endpoint::{Endpoint, Endpoints, EndpointPath};
use self::url::Url;
use handlers::{Redirection, extract_url};
use self::auth::{Authorization, Authorized};
use self::redirect::Redirection;
/// Special endpoints are accessible on every domain (every dapp)
#[derive(Debug, PartialEq, Hash, Eq)]
@ -124,32 +119,6 @@ impl<A: Authorization> Router<A> {
}
}
fn extract_url(req: &server::Request<HttpStream>) -> Option<Url> {
match *req.uri() {
uri::RequestUri::AbsoluteUri(ref url) => {
match Url::from_generic_url(url.clone()) {
Ok(url) => Some(url),
_ => None,
}
},
uri::RequestUri::AbsolutePath(ref path) => {
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
let url_string = match req.headers().get::<header::Host>() {
Some(ref host) => {
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
},
None => return None,
};
match Url::parse(&url_string) {
Ok(url) => Some(url),
_ => None,
}
},
_ => None,
}
}
fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
fn special_endpoint(url: &Url) -> SpecialEndpoint {
if url.path.len() <= 1 {

View File

@ -16,14 +16,14 @@
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
use url::Host;
use url::{self};
use url_lib::{self};
pub use url_lib::Host;
/// HTTP/HTTPS URL type for Iron.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Url {
/// Raw url of url
pub raw: url::Url,
pub raw: url_lib::Url,
/// The host field of the URL, probably a domain.
pub host: Host,
@ -62,14 +62,14 @@ impl Url {
/// See: http://url.spec.whatwg.org/#special-scheme
pub fn parse(input: &str) -> Result<Url, String> {
// Parse the string using rust-url, then convert.
match url::Url::parse(input) {
match url_lib::Url::parse(input) {
Ok(raw_url) => Url::from_generic_url(raw_url),
Err(e) => Err(format!("{}", e))
}
}
/// Create a `Url` from a `rust-url` `Url`.
pub fn from_generic_url(raw_url: url::Url) -> Result<Url, String> {
pub fn from_generic_url(raw_url: url_lib::Url) -> Result<Url, String> {
// Map empty usernames to None.
let username = match raw_url.username() {
"" => None,

View File

@ -12,7 +12,7 @@ syntex = "*"
ethcore-ipc-codegen = { path = "../ipc/codegen" }
[dependencies]
clippy = { version = "0.0.77", optional = true}
clippy = { version = "0.0.78", optional = true}
ethcore-devtools = { path = "../devtools" }
ethcore-ipc = { path = "../ipc/rpc" }
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }

View File

@ -22,7 +22,7 @@ ethcore-util = { path = "../util" }
evmjit = { path = "../evmjit", optional = true }
ethash = { path = "../ethash" }
num_cpus = "0.2"
clippy = { version = "0.0.77", optional = true}
clippy = { version = "0.0.78", optional = true}
crossbeam = "0.2.9"
lazy_static = "0.2"
ethcore-devtools = { path = "../devtools" }
@ -31,6 +31,8 @@ bloomchain = "0.1"
"ethcore-ipc" = { path = "../ipc/rpc" }
rayon = "0.3.1"
ethstore = { path = "../ethstore" }
semver = "0.2"
ethcore-ipc-nano = { path = "../ipc/nano" }
[dependencies.hyper]
git = "https://github.com/ethcore/hyper"
@ -43,3 +45,4 @@ json-tests = []
test-heavy = []
dev = ["clippy"]
default = []
benches = []

View File

@ -30,4 +30,18 @@ fn main() {
codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
// client interface
{
let src = Path::new("src/client/client.rs");
let intermediate = Path::new(&out_dir).join("client.intermediate.rs.in");
let mut registry = syntex::Registry::new();
codegen::register(&mut registry);
registry.expand("", &src, &intermediate).unwrap();
let dst = Path::new(&out_dir).join("client.ipc.rs");
let mut registry = syntex::Registry::new();
codegen::register(&mut registry);
registry.expand("", &intermediate, &dst).unwrap();
}
}

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x118c30",
"daoRescueSoftFork": false
"frontierCompatibilityModeLimit": "0x118c30"
}
}
},

File diff suppressed because it is too large Load Diff

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x118c30",
"daoRescueSoftFork": false
"frontierCompatibilityModeLimit": "0x118c30"
}
}
},

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x118c30",
"daoRescueSoftFork": true
"frontierCompatibilityModeLimit": "0x118c30"
}
}
},

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
"daoRescueSoftFork": false
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
}
}
},

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": 0,
"daoRescueSoftFork": false
"frontierCompatibilityModeLimit": 0
}
}
},

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": 0,
"daoRescueSoftFork": true
"frontierCompatibilityModeLimit": 0
}
}
},

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": "",
"frontierCompatibilityModeLimit": "0x789b0",
"daoRescueSoftFork": false
"frontierCompatibilityModeLimit": "0x789b0"
}
}
},

View File

@ -9,8 +9,7 @@
"durationLimit": "0x08",
"blockReward": "0x14D1120D7B160000",
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
"daoRescueSoftFork": false
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
}
}
},

View File

@ -127,7 +127,7 @@ impl Account {
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
using it will not fail.");
(Filth::Clean, H256::from(db.get(key.bytes()).map_or(U256::zero(), |v| -> U256 {decode(v)})))
(Filth::Clean, H256::from(db.get(key).map_or(U256::zero(), |v| -> U256 {decode(v)})))
}).1.clone()
}
@ -214,8 +214,8 @@ impl Account {
}
/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
pub fn commit_storage(&mut self, db: &mut AccountDBMut) {
let mut t = SecTrieDBMut::from_existing(db, &mut self.storage_root)
pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) {
let mut t = trie_factory.from_existing(db, &mut self.storage_root)
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
using it will not fail.");
@ -275,7 +275,7 @@ mod tests {
let rlp = {
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.commit_storage(&Default::default(), &mut db);
a.init_code(vec![]);
a.commit_code(&mut db);
a.rlp()
@ -313,7 +313,7 @@ mod tests {
let mut db = AccountDBMut::new(&mut db, &Address::new());
a.set_storage(0.into(), 0x1234.into());
assert_eq!(a.storage_root(), None);
a.commit_storage(&mut db);
a.commit_storage(&Default::default(), &mut db);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
}
@ -323,11 +323,11 @@ mod tests {
let mut db = MemoryDB::new();
let mut db = AccountDBMut::new(&mut db, &Address::new());
a.set_storage(0.into(), 0x1234.into());
a.commit_storage(&mut db);
a.commit_storage(&Default::default(), &mut db);
a.set_storage(1.into(), 0x1234.into());
a.commit_storage(&mut db);
a.commit_storage(&Default::default(), &mut db);
a.set_storage(1.into(), 0.into());
a.commit_storage(&mut db);
a.commit_storage(&Default::default(), &mut db);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
}

View File

@ -1,11 +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/>.
//! DB backend wrapper for Account trie
use util::*;
static NULL_RLP_STATIC: [u8; 1] = [0x80; 1];
// combines a key with an address hash to ensure uniqueness.
// leaves the first 96 bits untouched in order to support partial key lookup.
#[inline]
fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 {
address_hash ^ key
let mut dst = key.clone();
{
let last_src: &[u8] = &*address_hash;
let last_dst: &mut [u8] = &mut *dst;
for (k, a) in last_dst[12..].iter_mut().zip(&last_src[12..]) {
*k ^= *a
}
}
dst
}
// TODO: introduce HashDBMut?

View File

@ -203,7 +203,6 @@ mod tests {
timestamp: 0,
difficulty: 0.into(),
last_hashes: vec![],
dao_rescue_block_gas_limit: None,
gas_used: 0.into(),
gas_limit: 0.into(),
});
@ -254,7 +253,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, None, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock();
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
assert!(b.try_seal(engine.deref(), seal).is_ok());

View File

@ -183,7 +183,6 @@ pub struct OpenBlock<'x> {
engine: &'x Engine,
vm_factory: &'x EvmFactory,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
}
/// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
@ -195,7 +194,6 @@ pub struct ClosedBlock {
block: ExecutedBlock,
uncle_bytes: Bytes,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
unclosed_state: State,
}
@ -222,22 +220,21 @@ impl<'x> OpenBlock<'x> {
pub fn new(
engine: &'x Engine,
vm_factory: &'x EvmFactory,
trie_factory: TrieFactory,
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
author: Address,
gas_range_target: (U256, U256),
extra_data: Bytes,
) -> Result<Self, Error> {
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce()));
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), trie_factory));
let mut r = OpenBlock {
block: ExecutedBlock::new(state, tracing),
engine: engine,
vm_factory: vm_factory,
last_hashes: last_hashes,
dao_rescue_block_gas_limit: dao_rescue_block_gas_limit,
};
r.block.base.header.parent_hash = parent.hash();
@ -294,7 +291,6 @@ impl<'x> OpenBlock<'x> {
/// Get the environment info concerning this block.
pub fn env_info(&self) -> EnvInfo {
// TODO: memoise.
const SOFT_FORK_BLOCK: u64 = 1_800_000;
EnvInfo {
number: self.block.base.header.number,
author: self.block.base.header.author.clone(),
@ -303,7 +299,6 @@ impl<'x> OpenBlock<'x> {
last_hashes: self.last_hashes.clone(), // TODO: should be a reference.
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
gas_limit: self.block.base.header.gas_limit.clone(),
dao_rescue_block_gas_limit: if self.block.base.header.number == SOFT_FORK_BLOCK { Some(self.block.base.header.gas_limit) } else { self.dao_rescue_block_gas_limit },
}
}
@ -350,7 +345,6 @@ impl<'x> OpenBlock<'x> {
block: s.block,
uncle_bytes: uncle_bytes,
last_hashes: s.last_hashes,
dao_rescue_block_gas_limit: s.dao_rescue_block_gas_limit,
unclosed_state: unclosed_state,
}
}
@ -410,7 +404,6 @@ impl ClosedBlock {
engine: engine,
vm_factory: vm_factory,
last_hashes: self.last_hashes,
dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit,
}
}
}
@ -480,17 +473,17 @@ pub fn enact(
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory
vm_factory: &EvmFactory,
trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> {
{
if ::log::max_log_level() >= ::log::LogLevel::Trace {
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce()));
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), trie_factory.clone()));
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
}
}
let mut b = try!(OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, header.author().clone(), (3141562.into(), 31415620.into()), header.extra_data().clone()));
let mut b = try!(OpenBlock::new(engine, vm_factory, trie_factory, tracing, db, parent, last_hashes, header.author().clone(), (3141562.into(), 31415620.into()), header.extra_data().clone()));
b.set_difficulty(*header.difficulty());
b.set_gas_limit(*header.gas_limit());
b.set_timestamp(header.timestamp());
@ -508,12 +501,12 @@ pub fn enact_bytes(
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory
vm_factory: &EvmFactory,
trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> {
let block = BlockView::new(block_bytes);
let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
@ -525,11 +518,11 @@ pub fn enact_verified(
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory
vm_factory: &EvmFactory,
trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> {
let view = BlockView::new(&block.bytes);
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
@ -541,11 +534,11 @@ pub fn enact_and_seal(
db: Box<JournalDB>,
parent: &Header,
last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory
vm_factory: &EvmFactory,
trie_factory: TrieFactory,
) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)).seal(engine, header.seal())))
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)).seal(engine, header.seal())))
}
#[cfg(test)]
@ -565,7 +558,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, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock();
let _ = b.seal(engine.deref(), vec![]);
}
@ -581,7 +574,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()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
.close_and_lock().seal(engine.deref(), vec![]).unwrap();
let orig_bytes = b.rlp_bytes();
let orig_db = b.drain();
@ -589,7 +582,7 @@ mod tests {
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default()).unwrap();
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default(), Default::default()).unwrap();
assert_eq!(e.rlp_bytes(), orig_bytes);
@ -609,7 +602,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()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut uncle1_header = Header::new();
uncle1_header.extra_data = b"uncle1".to_vec();
let mut uncle2_header = Header::new();
@ -624,7 +617,7 @@ mod tests {
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default()).unwrap();
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default(), Default::default()).unwrap();
let bytes = e.rlp_bytes();
assert_eq!(bytes, orig_bytes);

View File

@ -28,6 +28,8 @@ use service::*;
use client::BlockStatus;
use util::panics::*;
pub use types::block_queue_info::BlockQueueInfo;
known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock);
const MIN_MEM_LIMIT: usize = 16384;
@ -53,22 +55,6 @@ impl Default for BlockQueueConfig {
}
}
/// Block queue status
#[derive(Debug)]
pub struct BlockQueueInfo {
/// Number of queued blocks pending verification
pub unverified_queue_size: usize,
/// Number of verified queued blocks pending import
pub verified_queue_size: usize,
/// Number of blocks being verified
pub verifying_queue_size: usize,
/// Configured maximum number of blocks in the queue
pub max_queue_size: usize,
/// Configured maximum number of bytes to use
pub max_mem_use: usize,
/// Heap memory used in bytes
pub mem_used: usize,
}
impl BlockQueueInfo {
/// The total size of the queues.

View File

@ -17,8 +17,6 @@
use util::numbers::{U256,H256};
use header::BlockNumber;
use util::bytes::{FromRawBytesVariable, FromBytesError, ToBytesWithMap};
/// Brief info about inserted block.
#[derive(Clone)]
pub struct BlockInfo {
@ -54,43 +52,3 @@ pub struct BranchBecomingCanonChainData {
/// Hashes of the blocks which were invalidated.
pub retracted: Vec<H256>,
}
impl FromRawBytesVariable for BranchBecomingCanonChainData {
fn from_bytes_variable(bytes: &[u8]) -> Result<BranchBecomingCanonChainData, FromBytesError> {
type Tuple = (Vec<H256>, Vec<H256>, H256);
let (enacted, retracted, ancestor) = try!(Tuple::from_bytes_variable(bytes));
Ok(BranchBecomingCanonChainData { ancestor: ancestor, enacted: enacted, retracted: retracted })
}
}
impl FromRawBytesVariable for BlockLocation {
fn from_bytes_variable(bytes: &[u8]) -> Result<BlockLocation, FromBytesError> {
match bytes[0] {
0 => Ok(BlockLocation::CanonChain),
1 => Ok(BlockLocation::Branch),
2 => Ok(BlockLocation::BranchBecomingCanonChain(
try!(BranchBecomingCanonChainData::from_bytes_variable(&bytes[1..bytes.len()])))),
_ => Err(FromBytesError::UnknownMarker)
}
}
}
impl ToBytesWithMap for BranchBecomingCanonChainData {
fn to_bytes_map(&self) -> Vec<u8> {
(&self.enacted, &self.retracted, &self.ancestor).to_bytes_map()
}
}
impl ToBytesWithMap for BlockLocation {
fn to_bytes_map(&self) -> Vec<u8> {
match *self {
BlockLocation::CanonChain => vec![0u8],
BlockLocation::Branch => vec![1u8],
BlockLocation::BranchBecomingCanonChain(ref data) => {
let mut bytes = (&data.enacted, &data.retracted, &data.ancestor).to_bytes_map();
bytes.insert(0, 2u8);
bytes
}
}
}
}

View File

@ -30,7 +30,7 @@ use blockchain::best_block::BestBlock;
use types::tree_route::TreeRoute;
use blockchain::update::ExtrasUpdate;
use blockchain::{CacheSize, ImportRoute, Config};
use db::{Writable, Readable, CacheUpdatePolicy};
use db::{Writable, Readable, CacheUpdatePolicy, Key};
const LOG_BLOOMS_LEVELS: usize = 3;
const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16;
@ -295,7 +295,22 @@ impl BlockChain {
// load best block
let best_block_hash = match bc.extras_db.get(b"best").unwrap() {
Some(best) => H256::from_slice(&best),
Some(best) => {
let best = H256::from_slice(&best);
let mut b = best.clone();
while !bc.blocks_db.get(&b).unwrap().is_some() {
// track back to the best block we have in the blocks database
let extras: BlockDetails = bc.extras_db.read(&b).unwrap();
type DetailsKey = Key<BlockDetails, Target=H264>;
bc.extras_db.delete(&(DetailsKey::key(&b))).unwrap();
b = extras.parent;
}
if b != best {
info!("Restored mismatched best block. Was: {}, new: {}", best.hex(), b.hex());
bc.extras_db.put(b"best", &b).unwrap();
}
b
}
None => {
// best block does not exist
// we need to insert genesis into the cache

View File

@ -14,16 +14,34 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Blockchain database client.
use std::collections::{HashSet, HashMap};
use std::ops::Deref;
use std::mem;
use std::collections::VecDeque;
use std::sync::*;
use std::path::{Path, PathBuf};
use std::fmt;
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
use std::time::Instant;
use std::fs::{create_dir, File};
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use util::*;
// util
use util::numbers::*;
use util::panics::*;
use util::network::*;
use util::io::*;
use util::rlp;
use util::sha3::*;
use util::Bytes;
use util::rlp::{RlpStream, Rlp, UntrustedRlp};
use util::journaldb;
use util::journaldb::JournalDB;
use util::kvdb::*;
use util::{Applyable, Stream, View, PerfTimer, Itertools, Colour};
// other
use views::BlockView;
use error::{Error, ImportError, ExecutionError, BlockError, ImportResult};
use header::{BlockNumber, Header};
use error::{ImportError, ExecutionError, BlockError, ImportResult};
use header::BlockNumber;
use state::State;
use spec::Spec;
use engine::Engine;
@ -35,24 +53,32 @@ use verification::{PreverifiedBlock, Verifier};
use block::*;
use transaction::{LocalizedTransaction, SignedTransaction, Action};
use blockchain::extras::TransactionAddress;
use filter::Filter;
use types::filter::Filter;
use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig,
DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient,
TraceFilter, CallAnalytics, BlockImportError, Mode};
use client::Error as ClientError;
use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address};
use receipt::LocalizedReceipt;
pub use blockchain::CacheSize as BlockChainCacheSize;
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace;
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService};
use util::TrieFactory;
use ipc::IpcConfig;
use ipc::binary::{BinaryConvertError};
// re-export
pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus;
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService, TransactionImportResult, AccountDetails};
pub use blockchain::CacheSize as BlockChainCacheSize;
const MAX_TX_QUEUE_SIZE: usize = 4096;
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
impl fmt::Display for BlockChainInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -82,9 +108,24 @@ impl ClientReport {
}
}
struct SleepState {
last_activity: Option<Instant>,
last_autosleep: Option<Instant>,
}
impl SleepState {
fn new(awake: bool) -> Self {
SleepState {
last_activity: match awake { false => None, true => Some(Instant::now()) },
last_autosleep: match awake { false => Some(Instant::now()), true => None },
}
}
}
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
pub struct Client {
mode: Mode,
chain: Arc<BlockChain>,
tracedb: Arc<TraceDB<BlockChain>>,
engine: Arc<Box<Engine>>,
@ -95,9 +136,13 @@ pub struct Client {
panic_handler: Arc<PanicHandler>,
verifier: Box<Verifier>,
vm_factory: Arc<EvmFactory>,
trie_factory: TrieFactory,
miner: Arc<Miner>,
sleep_state: Mutex<SleepState>,
liveness: AtomicBool,
io_channel: IoChannel<NetSyncMessage>,
queue_transactions: AtomicUsize,
previous_enode: Mutex<Option<String>>,
}
const HISTORY: u64 = 1200;
@ -132,9 +177,8 @@ impl Client {
spec: Spec,
path: &Path,
miner: Arc<Miner>,
message_channel: IoChannel<NetSyncMessage>)
-> Result<Arc<Client>, ClientError>
{
message_channel: IoChannel<NetSyncMessage>
) -> Result<Arc<Client>, 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));
@ -165,7 +209,11 @@ impl Client {
let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue);
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
let client = Client {
sleep_state: Mutex::new(SleepState::new(awake)),
liveness: AtomicBool::new(awake),
mode: config.mode,
chain: chain,
tracedb: tracedb,
engine: engine,
@ -176,11 +224,12 @@ impl Client {
panic_handler: panic_handler,
verifier: verification::new(config.verifier_type),
vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
trie_factory: TrieFactory::new(config.trie_spec),
miner: miner,
io_channel: message_channel,
queue_transactions: AtomicUsize::new(0),
previous_enode: Mutex::new(None),
};
Ok(Arc::new(client))
}
@ -234,7 +283,7 @@ impl Client {
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
let db = self.state_db.lock().unwrap().boxed_clone();
let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.dao_rescue_block_gas_limit(header.parent_hash.clone()), &self.vm_factory);
let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, &self.vm_factory, self.trie_factory.clone());
if let Err(e) = enact_result {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
@ -349,7 +398,7 @@ impl Client {
imported
}
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &Bytes) -> ImportRoute where B: IsBlock + Drain {
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
let number = block.header().number();
// Are we committing an era?
let ancient = if number >= HISTORY {
@ -385,12 +434,8 @@ impl Client {
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
let _timer = PerfTimer::new("import_queued_transactions");
self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst);
let fetch_account = |a: &Address| AccountDetails {
nonce: self.latest_nonce(a),
balance: self.latest_balance(a),
};
let tx = transactions.iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
let results = self.miner.import_transactions(self, tx, fetch_account);
let txs = transactions.iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
let results = self.miner.import_external_transactions(self, txs);
results.len()
}
@ -419,14 +464,18 @@ impl Client {
let root = HeaderView::new(&header).state_root();
State::from_existing(db, root, self.engine.account_start_nonce()).ok()
State::from_existing(db, root, self.engine.account_start_nonce(), self.trie_factory.clone()).ok()
})
}
/// Get a copy of the best block's state.
pub fn state(&self) -> State {
State::from_existing(self.state_db.lock().unwrap().boxed_clone(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce())
.expect("State root of best block header always valid.")
State::from_existing(
self.state_db.lock().unwrap().boxed_clone(),
HeaderView::new(&self.best_block_header()).state_root(),
self.engine.account_start_nonce(),
self.trie_factory.clone())
.expect("State root of best block header always valid.")
}
/// Get info on the cache.
@ -442,9 +491,41 @@ impl Client {
}
/// Tick the client.
// TODO: manage by real events.
pub fn tick(&self) {
self.chain.collect_garbage();
self.block_queue.collect_garbage();
match self.mode {
Mode::Dark(timeout) => {
let mut ss = self.sleep_state.lock().unwrap();
if let Some(t) = ss.last_activity {
if Instant::now() > t + timeout {
self.sleep();
ss.last_activity = None;
}
}
}
Mode::Passive(timeout, wakeup_after) => {
let mut ss = self.sleep_state.lock().unwrap();
let now = Instant::now();
if let Some(t) = ss.last_activity {
if now > t + timeout {
self.sleep();
ss.last_activity = None;
ss.last_autosleep = Some(now);
}
}
if let Some(t) = ss.last_autosleep {
if now > t + wakeup_after {
self.wake_up();
ss.last_activity = Some(now);
ss.last_autosleep = None;
}
}
}
_ => {}
}
}
/// Set up the cache behaviour.
@ -480,8 +561,45 @@ impl Client {
})
}
}
fn wake_up(&self) {
if !self.liveness.load(AtomicOrdering::Relaxed) {
self.liveness.store(true, AtomicOrdering::Relaxed);
self.io_channel.send(NetworkIoMessage::User(SyncMessage::StartNetwork)).unwrap();
trace!(target: "mode", "wake_up: Waking.");
}
}
fn sleep(&self) {
if self.liveness.load(AtomicOrdering::Relaxed) {
// only sleep if the import queue is mostly empty.
if self.queue_info().total_queue_size() <= MAX_QUEUE_SIZE_TO_SLEEP_ON {
self.liveness.store(false, AtomicOrdering::Relaxed);
self.io_channel.send(NetworkIoMessage::User(SyncMessage::StopNetwork)).unwrap();
trace!(target: "mode", "sleep: Sleeping.");
} else {
trace!(target: "mode", "sleep: Cannot sleep - syncing ongoing.");
// TODO: Consider uncommenting.
//*self.last_activity.lock().unwrap() = Some(Instant::now());
}
}
}
/// Notify us that the network has been started.
pub fn network_started(&self, url: &String) {
let mut previous_enode = self.previous_enode.lock().unwrap();
if let Some(ref u) = *previous_enode {
if u == url {
return;
}
}
*previous_enode = Some(url.clone());
info!(target: "mode", "Public node URL: {}", url.apply(Colour::White.bold()));
}
}
#[derive(Ipc)]
#[ipc(client_ident="RemoteClient")]
impl BlockChainClient for Client {
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let header = self.block_header(BlockID::Latest).unwrap();
@ -495,7 +613,6 @@ impl BlockChainClient for Client {
last_hashes: last_hashes,
gas_used: U256::zero(),
gas_limit: U256::max_value(),
dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit(view.parent_hash()),
};
// that's just a copy of the state.
let mut state = self.state();
@ -521,6 +638,12 @@ impl BlockChainClient for Client {
ret
}
fn keep_alive(&self) {
if self.mode != Mode::Active {
self.wake_up();
(*self.sleep_state.lock().unwrap()).last_activity = Some(Instant::now());
}
}
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()))
@ -580,9 +703,9 @@ impl BlockChainClient for Client {
self.transaction_address(id).and_then(|address| self.chain.transaction(&address))
}
fn uncle(&self, id: UncleID) -> Option<Header> {
let index = id.1;
self.block(id.0).and_then(|block| BlockView::new(&block).uncle_at(index))
fn uncle(&self, id: UncleID) -> Option<Bytes> {
let index = id.position;
self.block(id.block).and_then(|block| BlockView::new(&block).uncle_rlp_at(index))
}
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
@ -649,17 +772,17 @@ impl BlockChainClient for Client {
self.chain.block_receipts(hash).map(|receipts| rlp::encode(&receipts).to_vec())
}
fn import_block(&self, bytes: Bytes) -> ImportResult {
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
{
let header = BlockView::new(&bytes).header_view();
if self.chain.is_known(&header.sha3()) {
return Err(ImportError::AlreadyInChain.into());
return Err(BlockImportError::Import(ImportError::AlreadyInChain));
}
if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown {
return Err(BlockError::UnknownParent(header.parent_hash()).into());
return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash())));
}
}
self.block_queue.import_block(bytes)
Ok(try!(self.block_queue.import_block(bytes)))
}
fn queue_info(&self) -> BlockQueueInfo {
@ -713,8 +836,8 @@ impl BlockChainClient for Client {
receipt.logs.into_iter()
.enumerate()
.filter(|tuple| filter.matches(&tuple.1))
.map(|(i, log)| LocalizedLogEntry {
entry: log,
.map(|(i, log)| LocalizedLogEntry {
entry: log,
block_hash: hash.clone(),
block_number: number,
transaction_hash: hashes.get(index).cloned().unwrap_or_else(H256::new),
@ -724,7 +847,6 @@ impl BlockChainClient for Client {
.collect::<Vec<LocalizedLogEntry>>()
})
.collect::<Vec<LocalizedLogEntry>>()
})
.collect()
}
@ -773,14 +895,6 @@ impl BlockChainClient for Client {
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(self, transactions, fetch_account)
}
fn queue_transactions(&self, transactions: Vec<Bytes>) {
if self.queue_transactions.load(AtomicOrdering::Relaxed) > MAX_TX_QUEUE_SIZE {
debug!("Ignoring {} transactions: queue is full", transactions.len());
@ -800,44 +914,6 @@ impl BlockChainClient for Client {
fn pending_transactions(&self) -> Vec<SignedTransaction> {
self.miner.pending_transactions()
}
fn take_snapshot(&self, root_dir: &Path) {
use snapshot::{ManifestData, chunk_blocks, chunk_state};
let best_header_bytes = self.best_block_header();
let best_header = HeaderView::new(&best_header_bytes);
let state_root = best_header.state_root();
trace!(target: "snapshot", "Taking snapshot starting at block {}", best_header.number());
let mut path = root_dir.to_owned();
path.push("snapshot/");
let _ = create_dir(&path);
// lock the state db while we create the state chunks.
let state_hashes = {
let state_db = self.state_db.lock().unwrap().boxed_clone();
chunk_state(state_db.as_hashdb(), &state_root, &path).unwrap()
};
let best_hash = best_header.hash();
let genesis_hash = self.chain.genesis_hash();
let block_hashes = chunk_blocks(self, best_hash, genesis_hash, &path).unwrap();
trace!(target: "snapshot", "produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
let manifest_data = ManifestData {
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: state_root,
};
path.push("MANIFEST");
let mut manifest_file = File::create(&path).unwrap();
manifest_file.write_all(&manifest_data.to_rlp()).unwrap();
}
}
impl MiningBlockChainClient for Client {
@ -848,11 +924,11 @@ impl MiningBlockChainClient for Client {
let mut open_block = OpenBlock::new(
engine,
&self.vm_factory,
self.trie_factory.clone(),
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
self.state_db.lock().unwrap().boxed_clone(),
&self.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"),
self.build_last_hashes(h.clone()),
self.dao_rescue_block_gas_limit(h.clone()),
author,
gas_range_target,
extra_data,
@ -914,3 +990,5 @@ impl MayPanic for Client {
self.panic_handler.on_panic(closure);
}
}
impl IpcConfig for Client { }

View File

@ -14,12 +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/>.
pub use std::time::Duration;
pub use block_queue::BlockQueueConfig;
pub use blockchain::Config as BlockChainConfig;
pub use trace::{Config as TraceConfig, Switch};
pub use evm::VMType;
pub use verification::VerifierType;
use util::journaldb;
use util::trie::TrieSpec;
/// Client state db compaction profile
#[derive(Debug, PartialEq)]
@ -34,6 +36,23 @@ impl Default for DatabaseCompactionProfile {
fn default() -> Self { DatabaseCompactionProfile::Default }
}
/// Operating mode for the client.
#[derive(Debug, Eq, PartialEq)]
pub enum Mode {
/// Always on.
Active,
/// Goes offline after RLP is inactive for some (given) time, but
/// comes back online after a while of inactivity.
Passive(Duration, Duration),
/// Goes offline after RLP is inactive for some (given) time and
/// stays inactive.
Dark(Duration),
}
impl Default for Mode {
fn default() -> Self { Mode::Active }
}
/// Client configuration. Includes configs for all sub-systems.
#[derive(Debug, Default)]
pub struct ClientConfig {
@ -45,6 +64,8 @@ pub struct ClientConfig {
pub tracing: TraceConfig,
/// VM type.
pub vm_type: VMType,
/// Trie type.
pub trie_spec: TrieSpec,
/// The JournalDB ("pruning") algorithm to use.
pub pruning: journaldb::Algorithm,
/// The name of the client instance.
@ -53,6 +74,8 @@ pub struct ClientConfig {
pub db_cache_size: Option<usize>,
/// State db compaction profile
pub db_compaction: DatabaseCompactionProfile,
/// Operating mode
pub mode: Mode,
/// Type of block verifier used by client.
pub verifier_type: VerifierType,
}

View File

@ -16,22 +16,20 @@
//! Blockchain database client.
mod client;
mod config;
mod error;
mod test_client;
mod trace;
pub use self::client::*;
pub use self::config::{ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::error::Error;
pub use types::ids::*;
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use self::trace::Filter as TraceFilter;
pub use types::trace_filter::Filter as TraceFilter;
pub use executive::{Executed, Executive, TransactOptions};
pub use env_info::{LastHashes, EnvInfo};
use std::path::Path;
use util::bytes::Bytes;
use util::hash::{Address, H256, H2048};
use util::numbers::U256;
@ -39,31 +37,34 @@ use util::Itertools;
use blockchain::TreeRoute;
use block_queue::BlockQueueInfo;
use block::{OpenBlock, SealedBlock};
use header::{BlockNumber, Header};
use header::{BlockNumber};
use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry;
use filter::Filter;
use views::{HeaderView, BlockView};
use views::{BlockView};
use error::{ImportResult, ExecutionError};
use receipt::LocalizedReceipt;
use trace::LocalizedTrace;
use evm::Factory as EvmFactory;
use miner::{TransactionImportResult};
use error::Error as EthError;
pub use types::call_analytics::CallAnalytics;
pub use block_import_error::BlockImportError;
pub use transaction_import::TransactionImportResult;
pub use transaction_import::TransactionImportError;
/// Options concerning what analytics we run on the call.
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)]
pub struct CallAnalytics {
/// Make a transaction trace.
pub transaction_tracing: bool,
/// Make a VM trace.
pub vm_tracing: bool,
/// Make a diff.
pub state_diffing: bool,
pub mod client {
//! Blockchain database client.
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
include!(concat!(env!("OUT_DIR"), "/client.ipc.rs"));
}
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
/// Should be called by any external-facing interface when actively using the client.
/// To minimise chatter, there's no need to call more than once every 30s.
fn keep_alive(&self) {}
/// Get raw block header data by block id.
fn block_header(&self, id: BlockID) -> Option<Bytes>;
@ -127,7 +128,7 @@ pub trait BlockChainClient : Sync + Send {
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
/// Get uncle with given id.
fn uncle(&self, id: UncleID) -> Option<Header>;
fn uncle(&self, id: UncleID) -> Option<Bytes>;
/// Get transaction receipt with given hash.
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt>;
@ -146,7 +147,7 @@ pub trait BlockChainClient : Sync + Send {
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
/// Import a block into the blockchain.
fn import_block(&self, bytes: Bytes) -> ImportResult;
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError>;
/// Get block queue information.
fn queue_info(&self) -> BlockQueueInfo;
@ -188,16 +189,9 @@ 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>>;
/// Queue transactions for importing.
fn queue_transactions(&self, transactions: Vec<Bytes>);
/// Generate a PV64 snapshot for the current best block, saving it within the
/// root directory, whose path is given.
fn take_snapshot(&self, root_dir: &Path);
/// list all transactions
fn pending_transactions(&self) -> Vec<SignedTransaction>;
@ -226,28 +220,6 @@ pub trait BlockChainClient : Sync + Send {
Err(())
}
}
/// Get `Some` gas limit of SOFT_FORK_BLOCK, or `None` if chain is not yet that long.
fn dao_rescue_block_gas_limit(&self, chain_hash: H256) -> Option<U256> {
const SOFT_FORK_BLOCK: u64 = 1800000;
// shortcut if the canon chain is already known.
if self.chain_info().best_block_number > SOFT_FORK_BLOCK + 1000 {
return self.block_header(BlockID::Number(SOFT_FORK_BLOCK)).map(|header| HeaderView::new(&header).gas_limit());
}
// otherwise check according to `chain_hash`.
if let Some(mut header) = self.block_header(BlockID::Hash(chain_hash)) {
if HeaderView::new(&header).number() < SOFT_FORK_BLOCK {
None
} else {
while HeaderView::new(&header).number() != SOFT_FORK_BLOCK {
header = self.block_header(BlockID::Hash(HeaderView::new(&header).parent_hash())).expect("chain is complete; parent of chain entry must be in chain; qed");
}
Some(HeaderView::new(&header).gas_limit())
}
} else {
None
}
}
}
/// Extended client interface used for mining

View File

@ -20,7 +20,9 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
use util::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute;
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics};
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID,
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics,
BlockImportError};
use header::{Header as BlockHeader, BlockNumber};
use filter::Filter;
use log_entry::LocalizedLogEntry;
@ -34,12 +36,9 @@ use spec::Spec;
use block_queue::BlockQueueInfo;
use block::{OpenBlock, SealedBlock};
use executive::Executed;
use error::{ExecutionError};
use error::ExecutionError;
use trace::LocalizedTrace;
use miner::{TransactionImportResult, AccountDetails};
use error::Error as EthError;
/// Test client.
pub struct TestBlockChainClient {
/// Blocks.
@ -244,7 +243,7 @@ impl MiningBlockChainClient for TestBlockChainClient {
fn prepare_open_block(&self, _author: Address, _gas_range_target: (U256, U256), _extra_data: Bytes) -> OpenBlock {
unimplemented!();
}
fn vm_factory(&self) -> &EvmFactory {
unimplemented!();
}
@ -274,6 +273,10 @@ impl BlockChainClient for TestBlockChainClient {
}
}
fn latest_nonce(&self, address: &Address) -> U256 {
self.nonce(address, BlockID::Latest).unwrap()
}
fn code(&self, address: &Address) -> Option<Bytes> {
self.code.read().unwrap().get(address).cloned()
}
@ -286,6 +289,10 @@ impl BlockChainClient for TestBlockChainClient {
}
}
fn latest_balance(&self, address: &Address) -> U256 {
self.balance(address, BlockID::Latest).unwrap()
}
fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256> {
if let BlockID::Latest = id {
Some(self.storage.read().unwrap().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new))
@ -298,7 +305,7 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!();
}
fn uncle(&self, _id: UncleID) -> Option<BlockHeader> {
fn uncle(&self, _id: UncleID) -> Option<Bytes> {
unimplemented!();
}
@ -402,7 +409,7 @@ impl BlockChainClient for TestBlockChainClient {
None
}
fn import_block(&self, b: Bytes) -> ImportResult {
fn import_block(&self, b: Bytes) -> Result<H256, BlockImportError> {
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
let h = header.hash();
let number: usize = header.number as usize;
@ -487,25 +494,10 @@ impl BlockChainClient for TestBlockChainClient {
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(self, transactions, &fetch_account)
}
fn queue_transactions(&self, transactions: Vec<Bytes>) {
// import right here
let tx = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
self.import_transactions(tx);
}
fn take_snapshot(&self, _root_dir: &Path) {
unimplemented!()
let txs = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
self.miner.import_external_transactions(self, txs);
}
fn pending_transactions(&self) -> Vec<SignedTransaction> {

View File

@ -1,13 +1,12 @@
//! Bridge between Tracedb and Blockchain.
use std::ops::Range;
use util::{Address, H256};
use util::{H256};
use header::BlockNumber;
use trace::DatabaseExtras as TraceDatabaseExtras;
use blockchain::{BlockChain, BlockProvider};
use blockchain::extras::TransactionAddress;
use super::BlockID;
pub use types::trace_filter::Filter;
impl TraceDatabaseExtras for BlockChain {
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
@ -26,13 +25,3 @@ impl TraceDatabaseExtras for BlockChain {
.map(|tx| tx.hash())
}
}
/// Easy to use trace filter.
pub struct Filter {
/// Range of filtering.
pub range: Range<BlockID>,
/// From address.
pub from_address: Vec<Address>,
/// To address.
pub to_address: Vec<Address>,
}

View File

@ -39,9 +39,6 @@ pub struct EnvInfo {
pub last_hashes: LastHashes,
/// The gas used.
pub gas_used: U256,
/// Block gas limit at DAO rescue block SOFT_FORK_BLOCK or None if not yet there.
pub dao_rescue_block_gas_limit: Option<U256>,
}
impl Default for EnvInfo {
@ -54,7 +51,6 @@ impl Default for EnvInfo {
gas_limit: 0.into(),
last_hashes: vec![],
gas_used: 0.into(),
dao_rescue_block_gas_limit: None,
}
}
}
@ -70,7 +66,6 @@ impl From<ethjson::vm::Env> for EnvInfo {
timestamp: e.timestamp.into(),
last_hashes: (1..cmp::min(number + 1, 257)).map(|i| format!("{}", number - i).as_bytes().sha3()).collect(),
gas_used: U256::zero(),
dao_rescue_block_gas_limit: None,
}
}
}

View File

@ -20,10 +20,11 @@ use util::*;
use header::BlockNumber;
use basic_types::LogBloom;
use client::Error as ClientError;
use ipc::binary::{BinaryConvertError, BinaryConvertable};
use types::block_import_error::BlockImportError;
pub use types::executed::ExecutionError;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
/// Errors concerning transaction processing.
pub enum TransactionError {
/// Transaction is already imported to the queue
@ -330,6 +331,20 @@ impl From<::util::snappy::InvalidInput> for Error {
}
}
impl From<BlockImportError> for Error {
fn from(err: BlockImportError) -> Error {
match err {
BlockImportError::Block(e) => Error::Block(e),
BlockImportError::Import(e) => Error::Import(e),
BlockImportError::Other(s) => Error::Util(UtilError::SimpleString(s)),
}
}
}
binary_fixed_size!(BlockError);
binary_fixed_size!(ImportError);
binary_fixed_size!(TransactionError);
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
/*#![feature(concat_idents)]
macro_rules! assimilate {

View File

@ -39,8 +39,6 @@ pub struct EthashParams {
pub registrar: Address,
/// Homestead transition block number.
pub frontier_compatibility_mode_limit: u64,
/// Enable the soft-fork logic.
pub dao_rescue_soft_fork: bool,
}
impl From<ethjson::spec::EthashParams> for EthashParams {
@ -53,7 +51,6 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
block_reward: p.block_reward.into(),
registrar: p.registrar.into(),
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(),
dao_rescue_soft_fork: p.dao_rescue_soft_fork.into(),
}
}
}
@ -102,11 +99,7 @@ impl Engine for Ethash {
if env_info.number < self.ethash_params.frontier_compatibility_mode_limit {
Schedule::new_frontier()
} else {
let mut s = Schedule::new_homestead();
if self.ethash_params.dao_rescue_soft_fork {
s.reject_dao_transactions = env_info.dao_rescue_block_gas_limit.map_or(false, |x| x <= 4_000_000.into());
}
s
Schedule::new_homestead()
}
}
@ -325,7 +318,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, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
}
@ -340,7 +333,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, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut uncle = Header::new();
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
uncle.author = uncle_author.clone();
@ -369,7 +362,6 @@ mod tests {
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into(),
dao_rescue_block_gas_limit: None,
});
assert!(schedule.stack_limit > 0);
@ -382,7 +374,6 @@ mod tests {
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into(),
dao_rescue_block_gas_limit: None,
});
assert!(!schedule.have_delegate_call);

View File

@ -33,11 +33,8 @@ use super::spec::*;
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
/// Create a new Frontier mainnet chain spec.
pub fn new_frontier(dao_rescue: bool) -> Spec {
Spec::load(match dao_rescue {
true => include_bytes!("../../res/ethereum/frontier_dao_rescue.json"),
false => include_bytes!("../../res/ethereum/frontier.json"),
})
pub fn new_frontier() -> Spec {
Spec::load(include_bytes!("../../res/ethereum/frontier.json"))
}
/// Create a new Frontier chain spec as though it never changes to Homestead.
@ -67,7 +64,7 @@ mod tests {
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut());
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce()).unwrap();
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce(), Default::default()).unwrap();
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000003")), U256::from(1u64));
@ -89,7 +86,7 @@ mod tests {
#[test]
fn frontier() {
let frontier = new_frontier(true);
let frontier = new_frontier();
assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap());
let genesis = frontier.genesis_block();

View File

@ -0,0 +1,126 @@
// 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/>.
//! benchmarking for EVM
//! should be started with:
//! ```bash
//! multirust run nightly cargo bench
//! ```
extern crate test;
use self::test::{Bencher, black_box};
use common::*;
use evm::{self, Factory, VMType};
use evm::tests::FakeExt;
#[bench]
fn simple_loop_log0_usize(b: &mut Bencher) {
simple_loop_log0(U256::from(::std::usize::MAX), b)
}
#[bench]
fn simple_loop_log0_u256(b: &mut Bencher) {
simple_loop_log0(!U256::zero(), b)
}
fn simple_loop_log0(gas: U256, b: &mut Bencher) {
let mut vm = Factory::new(VMType::Interpreter).create(gas);
let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = black_box(
"62ffffff5b600190036000600fa0600357".from_hex().unwrap()
);
b.iter(|| {
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = gas;
params.code = Some(code.clone());
result(vm.exec(params, &mut ext))
});
}
#[bench]
fn mem_gas_calculation_same_usize(b: &mut Bencher) {
mem_gas_calculation_same(U256::from(::std::usize::MAX), b)
}
#[bench]
fn mem_gas_calculation_same_u256(b: &mut Bencher) {
mem_gas_calculation_same(!U256::zero(), b)
}
fn mem_gas_calculation_same(gas: U256, b: &mut Bencher) {
let mut vm = Factory::new(VMType::Interpreter).create(gas);
let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
b.iter(|| {
let code = black_box(
"6110006001556001546000555b610fff805560016000540380600055600c57".from_hex().unwrap()
);
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = gas;
params.code = Some(code.clone());
result(vm.exec(params, &mut ext))
});
}
#[bench]
fn mem_gas_calculation_increasing_usize(b: &mut Bencher) {
mem_gas_calculation_increasing(U256::from(::std::usize::MAX), b)
}
#[bench]
fn mem_gas_calculation_increasing_u256(b: &mut Bencher) {
mem_gas_calculation_increasing(!U256::zero(), b)
}
fn mem_gas_calculation_increasing(gas: U256, b: &mut Bencher) {
let mut vm = Factory::new(VMType::Interpreter).create(gas);
let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
b.iter(|| {
let code = black_box(
"6110006001556001546000555b610fff60005401805560016000540380600055600c57".from_hex().unwrap()
);
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = gas;
params.code = Some(code.clone());
result(vm.exec(params, &mut ext))
});
}
fn result(r: evm::Result<evm::GasLeft>) -> U256 {
match r {
Ok(evm::GasLeft::Known(v)) => v,
Ok(evm::GasLeft::NeedsReturn(v, _)) => v,
_ => U256::zero(),
}
}

View File

@ -95,6 +95,61 @@ impl<'a> Finalize for Result<GasLeft<'a>> {
}
}
pub trait CostType: ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> + ops::Sub<Output=Self> + ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self> + cmp::Ord + Sized + From<usize> + Copy {
fn as_u256(&self) -> U256;
fn from_u256(val: U256) -> Result<Self>;
fn as_usize(&self) -> usize;
fn overflow_add(self, other: Self) -> (Self, bool);
fn overflow_mul(self, other: Self) -> (Self, bool);
}
impl CostType for U256 {
fn as_u256(&self) -> U256 {
*self
}
fn from_u256(val: U256) -> Result<Self> {
Ok(val)
}
fn as_usize(&self) -> usize {
self.as_u64() as usize
}
fn overflow_add(self, other: Self) -> (Self, bool) {
Uint::overflowing_add(self, other)
}
fn overflow_mul(self, other: Self) -> (Self, bool) {
Uint::overflowing_mul(self, other)
}
}
impl CostType for usize {
fn as_u256(&self) -> U256 {
U256::from(*self)
}
fn from_u256(val: U256) -> Result<Self> {
if U256::from(val.low_u64()) != val {
return Err(Error::OutOfGas);
}
Ok(val.low_u64() as usize)
}
fn as_usize(&self) -> usize {
*self
}
fn overflow_add(self, other: Self) -> (Self, bool) {
self.overflowing_add(other)
}
fn overflow_mul(self, other: Self) -> (Self, bool) {
self.overflowing_mul(other)
}
}
/// Evm interface
pub trait Evm {
/// This function should be used to execute transaction.

View File

@ -19,6 +19,7 @@
//! TODO: consider spliting it into two separate files.
use std::fmt;
use evm::Evm;
use util::{U256, Uint};
#[derive(Debug, Clone)]
/// Type of EVM to use.
@ -85,24 +86,30 @@ pub struct Factory {
impl Factory {
/// Create fresh instance of VM
/// Might choose implementation depending on supplied gas.
#[cfg(feature = "jit")]
pub fn create(&self) -> Box<Evm> {
pub fn create(&self, gas: U256) -> Box<Evm> {
match self.evm {
VMType::Jit => {
Box::new(super::jit::JitEvm::default())
},
VMType::Interpreter => {
Box::new(super::interpreter::Interpreter::default())
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
Box::new(super::interpreter::Interpreter::<usize>::default())
} else {
Box::new(super::interpreter::Interpreter::<U256>::default())
}
}
}
/// Create fresh instance of VM
/// Might choose implementation depending on supplied gas.
#[cfg(not(feature = "jit"))]
pub fn create(&self) -> Box<Evm> {
pub fn create(&self, gas: U256) -> Box<Evm> {
match self.evm {
VMType::Interpreter => {
Box::new(super::interpreter::Interpreter::default())
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
Box::new(super::interpreter::Interpreter::<usize>::default())
} else {
Box::new(super::interpreter::Interpreter::<U256>::default())
}
}
}
@ -113,6 +120,10 @@ impl Factory {
evm: evm
}
}
fn can_fit_in_usize(gas: U256) -> bool {
gas == U256::from(gas.low_u64() as usize)
}
}
impl Default for Factory {
@ -135,7 +146,7 @@ impl Default for Factory {
#[test]
fn test_create_vm() {
let _vm = Factory::default().create();
let _vm = Factory::default().create(U256::zero());
}
/// Create tests by injecting different VM factories

View File

@ -79,7 +79,7 @@ fn test_get_log_topics() {
assert_eq!(get_log_topics(LOG4), 4);
}
#[derive(PartialEq)]
#[derive(PartialEq, Clone, Copy)]
pub enum GasPriceTier {
/// 0 Zero
Zero,

View File

@ -0,0 +1,261 @@
// 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 common::*;
use super::u256_to_address;
use evm::{self, CostType};
use evm::instructions::{self, Instruction, InstructionInfo};
use evm::interpreter::stack::Stack;
macro_rules! overflowing {
($x: expr) => {{
let (v, overflow) = $x;
if overflow { return Err(evm::Error::OutOfGas); }
v
}}
}
#[cfg_attr(feature="dev", allow(enum_variant_names))]
enum InstructionCost<Cost: CostType> {
Gas(Cost),
GasMem(Cost, Cost),
GasMemCopy(Cost, Cost, Cost)
}
pub struct Gasometer<Gas: CostType> {
pub current_gas: Gas,
}
impl<Gas: CostType> Gasometer<Gas> {
pub fn new(current_gas: Gas) -> Self {
Gasometer {
current_gas: current_gas,
}
}
pub fn verify_gas(&self, gas_cost: &Gas) -> evm::Result<()> {
match &self.current_gas < gas_cost {
true => Err(evm::Error::OutOfGas),
false => Ok(())
}
}
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
pub fn get_gas_cost_mem(
&mut self,
ext: &evm::Ext,
instruction: Instruction,
info: &InstructionInfo,
stack: &Stack<U256>,
current_mem_size: usize,
) -> evm::Result<(Gas, usize)> {
let schedule = ext.schedule();
let tier = instructions::get_tier_idx(info.tier);
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
let cost = match instruction {
instructions::SSTORE => {
let address = H256::from(stack.peek(0));
let newval = stack.peek(1);
let val = U256::from(ext.storage_at(&address).as_slice());
let gas = if U256::zero() == val && &U256::zero() != newval {
schedule.sstore_set_gas
} else {
// Refund for below case is added when actually executing sstore
// !self.is_zero(&val) && self.is_zero(newval)
schedule.sstore_reset_gas
};
InstructionCost::Gas(Gas::from(gas))
},
instructions::SLOAD => {
InstructionCost::Gas(Gas::from(schedule.sload_gas))
},
instructions::MSTORE | instructions::MLOAD => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 32)))
},
instructions::MSTORE8 => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 1)))
},
instructions::RETURN => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::SHA3 => {
let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31));
let words = w >> 5;
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::CALLDATACOPY | instructions::CODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
},
instructions::EXTCODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
},
instructions::JUMPDEST => {
InstructionCost::Gas(Gas::from(1))
},
instructions::LOG0...instructions::LOG4 => {
let no_of_topics = instructions::get_log_topics(instruction);
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas)));
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::CALL | instructions::CALLCODE => {
let mut gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
let mem = cmp::max(
try!(self.mem_needed(stack.peek(5), stack.peek(6))),
try!(self.mem_needed(stack.peek(3), stack.peek(4)))
);
let address = u256_to_address(stack.peek(1));
if instruction == instructions::CALL && !ext.exists(&address) {
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_new_account_gas)));
};
if stack.peek(2) > &U256::zero() {
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_value_transfer_gas)));
};
InstructionCost::GasMem(gas,mem)
},
instructions::DELEGATECALL => {
let gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
let mem = cmp::max(
try!(self.mem_needed(stack.peek(4), stack.peek(5))),
try!(self.mem_needed(stack.peek(2), stack.peek(3)))
);
InstructionCost::GasMem(gas, mem)
},
instructions::CREATE => {
let gas = Gas::from(schedule.create_gas);
let mem = try!(self.mem_needed(stack.peek(1), stack.peek(2)));
InstructionCost::GasMem(gas, mem)
},
instructions::EXP => {
let expon = stack.peek(1);
let bytes = ((expon.bits() + 7) / 8) as usize;
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
InstructionCost::Gas(gas)
},
_ => InstructionCost::Gas(default_gas)
};
match cost {
InstructionCost::Gas(gas) => {
Ok((gas, 0))
},
InstructionCost::GasMem(gas, mem_size) => {
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
let gas = overflowing!(gas.overflow_add(mem_gas));
Ok((gas, new_mem_size))
},
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
let copy = overflowing!(add_gas_usize(copy, 31));
let copy_gas = Gas::from(schedule.copy_gas) * (copy / Gas::from(32 as usize));
let gas = overflowing!(gas.overflow_add(copy_gas));
let gas = overflowing!(gas.overflow_add(mem_gas));
Ok((gas, new_mem_size))
}
}
}
fn is_zero(&self, val: &Gas) -> bool {
&Gas::from(0) == val
}
fn mem_needed_const(&self, mem: &U256, add: usize) -> evm::Result<Gas> {
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
}
fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result<Gas> {
if self.is_zero(&try!(Gas::from_u256(*size))) {
return Ok(Gas::from(0));
}
Gas::from_u256(overflowing!(offset.overflowing_add(*size)))
}
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, usize)> {
let gas_for_mem = |mem_size: Gas| {
let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div
let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas)));
// We need to go to U512 to calculate s*s/quad_coeff_div
let b = U512::from(s.as_u256()) * U512::from(s.as_u256()) / U512::from(schedule.quad_coeff_div);
if b > U512::from(!U256::zero()) {
Err(evm::Error::OutOfGas)
} else {
Ok(overflowing!(a.overflow_add(try!(Gas::from_u256(U256::from(b))))))
}
};
let current_mem_size = Gas::from(current_mem_size);
let req_mem_size_rounded = (overflowing!(mem_size.overflow_add(Gas::from(31 as usize))) >> 5) << 5;
let mem_gas_cost = if req_mem_size_rounded > current_mem_size {
let new_mem_gas = try!(gas_for_mem(req_mem_size_rounded));
let current_mem_gas = try!(gas_for_mem(current_mem_size));
new_mem_gas - current_mem_gas
} else {
Gas::from(0)
};
Ok((mem_gas_cost, req_mem_size_rounded.as_usize()))
}
}
#[inline]
fn add_gas_usize<Gas: CostType>(value: Gas, num: usize) -> (Gas, bool) {
value.overflow_add(Gas::from(num))
}
#[test]
fn test_mem_gas_cost() {
// given
let gasometer = Gasometer::<U256>::new(U256::zero());
let schedule = evm::Schedule::default();
let current_mem_size = 5;
let mem_size = !U256::zero();
// when
let result = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size);
// then
if let Ok(_) = result {
assert!(false, "Should fail with OutOfGas");
}
}
#[test]
fn test_calculate_mem_cost() {
// given
let gasometer = Gasometer::<usize>::new(0);
let schedule = evm::Schedule::default();
let current_mem_size = 0;
let mem_size = 5;
// when
let (mem_cost, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
// then
assert_eq!(mem_cost, 3);
assert_eq!(mem_size, 32);
}

View File

@ -0,0 +1,150 @@
// 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 util::{U256, Uint};
pub trait Memory {
/// Retrieve current size of the memory
fn size(&self) -> usize;
/// Resize (shrink or expand) the memory to specified size (fills 0)
fn resize(&mut self, new_size: usize);
/// Resize the memory only if its smaller
fn expand(&mut self, new_size: usize);
/// Write single byte to memory
fn write_byte(&mut self, offset: U256, value: U256);
/// Write a word to memory. Does not resize memory!
fn write(&mut self, offset: U256, value: U256);
/// Read a word from memory
fn read(&self, offset: U256) -> U256;
/// Write slice of bytes to memory. Does not resize memory!
fn write_slice(&mut self, offset: U256, &[u8]);
/// Retrieve part of the memory between offset and offset + size
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
/// Retrieve writeable part of memory
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
fn dump(&self);
}
/// Checks whether offset and size is valid memory range
fn is_valid_range(off: usize, size: usize) -> bool {
// When size is zero we haven't actually expanded the memory
let overflow = off.overflowing_add(size).1;
size > 0 && !overflow
}
impl Memory for Vec<u8> {
fn dump(&self) {
println!("MemoryDump:");
for i in self.iter() {
println!("{:02x} ", i);
}
println!("");
}
fn size(&self) -> usize {
self.len()
}
fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] {
let off = init_off_u.low_u64() as usize;
let size = init_size_u.low_u64() as usize;
if !is_valid_range(off, size) {
&self[0..0]
} else {
&self[off..off+size]
}
}
fn read(&self, offset: U256) -> U256 {
let off = offset.low_u64() as usize;
U256::from(&self[off..off+32])
}
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
let off = offset.low_u64() as usize;
let s = size.low_u64() as usize;
if !is_valid_range(off, s) {
&mut self[0..0]
} else {
&mut self[off..off+s]
}
}
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
let off = offset.low_u64() as usize;
// TODO [todr] Optimize?
for pos in off..off+slice.len() {
self[pos] = slice[pos - off];
}
}
fn write(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
let mut val = value;
let end = off + 32;
for pos in 0..32 {
self[end - pos - 1] = val.low_u64() as u8;
val = val >> 8;
}
}
fn write_byte(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
let val = value.low_u64() as u64;
self[off] = val as u8;
}
fn resize(&mut self, new_size: usize) {
self.resize(new_size, 0);
}
fn expand(&mut self, size: usize) {
if size > self.len() {
Memory::resize(self, size)
}
}
}
#[test]
fn test_memory_read_and_write() {
// given
let mem: &mut Memory = &mut vec![];
mem.resize(0x80 + 32);
// when
mem.write(U256::from(0x80), U256::from(0xabcdef));
// then
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
}
#[test]
fn test_memory_read_and_write_byte() {
// given
let mem: &mut Memory = &mut vec![];
mem.resize(32);
// when
mem.write_byte(U256::from(0x1d), U256::from(0xab));
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
mem.write_byte(U256::from(0x1f), U256::from(0xef));
// then
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
}

View File

@ -16,12 +16,6 @@
///! Rust VM implementation
use common::*;
use super::instructions as instructions;
use super::instructions::{Instruction, get_info};
use std::marker::Copy;
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft};
#[cfg(not(feature = "evm-debug"))]
macro_rules! evm_debug {
($x: expr) => {}
@ -34,6 +28,19 @@ macro_rules! evm_debug {
}
}
mod gasometer;
mod stack;
mod memory;
use self::gasometer::Gasometer;
use self::stack::{Stack, VecStack};
use self::memory::Memory;
use std::marker::PhantomData;
use common::*;
use super::instructions::{self, Instruction, InstructionInfo};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
#[cfg(feature = "evm-debug")]
fn color(instruction: Instruction, name: &'static str) -> String {
let c = instruction as usize % 6;
@ -41,209 +48,9 @@ fn color(instruction: Instruction, name: &'static str) -> String {
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name)
}
macro_rules! overflowing {
($x: expr) => {{
let (v, overflow) = $x;
if overflow { return Err(evm::Error::OutOfGas); }
v
}}
}
type CodePosition = usize;
type Gas = U256;
type ProgramCounter = usize;
/// Stack trait with VM-friendly API
trait Stack<T> {
/// Returns `Stack[len(Stack) - no_from_top]`
fn peek(&self, no_from_top: usize) -> &T;
/// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top]
fn swap_with_top(&mut self, no_from_top: usize);
/// Returns true if Stack has at least `no_of_elems` elements
fn has(&self, no_of_elems: usize) -> bool;
/// Get element from top and remove it from Stack. Panics if stack is empty.
fn pop_back(&mut self) -> T;
/// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty.
fn pop_n(&mut self, no_of_elems: usize) -> &[T];
/// Add element on top of the Stack
fn push(&mut self, elem: T);
/// Get number of elements on Stack
fn size(&self) -> usize;
/// Returns all data on stack.
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
}
struct VecStack<S> {
stack: Vec<S>,
logs: [S; instructions::MAX_NO_OF_TOPICS]
}
impl<S : Copy> VecStack<S> {
fn with_capacity(capacity: usize, zero: S) -> Self {
VecStack {
stack: Vec::with_capacity(capacity),
logs: [zero; instructions::MAX_NO_OF_TOPICS]
}
}
}
impl<S : fmt::Display> Stack<S> for VecStack<S> {
fn peek(&self, no_from_top: usize) -> &S {
&self.stack[self.stack.len() - no_from_top - 1]
}
fn swap_with_top(&mut self, no_from_top: usize) {
let len = self.stack.len();
self.stack.swap(len - no_from_top - 1, len - 1);
}
fn has(&self, no_of_elems: usize) -> bool {
self.stack.len() >= no_of_elems
}
fn pop_back(&mut self) -> S {
let val = self.stack.pop();
match val {
Some(x) => {
evm_debug!({
println!(" POP: {}", x)
});
x
},
None => panic!("Tried to pop from empty stack.")
}
}
fn pop_n(&mut self, no_of_elems: usize) -> &[S] {
assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS);
for i in 0..no_of_elems {
self.logs[i] = self.pop_back();
}
&self.logs[0..no_of_elems]
}
fn push(&mut self, elem: S) {
evm_debug!({
println!(" PUSH: {}", elem)
});
self.stack.push(elem);
}
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 {
/// Retrieve current size of the memory
fn size(&self) -> usize;
/// Resize (shrink or expand) the memory to specified size (fills 0)
fn resize(&mut self, new_size: usize);
/// Resize the memory only if its smaller
fn expand(&mut self, new_size: usize);
/// Write single byte to memory
fn write_byte(&mut self, offset: U256, value: U256);
/// Write a word to memory. Does not resize memory!
fn write(&mut self, offset: U256, value: U256);
/// Read a word from memory
fn read(&self, offset: U256) -> U256;
/// Write slice of bytes to memory. Does not resize memory!
fn write_slice(&mut self, offset: U256, &[u8]);
/// Retrieve part of the memory between offset and offset + size
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
/// Retrieve writeable part of memory
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
fn dump(&self);
}
/// Checks whether offset and size is valid memory range
fn is_valid_range(off: usize, size: usize) -> bool {
// When size is zero we haven't actually expanded the memory
let overflow = off.overflowing_add(size).1;
size > 0 && !overflow
}
impl Memory for Vec<u8> {
fn dump(&self) {
println!("MemoryDump:");
for i in self.iter() {
println!("{:02x} ", i);
}
println!("");
}
fn size(&self) -> usize {
self.len()
}
fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] {
let off = init_off_u.low_u64() as usize;
let size = init_size_u.low_u64() as usize;
if !is_valid_range(off, size) {
&self[0..0]
} else {
&self[off..off+size]
}
}
fn read(&self, offset: U256) -> U256 {
let off = offset.low_u64() as usize;
U256::from(&self[off..off+32])
}
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
let off = offset.low_u64() as usize;
let s = size.low_u64() as usize;
if !is_valid_range(off, s) {
&mut self[0..0]
} else {
&mut self[off..off+s]
}
}
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
let off = offset.low_u64() as usize;
// TODO [todr] Optimize?
for pos in off..off+slice.len() {
self[pos] = slice[pos - off];
}
}
fn write(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
let mut val = value;
let end = off + 32;
for pos in 0..32 {
self[end - pos - 1] = val.low_u64() as u8;
val = val >> 8;
}
}
fn write_byte(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
let val = value.low_u64() as u64;
self[off] = val as u8;
}
fn resize(&mut self, new_size: usize) {
self.resize(new_size, 0);
}
fn expand(&mut self, size: usize) {
if size > self.len() {
Memory::resize(self, size)
}
}
}
/// Abstraction over raw vector of Bytes. Easier state management of PC.
struct CodeReader<'a> {
position: ProgramCounter,
@ -265,38 +72,33 @@ impl<'a> CodeReader<'a> {
}
}
#[cfg_attr(feature="dev", allow(enum_variant_names))]
enum InstructionCost {
Gas(U256),
GasMem(U256, U256),
GasMemCopy(U256, U256, U256)
}
enum InstructionResult {
enum InstructionResult<Gas> {
Ok,
UseAllGas,
GasLeft(U256),
UnusedGas(U256),
GasLeft(Gas),
UnusedGas(Gas),
JumpToPosition(U256),
// gas left, init_orf, init_size
StopExecutionNeedsReturn(U256, U256, U256),
StopExecutionNeedsReturn(Gas, U256, U256),
StopExecution,
}
/// Intepreter EVM implementation
#[derive(Default)]
pub struct Interpreter {
pub struct Interpreter<Cost: CostType> {
mem: Vec<u8>,
_type: PhantomData<Cost>,
}
impl evm::Evm for Interpreter {
impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result<GasLeft> {
self.mem.clear();
let code = &params.code.as_ref().unwrap();
let valid_jump_destinations = self.find_jump_destinations(&code);
let mut current_gas = params.gas;
let mut gasometer = Gasometer::<Cost>::new(try!(Cost::from_u256(params.gas)));
let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero());
let mut reader = CodeReader {
position: 0,
@ -305,26 +107,27 @@ impl evm::Evm for Interpreter {
while reader.position < code.len() {
let instruction = code[reader.position];
// Calculate gas cost
let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &stack));
// TODO: make compile-time removable if too much of a performance hit.
let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost);
reader.position += 1;
try!(self.verify_gas(&current_gas, &gas_cost));
let info = instructions::get_info(instruction);
try!(self.verify_instruction(ext, instruction, &info, &stack));
// Calculate gas cost
let (gas_cost, mem_size) = try!(gasometer.get_gas_cost_mem(ext, instruction, &info, &stack, self.mem.size()));
// TODO: make compile-time removable if too much of a performance hit.
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256());
try!(gasometer.verify_gas(&gas_cost));
self.mem.expand(mem_size);
current_gas = current_gas - gas_cost; //TODO: use operator -=
gasometer.current_gas = gasometer.current_gas - gas_cost;
evm_debug!({
println!("[0x{:x}][{}(0x{:x}) Gas: {:x}\n Gas Before: {:x}",
reader.position,
color(instruction, instructions::get_info(instruction).name),
color(instruction, info.name),
instruction,
gas_cost,
current_gas + gas_cost
gasometer.current_gas + gas_cost
);
});
@ -335,50 +138,44 @@ impl evm::Evm for Interpreter {
// Execute instruction
let result = try!(self.exec_instruction(
current_gas, &params, ext, instruction, &mut reader, &mut stack
gasometer.current_gas, &params, ext, instruction, &mut reader, &mut stack
));
if trace_executed {
ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
}
// Advance
match result {
InstructionResult::Ok => {},
InstructionResult::UnusedGas(gas) => {
current_gas = current_gas + gas; //TODO: use operator +=
gasometer.current_gas = gasometer.current_gas + gas;
},
InstructionResult::UseAllGas => {
current_gas = U256::zero();
gasometer.current_gas = Cost::from(0);
},
InstructionResult::GasLeft(gas_left) => {
current_gas = gas_left;
gasometer.current_gas = gas_left;
},
InstructionResult::JumpToPosition(position) => {
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
reader.position = pos;
},
InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
return Ok(GasLeft::NeedsReturn(gas, self.mem.read_slice(off, size)));
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
},
InstructionResult::StopExecution => break,
}
}
Ok(GasLeft::Known(current_gas))
Ok(GasLeft::Known(gasometer.current_gas.as_u256()))
}
}
impl Interpreter {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn get_gas_cost_mem(
&mut self,
ext: &evm::Ext,
instruction: Instruction,
stack: &Stack<U256>
) -> evm::Result<(U256, usize)> {
impl<Cost: CostType> Interpreter<Cost> {
fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> evm::Result<()> {
let schedule = ext.schedule();
let info = instructions::get_info(instruction);
if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL {
return Err(evm::Error::BadInstruction {
@ -391,119 +188,20 @@ impl Interpreter {
});
}
try!(self.verify_instructions_requirements(&info, schedule.stack_limit, stack));
let tier = instructions::get_tier_idx(info.tier);
let default_gas = U256::from(schedule.tier_step_gas[tier]);
let cost = match instruction {
instructions::SSTORE => {
let address = H256::from(stack.peek(0));
let newval = stack.peek(1);
let val = U256::from(ext.storage_at(&address).as_slice());
let gas = if self.is_zero(&val) && !self.is_zero(newval) {
schedule.sstore_set_gas
} else {
// Refund for below case is added when actually executing sstore
// !self.is_zero(&val) && self.is_zero(newval)
schedule.sstore_reset_gas
};
InstructionCost::Gas(U256::from(gas))
},
instructions::SLOAD => {
InstructionCost::Gas(U256::from(schedule.sload_gas))
},
instructions::MSTORE | instructions::MLOAD => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 32)))
},
instructions::MSTORE8 => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 1)))
},
instructions::RETURN => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::SHA3 => {
let w = overflowing!(add_u256_usize(stack.peek(1), 31));
let words = w >> 5;
let gas = U256::from(schedule.sha3_gas) + (U256::from(schedule.sha3_word_gas) * words);
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::CALLDATACOPY | instructions::CODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(2))), stack.peek(2).clone())
},
instructions::EXTCODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), stack.peek(3).clone())
},
instructions::JUMPDEST => {
InstructionCost::Gas(U256::one())
},
instructions::LOG0...instructions::LOG4 => {
let no_of_topics = instructions::get_log_topics(instruction);
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
let data_gas = overflowing!(stack.peek(1).overflowing_mul(U256::from(schedule.log_data_gas)));
let gas = overflowing!(data_gas.overflowing_add(U256::from(log_gas)));
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::CALL | instructions::CALLCODE => {
let mut gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas));
let mem = cmp::max(
try!(self.mem_needed(stack.peek(5), stack.peek(6))),
try!(self.mem_needed(stack.peek(3), stack.peek(4)))
);
let address = u256_to_address(stack.peek(1));
if instruction == instructions::CALL && !ext.exists(&address) {
gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_new_account_gas)));
};
if stack.peek(2).clone() > U256::zero() {
gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_value_transfer_gas)));
};
InstructionCost::GasMem(gas,mem)
},
instructions::DELEGATECALL => {
let gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas));
let mem = cmp::max(
try!(self.mem_needed(stack.peek(4), stack.peek(5))),
try!(self.mem_needed(stack.peek(2), stack.peek(3)))
);
InstructionCost::GasMem(gas, mem)
},
instructions::CREATE => {
let gas = U256::from(schedule.create_gas);
let mem = try!(self.mem_needed(stack.peek(1), stack.peek(2)));
InstructionCost::GasMem(gas, mem)
},
instructions::EXP => {
let expon = stack.peek(1);
let bytes = ((expon.bits() + 7) / 8) as usize;
let gas = U256::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
InstructionCost::Gas(gas)
},
_ => InstructionCost::Gas(default_gas)
};
match cost {
InstructionCost::Gas(gas) => {
Ok((gas, 0))
},
InstructionCost::GasMem(gas, mem_size) => {
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size));
let gas = overflowing!(gas.overflowing_add(mem_gas));
Ok((gas, new_mem_size))
},
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size));
let copy = overflowing!(add_u256_usize(&copy, 31));
let copy_gas = U256::from(schedule.copy_gas) * (copy / U256::from(32));
let gas = overflowing!(gas.overflowing_add(copy_gas));
let gas = overflowing!(gas.overflowing_add(mem_gas));
Ok((gas, new_mem_size))
}
if !stack.has(info.args) {
Err(evm::Error::StackUnderflow {
instruction: info.name,
wanted: info.args,
on_stack: stack.size()
})
} else if stack.size() - info.args + info.ret > schedule.stack_limit {
Err(evm::Error::OutOfStack {
instruction: info.name,
wanted: info.ret - info.args,
limit: schedule.stack_limit
})
} else {
Ok(())
}
}
@ -532,53 +230,16 @@ impl Interpreter {
}
}
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> evm::Result<(U256, usize)> {
let gas_for_mem = |mem_size: U256| {
let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div
let a = overflowing!(s.overflowing_mul(U256::from(schedule.memory_gas)));
// We need to go to U512 to calculate s*s/quad_coeff_div
let b = U512::from(s) * U512::from(s) / U512::from(schedule.quad_coeff_div);
if b > U512::from(!U256::zero()) {
Err(evm::Error::OutOfGas)
} else {
Ok(overflowing!(a.overflowing_add(U256::from(b))))
}
};
let current_mem_size = U256::from(current_mem_size);
let req_mem_size_rounded = (overflowing!(mem_size.overflowing_add(U256::from(31))) >> 5) << 5;
let new_mem_gas = try!(gas_for_mem(U256::from(req_mem_size_rounded)));
let current_mem_gas = try!(gas_for_mem(current_mem_size));
Ok((if req_mem_size_rounded > current_mem_size {
new_mem_gas - current_mem_gas
} else {
U256::zero()
}, req_mem_size_rounded.low_u64() as usize))
}
fn mem_needed_const(&self, mem: &U256, add: usize) -> evm::Result<U256> {
Ok(overflowing!(mem.overflowing_add(U256::from(add))))
}
fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result<U256> {
if self.is_zero(size) {
return Ok(U256::zero());
}
Ok(overflowing!(offset.overflowing_add(size.clone())))
}
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn exec_instruction(
&mut self,
gas: Gas,
gas: Cost,
params: &ActionParams,
ext: &mut evm::Ext,
instruction: Instruction,
code: &mut CodeReader,
stack: &mut Stack<U256>
) -> evm::Result<InstructionResult> {
) -> evm::Result<InstructionResult<Cost>> {
match instruction {
instructions::JUMP => {
let jump = stack.pop_back();
@ -611,11 +272,11 @@ impl Interpreter {
return Ok(InstructionResult::Ok);
}
let create_result = ext.create(&gas, &endowment, &contract_code);
let create_result = ext.create(&gas.as_u256(), &endowment, &contract_code);
return match create_result {
ContractCreateResult::Created(address, gas_left) => {
stack.push(address_to_u256(address));
Ok(InstructionResult::GasLeft(gas_left))
Ok(InstructionResult::GasLeft(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
},
ContractCreateResult::Failed => {
stack.push(U256::zero());
@ -626,7 +287,7 @@ impl Interpreter {
},
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
let call_gas = stack.pop_back();
let call_gas = Cost::from_u256(stack.pop_back()).expect("Gas is already validated.");
let code_address = stack.pop_back();
let code_address = u256_to_address(&code_address);
@ -642,9 +303,9 @@ impl Interpreter {
let out_size = stack.pop_back();
// Add stipend (only CALL|CALLCODE when value > 0)
let call_gas = call_gas + value.map_or_else(U256::zero, |val| match val > U256::zero() {
true => U256::from(ext.schedule().call_stipend),
false => U256::zero()
let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val > U256::zero() {
true => Cost::from(ext.schedule().call_stipend),
false => Cost::from(0)
});
// Get sender & receive addresses, check if we have balance
@ -672,13 +333,13 @@ impl Interpreter {
// and we don't want to copy
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
let output = self.mem.writeable_slice(out_off, out_size);
ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output)
ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, output)
};
return match call_result {
MessageCallResult::Success(gas_left) => {
stack.push(U256::one());
Ok(InstructionResult::UnusedGas(gas_left))
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater then current one")))
},
MessageCallResult::Failed => {
stack.push(U256::zero());
@ -759,7 +420,7 @@ impl Interpreter {
stack.push(U256::from(code.position - 1));
},
instructions::GAS => {
stack.push(gas.clone());
stack.push(gas.as_u256());
},
instructions::ADDRESS => {
stack.push(address_to_u256(params.address.clone()));
@ -876,36 +537,6 @@ impl Interpreter {
}
}
fn verify_instructions_requirements(
&self,
info: &instructions::InstructionInfo,
stack_limit: usize,
stack: &Stack<U256>
) -> evm::Result<()> {
if !stack.has(info.args) {
Err(evm::Error::StackUnderflow {
instruction: info.name,
wanted: info.args,
on_stack: stack.size()
})
} else if stack.size() - info.args + info.ret > stack_limit {
Err(evm::Error::OutOfStack {
instruction: info.name,
wanted: info.ret - info.args,
limit: stack_limit
})
} else {
Ok(())
}
}
fn verify_gas(&self, current_gas: &U256, gas_cost: &U256) -> evm::Result<()> {
match current_gas < gas_cost {
true => Err(evm::Error::OutOfGas),
false => Ok(())
}
}
fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet<usize>) -> evm::Result<usize> {
let jump = jump_u.low_u64() as usize;
@ -1163,11 +794,6 @@ fn set_sign(value: U256, sign: bool) -> U256 {
}
}
#[inline]
fn add_u256_usize(value: &U256, num: usize) -> (U256, bool) {
value.clone().overflowing_add(U256::from(num))
}
#[inline]
fn u256_to_address(value: &U256) -> Address {
Address::from(H256::from(value))
@ -1179,82 +805,14 @@ fn address_to_u256(value: Address) -> U256 {
}
#[test]
fn test_mem_gas_cost() {
fn test_find_jump_destinations() {
// given
let interpreter = Interpreter::default();
let schedule = evm::Schedule::default();
let current_mem_size = 5;
let mem_size = !U256::zero();
let interpreter = Interpreter::<U256>::default();
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
// when
let result = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size);
let valid_jump_destinations = interpreter.find_jump_destinations(&code);
// then
if let Ok(_) = result {
assert!(false, "Should fail with OutOfGas");
}
}
#[cfg(test)]
mod tests {
use common::*;
use super::*;
use evm;
#[test]
fn test_find_jump_destinations() {
// given
let interpreter = Interpreter::default();
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
// when
let valid_jump_destinations = interpreter.find_jump_destinations(&code);
// then
assert!(valid_jump_destinations.contains(&66));
}
#[test]
fn test_calculate_mem_cost() {
// given
let interpreter = Interpreter::default();
let schedule = evm::Schedule::default();
let current_mem_size = 0;
let mem_size = U256::from(5);
// when
let (mem_cost, mem_size) = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
// then
assert_eq!(mem_cost, U256::from(3));
assert_eq!(mem_size, 32);
}
#[test]
fn test_memory_read_and_write() {
// given
let mem: &mut super::Memory = &mut vec![];
mem.resize(0x80 + 32);
// when
mem.write(U256::from(0x80), U256::from(0xabcdef));
// then
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
}
#[test]
fn test_memory_read_and_write_byte() {
// given
let mem: &mut super::Memory = &mut vec![];
mem.resize(32);
// when
mem.write_byte(U256::from(0x1d), U256::from(0xab));
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
mem.write_byte(U256::from(0x1f), U256::from(0xef));
// then
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
}
assert!(valid_jump_destinations.contains(&66));
}

View File

@ -0,0 +1,106 @@
// 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::fmt;
use evm::instructions;
/// Stack trait with VM-friendly API
pub trait Stack<T> {
/// Returns `Stack[len(Stack) - no_from_top]`
fn peek(&self, no_from_top: usize) -> &T;
/// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top]
fn swap_with_top(&mut self, no_from_top: usize);
/// Returns true if Stack has at least `no_of_elems` elements
fn has(&self, no_of_elems: usize) -> bool;
/// Get element from top and remove it from Stack. Panics if stack is empty.
fn pop_back(&mut self) -> T;
/// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty.
fn pop_n(&mut self, no_of_elems: usize) -> &[T];
/// Add element on top of the Stack
fn push(&mut self, elem: T);
/// Get number of elements on Stack
fn size(&self) -> usize;
/// Returns all data on stack.
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
}
pub struct VecStack<S> {
stack: Vec<S>,
logs: [S; instructions::MAX_NO_OF_TOPICS]
}
impl<S : Copy> VecStack<S> {
pub fn with_capacity(capacity: usize, zero: S) -> Self {
VecStack {
stack: Vec::with_capacity(capacity),
logs: [zero; instructions::MAX_NO_OF_TOPICS]
}
}
}
impl<S : fmt::Display> Stack<S> for VecStack<S> {
fn peek(&self, no_from_top: usize) -> &S {
&self.stack[self.stack.len() - no_from_top - 1]
}
fn swap_with_top(&mut self, no_from_top: usize) {
let len = self.stack.len();
self.stack.swap(len - no_from_top - 1, len - 1);
}
fn has(&self, no_of_elems: usize) -> bool {
self.stack.len() >= no_of_elems
}
fn pop_back(&mut self) -> S {
let val = self.stack.pop();
match val {
Some(x) => {
evm_debug!({
println!(" POP: {}", x)
});
x
},
None => panic!("Tried to pop from empty stack.")
}
}
fn pop_n(&mut self, no_of_elems: usize) -> &[S] {
assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS);
for i in 0..no_of_elems {
self.logs[i] = self.pop_back();
}
&self.logs[0..no_of_elems]
}
fn push(&mut self, elem: S) {
evm_debug!({
println!(" PUSH: {}", elem)
});
self.stack.push(elem);
}
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()]
}
}

View File

@ -28,8 +28,10 @@ mod jit;
#[cfg(test)]
mod tests;
#[cfg(all(feature="benches", test))]
mod benches;
pub use self::evm::{Evm, Error, Finalize, GasLeft, Result};
pub use self::evm::{Evm, Error, Finalize, GasLeft, Result, CostType};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule;

View File

@ -18,18 +18,18 @@ use common::*;
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
use std::fmt::Debug;
struct FakeLogEntry {
pub struct FakeLogEntry {
topics: Vec<H256>,
data: Bytes
}
#[derive(PartialEq, Eq, Hash, Debug)]
enum FakeCallType {
pub enum FakeCallType {
Call, Create
}
#[derive(PartialEq, Eq, Hash, Debug)]
struct FakeCall {
pub struct FakeCall {
call_type: FakeCallType,
gas: U256,
sender_address: Option<Address>,
@ -43,7 +43,7 @@ struct FakeCall {
///
/// Can't do recursive calls.
#[derive(Default)]
struct FakeExt {
pub struct FakeExt {
sstore_clears: usize,
depth: usize,
store: HashMap<H256, H256>,
@ -67,7 +67,7 @@ fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
}
impl FakeExt {
fn new() -> Self {
pub fn new() -> Self {
FakeExt::default()
}
}
@ -181,7 +181,7 @@ fn test_stack_underflow() {
let mut ext = FakeExt::new();
let err = {
let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::default());
let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::<usize>::default());
test_finalize(vm.exec(params, &mut ext)).unwrap_err()
};
@ -208,7 +208,7 @@ fn test_add(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -228,7 +228,7 @@ fn test_sha3(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -248,7 +248,7 @@ fn test_address(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -270,7 +270,7 @@ fn test_origin(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -292,7 +292,7 @@ fn test_sender(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -327,7 +327,7 @@ fn test_extcodecopy(factory: super::Factory) {
ext.codes.insert(sender, sender_code);
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -347,7 +347,7 @@ fn test_log_empty(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -379,7 +379,7 @@ fn test_log_sender(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -404,7 +404,7 @@ fn test_blockhash(factory: super::Factory) {
ext.blockhashes.insert(U256::zero(), blockhash.clone());
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -426,7 +426,7 @@ fn test_calldataload(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -447,7 +447,7 @@ fn test_author(factory: super::Factory) {
ext.info.author = author;
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -467,7 +467,7 @@ fn test_timestamp(factory: super::Factory) {
ext.info.timestamp = timestamp;
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -487,7 +487,7 @@ fn test_number(factory: super::Factory) {
ext.info.number = number;
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -507,7 +507,7 @@ fn test_difficulty(factory: super::Factory) {
ext.info.difficulty = difficulty;
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -527,7 +527,7 @@ fn test_gas_limit(factory: super::Factory) {
ext.info.gas_limit = gas_limit;
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -545,7 +545,7 @@ fn test_mul(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -563,7 +563,7 @@ fn test_sub(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -581,7 +581,7 @@ fn test_div(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -599,7 +599,7 @@ fn test_div_zero(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -617,7 +617,7 @@ fn test_mod(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -636,7 +636,7 @@ fn test_smod(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -655,7 +655,7 @@ fn test_sdiv(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -674,7 +674,7 @@ fn test_exp(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -694,7 +694,7 @@ fn test_comparison(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -715,7 +715,7 @@ fn test_signed_comparison(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -736,7 +736,7 @@ fn test_bitops(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -759,7 +759,7 @@ fn test_addmod_mulmod(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -780,7 +780,7 @@ fn test_byte(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -799,7 +799,7 @@ fn test_signextend(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -819,7 +819,7 @@ fn test_badinstruction_int() {
let mut ext = FakeExt::new();
let err = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap_err()
};
@ -839,7 +839,7 @@ fn test_pop(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -859,7 +859,7 @@ fn test_extops(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -882,7 +882,7 @@ fn test_jumps(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@ -911,7 +911,7 @@ fn test_calls(factory: super::Factory) {
};
let gas_left = {
let mut vm = factory.create();
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};

View File

@ -211,7 +211,7 @@ impl<'a> Executive<'a> {
let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
return vm_factory.create().exec(params, &mut ext).finalize(ext);
return vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext);
}
// Start in new thread to reset stack
@ -222,7 +222,7 @@ impl<'a> Executive<'a> {
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
scope.spawn(move || {
vm_factory.create().exec(params, &mut ext).finalize(ext)
vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext)
})
}).join()
}

View File

@ -319,7 +319,6 @@ mod tests {
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into(),
dao_rescue_block_gas_limit: None,
}
}

View File

@ -92,10 +92,10 @@ impl PartialEq for Header {
impl Default for Header {
fn default() -> Self {
Header {
parent_hash: ZERO_H256.clone(),
parent_hash: H256::default(),
timestamp: 0,
number: 0,
author: ZERO_ADDRESS.clone(),
author: Address::default(),
transactions_root: SHA3_NULL_RLP,
uncles_hash: SHA3_EMPTY_LIST_RLP,
@ -104,10 +104,10 @@ impl Default for Header {
state_root: SHA3_NULL_RLP,
receipts_root: SHA3_NULL_RLP,
log_bloom: ZERO_LOGBLOOM.clone(),
gas_used: ZERO_U256,
gas_limit: ZERO_U256,
gas_used: U256::default(),
gas_limit: U256::default(),
difficulty: ZERO_U256,
difficulty: U256::default(),
seal: vec![],
hash: RefCell::new(None),
bare_hash: RefCell::new(None),

View File

@ -208,7 +208,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
&mut tracer,
&mut vm_tracer,
);
let mut evm = vm_factory.create();
let mut evm = vm_factory.create(params.gas);
let res = evm.exec(params, &mut ex);
// a return in finalize will not alter callcreates
let callcreates = ex.callcreates.clone();

View File

@ -23,3 +23,4 @@ mod state;
mod chain;
mod homestead_state;
mod homestead_chain;
mod trie;

View File

@ -0,0 +1,69 @@
// 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 ethjson;
use util::{H256, MemoryDB, TrieMut, TrieSpec, TrieFactory};
fn test_trie(json: &[u8], trie: TrieSpec) -> Vec<String> {
let tests = ethjson::trie::Test::load(json).unwrap();
let factory = TrieFactory::new(trie);
let mut result = vec![];
for (name, test) in tests.into_iter() {
let mut memdb = MemoryDB::new();
let mut root = H256::default();
let mut t = factory.create(&mut memdb, &mut root);
for (key, value) in test.input.data.into_iter() {
let key: Vec<u8> = key.into();
let value: Vec<u8> = value.map_or_else(Vec::new, Into::into);
t.insert(&key, &value);
}
if *t.root() != test.root.into() {
result.push(format!("Trie test '{:?}' failed.", name));
}
}
for i in &result {
println!("FAILED: {}", i);
}
result
}
mod generic {
use util::TrieSpec;
fn do_json_test(json: &[u8]) -> Vec<String> {
super::test_trie(json, TrieSpec::Generic)
}
declare_test!{TrieTests_trietest, "TrieTests/trietest"}
declare_test!{TrieTests_trieanyorder, "TrieTests/trieanyorder"}
}
mod secure {
use util::TrieSpec;
fn do_json_test(json: &[u8]) -> Vec<String> {
super::test_trie(json, TrieSpec::Secure)
}
declare_test!{TrieTests_hex_encoded_secure, "TrieTests/hex_encoded_securetrie_test"}
declare_test!{TrieTests_trietest_secure, "TrieTests/trietest_secureTrie"}
declare_test!{TrieTests_trieanyorder_secure, "TrieTests/trieanyorder_secureTrie"}
}

View File

@ -31,6 +31,7 @@
#![cfg_attr(feature="dev", allow(needless_borrow))]
#![cfg_attr(feature="dev", allow(assign_op_pattern))]
#![cfg_attr(feature="benches", feature(test))]
//! Ethcore library
//!
@ -94,6 +95,8 @@ extern crate rayon;
extern crate hyper;
extern crate ethash;
pub extern crate ethstore;
extern crate semver;
extern crate ethcore_ipc_nano as nanoipc;
#[cfg(test)] extern crate ethcore_devtools as devtools;
#[cfg(feature = "jit" )] extern crate evmjit;
@ -105,7 +108,6 @@ pub mod block_queue;
pub mod client;
pub mod error;
pub mod ethereum;
pub mod filter;
pub mod header;
pub mod service;
pub mod trace;

View File

@ -1,3 +1,19 @@
// 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/>.
//! Extras database migrations.
mod v6;

View File

@ -1,4 +1,20 @@
use util::migration::Migration;
// 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 util::migration::SimpleMigration;
/// This migration reduces the sizes of keys and moves `ExtrasIndex` byte from back to the front.
pub struct ToV6;
@ -17,12 +33,12 @@ impl ToV6 {
}
}
impl Migration for ToV6 {
impl SimpleMigration for ToV6 {
fn version(&self) -> u32 {
6
}
fn simple_migrate(&self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
//// at this version all extras keys are 33 bytes long.
if key.len() == 33 {

View File

@ -1,5 +1,21 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! State database migrations.
mod v7;
pub use self::v7::ToV7;
pub use self::v7::{ArchiveV7, OverlayRecentV7};

View File

@ -1,31 +1,205 @@
use util::hash::{FixedHash, H256};
use util::migration::Migration;
// 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/>.
//! This migration migrates the state db to use an accountdb which ensures uniqueness
//! using an address' hash as opposed to the address itself.
use std::collections::HashMap;
use util::Bytes;
use util::hash::{Address, FixedHash, H256};
use util::kvdb::Database;
use util::migration::{Batch, Config, Error, Migration, SimpleMigration};
use util::rlp::{decode, Rlp, RlpStream, Stream, View};
use util::sha3::Hashable;
/// This migration migrates the state db to use an accountdb which ensures uniqueness
/// using an address' hash as opposed to the address itself.
pub struct ToV7;
// attempt to migrate a key, value pair. None if migration not possible.
fn attempt_migrate(mut key_h: H256, val: &[u8]) -> Option<H256> {
let val_hash = val.sha3();
impl Migration for ToV7 {
if key_h != val_hash {
// this is a key which has been xor'd with an address.
// recover the address.
let address = key_h ^ val_hash;
// check that the address is actually a 20-byte value.
// the leftmost 12 bytes should be zero.
if &address[0..12] != &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] {
return None;
}
let address_hash = Address::from(address).sha3();
// create the xor'd key in place.
key_h.copy_from_slice(&*val_hash);
assert_eq!(key_h, val_hash);
{
let last_src: &[u8] = &*address_hash;
let last_dst: &mut [u8] = &mut *key_h;
for (k, a) in last_dst[12..].iter_mut().zip(&last_src[12..]) {
*k ^= *a;
}
}
Some(key_h)
} else {
None
}
}
/// Version for ArchiveDB.
pub struct ArchiveV7;
impl SimpleMigration for ArchiveV7 {
fn version(&self) -> u32 {
7
}
fn simple_migrate(&self, mut key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
let val_hash = value.sha3();
assert!(key.len() == 32); // all keys in the state db are hashes.
let key_h = H256::from_slice(&key[..]);
if key_h != val_hash {
// this is a key which has been xor'd with an address.
// recover the address
let address = key_h ^ val_hash;
let address_hash = address.sha3();
let new_key = address_hash ^ val_hash;
key.copy_from_slice(&new_key[..]);
fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
if key.len() != 32 {
// metadata key, ignore.
return Some((key, value));
}
let key_h = H256::from_slice(&key[..]);
if let Some(new_key) = attempt_migrate(key_h, &value[..]) {
Some((new_key[..].to_owned(), value))
} else {
Some((key, value))
}
// nothing to do here
Some((key, value))
}
}
// magic numbers and constants for overlay-recent at v6.
// re-written here because it may change in the journaldb module.
const V7_LATEST_ERA_KEY: &'static [u8] = &[ b'l', b'a', b's', b't', 0, 0, 0, 0, 0, 0, 0, 0 ];
const V7_VERSION_KEY: &'static [u8] = &[ b'j', b'v', b'e', b'r', 0, 0, 0, 0, 0, 0, 0, 0 ];
const DB_VERSION: u32 = 0x203;
const PADDING : [u8; 10] = [0u8; 10];
/// Version for OverlayRecent database.
/// more involved than the archive version because of journaling.
#[derive(Default)]
pub struct OverlayRecentV7 {
migrated_keys: HashMap<H256, H256>,
}
impl OverlayRecentV7 {
// walk all journal entries in the database backwards,
// replacing any known migrated keys with their counterparts
// and then committing again.
fn migrate_journal(&self, source: &Database, mut batch: Batch, dest: &mut Database) -> Result<(), Error> {
if let Some(val) = try!(source.get(V7_LATEST_ERA_KEY).map_err(Error::Custom)) {
try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.to_owned(), dest));
let mut era = decode::<u64>(&val);
loop {
let mut index: usize = 0;
loop {
let entry_key = {
let mut r = RlpStream::new_list(3);
r.append(&era).append(&index).append(&&PADDING[..]);
r.out()
};
if let Some(journal_raw) = try!(source.get(&entry_key).map_err(Error::Custom)) {
let rlp = Rlp::new(&journal_raw);
let id: H256 = rlp.val_at(0);
let mut inserted_keys: Vec<(H256, Bytes)> = Vec::new();
// migrate all inserted keys.
for r in rlp.at(1).iter() {
let mut key: H256 = r.val_at(0);
let v: Bytes = r.val_at(1);
if let Some(new_key) = self.migrated_keys.get(&key) {
key = *new_key;
} else if let Some(new_key) = attempt_migrate(key, &v) {
key = new_key;
}
inserted_keys.push((key, v));
}
// migrate all deleted keys.
let mut deleted_keys: Vec<H256> = rlp.val_at(2);
for old_key in &mut deleted_keys {
if let Some(new) = self.migrated_keys.get(&*old_key) {
*old_key = new.clone();
}
}
// rebuild the journal entry rlp.
let mut stream = RlpStream::new_list(3);
stream.append(&id);
stream.begin_list(inserted_keys.len());
for (k, v) in inserted_keys {
stream.begin_list(2).append(&k).append(&v);
}
stream.append(&deleted_keys);
// and insert it into the new database.
try!(batch.insert(entry_key, stream.out(), dest));
index += 1;
} else {
break;
}
}
if index == 0 || era == 0 {
break;
}
era -= 1;
}
}
batch.commit(dest)
}
}
impl Migration for OverlayRecentV7 {
fn version(&self) -> u32 { 7 }
// walk all records in the database, attempting to migrate any possible and
// keeping records of those that we do. then migrate the journal using
// this information.
fn migrate(&mut self, source: &Database, config: &Config, dest: &mut Database) -> Result<(), Error> {
let mut batch = Batch::new(config);
// check version metadata.
match try!(source.get(V7_VERSION_KEY).map_err(Error::Custom)) {
Some(ref version) if decode::<u32>(&*version) == DB_VERSION => {}
_ => return Err(Error::MigrationImpossible), // missing or wrong version
}
for (key, value) in source.iter() {
let mut key = key.into_vec();
if key.len() == 32 {
let key_h = H256::from_slice(&key[..]);
if let Some(new_key) = attempt_migrate(key_h.clone(), &value) {
self.migrated_keys.insert(key_h, new_key);
key.copy_from_slice(&new_key[..]);
}
}
try!(batch.insert(key, value.into_vec(), dest));
}
self.migrate_journal(source, batch, dest)
}
}

View File

@ -19,7 +19,7 @@ use std::sync::atomic::AtomicBool;
use std::time::{Instant, Duration};
use util::*;
use util::Colour::White;
use util::using_queue::{UsingQueue, GetAction};
use account_provider::AccountProvider;
use views::{BlockView, HeaderView};
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
@ -29,8 +29,10 @@ use transaction::SignedTransaction;
use receipt::{Receipt};
use spec::Spec;
use engine::Engine;
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin};
use miner::work_notify::WorkPoster;
use client::TransactionImportResult;
/// Different possible definitions for pending transaction set.
#[derive(Debug)]
@ -198,17 +200,23 @@ impl Miner {
let hash = tx.hash();
match open_block.push_transaction(tx, None) {
Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => {
trace!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash);
debug!(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 = 21000.into(); // TODO: figure this out properly.
if gas_limit - gas_used < min_tx_gas {
break;
}
},
Err(Error::Transaction(TransactionError::AlreadyImported)) => {} // already have transaction - ignore
// Invalid nonce error can happen only if previous transaction is skipped because of gas limit.
// If there is errornous state of transaction queue it will be fixed when next block is imported.
Err(Error::Execution(ExecutionError::InvalidNonce { .. })) => {
debug!(target: "miner", "Skipping adding transaction to block because of invalid nonce: {:?}", hash);
},
// already have transaction - ignore
Err(Error::Transaction(TransactionError::AlreadyImported)) => {},
Err(e) => {
invalid_transactions.insert(hash);
trace!(target: "miner",
debug!(target: "miner",
"Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}",
block_number, hash, e);
},
@ -265,6 +273,10 @@ impl Miner {
let difficulty = *block.block().fields().header.difficulty();
let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h);
sealing_work.push(block);
// If push notifications are enabled we assume all work items are used.
if self.work_poster.is_some() && is_new {
sealing_work.use_last_ref();
}
(Some((pow_hash, difficulty, number)), is_new)
} else {
(None, false)
@ -303,6 +315,19 @@ impl Miner {
!have_work
}
fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, origin: TransactionOrigin, transaction_queue: &mut TransactionQueue) ->
Vec<Result<TransactionImportResult, Error>> {
let fetch_account = |a: &Address| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
};
transactions.into_iter()
.map(|tx| transaction_queue.add(tx, &fetch_account, origin))
.collect()
}
/// Are we allowed to do a non-mandatory reseal?
fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock().unwrap() }
}
@ -343,7 +368,6 @@ impl MinerService for Miner {
last_hashes: last_hashes,
gas_used: U256::zero(),
gas_limit: U256::max_value(),
dao_rescue_block_gas_limit: chain.dao_rescue_block_gas_limit(header.parent_hash().clone()),
};
// that's just a copy of the state.
let mut state = block.state().clone();
@ -466,27 +490,24 @@ impl MinerService for Miner {
self.gas_range_target.read().unwrap().1
}
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) ->
Vec<Result<TransactionImportResult, Error>>
where T: Fn(&Address) -> AccountDetails {
let results: Vec<Result<TransactionImportResult, Error>> = {
let mut transaction_queue = self.transaction_queue.lock().unwrap();
transactions.into_iter()
.map(|tx| transaction_queue.add(tx, &fetch_account, TransactionOrigin::External))
.collect()
};
if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() {
fn import_external_transactions(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>) ->
Vec<Result<TransactionImportResult, Error>> {
let mut transaction_queue = self.transaction_queue.lock().unwrap();
let results = self.add_transactions_to_queue(chain, transactions, TransactionOrigin::External,
&mut transaction_queue);
if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() {
self.update_sealing(chain);
}
results
}
fn import_own_transaction<T>(
fn import_own_transaction(
&self,
chain: &MiningBlockChainClient,
transaction: SignedTransaction,
fetch_account: T
) -> Result<TransactionImportResult, Error> where T: Fn(&Address) -> AccountDetails {
) -> Result<TransactionImportResult, Error> {
let hash = transaction.hash();
trace!(target: "own_tx", "Importing transaction: {:?}", transaction);
@ -494,7 +515,7 @@ impl MinerService for Miner {
let imported = {
// Be sure to release the lock before we call enable_and_prepare_sealing
let mut transaction_queue = self.transaction_queue.lock().unwrap();
let import = transaction_queue.add(transaction, &fetch_account, TransactionOrigin::Local);
let import = self.add_transactions_to_queue(chain, vec![transaction], TransactionOrigin::Local, &mut transaction_queue).pop().unwrap();
match import {
Ok(ref res) => {
@ -633,7 +654,7 @@ impl MinerService for Miner {
let n = sealed.header().number();
let h = sealed.header().hash();
try!(chain.import_sealed_block(sealed));
info!(target: "miner", "Mined block imported OK. #{}: {}", paint(White.bold(), format!("{}", n)), paint(White.bold(), h.hex()));
info!(target: "miner", "Mined block imported OK. #{}: {}", format!("{}", n).apply(Colour::White.bold()), h.hex().apply(Colour::White.bold()));
Ok(())
})
}
@ -645,7 +666,12 @@ impl MinerService for Miner {
// Client should send message after commit to db and inserting to chain.
.expect("Expected in-chain blocks.");
let block = BlockView::new(&block);
block.transactions()
let txs = block.transactions();
// populate sender
for tx in &txs {
let _sender = tx.sender();
}
txs
}
// 1. We ignore blocks that were `imported` (because it means that they are not in canon-chain, and transactions
@ -662,14 +688,10 @@ impl MinerService for Miner {
.par_iter()
.map(|h| fetch_transactions(chain, h));
out_of_chain.for_each(|txs| {
// populate sender
for tx in &txs {
let _sender = tx.sender();
}
let _ = self.import_transactions(chain, txs, |a| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
});
let mut transaction_queue = self.transaction_queue.lock().unwrap();
let _ = self.add_transactions_to_queue(
chain, txs, TransactionOrigin::External, &mut transaction_queue
);
});
}

View File

@ -33,7 +33,7 @@
//! use ethcore::miner::{Miner, MinerService};
//!
//! fn main() {
//! let miner: Miner = Miner::with_spec(ethereum::new_frontier(true));
//! let miner: Miner = Miner::with_spec(ethereum::new_frontier());
//! // get status
//! assert_eq!(miner.status().transactions_in_pending_queue, 0);
//!
@ -47,9 +47,10 @@ mod external;
mod transaction_queue;
mod work_notify;
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionOrigin};
pub use self::miner::{Miner, MinerOptions, PendingSet};
pub use self::external::{ExternalMiner, ExternalMinerService};
pub use client::TransactionImportResult;
use std::collections::BTreeMap;
use util::{H256, U256, Address, Bytes};
@ -106,14 +107,12 @@ pub trait MinerService : Send + Sync {
fn set_tx_gas_limit(&self, limit: U256);
/// Imports transactions to transaction queue.
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) ->
Vec<Result<TransactionImportResult, Error>>
where T: Fn(&Address) -> AccountDetails, Self: Sized;
fn import_external_transactions(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>) ->
Vec<Result<TransactionImportResult, Error>>;
/// Imports own (node owner) transaction to queue.
fn import_own_transaction<T>(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction, fetch_account: T) ->
Result<TransactionImportResult, Error>
where T: Fn(&Address) -> AccountDetails, Self: Sized;
fn import_own_transaction(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction) ->
Result<TransactionImportResult, Error>;
/// Returns hashes of transactions currently in pending
fn pending_transactions_hashes(&self) -> Vec<H256>;

View File

@ -87,9 +87,10 @@ use std::cmp;
use std::collections::{HashMap, BTreeSet};
use util::numbers::{Uint, U256};
use util::hash::{Address, H256};
use util::table::*;
use util::table::Table;
use transaction::*;
use error::{Error, TransactionError};
use client::TransactionImportResult;
/// Transaction origin
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -197,6 +198,7 @@ struct VerifiedTransaction {
/// transaction origin
origin: TransactionOrigin,
}
impl VerifiedTransaction {
fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result<Self, Error> {
try!(transaction.sender());
@ -309,15 +311,6 @@ pub struct TransactionQueueStatus {
pub future: usize,
}
#[derive(Debug, PartialEq)]
/// Represents the result of importing transaction.
pub enum TransactionImportResult {
/// Transaction was imported to current queue.
Current,
/// Transaction was imported to future queue.
Future
}
/// Details of account
pub struct AccountDetails {
/// Most recent account nonce
@ -439,10 +432,10 @@ impl TransactionQueue {
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_account: &T, origin: TransactionOrigin) -> Result<TransactionImportResult, Error>
where T: Fn(&Address) -> AccountDetails {
trace!(target: "miner", "Importing: {:?}", tx.hash());
trace!(target: "txqueue", "Importing: {:?}", tx.hash());
if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local {
trace!(target: "miner",
trace!(target: "txqueue",
"Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})",
tx.hash(),
tx.gas_price,
@ -458,7 +451,7 @@ impl TransactionQueue {
try!(tx.check_low_s());
if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit {
trace!(target: "miner",
trace!(target: "txqueue",
"Dropping transaction above gas limit: {:?} ({} > min({}, {}))",
tx.hash(),
tx.gas,
@ -477,7 +470,7 @@ impl TransactionQueue {
let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas;
if client_account.balance < cost {
trace!(target: "miner",
trace!(target: "txqueue",
"Dropping transaction without sufficient balance: {:?} ({} < {})",
vtx.hash(),
client_account.balance,
@ -565,7 +558,7 @@ impl TransactionQueue {
if k >= current_nonce {
self.future.insert(*sender, k, order.update_height(k, current_nonce));
} else {
trace!(target: "miner", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
// Remove the transaction completely
self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`");
}
@ -586,7 +579,7 @@ impl TransactionQueue {
if k >= current_nonce {
self.future.insert(*sender, k, order.update_height(k, current_nonce));
} else {
trace!(target: "miner", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`");
}
}
@ -674,7 +667,7 @@ impl TransactionQueue {
if self.by_hash.get(&tx.hash()).is_some() {
// Transaction is already imported.
trace!(target: "miner", "Dropping already imported transaction: {:?}", tx.hash());
trace!(target: "txqueue", "Dropping already imported transaction: {:?}", tx.hash());
return Err(TransactionError::AlreadyImported);
}
@ -691,7 +684,7 @@ impl TransactionQueue {
// nonce height would result in overflow.
if nonce < state_nonce {
// Droping transaction
trace!(target: "miner", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, next_nonce);
trace!(target: "txqueue", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, next_nonce);
return Err(TransactionError::Old);
} else if nonce > next_nonce {
// We have a gap - put to future.
@ -727,7 +720,7 @@ impl TransactionQueue {
// Trigger error if the transaction we are importing was removed.
try!(check_if_removed(&address, &nonce, removed));
trace!(target: "miner", "status: {:?}", self.status());
trace!(target: "txqueue", "status: {:?}", self.status());
Ok(TransactionImportResult::Current)
}
@ -812,6 +805,7 @@ mod test {
use error::{Error, TransactionError};
use super::*;
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
use client::TransactionImportResult;
fn unwrap_tx_err(err: Result<TransactionImportResult, Error>) -> TransactionError {
match err.unwrap_err() {

View File

@ -52,10 +52,10 @@ impl WorkPoster {
}
fn create_client() -> Client<PostHandler> {
let client = Client::<PostHandler>::configure()
Client::<PostHandler>::configure()
.keep_alive(true)
.build().expect("Error creating HTTP client") as Client<PostHandler>;
client
.build()
.expect("Error creating HTTP client")
}
pub fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) {
@ -63,8 +63,10 @@ impl WorkPoster {
let target = Ethash::difficulty_to_boundary(&difficulty);
let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(number);
let seed_hash = H256::from_slice(&seed_hash[..]);
let body = format!(r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#,
pow_hash.hex(), seed_hash.hex(), target.hex(), number);
let body = format!(
r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#,
pow_hash.hex(), seed_hash.hex(), target.hex(), number
);
let mut client = self.client.lock().unwrap();
for u in &self.urls {
if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) {
@ -104,12 +106,12 @@ impl hyper::client::Handler<HttpStream> for PostHandler {
}
fn on_response_readable(&mut self, _decoder: &mut hyper::Decoder<HttpStream>) -> Next {
Next::end()
Next::end()
}
fn on_error(&mut self, err: hyper::Error) -> Next {
fn on_error(&mut self, err: hyper::Error) -> Next {
trace!("Error posting work data: {}", err);
Next::end()
}
}
}

View File

@ -72,7 +72,7 @@ impl fmt::Display for PodState {
/// Calculate and return diff between `pre` state and `post` state.
pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff {
StateDiff(pre.get().keys().merge(post.get().keys()).filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect())
StateDiff { raw: pre.get().keys().merge(post.get().keys()).filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect() }
}
#[cfg(test)]
@ -86,22 +86,22 @@ mod test {
#[test]
fn create_delete() {
let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]);
assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff(map![
assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff { raw: map![
1.into() => AccountDiff{
balance: Diff::Died(69.into()),
nonce: Diff::Died(0.into()),
code: Diff::Died(vec![]),
storage: map![],
}
]));
assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff(map![
]});
assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff{ raw: map![
1.into() => AccountDiff{
balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()),
code: Diff::Born(vec![]),
storage: map![],
}
]));
]});
}
#[test]
@ -111,22 +111,22 @@ mod test {
1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]),
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
]);
assert_eq!(super::diff_pod(&a, &b), StateDiff(map![
assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![
2.into() => AccountDiff{
balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()),
code: Diff::Born(vec![]),
storage: map![],
}
]));
assert_eq!(super::diff_pod(&b, &a), StateDiff(map![
]});
assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![
2.into() => AccountDiff{
balance: Diff::Died(69.into()),
nonce: Diff::Died(0.into()),
code: Diff::Died(vec![]),
storage: map![],
}
]));
]});
}
#[test]
@ -139,14 +139,14 @@ mod test {
1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]),
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
]);
assert_eq!(super::diff_pod(&a, &b), StateDiff(map![
assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![
1.into() => AccountDiff{
balance: Diff::Same,
nonce: Diff::Changed(0.into(), 1.into()),
code: Diff::Same,
storage: map![],
}
]));
]});
}
}

View File

@ -17,7 +17,6 @@
//! Creates and registers client and network services.
use util::*;
use util::Colour::{Yellow, White};
use util::panics::*;
use spec::Spec;
use error::*;
@ -72,7 +71,7 @@ impl ClientService {
try!(net_service.start());
}
info!("Configured for {} using {} engine", paint(White.bold(), spec.name.clone()), paint(Yellow.bold(), spec.engine.name().to_owned()));
info!("Configured for {} using {} engine", spec.name.clone().apply(Colour::White.bold()), spec.engine.name().apply(Colour::Yellow.bold()));
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 {
@ -135,16 +134,14 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
#[cfg_attr(feature="dev", allow(single_match))]
fn message(&self, io: &IoContext<NetSyncMessage>, net_message: &NetSyncMessage) {
if let UserMessage(ref message) = *net_message {
match *message {
SyncMessage::BlockVerified => {
self.client.import_verified_blocks(&io.channel());
},
SyncMessage::NewTransactions(ref transactions) => {
self.client.import_queued_transactions(&transactions);
},
_ => {}, // ignore other messages
}
match *net_message {
UserMessage(ref message) => match *message {
SyncMessage::BlockVerified => { self.client.import_verified_blocks(&io.channel()); }
SyncMessage::NewTransactions(ref transactions) => { self.client.import_queued_transactions(&transactions); }
_ => {} // ignore other messages
},
NetworkIoMessage::NetworkStarted(ref url) => { self.client.network_started(url); }
_ => {} // ignore other messages
}
}
}

View File

@ -17,7 +17,6 @@
//! Account state encoding and decoding
use account_db::{AccountDB, AccountDBMut};
use client::BlockChainClient;
use error::Error;
use util::{Bytes, HashDB, SHA3_EMPTY, TrieDB};

View File

@ -17,15 +17,15 @@
//! Snapshot creation helpers.
use std::collections::VecDeque;
use std::fs::File;
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::Path;
use std::path::{Path, PathBuf};
use account_db::{AccountDB, AccountDBMut};
use client::BlockChainClient;
use error::Error;
use ids::BlockID;
use views::BlockView;
use views::{BlockView, HeaderView};
use util::{Bytes, Hashable, HashDB, JournalDB, snappy, TrieDB, TrieDBMut, TrieMut};
use util::hash::{FixedHash, H256};
@ -42,6 +42,40 @@ mod block;
// Try to have chunks be around 16MB (before compression)
const PREFERRED_CHUNK_SIZE: usize = 16 * 1024 * 1024;
/// Take a snapshot using the given client and database, writing into `path`.
pub fn take_snapshot(client: &BlockChainClient, mut path: PathBuf, state_db: &HashDB) {
let chain_info = client.chain_info();
let genesis_hash = chain_info.genesis_hash;
let best_header_raw = client.best_block_header();
let best_header = HeaderView::new(&best_header_raw);
let state_root = best_header.state_root();
trace!(target: "snapshot", "Taking snapshot starting at block {}", best_header.number());
let _ = create_dir_all(&path);
// lock the state db while we create the state chunks.
let state_hashes = {
chunk_state(state_db, &state_root, &path).unwrap()
};
let block_hashes = chunk_blocks(client, best_header.hash(), genesis_hash, &path).unwrap();
trace!(target: "snapshot", "produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
let manifest_data = ManifestData {
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: state_root,
};
path.push("MANIFEST");
let mut manifest_file = File::create(&path).unwrap();
manifest_file.write_all(&manifest_data.to_rlp()).unwrap();
}
// shared portion of write_chunk
// returns either a (hash, compressed_size) pair or an io error.
fn write_chunk(raw_data: &[u8], compression_buffer: &mut Vec<u8>, path: &Path) -> Result<(H256, usize), Error> {

View File

@ -42,6 +42,7 @@ pub struct State {
cache: RefCell<HashMap<Address, Option<Account>>>,
snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>,
account_start_nonce: U256,
trie_factory: TrieFactory,
}
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
@ -50,11 +51,11 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v
impl State {
/// Creates new state with empty state root
#[cfg(test)]
pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256) -> State {
pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256, trie_factory: TrieFactory) -> State {
let mut root = H256::new();
{
// init trie and reset root too null
let _ = SecTrieDBMut::new(db.as_hashdb_mut(), &mut root);
let _ = trie_factory.create(db.as_hashdb_mut(), &mut root);
}
State {
@ -63,22 +64,26 @@ impl State {
cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce,
trie_factory: trie_factory,
}
}
/// Creates new state with existing state root
pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256) -> Result<State, TrieError> {
pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result<State, TrieError> {
if !db.as_hashdb().contains(&root) {
Err(TrieError::InvalidStateRoot)
} else {
Ok(State {
db: db,
root: root,
cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce,
})
return Err(TrieError::InvalidStateRoot);
}
let state = State {
db: db,
root: root,
cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce,
trie_factory: trie_factory,
};
Ok(state)
}
/// Create a recoverable snaphot of this state
@ -156,7 +161,7 @@ impl State {
/// Determine whether an account exists.
pub fn exists(&self, a: &Address) -> bool {
let db = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
self.cache.borrow().get(&a).unwrap_or(&None).is_some() || db.contains(&a)
}
@ -242,7 +247,10 @@ impl State {
for a in &addresses {
if self.code(a).map_or(false, |c| c.sha3() == broken_dao) {
// Figure out if the balance has been reduced.
let maybe_original = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR).get(&a).map(Account::from_rlp);
let maybe_original = self.trie_factory
.readonly(self.db.as_hashdb(), &self.root)
.expect(SEC_TRIE_DB_UNWRAP_STR)
.get(&a).map(Account::from_rlp);
if maybe_original.map_or(false, |original| *original.balance() > self.balance(a)) {
return Err(Error::Transaction(TransactionError::DAORescue));
}
@ -262,14 +270,14 @@ impl State {
/// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.
/// `accounts` is mutable because we may need to commit the code or storage and record that.
#[cfg_attr(feature="dev", allow(match_ref_pats))]
pub fn commit_into(db: &mut HashDB, root: &mut H256, accounts: &mut HashMap<Address, Option<Account>>) {
pub fn commit_into(trie_factory: &TrieFactory, db: &mut HashDB, root: &mut H256, accounts: &mut HashMap<Address, Option<Account>>) {
// first, commit the sub trees.
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
for (address, ref mut a) in accounts.iter_mut() {
match a {
&mut&mut Some(ref mut account) => {
let mut account_db = AccountDBMut::new(db, address);
account.commit_storage(&mut account_db);
account.commit_storage(trie_factory, &mut account_db);
account.commit_code(&mut account_db);
}
&mut&mut None => {}
@ -277,7 +285,7 @@ impl State {
}
{
let mut trie = SecTrieDBMut::from_existing(db, root).unwrap();
let mut trie = trie_factory.from_existing(db, root).unwrap();
for (address, ref a) in accounts.iter() {
match **a {
Some(ref account) => trie.insert(address, &account.rlp()),
@ -290,7 +298,7 @@ impl State {
/// Commits our cached account changes into the trie.
pub fn commit(&mut self) {
assert!(self.snapshots.borrow().is_empty());
Self::commit_into(self.db.as_hashdb_mut(), &mut self.root, self.cache.borrow_mut().deref_mut());
Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, self.cache.borrow_mut().deref_mut());
}
#[cfg(test)]
@ -340,7 +348,7 @@ impl State {
fn get<'a>(&'a self, a: &Address, require_code: bool) -> &'a Option<Account> {
let have_key = self.cache.borrow().contains_key(a);
if !have_key {
let db = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
self.insert_cache(a, db.get(&a).map(Account::from_rlp))
}
if require_code {
@ -361,7 +369,7 @@ impl State {
fn require_or_from<'a, F: FnOnce() -> Account, G: FnOnce(&mut Account)>(&self, a: &Address, require_code: bool, default: F, not_default: G) -> &'a mut Account {
let have_key = self.cache.borrow().contains_key(a);
if !have_key {
let db = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
self.insert_cache(a, db.get(&a).map(Account::from_rlp))
} else {
self.note_cache(a);
@ -396,6 +404,7 @@ impl Clone for State {
cache: RefCell::new(self.cache.borrow().clone()),
snapshots: RefCell::new(self.snapshots.borrow().clone()),
account_start_nonce: self.account_start_nonce.clone(),
trie_factory: self.trie_factory.clone(),
}
}
}
@ -1179,7 +1188,7 @@ fn code_from_database() {
state.drop()
};
let state = State::from_existing(db, root, U256::from(0u8)).unwrap();
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec()));
}
@ -1194,7 +1203,7 @@ fn storage_at_from_database() {
state.drop()
};
let s = State::from_existing(db, root, U256::from(0u8)).unwrap();
let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(s.storage_at(&a, &H256::from(&U256::from(01u64))), H256::from(&U256::from(69u64)));
}
@ -1211,7 +1220,7 @@ fn get_from_database() {
state.drop()
};
let state = State::from_existing(db, root, U256::from(0u8)).unwrap();
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(state.balance(&a), U256::from(69u64));
assert_eq!(state.nonce(&a), U256::from(1u64));
}
@ -1244,7 +1253,7 @@ fn remove_from_database() {
};
let (root, db) = {
let mut state = State::from_existing(db, root, U256::from(0u8)).unwrap();
let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(state.exists(&a), true);
assert_eq!(state.nonce(&a), U256::from(1u64));
state.kill_account(&a);
@ -1254,7 +1263,7 @@ fn remove_from_database() {
state.drop()
};
let state = State::from_existing(db, root, U256::from(0u8)).unwrap();
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(state.exists(&a), false);
assert_eq!(state.nonce(&a), U256::from(0u64));
}

View File

@ -175,11 +175,11 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
let mut b = OpenBlock::new(
test_engine.deref(),
&vm_factory,
Default::default(),
false,
db,
&last_header,
last_hashes.clone(),
None,
author.clone(),
(3141562.into(), 31415620.into()),
vec![]
@ -315,7 +315,7 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
let journal_db = get_temp_journal_db_in(temp.as_path());
GuardedTempResult {
_temp: temp,
result: Some(State::new(journal_db, U256::from(0u8)))
result: Some(State::new(journal_db, U256::from(0), Default::default())),
}
}
@ -325,7 +325,7 @@ pub fn get_temp_journal_db_in(path: &Path) -> Box<JournalDB> {
pub fn get_temp_state_in(path: &Path) -> State {
let journal_db = get_temp_journal_db_in(path);
State::new(journal_db, U256::from(0u8))
State::new(journal_db, U256::from(0), Default::default())
}
pub fn get_good_dummy_block_seq(count: usize) -> Vec<Bytes> {

View File

@ -15,4 +15,5 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
pub mod helpers;
mod client;
mod client;
mod rpc;

71
ethcore/src/tests/rpc.rs Normal file
View File

@ -0,0 +1,71 @@
// 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/>.
//! Client RPC tests
use nanoipc;
use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicBool};
use client::{Client, BlockChainClient, ClientConfig, RemoteClient, BlockID};
use tests::helpers::*;
use devtools::*;
use miner::Miner;
use crossbeam;
use common::IoChannel;
pub fn run_test_worker(scope: &crossbeam::Scope, stop: Arc<AtomicBool>, socket_path: &str) {
let socket_path = socket_path.to_owned();
scope.spawn(move || {
let temp = RandomTempPath::create_dir();
let client = Client::new(
ClientConfig::default(),
get_test_spec(),
temp.as_path(),
Arc::new(Miner::with_spec(get_test_spec())),
IoChannel::disconnected()).unwrap();
let mut worker = nanoipc::Worker::new(&client);
worker.add_reqrep(&socket_path).unwrap();
while !stop.load(Ordering::Relaxed) {
worker.poll();
}
});
}
#[test]
fn can_handshake() {
crossbeam::scope(|scope| {
let stop_guard = StopGuard::new();
let socket_path = "ipc:///tmp/parity-client-rpc-10.ipc";
run_test_worker(scope, stop_guard.share(), socket_path);
let remote_client = nanoipc::init_client::<RemoteClient<_>>(socket_path).unwrap();
assert!(remote_client.handshake().is_ok());
})
}
#[test]
fn can_query_block() {
crossbeam::scope(|scope| {
let stop_guard = StopGuard::new();
let socket_path = "ipc:///tmp/parity-client-rpc-20.ipc";
run_test_worker(scope, stop_guard.share(), socket_path);
let remote_client = nanoipc::init_client::<RemoteClient<_>>(socket_path).unwrap();
let non_existant_block = remote_client.block_header(BlockID::Number(999));
assert!(non_existant_block.is_none());
})
}

View File

@ -16,11 +16,17 @@
//! Diff between two accounts.
use util::*;
use util::numbers::*;
use std::cmp::*;
use std::fmt;
use ipc::binary::{BinaryConvertError, BinaryConvertable};
use util::Bytes;
use std::collections::{VecDeque, BTreeMap};
use std::mem;
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, Binary)]
/// Diff type for specifying a change (or not).
pub enum Diff<T> where T: Eq {
pub enum Diff<T> where T: Eq + BinaryConvertable {
/// Both sides are the same.
Same,
/// Left (pre, source) side doesn't include value, right side (post, destination) does.
@ -31,7 +37,7 @@ pub enum Diff<T> where T: Eq {
Died(T),
}
impl<T> Diff<T> where T: Eq {
impl<T> Diff<T> where T: Eq + BinaryConvertable {
/// Construct new object with given `pre` and `post`.
pub fn new(pre: T, post: T) -> Self { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } }
@ -45,7 +51,7 @@ impl<T> Diff<T> where T: Eq {
pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false }}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, Binary)]
/// Account diff.
pub struct AccountDiff {
/// Change in balance, allowed to be `Diff::Same`.
@ -58,8 +64,8 @@ pub struct AccountDiff {
pub storage: BTreeMap<H256, Diff<H256>>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
/// Change in existance type.
#[derive(Debug, PartialEq, Eq, Clone, Binary)]
/// Change in existance type.
// TODO: include other types of change.
pub enum Existance {
/// Item came into existance.
@ -94,6 +100,8 @@ impl AccountDiff {
// TODO: refactor into something nicer.
fn interpreted_hash(u: &H256) -> String {
use util::bytes::*;
if u <= &H256::from(0xffffffff) {
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32())
} else if u <= &H256::from(u64::max_value()) {
@ -107,6 +115,8 @@ fn interpreted_hash(u: &H256) -> String {
impl fmt::Display for AccountDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use util::bytes::*;
match self.nonce {
Diff::Born(ref x) => try!(write!(f, " non {}", x)),
Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))),

View File

@ -0,0 +1,44 @@
// 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/>.
//! Block import error related types
use std::mem;
use ipc::binary::BinaryConvertError;
use std::collections::VecDeque;
use error::{ImportError, BlockError, Error};
use std::convert::From;
/// Error dedicated to import block function
#[derive(Binary, Debug)]
pub enum BlockImportError {
/// Import error
Import(ImportError),
/// Block error
Block(BlockError),
/// Other error
Other(String),
}
impl From<Error> for BlockImportError {
fn from(e: Error) -> Self {
match e {
Error::Block(block_error) => BlockImportError::Block(block_error),
Error::Import(import_error) => BlockImportError::Import(import_error),
_ => BlockImportError::Other(format!("other block import error: {:?}", e)),
}
}
}

View File

@ -0,0 +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/>.
//! Block queue info types
use std::mem;
use ipc::binary::BinaryConvertError;
use std::collections::VecDeque;
/// Block queue status
#[derive(Debug, Binary)]
pub struct BlockQueueInfo {
/// Number of queued blocks pending verification
pub unverified_queue_size: usize,
/// Number of verified queued blocks pending import
pub verified_queue_size: usize,
/// Number of blocks being verified
pub verifying_queue_size: usize,
/// Configured maximum number of blocks in the queue
pub max_queue_size: usize,
/// Configured maximum number of bytes to use
pub max_mem_use: usize,
/// Heap memory used in bytes
pub mem_used: usize,
}

View File

@ -0,0 +1,32 @@
// 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/>.
//! Call analytics related types
use std::mem;
use ipc::binary::{BinaryConvertError};
use std::collections::VecDeque;
/// Options concerning what analytics we run on the call.
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug, Binary)]
pub struct CallAnalytics {
/// Make a transaction trace.
pub transaction_tracing: bool,
/// Make a VM trace.
pub vm_tracing: bool,
/// Make a diff.
pub state_diffing: bool,
}

View File

@ -27,7 +27,7 @@ use std::mem;
use std::collections::VecDeque;
/// Transaction execution receipt.
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone, Binary)]
pub struct Executed {
/// Gas paid up front for execution of transaction.
pub gas: U256,

View File

@ -20,8 +20,12 @@ use util::hash::*;
use util::sha3::*;
use client::BlockID;
use log_entry::LogEntry;
use ipc::binary::BinaryConvertError;
use std::mem;
use std::collections::VecDeque;
/// Blockchain Filter.
#[derive(Binary)]
pub struct Filter {
/// Blockchain will be searched from this block.
pub from_block: BlockID,
@ -29,22 +33,27 @@ pub struct Filter {
/// Till this block.
pub to_block: BlockID,
/// Search addresses.
///
/// Search addresses.
///
/// If None, match all.
/// If specified, log must be produced by one of these addresses.
pub address: Option<Vec<Address>>,
/// Search topics.
///
///
/// If None, match all.
/// If specified, log must contain one of these topics.
pub topics: [Option<Vec<H256>>; 4],
pub topics: Vec<Option<Vec<H256>>>,
}
impl Clone for Filter {
fn clone(&self) -> Self {
let mut topics = [None, None, None, None];
let mut topics = [
None,
None,
None,
None,
];
for i in 0..4 {
topics[i] = self.topics[i].clone();
}
@ -53,13 +62,13 @@ impl Clone for Filter {
from_block: self.from_block.clone(),
to_block: self.to_block.clone(),
address: self.address.clone(),
topics: topics
topics: topics[..].to_vec()
}
}
}
impl Filter {
/// Returns combinations of each address and topic.
/// Returns combinations of each address and topic.
pub fn bloom_possibilities(&self) -> Vec<H2048> {
let blooms = match self.address {
Some(ref addresses) if !addresses.is_empty() =>
@ -71,7 +80,7 @@ impl Filter {
_ => vec![H2048::new()]
};
self.topics.iter().fold(blooms, | bs, topic | match *topic {
self.topics.iter().fold(blooms, |bs, topic| match *topic {
None => bs,
Some(ref topics) => bs.into_iter().flat_map(|bloom| {
topics.into_iter().map(|topic| {
@ -111,7 +120,7 @@ mod tests {
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: None,
topics: [None, None, None, None]
topics: vec![None, None, None, None],
};
let possibilities = none_filter.bloom_possibilities();
@ -126,9 +135,11 @@ mod tests {
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: [
topics: vec![
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
None, None, None
None,
None,
None,
]
};
@ -142,10 +153,11 @@ mod tests {
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: [
topics: vec![
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
None, None
None,
None,
]
};
@ -162,7 +174,7 @@ mod tests {
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
]),
topics: [
topics: vec![
Some(vec![
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()
@ -188,10 +200,11 @@ mod tests {
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: [
topics: vec![
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap()]),
None, None
None,
None,
]
};

View File

@ -47,6 +47,7 @@ pub enum TransactionID {
}
/// Uniquely identifies Trace.
#[derive(Binary)]
pub struct TraceId {
/// Transaction
pub transaction: TransactionID,
@ -55,10 +56,10 @@ pub struct TraceId {
}
/// Uniquely identifies Uncle.
#[derive(Debug)]
pub struct UncleID (
#[derive(Debug, Binary)]
pub struct UncleID {
/// Block id.
pub BlockID,
pub block: BlockID,
/// Position in block.
pub usize
);
pub position: usize
}

View File

@ -25,3 +25,9 @@ pub mod executed;
pub mod block_status;
pub mod account_diff;
pub mod state_diff;
pub mod block_queue_info;
pub mod filter;
pub mod trace_filter;
pub mod call_analytics;
pub mod transaction_import;
pub mod block_import_error;

View File

@ -16,24 +16,32 @@
//! State diff module.
use util::*;
use util::numbers::*;
use account_diff::*;
use ipc::binary::BinaryConvertError;
use std::mem;
use std::fmt;
use std::ops::*;
use std::collections::{VecDeque, BTreeMap};
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, Binary)]
/// Expression for the delta between two system states. Encoded the
/// delta of every altered account.
pub struct StateDiff (pub BTreeMap<Address, AccountDiff>);
pub struct StateDiff {
/// Raw diff key-value
pub raw: BTreeMap<Address, AccountDiff>
}
impl StateDiff {
/// Get the actual data.
pub fn get(&self) -> &BTreeMap<Address, AccountDiff> {
&self.0
&self.raw
}
}
impl fmt::Display for StateDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (add, acc) in &self.0 {
for (add, acc) in &self.raw {
try!(write!(f, "{} {}: {}", acc.existance(), add, acc));
}
Ok(())
@ -44,6 +52,6 @@ impl Deref for StateDiff {
type Target = BTreeMap<Address, AccountDiff>;
fn deref(&self) -> &Self::Target {
&self.0
&self.raw
}
}

View File

@ -0,0 +1,35 @@
// 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/>.
//! Trace filter related types
use std::mem;
use ipc::binary::{BinaryConvertError};
use std::collections::VecDeque;
use std::ops::Range;
use util::{Address};
use types::ids::BlockID;
/// Easy to use trace filter.
#[derive(Binary)]
pub struct Filter {
/// Range of filtering.
pub range: Range<BlockID>,
/// From address.
pub from_address: Vec<Address>,
/// To address.
pub to_address: Vec<Address>,
}

View File

@ -0,0 +1,52 @@
// 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/>.
//! Transaction import result related types
use ipc::binary::{BinaryConvertError, BinaryConvertable};
use std::collections::VecDeque;
use error::{TransactionError, Error};
use std::mem;
use util::Populatable;
#[derive(Debug, Clone, PartialEq)]
/// Represents the result of importing transaction.
pub enum TransactionImportResult {
/// Transaction was imported to current queue.
Current,
/// Transaction was imported to future queue.
Future
}
binary_fixed_size!(TransactionImportResult);
/// Api-level error for transaction import
#[derive(Debug, Clone, Binary)]
pub enum TransactionImportError {
/// Transaction error
Transaction(TransactionError),
/// Other error
Other(String),
}
impl From<Error> for TransactionImportError {
fn from(e: Error) -> Self {
match e {
Error::Transaction(transaction_error) => TransactionImportError::Transaction(transaction_error),
_ => TransactionImportError::Other(format!("other block import error: {:?}", e)),
}
}
}

View File

@ -17,9 +17,12 @@
//! Tree route info type definition
use util::numbers::H256;
use ipc::BinaryConvertError;
use std::collections::VecDeque;
use std::mem;
/// Represents a tree route between `from` block and `to` block:
#[derive(Debug)]
#[derive(Debug, Binary)]
pub struct TreeRoute {
/// A vector of hashes of all blocks, ordered from `from` to `to`.
pub blocks: Vec<H256>,

View File

@ -139,6 +139,11 @@ impl<'a> BlockView<'a> {
pub fn uncle_at(&self, index: usize) -> Option<Header> {
self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_val())
}
/// Return nth uncle rlp.
pub fn uncle_rlp_at(&self, index: usize) -> Option<Bytes> {
self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_raw().to_vec())
}
}
impl<'a> Hashable for BlockView<'a> {

View File

@ -22,5 +22,5 @@ aster = { version = "0.17", default-features = false }
clippy = { version = "^0.*", optional = true }
quasi = { version = "0.11", default-features = false }
quasi_macros = { version = "0.11", optional = true }
syntex = { version = "*", optional = true }
syntex_syntax = { version = "*", optional = true }
syntex = { version = "0.33", optional = true }
syntex_syntax = { version = "0.33", optional = true }

View File

@ -201,15 +201,20 @@ fn implement_dispatch_arm_invoke_stmt(
{
let _sp = ext_cx.call_site();
let mut tt = ::std::vec::Vec::new();
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("ipc"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("binary"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
if dispatch.return_type_ty.is_some() {
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("ipc"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("binary"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
}
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter());
@ -221,12 +226,25 @@ fn implement_dispatch_arm_invoke_stmt(
}
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
if dispatch.return_type_ty.is_some() {
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
}
else {
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Semi));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Vec"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("new"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
}
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
tt
})).unwrap()
}
@ -497,9 +515,9 @@ fn client_generics(builder: &aster::AstBuilder, interface_map: &InterfaceMap) ->
.build()
}
fn client_qualified_ident(builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> P<Ty> {
fn client_qualified_ident(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> P<Ty> {
let generics = client_generics(builder, interface_map);
aster::ty::TyBuilder::new().path().segment(interface_map.ident_map.client_ident(builder))
aster::ty::TyBuilder::new().path().segment(interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item))
.with_generics(generics).build()
.build()
}
@ -515,7 +533,7 @@ fn client_phantom_ident(builder: &aster::AstBuilder, interface_map: &InterfaceMa
/// for say `Service` it generates `ServiceClient`
fn push_client_struct(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap, push: &mut FnMut(Annotatable)) {
let generics = client_generics(builder, interface_map);
let client_short_ident = interface_map.ident_map.client_ident(builder);
let client_short_ident = interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item);
let phantom = client_phantom_ident(builder, interface_map);
let client_struct_item = quote_item!(cx,
@ -547,9 +565,9 @@ fn push_with_socket_client_implementation(
push: &mut FnMut(Annotatable))
{
let generics = client_generics(builder, interface_map);
let client_ident = client_qualified_ident(builder, interface_map);
let client_ident = client_qualified_ident(cx, builder, interface_map);
let where_clause = &generics.where_clause;
let client_short_ident = interface_map.ident_map.client_ident(builder);
let client_short_ident = interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item);
let implement = quote_item!(cx,
impl $generics ::ipc::WithSocket<S> for $client_ident $where_clause {
@ -578,7 +596,7 @@ fn push_client_implementation(
.collect::<Vec<P<ast::ImplItem>>>();
let generics = client_generics(builder, interface_map);
let client_ident = client_qualified_ident(builder, interface_map);
let client_ident = client_qualified_ident(cx, builder, interface_map);
let where_clause = &generics.where_clause;
let handshake_item = quote_impl_item!(cx,
@ -682,6 +700,51 @@ fn implement_handshake_arm(
}
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<String, ()> {
match lit.node {
ast::LitKind::Str(ref s, _) => Ok(format!("{}", s)),
_ => {
cx.span_err(
lit.span,
&format!("ipc client_ident annotation `{}` must be a string, not `{}`",
name,
::syntax::print::pprust::lit_to_string(lit)));
return Err(());
}
}
}
pub fn get_ipc_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
match attr.node.value.node {
ast::MetaItemKind::List(ref name, ref items) if name == &"ipc" => {
Some(items)
}
_ => None
}
}
fn client_ident_renamed(cx: &ExtCtxt, item: &ast::Item) -> Option<String> {
for meta_items in item.attrs().iter().filter_map(get_ipc_meta_items) {
for meta_item in meta_items {
match meta_item.node {
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"client_ident" => {
if let Ok(s) = get_str_from_lit(cx, name, lit) {
return Some(s);
}
}
_ => {
cx.span_err(
meta_item.span,
&format!("unknown client_ident container attribute `{}`",
::syntax::print::pprust::meta_item_to_string(meta_item)));
}
}
}
}
None
}
struct InterfaceMap {
pub original_item: Item,
pub item: P<ast::Item>,
@ -700,8 +763,13 @@ impl IdentMap {
builder.id(format!("{}", ::syntax::print::pprust::path_to_string(&self.original_path)))
}
fn client_ident(&self, builder: &aster::AstBuilder) -> Ident {
builder.id(format!("{}Client", self.original_path.segments[0].identifier))
fn client_ident(&self, cx: &ExtCtxt, builder: &aster::AstBuilder, item: &ast::Item) -> Ident {
if let Some(new_name) = client_ident_renamed(cx, item) {
builder.id(new_name)
}
else {
builder.id(format!("{}Client", self.original_path.segments[0].identifier))
}
}
fn qualified_ident(&self, builder: &aster::AstBuilder) -> Ident {

View File

@ -50,11 +50,36 @@ include!("lib.rs.in");
#[cfg(feature = "with-syntex")]
pub fn register(reg: &mut syntex::Registry) {
use syntax::{ast, fold};
#[cfg(feature = "with-syntex")]
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
struct StripAttributeFolder;
impl fold::Folder for StripAttributeFolder {
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
match attr.node.value.node {
ast::MetaItemKind::List(ref n, _) if n == &"ipc" => { return None; }
_ => {}
}
Some(attr)
}
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
fold::noop_fold_mac(mac, self)
}
}
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
}
reg.add_attr("feature(custom_derive)");
reg.add_attr("feature(custom_attribute)");
reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation);
reg.add_decorator("derive_Binary", serialization::expand_serialization_implementation);
reg.add_post_expansion_pass(strip_attributes);
}
#[cfg(not(feature = "with-syntex"))]
@ -67,4 +92,6 @@ pub fn register(reg: &mut rustc_plugin::Registry) {
syntax::parse::token::intern("derive_Binary"),
syntax::ext::base::MultiDecorator(
Box::new(serialization::expand_serialization_implementation)));
reg.register_attribute("ipc".to_owned(), AttributeType::Normal);
}

View File

@ -707,7 +707,12 @@ fn binary_expr_variant(
let buffer = &mut buffer[1..];
$write_expr
}),
read: quote_arm!(cx, $variant_index_ident => { $read_expr } ),
read: quote_arm!(cx,
$variant_index_ident => {
let buffer = &buffer[1..];
$read_expr
}
),
})
},
ast::VariantData::Struct(ref fields, _) => {
@ -742,7 +747,12 @@ fn binary_expr_variant(
let buffer = &mut buffer[1..];
$write_expr
}),
read: quote_arm!(cx, $variant_index_ident => { $read_expr } ),
read: quote_arm!(cx,
$variant_index_ident => {
let buffer = &buffer[1..];
$read_expr
}
),
})
},
}

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