diff --git a/Cargo.lock b/Cargo.lock index 8fdbfa351..a17bc4413 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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]] @@ -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]] @@ -388,9 +376,8 @@ dependencies = [ "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 +388,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)", ] @@ -506,26 +494,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 +547,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 +577,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 +607,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 +640,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 +844,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 +1100,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 +1191,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 +1301,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 +1322,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" diff --git a/README.md b/README.md index d605fa87c..fa20af4d0 100644 --- a/README.md +++ b/README.md @@ -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 ---- diff --git a/dapps/build.rs b/dapps/build.rs index 2bef33059..0776c03ec 100644 --- a/dapps/build.rs +++ b/dapps/build.rs @@ -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(); diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs index 95b01d442..ab3632074 100644 --- a/dapps/src/api/api.rs +++ b/dapps/src/api/api.rs @@ -15,38 +15,16 @@ // along with Parity. If not, see . use std::sync::Arc; -use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath}; - -use api::response::as_json; +use endpoint::{Endpoint, Endpoints, Handler, EndpointPath}; +use api::types::{App, ApiError}; +use api::response::{as_json, as_json_error}; +use hyper::{server, net, Decoder, Encoder, Next}; +#[derive(Clone)] pub struct RestApi { endpoints: Arc, } -#[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) -> Box { Box::new(RestApi { @@ -63,7 +41,39 @@ impl RestApi { impl Endpoint for RestApi { fn to_handler(&self, _path: EndpointPath) -> Box { - 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, +} + +impl server::Handler for RestApiRouter { + + fn on_request(&mut self, _request: server::Request) -> Next { + self.handler = as_json(&self.api.list_apps()); + Next::write() + } + + fn on_request_readable(&mut self, decoder: &mut Decoder) -> 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) -> Next { + self.handler.on_response_writable(encoder) + } + +} diff --git a/dapps/src/api/mod.rs b/dapps/src/api/mod.rs index 088d7f6b2..402e84257 100644 --- a/dapps/src/api/mod.rs +++ b/dapps/src/api/mod.rs @@ -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; diff --git a/dapps/src/api/response.rs b/dapps/src/api/response.rs index 345b8a6ee..ba0922e7a 100644 --- a/dapps/src/api/response.rs +++ b/dapps/src/api/response.rs @@ -16,8 +16,13 @@ use serde::Serialize; use serde_json; -use endpoint::{ContentHandler, Handler}; +use endpoint::Handler; +use handlers::ContentHandler; pub fn as_json(val: &T) -> Box { - 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(val: &T) -> Box { + Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned())) } diff --git a/util/json-tests/src/util.rs b/dapps/src/api/types.rs similarity index 80% rename from util/json-tests/src/util.rs rename to dapps/src/api/types.rs index f8beb269a..d99d767eb 100644 --- a/util/json-tests/src/util.rs +++ b/dapps/src/api/types.rs @@ -14,11 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use rustc_serialize::hex::FromHex; +#[cfg(feature = "serde_macros")] +include!("types.rs.in"); + +#[cfg(not(feature = "serde_macros"))] +include!(concat!(env!("OUT_DIR"), "/types.rs")); + -pub fn hex_or_string(s: &str) -> Vec { - match s.starts_with("0x") { - true => s[2..].from_hex().unwrap(), - false => From::from(s) - } -} diff --git a/dapps/src/api/types.rs.in b/dapps/src/api/types.rs.in new file mode 100644 index 000000000..72a8cd221 --- /dev/null +++ b/dapps/src/api/types.rs.in @@ -0,0 +1,51 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use 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, +} + + diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs index 075211e58..ba2116e1c 100644 --- a/dapps/src/endpoint.rs +++ b/dapps/src/endpoint.rs @@ -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 + Send>; + fn to_handler(&self, path: EndpointPath) -> Box + Send>; } pub type Endpoints = BTreeMap>; -pub type Handler = server::Handler + 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 for ContentHandler { - fn on_request(&mut self, _request: server::Request) -> Next { - Next::write() - } - - fn on_request_readable(&mut self, _decoder: &mut Decoder) -> 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) -> 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 + Send; diff --git a/dapps/src/handlers/auth.rs b/dapps/src/handlers/auth.rs new file mode 100644 index 000000000..7f72f7cf8 --- /dev/null +++ b/dapps/src/handlers/auth.rs @@ -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 . + +//! Authorization Handlers + +use hyper::{server, Decoder, Encoder, Next}; +use hyper::net::HttpStream; +use hyper::status::StatusCode; + +pub struct AuthRequiredHandler; + +impl server::Handler for AuthRequiredHandler { + fn on_request(&mut self, _request: server::Request) -> Next { + Next::write() + } + + fn on_request_readable(&mut self, _decoder: &mut Decoder) -> 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) -> Next { + Next::end() + } +} + diff --git a/dapps/src/handlers/content.rs b/dapps/src/handlers/content.rs new file mode 100644 index 000000000..a589e5492 --- /dev/null +++ b/dapps/src/handlers/content.rs @@ -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 . + +//! 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 for ContentHandler { + fn on_request(&mut self, _request: server::Request) -> Next { + Next::write() + } + + fn on_request_readable(&mut self, _decoder: &mut Decoder) -> 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) -> 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() + }, + } + } +} diff --git a/dapps/src/handlers/mod.rs b/dapps/src/handlers/mod.rs new file mode 100644 index 000000000..933538655 --- /dev/null +++ b/dapps/src/handlers/mod.rs @@ -0,0 +1,25 @@ +// 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 . + +//! Hyper handlers implementations. + +mod auth; +mod content; +mod redirect; + +pub use self::auth::AuthRequiredHandler; +pub use self::content::ContentHandler; +pub use self::redirect::Redirection; diff --git a/dapps/src/router/redirect.rs b/dapps/src/handlers/redirect.rs similarity index 100% rename from dapps/src/router/redirect.rs rename to dapps/src/handlers/redirect.rs diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 22b075afd..1c550fb07 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -59,6 +59,7 @@ mod endpoint; mod apps; mod page; mod router; +mod handlers; mod rpc; mod api; mod proxypac; diff --git a/dapps/src/proxypac.rs b/dapps/src/proxypac.rs index 9d91d58f0..aaa68dc0c 100644 --- a/dapps/src/proxypac.rs +++ b/dapps/src/proxypac.rs @@ -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())) } } diff --git a/dapps/src/router/auth.rs b/dapps/src/router/auth.rs index 9459d0719..d18424a00 100644 --- a/dapps/src/router/auth.rs +++ b/dapps/src/router/auth.rs @@ -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 + Send>), + No(Box), } /// Authorization interface pub trait Authorization : Send + Sync { /// Checks if authorization is valid. - fn is_authorized(&self, req: &server::Request)-> Authorized; + fn is_authorized(&self, req: &server::Request)-> 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)-> Authorized { + fn is_authorized(&self, _req: &server::Request)-> Authorized { Authorized::Yes } } impl Authorization for HttpBasicAuth { - fn is_authorized(&self, req: &server::Request) -> Authorized { + fn is_authorized(&self, req: &server::Request) -> 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, + "

Unauthorized

".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) -> Access { + fn check_auth(&self, req: &server::Request) -> Access { match req.headers().get::>() { 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 for UnauthorizedHandler { - fn on_request(&mut self, _request: server::Request) -> Next { - Next::write() - } - - fn on_request_readable(&mut self, _decoder: &mut Decoder) -> 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) -> 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 for AuthRequiredHandler { - fn on_request(&mut self, _request: server::Request) -> Next { - Next::write() - } - - fn on_request_readable(&mut self, _decoder: &mut Decoder) -> 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) -> Next { - Next::end() - } -} diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index 0cb0d38d0..f04c8b614 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -18,7 +18,6 @@ //! Processes request handling authorization and dispatching it to proper application. mod url; -mod redirect; pub mod auth; use DAPPS_DOMAIN; @@ -33,7 +32,7 @@ use apps; use endpoint::{Endpoint, Endpoints, EndpointPath}; use self::url::Url; use self::auth::{Authorization, Authorized}; -use self::redirect::Redirection; +use handlers::Redirection; /// Special endpoints are accessible on every domain (every dapp) #[derive(Debug, PartialEq, Hash, Eq)] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 0ea0023d6..acdcd667a 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -43,3 +43,4 @@ json-tests = [] test-heavy = [] dev = ["clippy"] default = [] +benches = [] diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs index 2db4ffcc0..5119b2e33 100644 --- a/ethcore/src/account.rs +++ b/ethcore/src/account.rs @@ -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() } diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 4b2246b21..252c17b34 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -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; + 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 diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 4f17901b7..f1c260970 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -17,11 +17,12 @@ //! Blockchain database client. use std::path::PathBuf; -use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; +use std::time::Instant; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}; use util::*; use util::panics::*; use views::BlockView; -use error::{Error, ImportError, ExecutionError, BlockError, ImportResult}; +use error::{ImportError, ExecutionError, BlockError, ImportResult}; use header::{BlockNumber}; use state::State; use spec::Spec; @@ -38,7 +39,9 @@ use filter::Filter; use log_entry::LocalizedLogEntry; use block_queue::{BlockQueue, BlockQueueInfo}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; -use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics}; +use client::{BlockID, TransactionID, UncleID, TraceId, Mode, ClientConfig, DatabaseCompactionProfile, + BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, TransactionImportError, + BlockImportError, TransactionImportResult}; use client::Error as ClientError; use env_info::EnvInfo; use executive::{Executive, Executed, TransactOptions, contract_address}; @@ -49,9 +52,10 @@ use trace; pub use types::blockchain_info::BlockChainInfo; pub use types::block_status::BlockStatus; use evm::Factory as EvmFactory; -use miner::{Miner, MinerService, TransactionImportResult, AccountDetails}; +use miner::{Miner, MinerService, AccountDetails}; 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 { @@ -81,9 +85,24 @@ impl ClientReport { } } +struct SleepState { + last_activity: Option, + last_autosleep: Option, +} + +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, tracedb: Arc>, engine: Arc>, @@ -96,6 +115,8 @@ pub struct Client { vm_factory: Arc, trie_factory: TrieFactory, miner: Arc, + sleep_state: Mutex, + liveness: AtomicBool, io_channel: IoChannel, queue_transactions: AtomicUsize, } @@ -132,9 +153,8 @@ impl Client { spec: Spec, path: &Path, miner: Arc, - message_channel: IoChannel) - -> Result, ClientError> - { + message_channel: IoChannel + ) -> Result, ClientError> { let path = get_db_path(path, config.pruning, spec.genesis_header().hash()); let gb = spec.genesis_block(); let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path)); @@ -165,7 +185,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, @@ -181,7 +205,6 @@ impl Client { io_channel: message_channel, queue_transactions: AtomicUsize::new(0), }; - Ok(Arc::new(client)) } @@ -447,9 +470,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. @@ -485,6 +540,29 @@ 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()); + } + } + } } impl BlockChainClient for Client { @@ -526,6 +604,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 { 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())) @@ -654,17 +738,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 { { 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 { @@ -778,12 +862,16 @@ impl BlockChainClient for Client { self.build_last_hashes(self.chain.best_block_hash()) } - fn import_transactions(&self, transactions: Vec) -> Vec> { + fn import_transactions(&self, transactions: Vec) -> Vec> { let fetch_account = |a: &Address| AccountDetails { nonce: self.latest_nonce(a), balance: self.latest_balance(a), }; - self.miner.import_transactions(self, transactions, fetch_account) + + self.miner.import_transactions(self, transactions, &fetch_account) + .into_iter() + .map(|res| res.map_err(|e| e.into())) + .collect() } fn queue_transactions(&self, transactions: Vec) { diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 6cb34c151..1010ce656 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +pub use std::time::Duration; pub use block_queue::BlockQueueConfig; pub use blockchain::Config as BlockChainConfig; pub use trace::{Config as TraceConfig, Switch}; @@ -35,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 { @@ -56,6 +74,8 @@ pub struct ClientConfig { pub db_cache_size: Option, /// State db compaction profile pub db_compaction: DatabaseCompactionProfile, + /// Operating mode + pub mode: Mode, /// Type of block verifier used by client. pub verifier_type: VerifierType, } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 27a1d3265..b81efe43a 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -23,7 +23,7 @@ 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}; @@ -47,8 +47,8 @@ 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 block_import_error::BlockImportError; +pub use transaction_import::{TransactionImportResult, TransactionImportError}; /// Options concerning what analytics we run on the call. #[derive(Eq, PartialEq, Default, Clone, Copy, Debug)] @@ -63,6 +63,11 @@ pub struct CallAnalytics { /// 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; @@ -145,7 +150,7 @@ pub trait BlockChainClient : Sync + Send { fn block_receipts(&self, hash: &H256) -> Option; /// Import a block into the blockchain. - fn import_block(&self, bytes: Bytes) -> ImportResult; + fn import_block(&self, bytes: Bytes) -> Result; /// Get block queue information. fn queue_info(&self) -> BlockQueueInfo; @@ -188,7 +193,7 @@ pub trait BlockChainClient : Sync + Send { fn last_hashes(&self) -> LastHashes; /// import transactions from network/other 3rd party - fn import_transactions(&self, transactions: Vec) -> Vec>; + fn import_transactions(&self, transactions: Vec) -> Vec>; /// Queue transactions for importing. fn queue_transactions(&self, transactions: Vec); diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index be90d9b67..1e51719c0 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -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, + TransactionImportError, BlockImportError}; use header::{Header as BlockHeader, BlockNumber}; use filter::Filter; use log_entry::LocalizedLogEntry; @@ -38,7 +40,6 @@ use error::{ExecutionError}; use trace::LocalizedTrace; use miner::{TransactionImportResult, AccountDetails}; -use error::Error as EthError; /// Test client. pub struct TestBlockChainClient { @@ -402,7 +403,7 @@ impl BlockChainClient for TestBlockChainClient { None } - fn import_block(&self, b: Bytes) -> ImportResult { + fn import_block(&self, b: Bytes) -> Result { let header = Rlp::new(&b).val_at::(0); let h = header.hash(); let number: usize = header.number as usize; @@ -487,7 +488,7 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } - fn import_transactions(&self, transactions: Vec) -> Vec> { + fn import_transactions(&self, transactions: Vec) -> Vec> { let nonces = self.nonces.read().unwrap(); let balances = self.balances.read().unwrap(); let fetch_account = |a: &Address| AccountDetails { @@ -496,6 +497,9 @@ impl BlockChainClient for TestBlockChainClient { }; self.miner.import_transactions(self, transactions, &fetch_account) + .into_iter() + .map(|res| res.map_err(|e| e.into())) + .collect() } fn queue_transactions(&self, transactions: Vec) { diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 92d3cbe6b..8c37e98ef 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -20,10 +20,11 @@ use util::*; use header::BlockNumber; use basic_types::LogBloom; use client::Error as ClientError; - pub use types::executed::ExecutionError; +use ipc::binary::{BinaryConvertError, BinaryConvertable}; +use types::block_import_error::BlockImportError; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] /// Errors concerning transaction processing. pub enum TransactionError { /// Transaction is already imported to the queue @@ -312,6 +313,21 @@ impl From for Error { } } +impl From 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 { diff --git a/ethcore/src/evm/benches/mod.rs b/ethcore/src/evm/benches/mod.rs new file mode 100644 index 000000000..8ef730d88 --- /dev/null +++ b/ethcore/src/evm/benches/mod.rs @@ -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 . + +//! 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) -> U256 { + match r { + Ok(evm::GasLeft::Known(v)) => v, + Ok(evm::GasLeft::NeedsReturn(v, _)) => v, + _ => U256::zero(), + } +} diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs index 740774f38..43919034e 100644 --- a/ethcore/src/evm/evm.rs +++ b/ethcore/src/evm/evm.rs @@ -95,6 +95,61 @@ impl<'a> Finalize for Result> { } } +pub trait CostType: ops::Mul + ops::Div + ops::Add + ops::Sub + ops::Shr + ops::Shl + cmp::Ord + Sized + From + Copy { + fn as_u256(&self) -> U256; + fn from_u256(val: U256) -> Result; + 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 { + 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 { + 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. diff --git a/ethcore/src/evm/factory.rs b/ethcore/src/evm/factory.rs index f55e064c7..082b9d050 100644 --- a/ethcore/src/evm/factory.rs +++ b/ethcore/src/evm/factory.rs @@ -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 { + pub fn create(&self, gas: U256) -> Box { 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::::default()) + } else { + Box::new(super::interpreter::Interpreter::::default()) } } } /// Create fresh instance of VM + /// Might choose implementation depending on supplied gas. #[cfg(not(feature = "jit"))] - pub fn create(&self) -> Box { + pub fn create(&self, gas: U256) -> Box { 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::::default()) + } else { + Box::new(super::interpreter::Interpreter::::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 diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs index c313726f1..9a1ea97cc 100644 --- a/ethcore/src/evm/instructions.rs +++ b/ethcore/src/evm/instructions.rs @@ -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, diff --git a/ethcore/src/evm/interpreter/gasometer.rs b/ethcore/src/evm/interpreter/gasometer.rs new file mode 100644 index 000000000..069d70e19 --- /dev/null +++ b/ethcore/src/evm/interpreter/gasometer.rs @@ -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 . + +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 { + Gas(Cost), + GasMem(Cost, Cost), + GasMemCopy(Cost, Cost, Cost) +} + +pub struct Gasometer { + pub current_gas: Gas, +} + +impl Gasometer { + + 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, + 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::from_u256(overflowing!(mem.overflowing_add(U256::from(add)))) + } + + fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result { + 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(value: Gas, num: usize) -> (Gas, bool) { + value.overflow_add(Gas::from(num)) +} + +#[test] +fn test_mem_gas_cost() { + // given + let gasometer = Gasometer::::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::::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); +} diff --git a/ethcore/src/evm/interpreter/memory.rs b/ethcore/src/evm/interpreter/memory.rs new file mode 100644 index 000000000..a77a6d494 --- /dev/null +++ b/ethcore/src/evm/interpreter/memory.rs @@ -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 . + +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 { + 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)); +} diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter/mod.rs similarity index 57% rename from ethcore/src/evm/interpreter.rs rename to ethcore/src/evm/interpreter/mod.rs index 5b97b66af..2e03f44b8 100644 --- a/ethcore/src/evm/interpreter.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -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 { - /// 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 { - stack: Vec, - logs: [S; instructions::MAX_NO_OF_TOPICS] -} - -impl VecStack { - fn with_capacity(capacity: usize, zero: S) -> Self { - VecStack { - stack: Vec::with_capacity(capacity), - logs: [zero; instructions::MAX_NO_OF_TOPICS] - } - } -} - -impl Stack for VecStack { - 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 { - 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 { 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 { mem: Vec, + _type: PhantomData, } -impl evm::Evm for Interpreter { +impl evm::Evm for Interpreter { fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result { self.mem.clear(); let code = ¶ms.code.as_ref().unwrap(); let valid_jump_destinations = self.find_jump_destinations(&code); - let mut current_gas = params.gas; + let mut gasometer = Gasometer::::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(¤t_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, ¶ms, ext, instruction, &mut reader, &mut stack + gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack )); if trace_executed { - ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written); + 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 - ) -> evm::Result<(U256, usize)> { +impl Interpreter { + + fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack) -> 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(©, 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 { - Ok(overflowing!(mem.overflowing_add(U256::from(add)))) - } - - fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result { - 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 - ) -> evm::Result { + ) -> evm::Result> { 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 - ) -> 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) -> evm::Result { 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::::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)); } diff --git a/ethcore/src/evm/interpreter/stack.rs b/ethcore/src/evm/interpreter/stack.rs new file mode 100644 index 000000000..98adf8539 --- /dev/null +++ b/ethcore/src/evm/interpreter/stack.rs @@ -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 . + +use std::fmt; +use evm::instructions; + +/// Stack trait with VM-friendly API +pub trait Stack { + /// 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 { + stack: Vec, + logs: [S; instructions::MAX_NO_OF_TOPICS] +} + +impl VecStack { + pub fn with_capacity(capacity: usize, zero: S) -> Self { + VecStack { + stack: Vec::with_capacity(capacity), + logs: [zero; instructions::MAX_NO_OF_TOPICS] + } + } +} + +impl Stack for VecStack { + 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()] + } +} + diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index 5e7b67cfb..de208c6a4 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -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; diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index ba156e6dd..573ffd3b4 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -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, 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
, @@ -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, @@ -67,7 +67,7 @@ fn test_finalize(res: Result) -> Result { } 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 = Box::new(super::interpreter::Interpreter::default()); + let mut vm : Box = Box::new(super::interpreter::Interpreter::::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() }; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 4525d6a24..f5ff95d71 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -211,7 +211,7 @@ impl<'a> Executive<'a> { let vm_factory = self.vm_factory; let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); - return vm_factory.create().exec(params, &mut ext).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(¶ms), 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() } diff --git a/ethcore/src/header.rs b/ethcore/src/header.rs index 0941c9ca6..77ec4813b 100644 --- a/ethcore/src/header.rs +++ b/ethcore/src/header.rs @@ -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), diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index f4a34a33e..28dde2a38 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -208,7 +208,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { &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(); diff --git a/ethcore/src/json_tests/mod.rs b/ethcore/src/json_tests/mod.rs index df67de76d..841db229e 100644 --- a/ethcore/src/json_tests/mod.rs +++ b/ethcore/src/json_tests/mod.rs @@ -23,3 +23,4 @@ mod state; mod chain; mod homestead_state; mod homestead_chain; +mod trie; diff --git a/ethcore/src/json_tests/trie.rs b/ethcore/src/json_tests/trie.rs new file mode 100644 index 000000000..2d23ff7d2 --- /dev/null +++ b/ethcore/src/json_tests/trie.rs @@ -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 . + +use ethjson; +use util::{H256, MemoryDB, TrieMut, TrieSpec, TrieFactory}; + +fn test_trie(json: &[u8], trie: TrieSpec) -> Vec { + 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 = key.into(); + let value: Vec = 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 { + 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 { + 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"} +} diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 54a944331..183a3c251 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -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 //! diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 40254b8c2..3e5d3d445 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -19,6 +19,7 @@ use std::sync::atomic::AtomicBool; use std::time::{Instant, Duration}; use util::*; +use util::using_queue::{UsingQueue, GetAction}; use util::Colour::White; use account_provider::AccountProvider; use views::{BlockView, HeaderView}; @@ -29,8 +30,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 +201,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); }, diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 152bd1a61..3d0185753 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -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}; diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 7f5b59c38..bdce9f504 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -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)] @@ -309,15 +310,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 +431,10 @@ impl TransactionQueue { pub fn add(&mut self, tx: SignedTransaction, fetch_account: &T, origin: TransactionOrigin) -> Result 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 +450,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 +469,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 +557,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 +578,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 +666,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 +683,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 +719,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 +804,7 @@ mod test { use error::{Error, TransactionError}; use super::*; use super::{TransactionSet, TransactionOrder, VerifiedTransaction}; + use client::TransactionImportResult; fn unwrap_tx_err(err: Result) -> TransactionError { match err.unwrap_err() { diff --git a/ethcore/src/types/block_import_error.rs b/ethcore/src/types/block_import_error.rs new file mode 100644 index 000000000..8b400613f --- /dev/null +++ b/ethcore/src/types/block_import_error.rs @@ -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 . + +//! 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 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)), + } + } +} diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in index b51e9e57b..97579da8a 100644 --- a/ethcore/src/types/mod.rs.in +++ b/ethcore/src/types/mod.rs.in @@ -25,3 +25,5 @@ pub mod executed; pub mod block_status; pub mod account_diff; pub mod state_diff; +pub mod transaction_import; +pub mod block_import_error; diff --git a/ethcore/src/types/transaction_import.rs b/ethcore/src/types/transaction_import.rs new file mode 100644 index 000000000..c52a9fb80 --- /dev/null +++ b/ethcore/src/types/transaction_import.rs @@ -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 . + +//! 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 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)), + } + } +} diff --git a/ipc/codegen/src/codegen.rs b/ipc/codegen/src/codegen.rs index 672132c9f..c07f0b37a 100644 --- a/ipc/codegen/src/codegen.rs +++ b/ipc/codegen/src/codegen.rs @@ -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() } diff --git a/ipc/tests/examples.rs b/ipc/tests/examples.rs index b4159f196..8b52be11c 100644 --- a/ipc/tests/examples.rs +++ b/ipc/tests/examples.rs @@ -190,4 +190,20 @@ mod tests { assert_eq!(struct_, new_struct); } + + #[test] + fn can_call_void_method() { + let mut socket = TestSocket::new(); + socket.read_buffer = vec![1]; + let service_client = ServiceClient::init(socket); + + service_client.void(99); + + assert_eq!(vec![ + 0, 19, + 0, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, + 99, 0, 0, 0, 0, 0, 0, 0], + service_client.socket().write().unwrap().write_buffer.clone()); + } } diff --git a/ipc/tests/service.rs.in b/ipc/tests/service.rs.in index 2c58122bf..6bee63133 100644 --- a/ipc/tests/service.rs.in +++ b/ipc/tests/service.rs.in @@ -39,12 +39,14 @@ impl Service { *lock = *lock + f as usize; f } + pub fn rollback(&self, a: Option, b: u32) -> i32 { let a_0 = a.unwrap_or_else(|| 0); let mut lock = self.rollbacks.write().unwrap(); *lock = *lock + a_0 as usize - b as usize; (a_0 - b) as i32 } + pub fn push_custom(&self, data: CustomData) -> bool { let mut clock = self.commits.write().unwrap(); let mut rlock = self.commits.write().unwrap(); @@ -54,6 +56,9 @@ impl Service { true } + + pub fn void(&self, a: u64) { + } } impl Service { diff --git a/json/src/bytes.rs b/json/src/bytes.rs index 18dc73844..7741b8d3b 100644 --- a/json/src/bytes.rs +++ b/json/src/bytes.rs @@ -16,15 +16,23 @@ //! Lenient bytes json deserialization for test json files. +use std::str::FromStr; +use std::ops::Deref; use rustc_serialize::hex::FromHex; use serde::{Deserialize, Deserializer, Error}; use serde::de::Visitor; -use std::ops::Deref; /// Lenient bytes json deserialization for test json files. -#[derive(Default, Debug, PartialEq, Clone)] +#[derive(Default, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)] pub struct Bytes(Vec); +impl Bytes { + /// Creates bytes struct. + pub fn new(v: Vec) -> Self { + Bytes(v) + } +} + impl Into> for Bytes { fn into(self) -> Vec { self.0 @@ -39,6 +47,25 @@ impl Deref for Bytes { } } +impl FromStr for Bytes { + type Err = String; + + fn from_str(value: &str) -> Result { + let v = match value.len() { + 0 => vec![], + 2 if value.starts_with("0x") => vec![], + _ if value.starts_with("0x") && value.len() % 2 == 1 => { + let v = "0".to_owned() + &value[2..]; + FromHex::from_hex(v.as_ref() as &str).unwrap_or(vec![]), + }, + _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]), + _ => FromHex::from_hex(value).unwrap_or(vec![]), + }; + + Ok(Bytes(v)) + } +} + impl Deserialize for Bytes { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer { @@ -52,17 +79,7 @@ impl Visitor for BytesVisitor { type Value = Bytes; fn visit_str(&mut self, value: &str) -> Result where E: Error { - let v = match value.len() { - 0 => vec![], - 2 if value.starts_with("0x") => vec![], - _ if value.starts_with("0x") && value.len() % 2 == 1 => { - let v = "0".to_owned() + &value[2..]; - FromHex::from_hex(v.as_ref() as &str).unwrap_or(vec![]), - }, - _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]), - _ => FromHex::from_hex(value).unwrap_or(vec![]), - }; - Ok(Bytes(v)) + Bytes::from_str(value).map_err(Error::custom) } fn visit_string(&mut self, value: String) -> Result where E: Error { diff --git a/json/src/lib.rs.in b/json/src/lib.rs.in index 73adaf07a..541629305 100644 --- a/json/src/lib.rs.in +++ b/json/src/lib.rs.in @@ -24,6 +24,7 @@ pub mod uint; pub mod bytes; pub mod blockchain; pub mod spec; +pub mod trie; pub mod vm; pub mod maybe; pub mod state; diff --git a/json/src/trie/input.rs b/json/src/trie/input.rs new file mode 100644 index 000000000..326f42dba --- /dev/null +++ b/json/src/trie/input.rs @@ -0,0 +1,155 @@ +// 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 . + +//! Trie test input deserialization. + +use std::collections::BTreeMap; +use std::str::FromStr; +use bytes::Bytes; +use serde::{Deserialize, Deserializer, Error}; +use serde::de::{Visitor, MapVisitor, SeqVisitor}; + +/// Trie test input. +#[derive(Debug, PartialEq)] +pub struct Input { + /// Input params. + pub data: BTreeMap>, +} + +impl Deserialize for Input { + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer + { + deserializer.deserialize(InputVisitor) + } +} + +struct InputVisitor; + +impl Visitor for InputVisitor { + type Value = Input; + + fn visit_map(&mut self, mut visitor: V) -> Result where V: MapVisitor { + let mut result = BTreeMap::new(); + + loop { + let key_str: Option = try!(visitor.visit_key()); + let key = match key_str { + Some(ref k) if k.starts_with("0x") => try!(Bytes::from_str(k).map_err(Error::custom)), + Some(k) => Bytes::new(k.into_bytes()), + None => { break; } + }; + + let val_str: Option = try!(visitor.visit_value()); + let val = match val_str { + Some(ref v) if v.starts_with("0x") => Some(try!(Bytes::from_str(v).map_err(Error::custom))), + Some(v) => Some(Bytes::new(v.into_bytes())), + None => None, + }; + + result.insert(key, val); + } + + try!(visitor.end()); + + let input = Input { + data: result + }; + + Ok(input) + } + + fn visit_seq(&mut self, mut visitor: V) -> Result where V: SeqVisitor { + let mut result = BTreeMap::new(); + + loop { + let keyval: Option>> = try!(visitor.visit()); + let keyval = match keyval { + Some(k) => k, + _ => { break; }, + }; + + if keyval.len() != 2 { + return Err(Error::custom("Invalid key value pair.")); + } + + let ref key_str: Option = keyval[0]; + let ref val_str: Option = keyval[1]; + + let key = match *key_str { + Some(ref k) if k.starts_with("0x") => try!(Bytes::from_str(k).map_err(Error::custom)), + Some(ref k) => Bytes::new(k.clone().into_bytes()), + None => { break; } + }; + + let val = match *val_str { + Some(ref v) if v.starts_with("0x") => Some(try!(Bytes::from_str(v).map_err(Error::custom))), + Some(ref v) => Some(Bytes::new(v.clone().into_bytes())), + None => None, + }; + + result.insert(key, val); + } + + try!(visitor.end()); + + let input = Input { + data: result + }; + + Ok(input) + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + use serde_json; + use bytes::Bytes; + use super::Input; + + #[test] + fn input_deserialization_from_map() { + let s = r#"{ + "0x0045" : "0x0123456789", + "be" : "e", + "0x0a" : null + }"#; + + let input: Input = serde_json::from_str(s).unwrap(); + let mut map = BTreeMap::new(); + map.insert(Bytes::new(vec![0, 0x45]), Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89]))); + map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); + map.insert(Bytes::new(vec![0x0a]), None); + assert_eq!(input.data, map); + } + + #[test] + fn input_deserialization_from_array() { + let s = r#"[ + ["0x0045", "0x0123456789"], + ["be", "e"], + ["0x0a", null] + ]"#; + + let input: Input = serde_json::from_str(s).unwrap(); + let mut map = BTreeMap::new(); + map.insert(Bytes::new(vec![0, 0x45]), Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89]))); + map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); + map.insert(Bytes::new(vec![0x0a]), None); + assert_eq!(input.data, map); + } +} diff --git a/dapps/src/api/mod.rs.in b/json/src/trie/mod.rs similarity index 83% rename from dapps/src/api/mod.rs.in rename to json/src/trie/mod.rs index a069c06b0..1c5d30067 100644 --- a/dapps/src/api/mod.rs.in +++ b/json/src/trie/mod.rs @@ -14,8 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -mod api; -mod response; +//! Trie test deserialization. -pub use self::api::RestApi; -pub use self::api::App; +mod input; +mod trie; +mod test; + +pub use self::input::Input; +pub use self::trie::Trie; +pub use self::test::Test; diff --git a/json/src/trie/test.rs b/json/src/trie/test.rs new file mode 100644 index 000000000..19becd5ee --- /dev/null +++ b/json/src/trie/test.rs @@ -0,0 +1,43 @@ +// 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 . + +//! TransactionTest test deserializer. + +use std::collections::BTreeMap; +use std::io::Read; +use serde_json; +use serde_json::Error; +use trie::Trie; + +/// TransactionTest test deserializer. +#[derive(Debug, PartialEq, Deserialize)] +pub struct Test(BTreeMap); + +impl IntoIterator for Test { + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Test { + /// Loads test from json. + pub fn load(reader: R) -> Result where R: Read { + serde_json::from_reader(reader) + } +} diff --git a/json/src/trie/trie.rs b/json/src/trie/trie.rs new file mode 100644 index 000000000..c04498591 --- /dev/null +++ b/json/src/trie/trie.rs @@ -0,0 +1,30 @@ +// 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 . + +//! Trie test deserialization. + +use hash::H256; +use trie::Input; + +/// Trie test deserialization. +#[derive(Debug, Deserialize, PartialEq)] +pub struct Trie { + /// Trie test input. + #[serde(rename="in")] + pub input: Input, + /// Trie root hash. + pub root: H256, +} diff --git a/parity/cli.rs b/parity/cli.rs index 17954eb2a..4c5d4cbc9 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -32,7 +32,19 @@ Usage: parity [options] parity ui [options] -Protocol Options: +Operating Options: + --mode MODE Set the operating mode. MODE can be one of: + active - Parity continuously syncs the chain. + passive - Parity syncs initially, then sleeps and + wakes regularly to resync. + dark - Parity syncs only when an external interface + is active. [default: active]. + --mode-timeout SECS Specify the number of seconds before inactivity + timeout occurs when mode is dark or passive + [default: 300]. + --mode-alarm SECS Specify the number of seconds before auto sleep + reawake timeout occurs when mode is passive + [default: 3600]. --chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, or testnet @@ -269,6 +281,9 @@ pub struct Args { pub arg_pid_file: String, pub arg_file: Option, pub arg_path: Vec, + pub flag_mode: String, + pub flag_mode_timeout: u64, + pub flag_mode_alarm: u64, pub flag_chain: String, pub flag_db_path: String, pub flag_identity: String, diff --git a/parity/configuration.rs b/parity/configuration.rs index 333e2f79f..056e61363 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -28,7 +28,7 @@ use util::*; use util::log::Colour::*; use ethcore::account_provider::AccountProvider; use util::network_settings::NetworkSettings; -use ethcore::client::{append_path, get_db_path, ClientConfig, DatabaseCompactionProfile, Switch, VMType}; +use ethcore::client::{append_path, get_db_path, Mode, ClientConfig, DatabaseCompactionProfile, Switch, VMType}; use ethcore::miner::{MinerOptions, PendingSet}; use ethcore::ethereum; use ethcore::spec::Spec; @@ -61,6 +61,15 @@ impl Configuration { } } + pub fn mode(&self) -> Mode { + match &(self.args.flag_mode[..]) { + "active" => Mode::Active, + "passive" => Mode::Passive(Duration::from_secs(self.args.flag_mode_timeout), Duration::from_secs(self.args.flag_mode_alarm)), + "dark" => Mode::Dark(Duration::from_secs(self.args.flag_mode_timeout)), + _ => die!("{}: Invalid address for --mode. Must be one of active, passive or dark.", self.args.flag_mode), + } + } + fn net_port(&self) -> u16 { self.args.flag_port } @@ -254,7 +263,7 @@ impl Configuration { let host = IpAddr::from_str(host).unwrap_or_else(|_| die!("Invalid host given with `--nat extip:{}`", host)); Some(SocketAddr::new(host, port)) } else { - listen_address + None }; (listen_address, public_address) } @@ -313,6 +322,8 @@ impl Configuration { pub fn client_config(&self, spec: &Spec) -> ClientConfig { let mut client_config = ClientConfig::default(); + client_config.mode = self.mode(); + match self.args.flag_cache { Some(mb) => { client_config.blockchain.max_cache_size = mb * 1024 * 1024; diff --git a/parity/main.rs b/parity/main.rs index ca1a9e03d..97279ed30 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -82,8 +82,8 @@ use rustc_serialize::hex::FromHex; use ctrlc::CtrlC; use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError, paint, Colour, version}; use util::panics::{MayPanic, ForwardPanic, PanicHandler}; -use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path}; -use ethcore::error::{Error, ImportError}; +use ethcore::client::{Mode, BlockID, BlockChainClient, ClientConfig, get_db_path, BlockImportError}; +use ethcore::error::{ImportError}; use ethcore::service::ClientService; use ethcore::spec::Spec; use ethsync::EthSync; @@ -213,7 +213,12 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) // Build client let mut service = ClientService::start( - client_config, spec, net_settings, Path::new(&conf.path()), miner.clone(), !conf.args.flag_no_network + client_config, + spec, + net_settings, + Path::new(&conf.path()), + miner.clone(), + match conf.mode() { Mode::Dark(..) => false, _ => !conf.args.flag_no_network } ).unwrap_or_else(|e| die_with_error("Client", e)); panic_handler.forward_from(&service); @@ -282,7 +287,7 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) }); // Register IO handler - let io_handler = Arc::new(ClientIoHandler { + let io_handler = Arc::new(ClientIoHandler { client: service.client(), info: Informant::new(conf.have_color()), sync: sync.clone(), @@ -458,7 +463,7 @@ fn execute_import(conf: Configuration) { while client.queue_info().is_full() { sleep(Duration::from_secs(1)); } match client.import_block(bytes) { Ok(_) => {} - Err(Error::Import(ImportError::AlreadyInChain)) => { trace!("Skipping block already in chain."); } + Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!("Skipping block already in chain."); } Err(e) => die!("Cannot import block: {:?}", e) } informant.tick(client.deref(), None); diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 5762237c6..4b971c167 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -165,7 +165,7 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone(), queue).to_delegate()) }, Api::EthcoreSet => { - server.add_delegate(EthcoreSetClient::new(&deps.miner, &deps.net_service).to_delegate()) + server.add_delegate(EthcoreSetClient::new(&deps.client, &deps.miner, &deps.net_service).to_delegate()) }, Api::Traces => { server.add_delegate(TracesClient::new(&deps.client, &deps.miner).to_delegate()) diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index 2acf98bf2..3df96b00e 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -16,8 +16,10 @@ mod poll_manager; mod poll_filter; +mod requests; mod signing_queue; pub use self::poll_manager::PollManager; pub use self::poll_filter::PollFilter; -pub use self::signing_queue::{ConfirmationsQueue, SigningQueue}; +pub use self::requests::{TransactionRequest, TransactionConfirmation, CallRequest}; +pub use self::signing_queue::{ConfirmationsQueue, SigningQueue, QueueEvent}; diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs new file mode 100644 index 000000000..d162774d9 --- /dev/null +++ b/rpc/src/v1/helpers/requests.rs @@ -0,0 +1,64 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use util::{Address, U256}; + +/// Transaction request coming from RPC +#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] +pub struct TransactionRequest { + /// Sender + pub from: Address, + /// Recipient + pub to: Option
, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value of transaction in wei + pub value: Option, + /// Additional data sent with transaction + pub data: Option>, + /// Transaction's nonce + pub nonce: Option, +} + +/// Transaction confirmation waiting in a queue +#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] +pub struct TransactionConfirmation { + /// Id of this confirmation + pub id: U256, + /// TransactionRequest + pub transaction: TransactionRequest, +} + +/// Call request +#[derive(Debug, Default, PartialEq)] +pub struct CallRequest { + /// From + pub from: Option
, + /// To + pub to: Option
, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value + pub value: Option, + /// Data + pub data: Option>, + /// Nonce + pub nonce: Option, +} diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs index 3d7f9e35b..756718000 100644 --- a/rpc/src/v1/helpers/signing_queue.rs +++ b/rpc/src/v1/helpers/signing_queue.rs @@ -18,9 +18,9 @@ use std::thread; use std::time::{Instant, Duration}; use std::sync::{mpsc, Mutex, RwLock, Arc}; use std::collections::HashMap; -use v1::types::{TransactionRequest, TransactionConfirmation}; -use util::U256; use jsonrpc_core; +use util::U256; +use v1::helpers::{TransactionRequest, TransactionConfirmation}; /// Result that can be returned from JSON RPC. pub type RpcResult = Result; @@ -301,10 +301,9 @@ mod test { use std::time::Duration; use std::thread; use std::sync::{Arc, Mutex}; - use util::hash::Address; - use util::numbers::{U256, H256}; - use v1::types::TransactionRequest; - use super::*; + use util::{Address, U256, H256}; + use v1::helpers::{SigningQueue, ConfirmationsQueue, QueueEvent, TransactionRequest}; + use v1::types::H256 as NH256; use jsonrpc_core::to_value; fn request() -> TransactionRequest { @@ -337,10 +336,10 @@ mod test { // Just wait for the other thread to start thread::sleep(Duration::from_millis(100)); } - queue.request_confirmed(id, to_value(&H256::from(1))); + queue.request_confirmed(id, to_value(&NH256::from(H256::from(1)))); // then - assert_eq!(handle.join().expect("Thread should finish nicely"), to_value(&H256::from(1))); + assert_eq!(handle.join().expect("Thread should finish nicely"), to_value(&NH256::from(H256::from(1)))); } #[test] diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index f2325de93..13f54feea 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -38,7 +38,8 @@ use ethcore::log_entry::LogEntry; use ethcore::filter::Filter as EthcoreFilter; use self::ethash::SeedHashCompute; use v1::traits::Eth; -use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; +use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, Index, Filter, Log, Receipt, H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256}; +use v1::helpers::CallRequest as CRequest; use v1::impls::{default_gas_price, dispatch_transaction, error_codes}; use serde; use ethcore::header::Header as BlockHeader; @@ -86,28 +87,28 @@ impl EthClient where let block_view = BlockView::new(&bytes); let view = block_view.header_view(); let block = Block { - hash: OptionalValue::Value(view.sha3()), - parent_hash: view.parent_hash(), - uncles_hash: view.uncles_hash(), - author: view.author(), - miner: view.author(), - state_root: view.state_root(), - transactions_root: view.transactions_root(), - receipts_root: view.receipts_root(), - number: OptionalValue::Value(U256::from(view.number())), - gas_used: view.gas_used(), - gas_limit: view.gas_limit(), - logs_bloom: view.log_bloom(), - timestamp: U256::from(view.timestamp()), - difficulty: view.difficulty(), - total_difficulty: total_difficulty, + hash: Some(view.sha3().into()), + parent_hash: view.parent_hash().into(), + uncles_hash: view.uncles_hash().into(), + author: view.author().into(), + miner: view.author().into(), + state_root: view.state_root().into(), + transactions_root: view.transactions_root().into(), + receipts_root: view.receipts_root().into(), + number: Some(view.number().into()), + gas_used: view.gas_used().into(), + gas_limit: view.gas_limit().into(), + logs_bloom: view.log_bloom().into(), + timestamp: view.timestamp().into(), + difficulty: view.difficulty().into(), + total_difficulty: total_difficulty.into(), seal_fields: view.seal().into_iter().map(|f| decode(&f)).map(Bytes::new).collect(), - uncles: block_view.uncle_hashes(), + uncles: block_view.uncle_hashes().into_iter().map(Into::into).collect(), transactions: { if include_txs { - BlockTransactions::Full(block_view.localized_transactions().into_iter().map(From::from).collect()) + BlockTransactions::Full(block_view.localized_transactions().into_iter().map(Into::into).collect()) } else { - BlockTransactions::Hashes(block_view.transaction_hashes()) + BlockTransactions::Hashes(block_view.transaction_hashes().into_iter().map(Into::into).collect()) } }, extra_data: Bytes::new(view.extra_data()) @@ -127,7 +128,6 @@ impl EthClient where fn uncle(&self, id: UncleID) -> Result { let client = take_weak!(self.client); - let uncle: BlockHeader = match client.uncle(id) { Some(rlp) => decode(&rlp), None => { return Ok(Value::Null); } @@ -138,22 +138,22 @@ impl EthClient where }; let block = Block { - hash: OptionalValue::Value(uncle.hash()), - parent_hash: uncle.parent_hash, - uncles_hash: uncle.uncles_hash, - author: uncle.author, - miner: uncle.author, - state_root: uncle.state_root, - transactions_root: uncle.transactions_root, - number: OptionalValue::Value(U256::from(uncle.number)), - gas_used: uncle.gas_used, - gas_limit: uncle.gas_limit, - logs_bloom: uncle.log_bloom, - timestamp: U256::from(uncle.timestamp), - difficulty: uncle.difficulty, - total_difficulty: uncle.difficulty + parent_difficulty, - receipts_root: uncle.receipts_root, - extra_data: Bytes::new(uncle.extra_data), + hash: Some(uncle.hash().into()), + parent_hash: uncle.parent_hash.into(), + uncles_hash: uncle.uncles_hash.into(), + author: uncle.author.into(), + miner: uncle.author.into(), + state_root: uncle.state_root.into(), + transactions_root: uncle.transactions_root.into(), + number: Some(uncle.number.into()), + gas_used: uncle.gas_used.into(), + gas_limit: uncle.gas_limit.into(), + logs_bloom: uncle.log_bloom.into(), + timestamp: uncle.timestamp.into(), + difficulty: uncle.difficulty.into(), + total_difficulty: (uncle.difficulty + parent_difficulty).into(), + receipts_root: uncle.receipts_root.into(), + extra_data: uncle.extra_data.into(), seal_fields: uncle.seal.into_iter().map(|f| decode(&f)).map(Bytes::new).collect(), uncles: vec![], transactions: BlockTransactions::Hashes(vec![]), @@ -161,7 +161,7 @@ impl EthClient where to_value(&block) } - fn sign_call(&self, request: CallRequest) -> Result { + fn sign_call(&self, request: CRequest) -> Result { let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); let from = request.from.unwrap_or(Address::zero()); Ok(EthTransaction { @@ -186,7 +186,7 @@ pub fn pending_logs(miner: &M, filter: &EthcoreFilter) -> Vec where M: M .filter(|pair| filter.matches(&pair.1)) .map(|pair| { let mut log = Log::from(pair.1); - log.transaction_hash = Some(pair.0); + log.transaction_hash = Some(pair.0.into()); log }) .collect(); @@ -241,6 +241,19 @@ fn no_author_err() -> Error { } } +impl EthClient where + C: MiningBlockChainClient + 'static, + S: SyncProvider + 'static, + M: MinerService + 'static, + EM: ExternalMinerService + 'static { + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } +} + impl Eth for EthClient where C: MiningBlockChainClient + 'static, S: SyncProvider + 'static, @@ -248,6 +261,7 @@ impl Eth for EthClient where EM: ExternalMinerService + 'static { fn protocol_version(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => Ok(Value::String(format!("{}", take_weak!(self.sync).status().protocol_version).to_owned())), _ => Err(Error::invalid_params()) @@ -255,6 +269,7 @@ impl Eth for EthClient where } fn syncing(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let status = take_weak!(self.sync).status(); @@ -262,15 +277,17 @@ impl Eth for EthClient where SyncState::Idle => SyncStatus::None, SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead => { let current_block = U256::from(take_weak!(self.client).chain_info().best_block_number); + let highest_block = U256::from(status.highest_block_number.unwrap_or(status.start_block_number)); - let info = SyncInfo { - starting_block: U256::from(status.start_block_number), - current_block: current_block, - highest_block: U256::from(status.highest_block_number.unwrap_or(status.start_block_number)) - }; - match info.highest_block > info.current_block + U256::from(6) { - true => SyncStatus::Info(info), - false => SyncStatus::None, + if highest_block > current_block + U256::from(6) { + let info = SyncInfo { + starting_block: status.start_block_number.into(), + current_block: current_block.into(), + highest_block: highest_block.into(), + }; + SyncStatus::Info(info) + } else { + SyncStatus::None } } }; @@ -281,13 +298,15 @@ impl Eth for EthClient where } fn author(&self, params: Params) -> Result { + try!(self.active()); match params { - Params::None => to_value(&take_weak!(self.miner).author()), + Params::None => to_value(&RpcH160::from(take_weak!(self.miner).author())), _ => Err(Error::invalid_params()), } } fn is_mining(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => to_value(&self.external_miner.is_mining()), _ => Err(Error::invalid_params()) @@ -295,118 +314,148 @@ impl Eth for EthClient where } fn hashrate(&self, params: Params) -> Result { + try!(self.active()); match params { - Params::None => to_value(&self.external_miner.hashrate()), + Params::None => to_value(&RpcU256::from(self.external_miner.hashrate())), _ => Err(Error::invalid_params()) } } fn gas_price(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); - to_value(&default_gas_price(&*client, &*miner)) + to_value(&RpcU256::from(default_gas_price(&*client, &*miner))) } _ => Err(Error::invalid_params()) } } fn accounts(&self, _: Params) -> Result { + try!(self.active()); let store = take_weak!(self.accounts); - to_value(&store.accounts()) + to_value(&store.accounts().into_iter().map(Into::into).collect::>()) } fn block_number(&self, params: Params) -> Result { + try!(self.active()); match params { - Params::None => to_value(&U256::from(take_weak!(self.client).chain_info().best_block_number)), + Params::None => to_value(&RpcU256::from(take_weak!(self.client).chain_info().best_block_number)), _ => Err(Error::invalid_params()) } } fn balance(&self, params: Params) -> Result { + try!(self.active()); from_params_default_second(params) - .and_then(|(address, block_number,)| match block_number { - BlockNumber::Pending => to_value(&take_weak!(self.miner).balance(take_weak!(self.client).deref(), &address)), - id => to_value(&try!(take_weak!(self.client).balance(&address, id.into()).ok_or_else(make_unsupported_err))), - }) - } - - fn storage_at(&self, params: Params) -> Result { - from_params_default_third::(params) - .and_then(|(address, position, block_number,)| match block_number { - BlockNumber::Pending => to_value(&U256::from(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)))), - id => match take_weak!(self.client).storage_at(&address, &H256::from(position), id.into()) { - Some(s) => to_value(&U256::from(s)), - None => Err(make_unsupported_err()), // None is only returned on unsupported requests. + .and_then(|(address, block_number,)| { + let address: Address = RpcH160::into(address); + match block_number { + BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).balance(take_weak!(self.client).deref(), &address))), + id => to_value(&RpcU256::from(try!(take_weak!(self.client).balance(&address, id.into()).ok_or_else(make_unsupported_err)))), } }) } + fn storage_at(&self, params: Params) -> Result { + try!(self.active()); + from_params_default_third::(params) + .and_then(|(address, position, block_number,)| { + let address: Address = RpcH160::into(address); + let position: U256 = RpcU256::into(position); + match block_number { + BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)))), + id => match take_weak!(self.client).storage_at(&address, &H256::from(position), id.into()) { + Some(s) => to_value(&RpcU256::from(s)), + None => Err(make_unsupported_err()), // None is only returned on unsupported requests. + } + } + }) + + } + fn transaction_count(&self, params: Params) -> Result { + try!(self.active()); from_params_default_second(params) - .and_then(|(address, block_number,)| match block_number { - BlockNumber::Pending => to_value(&take_weak!(self.miner).nonce(take_weak!(self.client).deref(), &address)), - id => to_value(&take_weak!(self.client).nonce(&address, id.into())), + .and_then(|(address, block_number,)| { + let address: Address = RpcH160::into(address); + match block_number { + BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).nonce(take_weak!(self.client).deref(), &address))), + id => to_value(&take_weak!(self.client).nonce(&address, id.into()).map(RpcU256::from)), + } }) } fn block_transaction_count_by_hash(&self, params: Params) -> Result { - from_params::<(H256,)>(params) + try!(self.active()); + from_params::<(RpcH256,)>(params) .and_then(|(hash,)| // match - take_weak!(self.client).block(BlockID::Hash(hash)) - .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).transactions_count())))) + take_weak!(self.client).block(BlockID::Hash(hash.into())) + .map_or(Ok(Value::Null), |bytes| to_value(&RpcU256::from(BlockView::new(&bytes).transactions_count())))) } fn block_transaction_count_by_number(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber,)>(params) .and_then(|(block_number,)| match block_number { BlockNumber::Pending => to_value( - &U256::from(take_weak!(self.miner).status().transactions_in_pending_block) + &RpcU256::from(take_weak!(self.miner).status().transactions_in_pending_block) ), _ => take_weak!(self.client).block(block_number.into()) - .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).transactions_count()))) + .map_or(Ok(Value::Null), |bytes| to_value(&RpcU256::from(BlockView::new(&bytes).transactions_count()))) }) } fn block_uncles_count_by_hash(&self, params: Params) -> Result { - from_params::<(H256,)>(params) + try!(self.active()); + from_params::<(RpcH256,)>(params) .and_then(|(hash,)| - take_weak!(self.client).block(BlockID::Hash(hash)) - .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).uncles_count())))) + take_weak!(self.client).block(BlockID::Hash(hash.into())) + .map_or(Ok(Value::Null), |bytes| to_value(&RpcU256::from(BlockView::new(&bytes).uncles_count())))) } fn block_uncles_count_by_number(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber,)>(params) .and_then(|(block_number,)| match block_number { - BlockNumber::Pending => to_value(&U256::from(0)), + BlockNumber::Pending => to_value(&RpcU256::from(0)), _ => take_weak!(self.client).block(block_number.into()) - .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).uncles_count()))) + .map_or(Ok(Value::Null), |bytes| to_value(&RpcU256::from(BlockView::new(&bytes).uncles_count()))) }) } fn code_at(&self, params: Params) -> Result { + try!(self.active()); from_params_default_second(params) - .and_then(|(address, block_number,)| match block_number { - BlockNumber::Pending => to_value(&take_weak!(self.miner).code(take_weak!(self.client).deref(), &address).map_or_else(Bytes::default, Bytes::new)), - BlockNumber::Latest => to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new)), - _ => Err(Error::invalid_params()), + .and_then(|(address, block_number,)| { + let address: Address = RpcH160::into(address); + match block_number { + BlockNumber::Pending => to_value(&take_weak!(self.miner).code(take_weak!(self.client).deref(), &address).map_or_else(Bytes::default, Bytes::new)), + BlockNumber::Latest => to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new)), + _ => Err(Error::invalid_params()), + } }) } fn block_by_hash(&self, params: Params) -> Result { - from_params::<(H256, bool)>(params) - .and_then(|(hash, include_txs)| self.block(BlockID::Hash(hash), include_txs)) + try!(self.active()); + from_params::<(RpcH256, bool)>(params) + .and_then(|(hash, include_txs)| self.block(BlockID::Hash(hash.into()), include_txs)) } fn block_by_number(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber, bool)>(params) .and_then(|(number, include_txs)| self.block(number.into(), include_txs)) } fn transaction_by_hash(&self, params: Params) -> Result { - from_params::<(H256,)>(params) + try!(self.active()); + from_params::<(RpcH256,)>(params) .and_then(|(hash,)| { let miner = take_weak!(self.miner); + let hash: H256 = hash.into(); match miner.transaction(&hash) { Some(pending_tx) => to_value(&Transaction::from(pending_tx)), None => self.transaction(TransactionID::Hash(hash)) @@ -415,19 +464,23 @@ impl Eth for EthClient where } fn transaction_by_block_hash_and_index(&self, params: Params) -> Result { - from_params::<(H256, Index)>(params) - .and_then(|(hash, index)| self.transaction(TransactionID::Location(BlockID::Hash(hash), index.value()))) + try!(self.active()); + from_params::<(RpcH256, Index)>(params) + .and_then(|(hash, index)| self.transaction(TransactionID::Location(BlockID::Hash(hash.into()), index.value()))) } fn transaction_by_block_number_and_index(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber, Index)>(params) .and_then(|(number, index)| self.transaction(TransactionID::Location(number.into(), index.value()))) } fn transaction_receipt(&self, params: Params) -> Result { - from_params::<(H256,)>(params) + try!(self.active()); + from_params::<(RpcH256,)>(params) .and_then(|(hash,)| { let miner = take_weak!(self.miner); + let hash: H256 = hash.into(); match miner.pending_receipts().get(&hash) { Some(receipt) if self.allow_pending_receipt_query => to_value(&Receipt::from(receipt.clone())), _ => { @@ -440,16 +493,19 @@ impl Eth for EthClient where } fn uncle_by_block_hash_and_index(&self, params: Params) -> Result { - from_params::<(H256, Index)>(params) - .and_then(|(hash, index)| self.uncle(UncleID { block: BlockID::Hash(hash), position: index.value() })) + try!(self.active()); + from_params::<(RpcH256, Index)>(params) + .and_then(|(hash, index)| self.uncle(UncleID { block: BlockID::Hash(hash.into()), position: index.value() })) } fn uncle_by_block_number_and_index(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber, Index)>(params) .and_then(|(number, index)| self.uncle(UncleID { block: number.into(), position: index.value() })) } fn compilers(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => to_value(&vec![] as &Vec), _ => Err(Error::invalid_params()) @@ -457,6 +513,7 @@ impl Eth for EthClient where } fn logs(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Filter,)>(params) .and_then(|(filter,)| { let include_pending = filter.to_block == Some(BlockNumber::Pending); @@ -476,6 +533,7 @@ impl Eth for EthClient where } fn work(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let client = take_weak!(self.client); @@ -503,8 +561,9 @@ impl Eth for EthClient where miner.map_sealing_work(client.deref(), |b| { let pow_hash = b.hash(); let target = Ethash::difficulty_to_boundary(b.block().header().difficulty()); - let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(b.block().header().number()); - to_value(&(pow_hash, H256::from_slice(&seed_hash[..]), target, &U256::from(b.block().header().number()))) + let seed_hash = self.seed_compute.lock().unwrap().get_seedhash(b.block().header().number()); + let block_number = RpcU256::from(b.block().header().number()); + to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number)) }).unwrap_or(Err(Error::internal_error())) // no work found. }, _ => Err(Error::invalid_params()) @@ -512,7 +571,11 @@ impl Eth for EthClient where } fn submit_work(&self, params: Params) -> Result { - from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| { + try!(self.active()); + from_params::<(RpcH64, RpcH256, RpcH256)>(params).and_then(|(nonce, pow_hash, mix_hash)| { + let nonce: H64 = nonce.into(); + let pow_hash: H256 = pow_hash.into(); + let mix_hash: H256 = mix_hash.into(); trace!(target: "miner", "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash); let miner = take_weak!(self.miner); let client = take_weak!(self.client); @@ -523,27 +586,31 @@ impl Eth for EthClient where } fn submit_hashrate(&self, params: Params) -> Result { - from_params::<(U256, H256)>(params).and_then(|(rate, id)| { - self.external_miner.submit_hashrate(rate, id); + try!(self.active()); + from_params::<(RpcU256, RpcH256)>(params).and_then(|(rate, id)| { + self.external_miner.submit_hashrate(rate.into(), id.into()); to_value(&true) }) } fn send_raw_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Bytes, )>(params) .and_then(|(raw_transaction, )| { let raw_transaction = raw_transaction.to_vec(); match UntrustedRlp::new(&raw_transaction).as_val() { Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction), - Err(_) => to_value(&H256::zero()), + Err(_) => to_value(&RpcH256::from(H256::from(0))), } }) } fn call(&self, params: Params) -> Result { + try!(self.active()); trace!(target: "jsonrpc", "call: {:?}", params); from_params_default_second(params) .and_then(|(request, block_number,)| { + let request = CallRequest::into(request); let signed = try!(self.sign_call(request)); let r = match block_number { BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()), @@ -555,27 +622,32 @@ impl Eth for EthClient where } fn estimate_gas(&self, params: Params) -> Result { + try!(self.active()); from_params_default_second(params) .and_then(|(request, block_number,)| { + let request = CallRequest::into(request); let signed = try!(self.sign_call(request)); let r = match block_number { BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()), BlockNumber::Latest => take_weak!(self.client).call(&signed, Default::default()), _ => return Err(Error::invalid_params()), }; - to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))) + to_value(&RpcU256::from(r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))) }) } fn compile_lll(&self, _: Params) -> Result { + try!(self.active()); rpc_unimplemented!() } fn compile_serpent(&self, _: Params) -> Result { + try!(self.active()); rpc_unimplemented!() } fn compile_solidity(&self, _: Params) -> Result { + try!(self.active()); rpc_unimplemented!() } } diff --git a/rpc/src/v1/impls/eth_filter.rs b/rpc/src/v1/impls/eth_filter.rs index b34a4f703..cf2006788 100644 --- a/rpc/src/v1/impls/eth_filter.rs +++ b/rpc/src/v1/impls/eth_filter.rs @@ -25,7 +25,7 @@ use ethcore::miner::MinerService; use ethcore::filter::Filter as EthcoreFilter; use ethcore::client::{BlockChainClient, BlockID}; use v1::traits::EthFilter; -use v1::types::{BlockNumber, Index, Filter, Log}; +use v1::types::{BlockNumber, Index, Filter, Log, H256 as RpcH256, U256 as RpcU256}; use v1::helpers::{PollFilter, PollManager}; use v1::impls::eth::pending_logs; @@ -52,6 +52,12 @@ impl EthFilterClient where polls: Mutex::new(PollManager::new()), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl EthFilter for EthFilterClient where @@ -59,40 +65,44 @@ impl EthFilter for EthFilterClient where M: MinerService + 'static { fn new_filter(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Filter,)>(params) .and_then(|(filter,)| { let mut polls = self.polls.lock().unwrap(); let block_number = take_weak!(self.client).chain_info().best_block_number; let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter)); - to_value(&U256::from(id)) + to_value(&RpcU256::from(id)) }) } fn new_block_filter(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let mut polls = self.polls.lock().unwrap(); let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number)); - to_value(&U256::from(id)) + to_value(&RpcU256::from(id)) }, _ => Err(Error::invalid_params()) } } fn new_pending_transaction_filter(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let mut polls = self.polls.lock().unwrap(); let pending_transactions = take_weak!(self.miner).pending_transactions_hashes(); let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions)); - to_value(&U256::from(id)) + to_value(&RpcU256::from(id)) }, _ => Err(Error::invalid_params()) } } fn filter_changes(&self, params: Params) -> Result { + try!(self.active()); let client = take_weak!(self.client); from_params::<(Index,)>(params) .and_then(|(index,)| { @@ -106,7 +116,8 @@ impl EthFilter for EthFilterClient where let hashes = (*block_number..current_number).into_iter() .map(BlockID::Number) .filter_map(|id| client.block_hash(id)) - .collect::>(); + .map(Into::into) + .collect::>(); *block_number = current_number; @@ -125,7 +136,8 @@ impl EthFilter for EthFilterClient where .iter() .filter(|hash| !previous_hashes_set.contains(hash)) .cloned() - .collect::>() + .map(Into::into) + .collect::>() }; // save all hashes of pending transactions @@ -181,6 +193,7 @@ impl EthFilter for EthFilterClient where } fn filter_logs(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Index,)>(params) .and_then(|(index,)| { let mut polls = self.polls.lock().unwrap(); @@ -206,6 +219,7 @@ impl EthFilter for EthFilterClient where } fn uninstall_filter(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Index,)>(params) .and_then(|(index,)| { self.polls.lock().unwrap().remove_poll(&index.value()); diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs index fa4330f46..4ba076358 100644 --- a/rpc/src/v1/impls/eth_signing.rs +++ b/rpc/src/v1/impls/eth_signing.rs @@ -20,17 +20,17 @@ use std::sync::{Arc, Weak}; use jsonrpc_core::*; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; -use util::numbers::*; +use util::{U256, Address, H256}; use ethcore::account_provider::AccountProvider; -use v1::helpers::{SigningQueue, ConfirmationsQueue}; +use v1::helpers::{SigningQueue, ConfirmationsQueue, TransactionRequest as TRequest}; use v1::traits::EthSigning; -use v1::types::{TransactionRequest, Bytes}; +use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520}; use v1::impls::{default_gas_price, sign_and_dispatch}; -fn fill_optional_fields(request: &mut TransactionRequest, client: &C, miner: &M) +fn fill_optional_fields(request: &mut TRequest, client: &C, miner: &M) where C: MiningBlockChainClient, M: MinerService { if request.value.is_none() { - request.value = Some(U256::zero()); + request.value = Some(U256::from(0)); } if request.gas.is_none() { request.gas = Some(miner.sensible_gas_limit()); @@ -39,7 +39,7 @@ fn fill_optional_fields(request: &mut TransactionRequest, client: &C, mine request.gas_price = Some(default_gas_price(client, miner)); } if request.data.is_none() { - request.data = Some(Bytes::new(Vec::new())); + request.data = Some(Vec::new()); } } @@ -61,6 +61,12 @@ impl EthSigningQueueClient where C: MiningBlockChainClient, M: Miner miner: Arc::downgrade(miner), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl EthSigning for EthSigningQueueClient @@ -68,14 +74,17 @@ impl EthSigning for EthSigningQueueClient { fn sign(&self, _params: Params) -> Result { + try!(self.active()); warn!("Invoking eth_sign is not yet supported with signer enabled."); // TODO [ToDr] Implement sign when rest of the signing queue is ready. rpc_unimplemented!() } fn send_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(TransactionRequest, )>(params) - .and_then(|(mut request, )| { + .and_then(|(request, )| { + let mut request: TRequest = request.into(); let accounts = take_weak!(self.accounts); let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); @@ -83,7 +92,7 @@ impl EthSigning for EthSigningQueueClient let sender = request.from; return match sign_and_dispatch(&*client, &*miner, request, &*accounts, sender) { Ok(hash) => to_value(&hash), - _ => to_value(&H256::zero()), + _ => to_value(&RpcH256::default()), } } @@ -91,7 +100,7 @@ impl EthSigning for EthSigningQueueClient fill_optional_fields(&mut request, &*client, &*miner); let id = queue.add_request(request); let result = id.wait_with_timeout(); - result.unwrap_or_else(|| to_value(&H256::new())) + result.unwrap_or_else(|| to_value(&RpcH256::default())) }) } } @@ -118,6 +127,12 @@ impl EthSigningUnsafeClient where accounts: Arc::downgrade(accounts), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl EthSigning for EthSigningUnsafeClient where @@ -125,18 +140,23 @@ impl EthSigning for EthSigningUnsafeClient where M: MinerService + 'static { fn sign(&self, params: Params) -> Result { - from_params::<(Address, H256)>(params).and_then(|(addr, msg)| { - to_value(&take_weak!(self.accounts).sign(addr, msg).unwrap_or(H520::zero())) + try!(self.active()); + from_params::<(RpcH160, RpcH256)>(params).and_then(|(address, msg)| { + let address: Address = address.into(); + let msg: H256 = msg.into(); + to_value(&take_weak!(self.accounts).sign(address, msg).ok().map_or_else(RpcH520::default, Into::into)) }) } fn send_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(TransactionRequest, )>(params) .and_then(|(request, )| { + let request: TRequest = request.into(); let sender = request.from; match sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*take_weak!(self.accounts), sender) { Ok(hash) => to_value(&hash), - _ => to_value(&H256::zero()), + _ => to_value(&RpcH256::default()), } }) } diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 3a58d8672..b62e21b0c 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -25,7 +25,7 @@ use ethcore::client::{MiningBlockChainClient}; use jsonrpc_core::*; use ethcore::miner::MinerService; use v1::traits::Ethcore; -use v1::types::{Bytes}; +use v1::types::{Bytes, U256}; use v1::helpers::{SigningQueue, ConfirmationsQueue}; use v1::impls::error_codes; @@ -52,56 +52,74 @@ impl EthcoreClient where C: MiningBlockChainClient, M: MinerService confirmations_queue: queue, } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl Ethcore for EthcoreClient where M: MinerService + 'static, C: MiningBlockChainClient + 'static { fn transactions_limit(&self, _: Params) -> Result { + try!(self.active()); to_value(&take_weak!(self.miner).transactions_limit()) } fn min_gas_price(&self, _: Params) -> Result { - to_value(&take_weak!(self.miner).minimal_gas_price()) + try!(self.active()); + to_value(&U256::from(take_weak!(self.miner).minimal_gas_price())) } fn extra_data(&self, _: Params) -> Result { + try!(self.active()); to_value(&Bytes::new(take_weak!(self.miner).extra_data())) } fn gas_floor_target(&self, _: Params) -> Result { - to_value(&take_weak!(self.miner).gas_floor_target()) + try!(self.active()); + to_value(&U256::from(take_weak!(self.miner).gas_floor_target())) } fn gas_ceil_target(&self, _: Params) -> Result { - to_value(&take_weak!(self.miner).gas_ceil_target()) + try!(self.active()); + to_value(&U256::from(take_weak!(self.miner).gas_ceil_target())) } fn dev_logs(&self, _params: Params) -> Result { + try!(self.active()); let logs = self.logger.logs(); to_value(&logs.deref().as_slice()) } fn dev_logs_levels(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.logger.levels()) } fn net_chain(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.settings.chain) } fn net_max_peers(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.settings.max_peers) } fn net_port(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.settings.network_port) } fn node_name(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.settings.name) } fn rpc_settings(&self, _params: Params) -> Result { + try!(self.active()); let mut map = BTreeMap::new(); map.insert("enabled".to_owned(), Value::Bool(self.settings.rpc_enabled)); map.insert("interface".to_owned(), Value::String(self.settings.rpc_interface.clone())); @@ -110,6 +128,7 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static, C: M } fn default_extra_data(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => to_value(&Bytes::new(version_data())), _ => Err(Error::invalid_params()), @@ -117,11 +136,12 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static, C: M } fn gas_price_statistics(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => match take_weak!(self.client).gas_price_statistics(100, 8) { Ok(stats) => to_value(&stats - .iter() - .map(|x| to_value(&x).expect("x must be U256; qed")) + .into_iter() + .map(|x| to_value(&U256::from(x)).expect("x must be U256; qed")) .collect::>()), _ => Err(Error::internal_error()), }, @@ -130,6 +150,7 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static, C: M } fn unsigned_transactions_count(&self, _params: Params) -> Result { + try!(self.active()); match self.confirmations_queue { None => Err(Error { code: ErrorCode::ServerError(error_codes::SIGNER_DISABLED), diff --git a/rpc/src/v1/impls/ethcore_set.rs b/rpc/src/v1/impls/ethcore_set.rs index baf5bf134..1b8dd474e 100644 --- a/rpc/src/v1/impls/ethcore_set.rs +++ b/rpc/src/v1/impls/ethcore_set.rs @@ -15,57 +15,74 @@ // along with Parity. If not, see . /// Ethcore-specific rpc interface for operations altering the settings. -use util::{U256, Address}; -use util::network::{NetworkService, NonReservedPeerMode}; use std::sync::{Arc, Weak}; use jsonrpc_core::*; use ethcore::miner::MinerService; +use ethcore::client::MiningBlockChainClient; use ethcore::service::SyncMessage; +use util::network::{NetworkService, NonReservedPeerMode}; use v1::traits::EthcoreSet; -use v1::types::Bytes; +use v1::types::{Bytes, H160, U256}; /// Ethcore-specific rpc interface for operations altering the settings. -pub struct EthcoreSetClient where +pub struct EthcoreSetClient where + C: MiningBlockChainClient, M: MinerService { + client: Weak, miner: Weak, net: Weak>, } -impl EthcoreSetClient where M: MinerService { +impl EthcoreSetClient where + C: MiningBlockChainClient, + M: MinerService { /// Creates new `EthcoreSetClient`. - pub fn new(miner: &Arc, net: &Arc>) -> Self { + pub fn new(client: &Arc, miner: &Arc, net: &Arc>) -> Self { EthcoreSetClient { + client: Arc::downgrade(client), miner: Arc::downgrade(miner), net: Arc::downgrade(net), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } -impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { +impl EthcoreSet for EthcoreSetClient where + C: MiningBlockChainClient + 'static, + M: MinerService + 'static { fn set_min_gas_price(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256,)>(params).and_then(|(gas_price,)| { - take_weak!(self.miner).set_minimal_gas_price(gas_price); + take_weak!(self.miner).set_minimal_gas_price(gas_price.into()); to_value(&true) }) } fn set_gas_floor_target(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256,)>(params).and_then(|(target,)| { - take_weak!(self.miner).set_gas_floor_target(target); + take_weak!(self.miner).set_gas_floor_target(target.into()); to_value(&true) }) } fn set_gas_ceil_target(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256,)>(params).and_then(|(target,)| { - take_weak!(self.miner).set_gas_ceil_target(target); + take_weak!(self.miner).set_gas_ceil_target(target.into()); to_value(&true) }) } fn set_extra_data(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Bytes,)>(params).and_then(|(extra_data,)| { take_weak!(self.miner).set_extra_data(extra_data.to_vec()); to_value(&true) @@ -73,13 +90,15 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn set_author(&self, params: Params) -> Result { - from_params::<(Address,)>(params).and_then(|(author,)| { - take_weak!(self.miner).set_author(author); + try!(self.active()); + from_params::<(H160,)>(params).and_then(|(author,)| { + take_weak!(self.miner).set_author(author.into()); to_value(&true) }) } fn set_transactions_limit(&self, params: Params) -> Result { + try!(self.active()); from_params::<(usize,)>(params).and_then(|(limit,)| { take_weak!(self.miner).set_transactions_limit(limit); to_value(&true) @@ -87,6 +106,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn set_tx_gas_limit(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256,)>(params).and_then(|(limit,)| { take_weak!(self.miner).set_tx_gas_limit(limit.into()); to_value(&true) @@ -94,6 +114,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn add_reserved_peer(&self, params: Params) -> Result { + try!(self.active()); from_params::<(String,)>(params).and_then(|(peer,)| { match take_weak!(self.net).add_reserved_peer(&peer) { Ok(()) => to_value(&true), @@ -103,6 +124,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn remove_reserved_peer(&self, params: Params) -> Result { + try!(self.active()); from_params::<(String,)>(params).and_then(|(peer,)| { match take_weak!(self.net).remove_reserved_peer(&peer) { Ok(()) => to_value(&true), @@ -112,11 +134,13 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn drop_non_reserved_peers(&self, _: Params) -> Result { + try!(self.active()); take_weak!(self.net).set_non_reserved_mode(NonReservedPeerMode::Deny); to_value(&true) } fn accept_non_reserved_peers(&self, _: Params) -> Result { + try!(self.active()); take_weak!(self.net).set_non_reserved_mode(NonReservedPeerMode::Accept); to_value(&true) } diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 1a6b1c398..d64619527 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -53,7 +53,8 @@ pub use self::ethcore_set::EthcoreSetClient; pub use self::traces::TracesClient; pub use self::rpc::RpcClient; -use v1::types::TransactionRequest; +use v1::helpers::TransactionRequest; +use v1::types::H256 as NH256; use ethcore::error::Error as EthcoreError; use ethcore::miner::{AccountDetails, MinerService}; use ethcore::client::MiningBlockChainClient; @@ -77,7 +78,7 @@ mod error_codes { fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result where C: MiningBlockChainClient, M: MinerService { - let hash = signed_transaction.hash(); + let hash = NH256::from(signed_transaction.hash()); let import = miner.import_own_transaction(client, signed_transaction, |a: &Address| { AccountDetails { diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 38191ed90..d6286afb9 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -18,10 +18,11 @@ use std::sync::{Arc, Weak}; use jsonrpc_core::*; use v1::traits::Personal; -use v1::types::TransactionRequest; +use v1::types::{H160 as RpcH160, H256 as RpcH256, TransactionRequest}; use v1::impls::unlock_sign_and_dispatch; +use v1::helpers::{TransactionRequest as TRequest}; use ethcore::account_provider::AccountProvider; -use util::numbers::*; +use util::Address; use ethcore::client::MiningBlockChainClient; use ethcore::miner::MinerService; @@ -43,27 +44,36 @@ impl PersonalClient where C: MiningBlockChainClient, M: MinerService signer_port: signer_port, } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl Personal for PersonalClient where C: MiningBlockChainClient, M: MinerService { fn signer_enabled(&self, _: Params) -> Result { + try!(self.active()); self.signer_port .map(|v| to_value(&v)) .unwrap_or_else(|| to_value(&false)) } fn accounts(&self, _: Params) -> Result { + try!(self.active()); let store = take_weak!(self.accounts); - to_value(&store.accounts()) + to_value(&store.accounts().into_iter().map(Into::into).collect::>()) } fn new_account(&self, params: Params) -> Result { + try!(self.active()); from_params::<(String, )>(params).and_then( |(pass, )| { let store = take_weak!(self.accounts); match store.new_account(&pass) { - Ok(address) => to_value(&address), + Ok(address) => to_value(&RpcH160::from(address)), Err(_) => Err(Error::internal_error()) } } @@ -71,8 +81,10 @@ impl Personal for PersonalClient where C: MiningBl } fn unlock_account(&self, params: Params) -> Result { - from_params::<(Address, String, u64)>(params).and_then( + try!(self.active()); + from_params::<(RpcH160, String, u64)>(params).and_then( |(account, account_pass, _)|{ + let account: Address = account.into(); let store = take_weak!(self.accounts); match store.unlock_account_temporarily(account, account_pass) { Ok(_) => Ok(Value::Bool(true)), @@ -82,14 +94,16 @@ impl Personal for PersonalClient where C: MiningBl } fn sign_and_send_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(TransactionRequest, String)>(params) .and_then(|(request, password)| { + let request: TRequest = request.into(); let sender = request.from; let accounts = take_weak!(self.accounts); match unlock_sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*accounts, sender, password) { - Ok(hash) => to_value(&hash), - _ => to_value(&H256::zero()), + Ok(hash) => Ok(hash), + _ => to_value(&RpcH256::default()), } }) } diff --git a/rpc/src/v1/impls/personal_signer.rs b/rpc/src/v1/impls/personal_signer.rs index 89f15c787..a31a3820f 100644 --- a/rpc/src/v1/impls/personal_signer.rs +++ b/rpc/src/v1/impls/personal_signer.rs @@ -18,14 +18,13 @@ use std::sync::{Arc, Weak}; use jsonrpc_core::*; -use v1::traits::PersonalSigner; -use v1::types::TransactionModification; -use v1::impls::unlock_sign_and_dispatch; -use v1::helpers::{SigningQueue, ConfirmationsQueue}; use ethcore::account_provider::AccountProvider; -use util::numbers::*; use ethcore::client::MiningBlockChainClient; use ethcore::miner::MinerService; +use v1::traits::PersonalSigner; +use v1::types::{TransactionModification, TransactionConfirmation, U256}; +use v1::impls::unlock_sign_and_dispatch; +use v1::helpers::{SigningQueue, ConfirmationsQueue}; /// Transactions confirmation (personal) rpc implementation. pub struct SignerClient where C: MiningBlockChainClient, M: MinerService { @@ -46,18 +45,27 @@ impl SignerClient where C: MiningBlockChainClient, miner: Arc::downgrade(miner), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl PersonalSigner for SignerClient where C: MiningBlockChainClient, M: MinerService { fn transactions_to_confirm(&self, _params: Params) -> Result { + try!(self.active()); let queue = take_weak!(self.queue); - to_value(&queue.requests()) + to_value(&queue.requests().into_iter().map(From::from).collect::>()) } fn confirm_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256, TransactionModification, String)>(params).and_then( |(id, modification, pass)| { + let id = id.into(); let accounts = take_weak!(self.accounts); let queue = take_weak!(self.queue); let client = take_weak!(self.client); @@ -66,7 +74,7 @@ impl PersonalSigner for SignerClient where C: Mini let mut request = confirmation.transaction; // apply modification if let Some(gas_price) = modification.gas_price { - request.gas_price = Some(gas_price); + request.gas_price = Some(gas_price.into()); } let sender = request.from; @@ -87,10 +95,11 @@ impl PersonalSigner for SignerClient where C: Mini } fn reject_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256, )>(params).and_then( |(id, )| { let queue = take_weak!(self.queue); - let res = queue.request_rejected(id); + let res = queue.request_rejected(id.into()); to_value(&res.is_some()) } ) diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index caf549c84..71176df27 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -19,12 +19,13 @@ use std::sync::{Weak, Arc}; use jsonrpc_core::*; use std::collections::BTreeMap; -use util::H256; +//use util::H256; use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId}; use ethcore::miner::MinerService; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::Traces; -use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff, VMTrace}; +use v1::helpers::CallRequest as CRequest; +use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff, VMTrace, H256}; /// Traces api implementation. pub struct TracesClient where C: BlockChainClient, M: MinerService { @@ -42,7 +43,7 @@ impl TracesClient where C: BlockChainClient, M: MinerService { } // TODO: share with eth.rs - fn sign_call(&self, request: CallRequest) -> Result { + fn sign_call(&self, request: CRequest) -> Result { let client = take_weak!(self.client); let miner = take_weak!(self.miner); let from = request.from.unwrap_or(0.into()); @@ -55,10 +56,17 @@ impl TracesClient where C: BlockChainClient, M: MinerService { data: request.data.map_or_else(Vec::new, |d| d.to_vec()) }.fake_sign(from)) } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl Traces for TracesClient where C: BlockChainClient + 'static, M: MinerService + 'static { fn filter(&self, params: Params) -> Result { + try!(self.active()); from_params::<(TraceFilter,)>(params) .and_then(|(filter, )| { let client = take_weak!(self.client); @@ -69,6 +77,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: } fn block_traces(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber,)>(params) .and_then(|(block_number,)| { let client = take_weak!(self.client); @@ -79,21 +88,23 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: } fn transaction_traces(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256,)>(params) .and_then(|(transaction_hash,)| { let client = take_weak!(self.client); - let traces = client.transaction_traces(TransactionID::Hash(transaction_hash)); + let traces = client.transaction_traces(TransactionID::Hash(transaction_hash.into())); let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(LocalizedTrace::from).collect()); to_value(&traces) }) } fn trace(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256, Vec)>(params) .and_then(|(transaction_hash, address)| { let client = take_weak!(self.client); let id = TraceId { - transaction: TransactionID::Hash(transaction_hash), + transaction: TransactionID::Hash(transaction_hash.into()), address: address.into_iter().map(|i| i.value()).collect() }; let trace = client.trace(id); @@ -103,9 +114,11 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: } fn call(&self, params: Params) -> Result { + try!(self.active()); trace!(target: "jsonrpc", "call: {:?}", params); from_params(params) .and_then(|(request, flags)| { + let request = CallRequest::into(request); let flags: Vec = flags; let analytics = CallAnalytics { transaction_tracing: flags.contains(&("trace".to_owned())), diff --git a/rpc/src/v1/impls/web3.rs b/rpc/src/v1/impls/web3.rs index 87343a18a..3f993f21c 100644 --- a/rpc/src/v1/impls/web3.rs +++ b/rpc/src/v1/impls/web3.rs @@ -18,7 +18,7 @@ use jsonrpc_core::*; use util::version; use v1::traits::Web3; -use v1::types::Bytes; +use v1::types::{H256, Bytes}; use util::sha3::Hashable; /// Web3 rpc implementation. @@ -40,9 +40,9 @@ impl Web3 for Web3Client { fn sha3(&self, params: Params) -> Result { from_params::<(Bytes,)>(params).and_then( |(data,)| { - let Bytes(ref v) = data; - let sha3 = v.sha3(); - to_value(&sha3) + let Bytes(ref vec) = data; + let sha3 = vec.sha3(); + to_value(&H256::from(sha3)) } ) } diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 2965a62d2..739c91b17 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -34,6 +34,7 @@ use util::{U256, H256, Uint}; use jsonrpc_core::IoHandler; use ethjson::blockchain::BlockChain; +use v1::types::U256 as NU256; use v1::traits::eth::{Eth, EthSigning}; use v1::impls::{EthClient, EthSigningUnsafeClient}; use v1::tests::helpers::{TestSyncProvider, Config}; @@ -330,7 +331,7 @@ fn verify_transaction_counts(name: String, chain: BlockChain) { "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "params": [ - "#.to_owned() + &::serde_json::to_string(&U256::from(num)).unwrap() + r#" + "#.to_owned() + &::serde_json::to_string(&NU256::from(num)).unwrap() + r#" ], "id": "# + format!("{}", *id).as_ref() + r#" }"#; diff --git a/rpc/src/v1/tests/mocked/ethcore_set.rs b/rpc/src/v1/tests/mocked/ethcore_set.rs index 19f52025f..f43233733 100644 --- a/rpc/src/v1/tests/mocked/ethcore_set.rs +++ b/rpc/src/v1/tests/mocked/ethcore_set.rs @@ -20,6 +20,7 @@ use jsonrpc_core::IoHandler; use v1::{EthcoreSet, EthcoreSetClient}; use ethcore::miner::MinerService; use ethcore::service::SyncMessage; +use ethcore::client::TestBlockChainClient; use v1::tests::helpers::TestMinerService; use util::numbers::*; use util::network::{NetworkConfiguration, NetworkService}; @@ -29,20 +30,25 @@ fn miner_service() -> Arc { Arc::new(TestMinerService::default()) } +fn client_service() -> Arc { + Arc::new(TestBlockChainClient::default()) +} + fn network_service() -> Arc> { Arc::new(NetworkService::new(NetworkConfiguration::new()).unwrap()) } -fn ethcore_set_client(miner: &Arc, net: &Arc>) -> EthcoreSetClient { - EthcoreSetClient::new(miner, net) +fn ethcore_set_client(client: &Arc, miner: &Arc, net: &Arc>) -> EthcoreSetClient { + EthcoreSetClient::new(client, miner, net) } #[test] fn rpc_ethcore_set_min_gas_price() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setMinGasPrice", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -50,12 +56,14 @@ fn rpc_ethcore_set_min_gas_price() { assert_eq!(io.handle_request(request), Some(response.to_owned())); assert_eq!(miner.minimal_gas_price(), U256::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); } + #[test] fn rpc_ethcore_set_gas_floor_target() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setGasFloorTarget", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -67,9 +75,10 @@ fn rpc_ethcore_set_gas_floor_target() { #[test] fn rpc_ethcore_set_extra_data() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setExtraData", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -81,9 +90,10 @@ fn rpc_ethcore_set_extra_data() { #[test] fn rpc_ethcore_set_author() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setAuthor", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -95,9 +105,10 @@ fn rpc_ethcore_set_author() { #[test] fn rpc_ethcore_set_transactions_limit() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setTransactionsLimit", "params":[10240240], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; diff --git a/rpc/src/v1/tests/mocked/personal_signer.rs b/rpc/src/v1/tests/mocked/personal_signer.rs index 6fa734cec..b2ef694a1 100644 --- a/rpc/src/v1/tests/mocked/personal_signer.rs +++ b/rpc/src/v1/tests/mocked/personal_signer.rs @@ -23,9 +23,7 @@ use ethcore::client::TestBlockChainClient; use ethcore::transaction::{Transaction, Action}; use v1::{SignerClient, PersonalSigner}; use v1::tests::helpers::TestMinerService; -use v1::helpers::{SigningQueue, ConfirmationsQueue}; -use v1::types::TransactionRequest; - +use v1::helpers::{SigningQueue, ConfirmationsQueue, TransactionRequest}; struct PersonalSignerTester { queue: Arc, diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index b86723357..63f596d4a 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -15,8 +15,7 @@ // along with Parity. If not, see . use serde::{Serialize, Serializer}; -use util::numbers::*; -use v1::types::{Bytes, Transaction, OptionalValue}; +use v1::types::{Bytes, Transaction, H160, H256, H2048, U256}; /// Block Transactions #[derive(Debug)] @@ -41,7 +40,7 @@ impl Serialize for BlockTransactions { #[derive(Debug, Serialize)] pub struct Block { /// Hash of the block - pub hash: OptionalValue, + pub hash: Option, /// Hash of the parent #[serde(rename="parentHash")] pub parent_hash: H256, @@ -49,10 +48,10 @@ pub struct Block { #[serde(rename="sha3Uncles")] pub uncles_hash: H256, /// Authors address - pub author: Address, + pub author: H160, // TODO: get rid of this one /// ? - pub miner: Address, + pub miner: H160, /// State root hash #[serde(rename="stateRoot")] pub state_root: H256, @@ -63,7 +62,7 @@ pub struct Block { #[serde(rename="receiptsRoot")] pub receipts_root: H256, /// Block number - pub number: OptionalValue, + pub number: Option, /// Gas Used #[serde(rename="gasUsed")] pub gas_used: U256, @@ -95,9 +94,8 @@ pub struct Block { #[cfg(test)] mod tests { use serde_json; - use util::numbers::*; - use v1::types::{Transaction, Bytes, OptionalValue}; - use super::*; + use v1::types::{Transaction, H160, H256, H2048, Bytes, U256}; + use super::{Block, BlockTransactions}; #[test] fn test_serialize_block_transactions() { @@ -105,7 +103,7 @@ mod tests { let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#); - let t = BlockTransactions::Hashes(vec![H256::default()]); + let t = BlockTransactions::Hashes(vec![H256::default().into()]); let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"["0x0000000000000000000000000000000000000000000000000000000000000000"]"#); } @@ -113,15 +111,15 @@ mod tests { #[test] fn test_serialize_block() { let block = Block { - hash: OptionalValue::Value(H256::default()), + hash: Some(H256::default()), parent_hash: H256::default(), uncles_hash: H256::default(), - author: Address::default(), - miner: Address::default(), + author: H160::default(), + miner: H160::default(), state_root: H256::default(), transactions_root: H256::default(), receipts_root: H256::default(), - number: OptionalValue::Value(U256::default()), + number: Some(U256::default()), gas_used: U256::default(), gas_limit: U256::default(), extra_data: Bytes::default(), @@ -131,7 +129,7 @@ mod tests { total_difficulty: U256::default(), seal_fields: vec![Bytes::default(), Bytes::default()], uncles: vec![], - transactions: BlockTransactions::Hashes(vec![]) + transactions: BlockTransactions::Hashes(vec![].into()) }; let serialized = serde_json::to_string(&block).unwrap(); diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 1bf5deb75..09c899057 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -42,6 +42,12 @@ impl From> for Bytes { } } +impl Into> for Bytes { + fn into(self) -> Vec { + self.0 + } +} + impl Serialize for Bytes { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { diff --git a/rpc/src/v1/types/call_request.rs b/rpc/src/v1/types/call_request.rs index 50ebbd1f0..38721e2f8 100644 --- a/rpc/src/v1/types/call_request.rs +++ b/rpc/src/v1/types/call_request.rs @@ -14,17 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::hash::Address; -use util::numbers::U256; -use v1::types::Bytes; +use v1::helpers::CallRequest as Request; +use v1::types::{Bytes, H160, U256}; /// Call request #[derive(Debug, Default, PartialEq, Deserialize)] pub struct CallRequest { /// From - pub from: Option
, + pub from: Option, /// To - pub to: Option
, + pub to: Option, /// Gas Price #[serde(rename="gasPrice")] pub gas_price: Option, @@ -38,18 +37,30 @@ pub struct CallRequest { pub nonce: Option, } +impl Into for CallRequest { + fn into(self) -> Request { + Request { + from: self.from.map(Into::into), + to: self.to.map(Into::into), + gas_price: self.gas_price.map(Into::into), + gas: self.gas.map(Into::into), + value: self.value.map(Into::into), + data: self.data.map(Into::into), + nonce: self.nonce.map(Into::into), + } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; use rustc_serialize::hex::FromHex; use serde_json; - use util::numbers::{U256}; - use util::hash::Address; - use v1::types::Bytes; - use super::*; + use v1::types::{U256, H160}; + use super::CallRequest; #[test] - fn transaction_request_deserialize() { + fn call_request_deserialize() { let s = r#"{ "from":"0x0000000000000000000000000000000000000001", "to":"0x0000000000000000000000000000000000000002", @@ -62,18 +73,18 @@ mod tests { let deserialized: CallRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, CallRequest { - from: Some(Address::from(1)), - to: Some(Address::from(2)), + from: Some(H160::from(1)), + to: Some(H160::from(2)), gas_price: Some(U256::from(1)), gas: Some(U256::from(2)), value: Some(U256::from(3)), - data: Some(Bytes::new(vec![0x12, 0x34, 0x56])), + data: Some(vec![0x12, 0x34, 0x56].into()), nonce: Some(U256::from(4)), }); } #[test] - fn transaction_request_deserialize2() { + fn call_request_deserialize2() { let s = r#"{ "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", @@ -85,23 +96,23 @@ mod tests { let deserialized: CallRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, CallRequest { - from: Some(Address::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), - to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), + to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()), gas: Some(U256::from_str("76c0").unwrap()), value: Some(U256::from_str("9184e72a").unwrap()), - data: Some(Bytes::new("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap())), + data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), nonce: None }); } #[test] - fn transaction_request_deserialize_empty() { + fn call_request_deserialize_empty() { let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#; let deserialized: CallRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, CallRequest { - from: Some(Address::from(1)), + from: Some(H160::from(1)), to: None, gas_price: None, gas: None, diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index 77a3f0500..a34bb9ae9 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -17,10 +17,9 @@ use serde::{Deserialize, Deserializer, Error}; use serde_json::value; use jsonrpc_core::Value; -use util::numbers::*; -use v1::types::BlockNumber; use ethcore::filter::Filter as EthFilter; use ethcore::client::BlockID; +use v1::types::{BlockNumber, H160, H256}; /// Variadic value #[derive(Debug, PartialEq, Clone)] @@ -49,7 +48,7 @@ impl Deserialize for VariadicValue where T: Deserialize { } /// Filter Address -pub type FilterAddress = VariadicValue
; +pub type FilterAddress = VariadicValue; /// Topic pub type Topic = VariadicValue; @@ -76,14 +75,14 @@ impl Into for Filter { to_block: self.to_block.map_or_else(|| BlockID::Latest, Into::into), address: self.address.and_then(|address| match address { VariadicValue::Null => None, - VariadicValue::Single(a) => Some(vec![a]), - VariadicValue::Multiple(a) => Some(a) + VariadicValue::Single(a) => Some(vec![a.into()]), + VariadicValue::Multiple(a) => Some(a.into_iter().map(Into::into).collect()) }), topics: { let mut iter = self.topics.map_or_else(Vec::new, |topics| topics.into_iter().take(4).map(|topic| match topic { VariadicValue::Null => None, - VariadicValue::Single(t) => Some(vec![t]), - VariadicValue::Multiple(t) => Some(t) + VariadicValue::Single(t) => Some(vec![t.into()]), + VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect()) }).filter_map(|m| m).collect()).into_iter(); [iter.next(), iter.next(), iter.next(), iter.next()] } @@ -104,11 +103,11 @@ mod tests { let s = r#"["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", null, ["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"]]"#; let deserialized: Vec = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, vec![ - VariadicValue::Single(H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap()), + VariadicValue::Single(H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap().into()), VariadicValue::Null, VariadicValue::Multiple(vec![ - H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(), - H256::from_str("0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc").unwrap() + H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap().into(), + H256::from_str("0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc").unwrap().into(), ]) ]); } diff --git a/rpc/src/v1/types/hash.rs b/rpc/src/v1/types/hash.rs new file mode 100644 index 000000000..f0606ed51 --- /dev/null +++ b/rpc/src/v1/types/hash.rs @@ -0,0 +1,145 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::fmt; +use std::str::FromStr; +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; +use serde; +use rustc_serialize::hex::{ToHex, FromHex}; +use util::{H64 as Eth64, H256 as EthH256, H520 as EthH520, H2048 as Eth2048, Address}; + +macro_rules! impl_hash { + ($name: ident, $other: ident, $size: expr) => { + /// Hash serialization + #[derive(Eq)] + pub struct $name([u8; $size]); + + impl Default for $name { + fn default() -> Self { + $name([0; $size]) + } + } + + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self.0.to_hex()) + } + } + + impl From for $name where $other: From { + fn from(o: T) -> Self { + $name($other::from(o).0) + } + } + + impl FromStr for $name { + type Err = <$other as FromStr>::Err; + + fn from_str(s: &str) -> Result { + $other::from_str(s).map(|x| $name(x.0)) + } + } + + impl Into<$other> for $name { + fn into(self) -> $other { + $other(self.0) + } + } + + impl PartialEq for $name { + fn eq(&self, other: &Self) -> bool { + let self_ref: &[u8] = &self.0; + let other_ref: &[u8] = &other.0; + self_ref == other_ref + } + } + + impl PartialOrd for $name { + fn partial_cmp(&self, other: &Self) -> Option { + let self_ref: &[u8] = &self.0; + let other_ref: &[u8] = &other.0; + self_ref.partial_cmp(other_ref) + } + } + + impl Ord for $name { + fn cmp(&self, other: &Self) -> Ordering { + let self_ref: &[u8] = &self.0; + let other_ref: &[u8] = &other.0; + self_ref.cmp(other_ref) + } + } + + impl Hash for $name { + fn hash(&self, state: &mut H) where H: Hasher { + let self_ref: &[u8] = &self.0; + Hash::hash(self_ref, state) + } + } + + impl Clone for $name { + fn clone(&self) -> Self { + let mut r = [0; $size]; + r.copy_from_slice(&self.0); + $name(r) + } + } + + impl serde::Serialize for $name { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: serde::Serializer { + let mut hex = "0x".to_owned(); + hex.push_str(&self.0.to_hex()); + serializer.serialize_str(&hex) + } + } + + impl serde::Deserialize for $name { + fn deserialize(deserializer: &mut D) -> Result<$name, D::Error> where D: serde::Deserializer { + struct HashVisitor; + + impl serde::de::Visitor for HashVisitor { + type Value = $name; + + fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { + match value[2..].from_hex() { + Ok(ref v) if v.len() == $size => { + let mut result = [0u8; $size]; + result.copy_from_slice(v); + Ok($name(result)) + }, + Ok(_) => Err(serde::Error::custom("Invalid length.")), + _ => Err(serde::Error::custom("Invalid hex value.")) + } + } + + fn visit_string(&mut self, value: String) -> Result where E: serde::Error { + self.visit_str(value.as_ref()) + } + } + + deserializer.deserialize(HashVisitor) + } + } + } +} + +impl_hash!(H64, Eth64, 8); +impl_hash!(H160, Address, 20); +impl_hash!(H256, EthH256, 32); +impl_hash!(H520, EthH520, 65); +impl_hash!(H2048, Eth2048, 256); diff --git a/rpc/src/v1/types/log.rs b/rpc/src/v1/types/log.rs index 72a482d1b..9d9261d94 100644 --- a/rpc/src/v1/types/log.rs +++ b/rpc/src/v1/types/log.rs @@ -14,15 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::numbers::*; use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; -use v1::types::Bytes; +use v1::types::{Bytes, H160, H256, U256}; /// Log #[derive(Debug, Serialize, PartialEq, Eq, Hash, Clone)] pub struct Log { - /// Address - pub address: Address, + /// H160 + pub address: H160, /// Topics pub topics: Vec, /// Data @@ -50,14 +49,14 @@ pub struct Log { impl From for Log { fn from(e: LocalizedLogEntry) -> Log { Log { - address: e.entry.address, - topics: e.entry.topics, - data: Bytes::new(e.entry.data), - block_hash: Some(e.block_hash), - block_number: Some(From::from(e.block_number)), - transaction_hash: Some(e.transaction_hash), - transaction_index: Some(From::from(e.transaction_index)), - log_index: Some(From::from(e.log_index)), + address: e.entry.address.into(), + topics: e.entry.topics.into_iter().map(Into::into).collect(), + data: e.entry.data.into(), + block_hash: Some(e.block_hash.into()), + block_number: Some(e.block_number.into()), + transaction_hash: Some(e.transaction_hash.into()), + transaction_index: Some(e.transaction_index.into()), + log_index: Some(e.log_index.into()), log_type: "mined".to_owned(), } } @@ -66,9 +65,9 @@ impl From for Log { impl From for Log { fn from(e: LogEntry) -> Log { Log { - address: e.address, - topics: e.topics, - data: Bytes::new(e.data), + address: e.address.into(), + topics: e.topics.into_iter().map(Into::into).collect(), + data: e.data.into(), block_hash: None, block_number: None, transaction_hash: None, @@ -83,25 +82,24 @@ impl From for Log { mod tests { use serde_json; use std::str::FromStr; - use util::numbers::*; - use v1::types::{Bytes, Log}; + use v1::types::{Log, H160, H256, U256}; #[test] fn log_serialization() { let s = r#"{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","logIndex":"0x01","type":"mined"}"#; let log = Log { - address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), + address: H160::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), topics: vec![ H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(), - H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap() + H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap(), ], - data: Bytes::new(vec![]), + data: vec![].into(), block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), block_number: Some(U256::from(0x4510c)), - transaction_hash: Some(H256::new()), - transaction_index: Some(U256::zero()), - log_index: Some(U256::one()), + transaction_hash: Some(H256::default()), + transaction_index: Some(U256::default()), + log_index: Some(U256::from(1)), log_type: "mined".to_owned(), }; diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 3f07bfb31..ecbe6d9a0 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -18,9 +18,9 @@ mod bytes; mod block; mod block_number; mod filter; +mod hash; mod index; mod log; -mod optionals; mod sync; mod transaction; mod transaction_request; @@ -28,14 +28,15 @@ mod call_request; mod receipt; mod trace; mod trace_filter; +mod uint; pub use self::bytes::Bytes; pub use self::block::{Block, BlockTransactions}; pub use self::block_number::BlockNumber; pub use self::filter::Filter; +pub use self::hash::{H64, H160, H256, H520, H2048}; pub use self::index::Index; pub use self::log::Log; -pub use self::optionals::OptionalValue; pub use self::sync::{SyncStatus, SyncInfo}; pub use self::transaction::Transaction; pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification}; @@ -43,3 +44,4 @@ pub use self::call_request::CallRequest; pub use self::receipt::Receipt; pub use self::trace::{Trace, LocalizedTrace, StateDiff, VMTrace}; pub use self::trace_filter::TraceFilter; +pub use self::uint::U256; diff --git a/rpc/src/v1/types/optionals.rs b/rpc/src/v1/types/optionals.rs deleted file mode 100644 index 5f62dc4a0..000000000 --- a/rpc/src/v1/types/optionals.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde_json::Value; - -/// Optional value -#[derive(Debug)] -pub enum OptionalValue where T: Serialize { - /// Some - Value(T), - /// None - Null -} - -impl Default for OptionalValue where T: Serialize + Deserialize { - fn default() -> Self { - OptionalValue::Null - } -} - -impl Into> for OptionalValue where T: Serialize + Deserialize { - fn into(self) -> Option { - match self { - OptionalValue::Null => None, - OptionalValue::Value(t) => Some(t), - } - } -} - -impl Serialize for OptionalValue where T: Serialize + Deserialize { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: Serializer { - match *self { - OptionalValue::Value(ref value) => value.serialize(serializer), - OptionalValue::Null => Value::Null.serialize(serializer) - } - } -} - -impl Deserialize for OptionalValue where T: Serialize + Deserialize { - fn deserialize(deserializer: &mut D) -> Result, D::Error> - where D: Deserializer { - let deser_result: Result = Deserialize::deserialize(deserializer); - match deser_result { - Ok(t) => Ok(OptionalValue::Value(t)), - Err(_) => Ok(OptionalValue::Null), - } - } -} - -#[cfg(test)] -mod tests { - use serde_json; - use util::hash::*; - use super::*; - - #[test] - fn test_serialize_optional_value() { - let v: OptionalValue = OptionalValue::Null; - let serialized = serde_json::to_string(&v).unwrap(); - assert_eq!(serialized, r#"null"#); - - let v = OptionalValue::Value(H256::default()); - let serialized = serde_json::to_string(&v).unwrap(); - assert_eq!(serialized, r#""0x0000000000000000000000000000000000000000000000000000000000000000""#); - } -} diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index c56cdf3d3..afe0268ee 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -14,9 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::numbers::U256; -use util::hash::{Address, H256}; -use v1::types::Log; +use v1::types::{Log, H160, H256, U256}; use ethcore::receipt::{Receipt as EthReceipt, LocalizedReceipt}; /// Receipt @@ -42,7 +40,7 @@ pub struct Receipt { pub gas_used: U256, /// Contract address #[serde(rename="contractAddress")] - pub contract_address: Option
, + pub contract_address: Option, /// Logs pub logs: Vec, } @@ -50,14 +48,14 @@ pub struct Receipt { impl From for Receipt { fn from(r: LocalizedReceipt) -> Self { Receipt { - transaction_hash: Some(r.transaction_hash), - transaction_index: Some(U256::from(r.transaction_index)), - block_hash: Some(r.block_hash), - block_number: Some(U256::from(r.block_number)), - cumulative_gas_used: r.cumulative_gas_used, - gas_used: r.gas_used, - contract_address: r.contract_address, - logs: r.logs.into_iter().map(From::from).collect(), + transaction_hash: Some(r.transaction_hash.into()), + transaction_index: Some(r.transaction_index.into()), + block_hash: Some(r.block_hash.into()), + block_number: Some(r.block_number.into()), + cumulative_gas_used: r.cumulative_gas_used.into(), + gas_used: r.gas_used.into(), + contract_address: r.contract_address.map(Into::into), + logs: r.logs.into_iter().map(Into::into).collect(), } } } @@ -69,10 +67,10 @@ impl From for Receipt { transaction_index: None, block_hash: None, block_number: None, - cumulative_gas_used: r.gas_used, - gas_used: r.gas_used, + cumulative_gas_used: r.gas_used.into(), + gas_used: r.gas_used.into(), contract_address: None, - logs: r.logs.into_iter().map(From::from).collect(), + logs: r.logs.into_iter().map(Into::into).collect(), } } } @@ -81,33 +79,32 @@ impl From for Receipt { mod tests { use serde_json; use std::str::FromStr; - use util::numbers::*; - use v1::types::{Bytes, Log, Receipt}; + use v1::types::{Log, Receipt, U256, H256, H160}; #[test] fn receipt_serialization() { let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","logIndex":"0x01","type":"mined"}]}"#; let receipt = Receipt { - transaction_hash: Some(H256::zero()), - transaction_index: Some(U256::zero()), + transaction_hash: Some(H256::from(0)), + transaction_index: Some(U256::from(0)), block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), block_number: Some(U256::from(0x4510c)), cumulative_gas_used: U256::from(0x20), gas_used: U256::from(0x10), contract_address: None, logs: vec![Log { - address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), + address: H160::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), topics: vec![ H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(), - H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap() + H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap(), ], - data: Bytes::new(vec![]), + data: vec![].into(), block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), block_number: Some(U256::from(0x4510c)), - transaction_hash: Some(H256::new()), - transaction_index: Some(U256::zero()), - log_index: Some(U256::one()), + transaction_hash: Some(H256::default()), + transaction_index: Some(U256::default()), + log_index: Some(U256::from(1)), log_type: "mined".to_owned(), }] }; diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index 6d750425e..49f422c86 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use serde::{Serialize, Serializer}; -use util::numbers::*; +use v1::types::U256; /// Sync info #[derive(Default, Debug, Serialize, PartialEq)] @@ -53,7 +53,7 @@ impl Serialize for SyncStatus { #[cfg(test)] mod tests { use serde_json; - use super::*; + use super::{SyncInfo, SyncStatus}; #[test] fn test_serialize_sync_info() { diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index d6226aea0..cc193c9fc 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -15,14 +15,14 @@ // along with Parity. If not, see . use std::collections::BTreeMap; -use util::{Address, U256, H256, Uint}; use serde::{Serialize, Serializer}; use ethcore::trace::trace; use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; use ethcore::trace as et; use ethcore::state_diff; use ethcore::account_diff; -use v1::types::Bytes; +use util::Uint; +use v1::types::{Bytes, H160, H256, U256}; #[derive(Debug, Serialize)] /// A diff of some chunk of memory. @@ -54,8 +54,8 @@ pub struct StorageDiff { impl From for StorageDiff { fn from(c: et::StorageDiff) -> Self { StorageDiff { - key: c.location, - val: c.value, + key: c.location.into(), + val: c.value.into(), } } } @@ -80,9 +80,9 @@ impl From for VMExecutedOperation { fn from(c: et::VMExecutedOperation) -> Self { VMExecutedOperation { used: c.gas_used.low_u64(), - push: c.stack_push, - mem: c.mem_diff.map(From::from), - store: c.store_diff.map(From::from), + push: c.stack_push.into_iter().map(Into::into).collect(), + mem: c.mem_diff.map(Into::into), + store: c.store_diff.map(Into::into), } } } @@ -105,8 +105,8 @@ impl From<(et::VMOperation, Option)> for VMOperation { VMOperation { pc: c.0.pc, cost: c.0.gas_cost.low_u64(), - ex: c.0.executed.map(From::from), - sub: c.1.map(From::from), + ex: c.0.executed.map(Into::into), + sub: c.1.map(Into::into), } } } @@ -188,13 +188,13 @@ impl From for AccountDiff { balance: c.balance.into(), nonce: c.nonce.into(), code: c.code.into(), - storage: c.storage.into_iter().map(|(k, v)| (k, v.into())).collect(), + storage: c.storage.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), } } } /// Serde-friendly `StateDiff` shadow. -pub struct StateDiff(BTreeMap); +pub struct StateDiff(BTreeMap); impl Serialize for StateDiff { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -205,7 +205,7 @@ impl Serialize for StateDiff { impl From for StateDiff { fn from(c: state_diff::StateDiff) -> Self { - StateDiff(c.0.into_iter().map(|(k, v)| (k, v.into())).collect()) + StateDiff(c.0.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) } } @@ -213,7 +213,7 @@ impl From for StateDiff { #[derive(Debug, Serialize)] pub struct Create { /// Sender - from: Address, + from: H160, /// Value value: U256, /// Gas @@ -225,9 +225,9 @@ pub struct Create { impl From for Create { fn from(c: trace::Create) -> Self { Create { - from: c.from, - value: c.value, - gas: c.gas, + from: c.from.into(), + value: c.value.into(), + gas: c.gas.into(), init: Bytes::new(c.init), } } @@ -237,9 +237,9 @@ impl From for Create { #[derive(Debug, Serialize)] pub struct Call { /// Sender - from: Address, + from: H160, /// Recipient - to: Address, + to: H160, /// Transfered Value value: U256, /// Gas @@ -251,11 +251,11 @@ pub struct Call { impl From for Call { fn from(c: trace::Call) -> Self { Call { - from: c.from, - to: c.to, - value: c.value, - gas: c.gas, - input: Bytes::new(c.input), + from: c.from.into(), + to: c.to.into(), + value: c.value.into(), + gas: c.gas.into(), + input: c.input.into(), } } } @@ -293,8 +293,8 @@ pub struct CallResult { impl From for CallResult { fn from(c: trace::CallResult) -> Self { CallResult { - gas_used: c.gas_used, - output: Bytes::new(c.output), + gas_used: c.gas_used.into(), + output: c.output.into(), } } } @@ -308,15 +308,15 @@ pub struct CreateResult { /// Code code: Bytes, /// Assigned address - address: Address, + address: H160, } impl From for CreateResult { fn from(c: trace::CreateResult) -> Self { CreateResult { - gas_used: c.gas_used, - code: Bytes::new(c.code), - address: c.address, + gas_used: c.gas_used.into(), + code: c.code.into(), + address: c.address.into(), } } } @@ -357,7 +357,7 @@ pub struct LocalizedTrace { /// Result result: Res, /// Trace address - #[serde(rename="traceAddress")] + #[serde(rename="traceH160")] trace_address: Vec, /// Subtraces subtraces: U256, @@ -378,14 +378,14 @@ pub struct LocalizedTrace { impl From for LocalizedTrace { fn from(t: EthLocalizedTrace) -> Self { LocalizedTrace { - action: From::from(t.action), - result: From::from(t.result), - trace_address: t.trace_address.into_iter().map(From::from).collect(), - subtraces: From::from(t.subtraces), - transaction_position: From::from(t.transaction_number), - transaction_hash: t.transaction_hash, - block_number: From::from(t.block_number), - block_hash: t.block_hash, + action: t.action.into(), + result: t.result.into(), + trace_address: t.trace_address.into_iter().map(Into::into).collect(), + subtraces: t.subtraces.into(), + transaction_position: t.transaction_number.into(), + transaction_hash: t.transaction_hash.into(), + block_number: t.block_number.into(), + block_hash: t.block_hash.into(), } } } @@ -409,7 +409,7 @@ impl From for Trace { depth: t.depth.into(), action: t.action.into(), result: t.result.into(), - subtraces: t.subs.into_iter().map(From::from).collect(), + subtraces: t.subs.into_iter().map(Into::into).collect(), } } } @@ -418,23 +418,22 @@ impl From for Trace { mod tests { use serde_json; use std::collections::BTreeMap; - use util::{U256, H256, Address}; - use v1::types::Bytes; + use v1::types::{Bytes, U256, H256, H160}; use super::*; #[test] fn test_trace_serialize() { let t = LocalizedTrace { action: Action::Call(Call { - from: Address::from(4), - to: Address::from(5), + from: H160::from(4), + to: H160::from(5), value: U256::from(6), gas: U256::from(7), input: Bytes::new(vec![0x12, 0x34]), }), result: Res::Call(CallResult { gas_used: U256::from(8), - output: Bytes::new(vec![0x56, 0x78]), + output: vec![0x56, 0x78].into(), }), trace_address: vec![U256::from(10)], subtraces: U256::from(1), @@ -444,7 +443,7 @@ mod tests { block_hash: H256::from(14), }; let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234"}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); + assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234"}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceH160":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); } #[test] @@ -515,16 +514,16 @@ mod tests { #[test] fn test_action_serialize() { let actions = vec![Action::Call(Call { - from: Address::from(1), - to: Address::from(2), + from: H160::from(1), + to: H160::from(2), value: U256::from(3), gas: U256::from(4), - input: Bytes::new(vec![0x12, 0x34]), + input: vec![0x12, 0x34].into(), }), Action::Create(Create { - from: Address::from(5), + from: H160::from(5), value: U256::from(6), gas: U256::from(7), - init: Bytes::new(vec![0x56, 0x78]), + init: vec![0x56, 0x78].into(), })]; let serialized = serde_json::to_string(&actions).unwrap(); @@ -536,12 +535,12 @@ mod tests { let results = vec![ Res::Call(CallResult { gas_used: U256::from(1), - output: Bytes::new(vec![0x12, 0x34]), + output: vec![0x12, 0x34].into(), }), Res::Create(CreateResult { gas_used: U256::from(2), - code: Bytes::new(vec![0x45, 0x56]), - address: Address::from(3), + code: vec![0x45, 0x56].into(), + address: H160::from(3), }), Res::FailedCall, Res::FailedCreate, diff --git a/rpc/src/v1/types/trace_filter.rs b/rpc/src/v1/types/trace_filter.rs index ee2f231f0..21e50e175 100644 --- a/rpc/src/v1/types/trace_filter.rs +++ b/rpc/src/v1/types/trace_filter.rs @@ -16,10 +16,9 @@ //! Trace filter deserialization. -use util::Address; use ethcore::client::BlockID; use ethcore::client; -use super::BlockNumber; +use v1::types::{BlockNumber, H160}; /// Trace filter #[derive(Debug, PartialEq, Deserialize)] @@ -32,10 +31,10 @@ pub struct TraceFilter { pub to_block: Option, /// From address #[serde(rename="fromAddress")] - pub from_address: Option>, + pub from_address: Option>, /// To address #[serde(rename="toAddress")] - pub to_address: Option>, + pub to_address: Option>, } impl Into for TraceFilter { @@ -44,8 +43,8 @@ impl Into for TraceFilter { let end = self.to_block.map_or(BlockID::Latest, Into::into); client::TraceFilter { range: start..end, - from_address: self.from_address.unwrap_or_else(Vec::new), - to_address: self.to_address.unwrap_or_else(Vec::new), + from_address: self.from_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), + to_address: self.to_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), } } } @@ -80,8 +79,8 @@ mod tests { assert_eq!(deserialized, TraceFilter { from_block: Some(BlockNumber::Latest), to_block: Some(BlockNumber::Latest), - from_address: Some(vec![Address::from(3)]), - to_address: Some(vec![Address::from(5)]), + from_address: Some(vec![Address::from(3).into()]), + to_address: Some(vec![Address::from(5).into()]), }); } } diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 6a9f0e590..cb554b172 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::numbers::*; use ethcore::contract_address; use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; -use v1::types::{Bytes, OptionalValue}; +use v1::types::{Bytes, H160, H256, U256}; /// Transaction #[derive(Debug, Default, Serialize)] @@ -28,17 +27,17 @@ pub struct Transaction { pub nonce: U256, /// Block hash #[serde(rename="blockHash")] - pub block_hash: OptionalValue, + pub block_hash: Option, /// Block number #[serde(rename="blockNumber")] - pub block_number: OptionalValue, + pub block_number: Option, /// Transaction Index #[serde(rename="transactionIndex")] - pub transaction_index: OptionalValue, + pub transaction_index: Option, /// Sender - pub from: Address, + pub from: H160, /// Recipient - pub to: OptionalValue
, + pub to: Option, /// Transfered value pub value: U256, /// Gas Price @@ -49,29 +48,29 @@ pub struct Transaction { /// Data pub input: Bytes, /// Creates contract - pub creates: OptionalValue
, + pub creates: Option, } impl From for Transaction { fn from(t: LocalizedTransaction) -> Transaction { Transaction { - hash: t.hash(), - nonce: t.nonce, - block_hash: OptionalValue::Value(t.block_hash.clone()), - block_number: OptionalValue::Value(U256::from(t.block_number)), - transaction_index: OptionalValue::Value(U256::from(t.transaction_index)), - from: t.sender().unwrap(), + hash: t.hash().into(), + nonce: t.nonce.into(), + block_hash: Some(t.block_hash.clone().into()), + block_number: Some(t.block_number.into()), + transaction_index: Some(t.transaction_index.into()), + from: t.sender().unwrap().into(), to: match t.action { - Action::Create => OptionalValue::Null, - Action::Call(ref address) => OptionalValue::Value(address.clone()) + Action::Create => None, + Action::Call(ref address) => Some(address.clone().into()) }, - value: t.value, - gas_price: t.gas_price, - gas: t.gas, + value: t.value.into(), + gas_price: t.gas_price.into(), + gas: t.gas.into(), input: Bytes::new(t.data.clone()), creates: match t.action { - Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), - Action::Call(_) => OptionalValue::Null, + Action::Create => Some(contract_address(&t.sender().unwrap(), &t.nonce).into()), + Action::Call(_) => None, }, } } @@ -80,23 +79,23 @@ impl From for Transaction { impl From for Transaction { fn from(t: SignedTransaction) -> Transaction { Transaction { - hash: t.hash(), - nonce: t.nonce, - block_hash: OptionalValue::Null, - block_number: OptionalValue::Null, - transaction_index: OptionalValue::Null, - from: t.sender().unwrap(), + hash: t.hash().into(), + nonce: t.nonce.into(), + block_hash: None, + block_number: None, + transaction_index: None, + from: t.sender().unwrap().into(), to: match t.action { - Action::Create => OptionalValue::Null, - Action::Call(ref address) => OptionalValue::Value(address.clone()) + Action::Create => None, + Action::Call(ref address) => Some(address.clone().into()) }, - value: t.value, - gas_price: t.gas_price, - gas: t.gas, + value: t.value.into(), + gas_price: t.gas_price.into(), + gas: t.gas.into(), input: Bytes::new(t.data.clone()), creates: match t.action { - Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), - Action::Call(_) => OptionalValue::Null, + Action::Create => Some(contract_address(&t.sender().unwrap(), &t.nonce).into()), + Action::Call(_) => None, }, } } @@ -104,7 +103,7 @@ impl From for Transaction { #[cfg(test)] mod tests { - use super::*; + use super::Transaction; use serde_json; #[test] diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index e7237b2c6..f697144b3 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -16,17 +16,16 @@ //! `TransactionRequest` type -use util::hash::Address; -use util::numbers::U256; -use v1::types::bytes::Bytes; +use v1::types::{Bytes, H160, U256}; +use v1::helpers::{TransactionRequest as Request, TransactionConfirmation as Confirmation}; /// Transaction request coming from RPC #[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct TransactionRequest { /// Sender - pub from: Address, + pub from: H160, /// Recipient - pub to: Option
, + pub to: Option, /// Gas Price #[serde(rename="gasPrice")] pub gas_price: Option, @@ -40,6 +39,34 @@ pub struct TransactionRequest { pub nonce: Option, } +impl From for TransactionRequest { + fn from(r: Request) -> Self { + TransactionRequest { + from: r.from.into(), + to: r.to.map(Into::into), + gas_price: r.gas_price.map(Into::into), + gas: r.gas.map(Into::into), + value: r.value.map(Into::into), + data: r.data.map(Into::into), + nonce: r.nonce.map(Into::into), + } + } +} + +impl Into for TransactionRequest { + fn into(self) -> Request { + Request { + from: self.from.into(), + to: self.to.map(Into::into), + gas_price: self.gas_price.map(Into::into), + gas: self.gas.map(Into::into), + value: self.value.map(Into::into), + data: self.data.map(Into::into), + nonce: self.nonce.map(Into::into), + } + } +} + /// Transaction confirmation waiting in a queue #[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize)] pub struct TransactionConfirmation { @@ -49,6 +76,15 @@ pub struct TransactionConfirmation { pub transaction: TransactionRequest, } +impl From for TransactionConfirmation { + fn from(c: Confirmation) -> Self { + TransactionConfirmation { + id: c.id.into(), + transaction: c.transaction.into(), + } + } +} + /// Possible modifications to the confirmed transaction sent by `SignerUI` #[derive(Debug, PartialEq, Deserialize)] pub struct TransactionModification { @@ -63,9 +99,7 @@ mod tests { use std::str::FromStr; use rustc_serialize::hex::FromHex; use serde_json; - use util::numbers::{U256}; - use util::hash::Address; - use v1::types::bytes::Bytes; + use v1::types::{U256, H160}; use super::*; #[test] @@ -82,12 +116,12 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Address::from(1), - to: Some(Address::from(2)), + from: H160::from(1), + to: Some(H160::from(2)), gas_price: Some(U256::from(1)), gas: Some(U256::from(2)), value: Some(U256::from(3)), - data: Some(Bytes::new(vec![0x12, 0x34, 0x56])), + data: Some(vec![0x12, 0x34, 0x56].into()), nonce: Some(U256::from(4)), }); } @@ -105,12 +139,12 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Address::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), - to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + from: H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), + to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()), gas: Some(U256::from_str("76c0").unwrap()), value: Some(U256::from_str("9184e72a").unwrap()), - data: Some(Bytes::new("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap())), + data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), nonce: None }); } @@ -121,7 +155,7 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Address::from(1), + from: H160::from(1).into(), to: None, gas_price: None, gas: None, @@ -144,12 +178,12 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Address::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap(), - to: Some(Address::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()), + from: H160::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap(), + to: Some(H160::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()), gas_price: Some(U256::from_str("0ba43b7400").unwrap()), gas: Some(U256::from_str("2fd618").unwrap()), value: None, - data: Some(Bytes::new(vec![0x85, 0x95, 0xba, 0xb1])), + data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), nonce: None, }); } diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs new file mode 100644 index 000000000..5111f3a42 --- /dev/null +++ b/rpc/src/v1/types/uint.rs @@ -0,0 +1,89 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::cmp; +use std::str::FromStr; +use rustc_serialize::hex::ToHex; +use serde; +use util::{U256 as EthU256, Uint}; + +macro_rules! impl_uint { + ($name: ident, $other: ident, $size: expr) => { + /// Uint serialization. + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + pub struct $name($other); + + impl From for $name where $other: From { + fn from(o: T) -> Self { + $name($other::from(o)) + } + } + + impl FromStr for $name { + type Err = <$other as FromStr>::Err; + + fn from_str(s: &str) -> Result { + $other::from_str(s).map($name) + } + } + + impl Into<$other> for $name { + fn into(self) -> $other { + self.0 + } + } + + impl serde::Serialize for $name { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer { + let mut hex = "0x".to_owned(); + let mut bytes = [0u8; 8 * $size]; + self.0.to_raw_bytes(&mut bytes); + let len = cmp::max((self.0.bits() + 7) / 8, 1); + hex.push_str(&bytes[bytes.len() - len..].to_hex()); + serializer.serialize_str(&hex) + } + } + + impl serde::Deserialize for $name { + fn deserialize(deserializer: &mut D) -> Result<$name, D::Error> + where D: serde::Deserializer { + struct UintVisitor; + + impl serde::de::Visitor for UintVisitor { + type Value = $name; + + fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { + // 0x + len + if value.len() > 2 + $size * 16 || value.len() < 2 { + return Err(serde::Error::custom("Invalid length.")); + } + + $other::from_str(&value[2..]).map($name).map_err(|_| serde::Error::custom("Invalid hex value.")) + } + + fn visit_string(&mut self, value: String) -> Result where E: serde::Error { + self.visit_str(&value) + } + } + + deserializer.deserialize(UintVisitor) + } + } + + } +} + +impl_uint!(U256, EthU256, 4); diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 1897b02f4..6845c32e3 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -93,7 +93,7 @@ use util::*; use std::mem::{replace}; use ethcore::views::{HeaderView, BlockView}; use ethcore::header::{BlockNumber, Header as BlockHeader}; -use ethcore::client::{BlockChainClient, BlockStatus, BlockID, BlockChainInfo}; +use ethcore::client::{BlockChainClient, BlockStatus, BlockID, BlockChainInfo, BlockImportError}; use ethcore::error::*; use ethcore::block::Block; use io::SyncIo; @@ -544,10 +544,10 @@ impl ChainSync { peer.latest_number = Some(header.number()); } match io.chain().import_block(block_rlp.as_raw().to_vec()) { - Err(Error::Import(ImportError::AlreadyInChain)) => { + Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!(target: "sync", "New block already in chain {:?}", h); }, - Err(Error::Import(ImportError::AlreadyQueued)) => { + Err(BlockImportError::Import(ImportError::AlreadyQueued)) => { trace!(target: "sync", "New block already queued {:?}", h); }, Ok(_) => { @@ -557,7 +557,7 @@ impl ChainSync { } trace!(target: "sync", "New block queued {:?} ({})", h, header.number); }, - Err(Error::Block(BlockError::UnknownParent(p))) => { + Err(BlockImportError::Block(BlockError::UnknownParent(p))) => { unknown = true; trace!(target: "sync", "New block with unknown parent ({:?}) {:?}", p, h); }, @@ -841,11 +841,11 @@ impl ChainSync { } match io.chain().import_block(block) { - Err(Error::Import(ImportError::AlreadyInChain)) => { + Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!(target: "sync", "Block already in chain {:?}", h); self.block_imported(&h, number, &parent); }, - Err(Error::Import(ImportError::AlreadyQueued)) => { + Err(BlockImportError::Import(ImportError::AlreadyQueued)) => { trace!(target: "sync", "Block already queued {:?}", h); self.block_imported(&h, number, &parent); }, @@ -854,7 +854,7 @@ impl ChainSync { imported.insert(h.clone()); self.block_imported(&h, number, &parent); }, - Err(Error::Block(BlockError::UnknownParent(_))) if self.state == SyncState::NewBlocks => { + Err(BlockImportError::Block(BlockError::UnknownParent(_))) if self.state == SyncState::NewBlocks => { trace!(target: "sync", "Unknown new block parent, restarting sync"); break; }, diff --git a/sync/src/lib.rs b/sync/src/lib.rs index fa26e7d85..cf1906511 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -71,8 +71,7 @@ extern crate heapsize; use std::ops::*; use std::sync::*; use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId}; -use util::TimerToken; -use util::{U256, ONE_U256}; +use util::{TimerToken, U256}; use ethcore::client::Client; use ethcore::service::{SyncMessage, NetSyncMessage}; use io::NetSyncIo; @@ -99,7 +98,7 @@ impl Default for SyncConfig { fn default() -> SyncConfig { SyncConfig { max_download_ahead_blocks: 20000, - network_id: ONE_U256, + network_id: U256::from(1), } } } diff --git a/util/Cargo.toml b/util/Cargo.toml index 4f0f321f3..cfb7acaae 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -27,16 +27,16 @@ itertools = "0.4" crossbeam = "0.2" slab = "0.2" sha3 = { path = "sha3" } -serde = "0.7.0" clippy = { version = "0.0.78", optional = true} -json-tests = { path = "json-tests" } -igd = "0.4.2" +igd = "0.5.0" ethcore-devtools = { path = "../devtools" } libc = "0.2.7" vergen = "0.1" target_info = "0.1" bigint = { path = "bigint" } chrono = "0.2" +using_queue = { path = "using_queue" } +table = { path = "table" } ansi_term = "0.7" [features] diff --git a/util/bigint/Cargo.toml b/util/bigint/Cargo.toml index fe9101df7..357e2956c 100644 --- a/util/bigint/Cargo.toml +++ b/util/bigint/Cargo.toml @@ -12,7 +12,6 @@ rustc_version = "0.1" [dependencies] rustc-serialize = "0.3" -serde = "0.7.0" heapsize = "0.3" [features] diff --git a/util/bigint/src/lib.rs b/util/bigint/src/lib.rs index 149878538..1d97871e3 100644 --- a/util/bigint/src/lib.rs +++ b/util/bigint/src/lib.rs @@ -17,7 +17,6 @@ #![cfg_attr(asm_available, feature(asm))] extern crate rustc_serialize; -extern crate serde; #[macro_use] extern crate heapsize; pub mod uint; diff --git a/util/bigint/src/uint.rs b/util/bigint/src/uint.rs index 5304781d4..2f58061d4 100644 --- a/util/bigint/src/uint.rs +++ b/util/bigint/src/uint.rs @@ -39,7 +39,6 @@ #[cfg(all(asm_available, target_arch="x86_64"))] use std::mem; use std::fmt; -use std::cmp; use std::str::{FromStr}; use std::convert::From; @@ -47,14 +46,15 @@ use std::hash::Hash; use std::ops::*; use std::cmp::*; -use serde; -use rustc_serialize::hex::{FromHex, FromHexError, ToHex}; +use rustc_serialize::hex::{FromHex, FromHexError}; /// Conversion from decimal string error #[derive(Debug, PartialEq)] pub enum FromDecStrErr { + /// Char not from range 0-9 + InvalidCharacter, /// Value does not fit into type - InvalidLength + InvalidLength, } macro_rules! impl_map_from { @@ -562,8 +562,11 @@ macro_rules! construct_uint { impl Uint for $name { - /// TODO: optimize, throw appropriate err fn from_dec_str(value: &str) -> Result { + if value.bytes().any(|b| b < 48 && b > 57) { + return Err(FromDecStrErr::InvalidCharacter) + } + let mut res = Self::default(); for b in value.bytes().map(|b| b - 48) { let (r, overflow) = res.overflowing_mul_u32(10); @@ -649,7 +652,7 @@ macro_rules! construct_uint { fn exp10(n: usize) -> Self { match n { 0 => Self::from(1u64), - _ => Self::exp10(n - 1) * Self::from(10u64) + _ => Self::exp10(n - 1).mul_u32(10) } } @@ -757,16 +760,16 @@ macro_rules! construct_uint { } impl $name { - #[allow(dead_code)] // not used when multiplied with inline assembly /// Multiplication by u32 + #[allow(dead_code)] // not used when multiplied with inline assembly fn mul_u32(self, other: u32) -> Self { let (ret, overflow) = self.overflowing_mul_u32(other); panic_on_overflow!(overflow); ret } - #[allow(dead_code)] // not used when multiplied with inline assembly /// Overflowing multiplication by u32 + #[allow(dead_code)] // not used when multiplied with inline assembly fn overflowing_mul_u32(self, other: u32) -> (Self, bool) { let $name(ref arr) = self; let mut ret = [0u64; $n_words]; @@ -789,44 +792,6 @@ macro_rules! construct_uint { } } - impl serde::Serialize for $name { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: serde::Serializer { - let mut hex = "0x".to_owned(); - let mut bytes = [0u8; 8 * $n_words]; - self.to_raw_bytes(&mut bytes); - let len = cmp::max((self.bits() + 7) / 8, 1); - hex.push_str(bytes[bytes.len() - len..].to_hex().as_ref()); - serializer.serialize_str(hex.as_ref()) - } - } - - impl serde::Deserialize for $name { - fn deserialize(deserializer: &mut D) -> Result<$name, D::Error> - where D: serde::Deserializer { - struct UintVisitor; - - impl serde::de::Visitor for UintVisitor { - type Value = $name; - - fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { - // 0x + len - if value.len() > 2 + $n_words * 16 || value.len() < 2 { - return Err(serde::Error::custom("Invalid length.")); - } - - $name::from_str(&value[2..]).map_err(|_| serde::Error::custom("Invalid hex value.")) - } - - fn visit_string(&mut self, value: String) -> Result where E: serde::Error { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize(UintVisitor) - } - } - impl From for $name { fn from(value: u64) -> $name { let mut ret = [0; $n_words]; @@ -959,8 +924,6 @@ macro_rules! construct_uint { } } - // TODO: optimise and traitify. - impl BitAnd<$name> for $name { type Output = $name; @@ -1434,12 +1397,6 @@ impl From for u32 { } } -/// Constant value of `U256::zero()` that can be used for a reference saving an additional instance creation. -pub const ZERO_U256: U256 = U256([0x00u64; 4]); -/// Constant value of `U256::one()` that can be used for a reference saving an additional instance creation. -pub const ONE_U256: U256 = U256([0x01u64, 0x00u64, 0x00u64, 0x00u64]); - - known_heap_size!(0, U128, U256); #[cfg(test)] @@ -1582,7 +1539,13 @@ mod tests { assert_eq!(U256::from(105u8) / U256::from(5u8), U256::from(21u8)); let div = mult / U256::from(300u16); assert_eq!(div, U256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0])); - //// TODO: bit inversion + + let a = U256::from_str("ff000000000000000000000000000000000000000000000000000000000000d1").unwrap(); + let b = U256::from_str("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2e").unwrap(); + println!("{:x}", a); + println!("{:x}", b); + assert_eq!(!a, b); + assert_eq!(a, !b); } #[test] diff --git a/util/json-tests/README.md b/util/json-tests/README.md deleted file mode 100644 index 9e6915ca3..000000000 --- a/util/json-tests/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# How to write json test file? - -Cause it's very hard to write generic json test files, each subdirectory should follow its own -convention. BUT all json files `within` same directory should be consistent. - -### Test files should always contain a single test with input and output. - -```json -{ - input: ..., - output: ... -} -``` - -As a reference, please use trietests. diff --git a/util/json-tests/json/.DS_Store b/util/json-tests/json/.DS_Store deleted file mode 100644 index 5ddc63de7..000000000 Binary files a/util/json-tests/json/.DS_Store and /dev/null differ diff --git a/util/json-tests/json/rlp/README.md b/util/json-tests/json/rlp/README.md deleted file mode 100644 index ab8b01020..000000000 --- a/util/json-tests/json/rlp/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Rlp tests guideline - -Rlp can be tested in various ways. It can encode/decode a value or an array of values. Let's start with encoding. - -Each operation must have field: - -- `operation` - `append`, `append_list`, `append_empty` or `append_raw` - -Additionally `append` and `append_raw` must additionally define a `value` field: - -- `value` - data - -Also `append_raw` and `append_list` requires `len` field - -- `len` - integer - -### Encoding Test Example - -```json -{ - "input": - [ - { - "operation": "append_list", - "len": 2 - }, - { - "operation": "append", - "value": "cat" - }, - { - "operation": "append", - "value": "dog" - } - ], - "output": "0xc88363617183646f67" -} -``` - diff --git a/util/json-tests/json/rlp/stream/bytestring0.json b/util/json-tests/json/rlp/stream/bytestring0.json deleted file mode 100644 index 2cfd89b2c..000000000 --- a/util/json-tests/json/rlp/stream/bytestring0.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "input": - [ - { - "operation": "append", - "value": "\u0000" - } - ], - "output": "0x00" -} diff --git a/util/json-tests/json/rlp/stream/bytestring1.json b/util/json-tests/json/rlp/stream/bytestring1.json deleted file mode 100644 index b14699f7a..000000000 --- a/util/json-tests/json/rlp/stream/bytestring1.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "input": - [ - { - "operation": "append", - "value": "\u0001" - } - ], - "output": "0x01" -} diff --git a/util/json-tests/json/rlp/stream/bytestring7.json b/util/json-tests/json/rlp/stream/bytestring7.json deleted file mode 100644 index 672050297..000000000 --- a/util/json-tests/json/rlp/stream/bytestring7.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "input": - [ - { - "operation": "append", - "value": "\u007f" - } - ], - "output": "0x7f" -} diff --git a/util/json-tests/json/rlp/stream/catdog.json b/util/json-tests/json/rlp/stream/catdog.json deleted file mode 100644 index 2cfd89b2c..000000000 --- a/util/json-tests/json/rlp/stream/catdog.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "input": - [ - { - "operation": "append", - "value": "\u0000" - } - ], - "output": "0x00" -} diff --git a/util/json-tests/json/rlp/stream/empty.json b/util/json-tests/json/rlp/stream/empty.json deleted file mode 100644 index 19cbf4185..000000000 --- a/util/json-tests/json/rlp/stream/empty.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "input": - [ - { - "operation": "append_empty" - } - ], - "output": "0x80" -} diff --git a/util/json-tests/json/rlp/stream/empty_lists.json b/util/json-tests/json/rlp/stream/empty_lists.json deleted file mode 100644 index 5ac649c2c..000000000 --- a/util/json-tests/json/rlp/stream/empty_lists.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "input": - [ - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append_list", - "len": 0 - }, - { - "operation": "append_list", - "len": 1 - }, - { - "operation": "append_list", - "len": 0 - }, - { - "operation": "append_list", - "len": 2 - }, - { - "operation": "append_list", - "len": 0 - }, - { - "operation": "append_list", - "len": 1 - }, - { - "operation": "append_list", - "len": 0 - } - ], - "output": "0xc7c0c1c0c3c0c1c0" -} diff --git a/util/json-tests/json/rlp/stream/integer.json b/util/json-tests/json/rlp/stream/integer.json deleted file mode 100644 index 1effa4a1e..000000000 --- a/util/json-tests/json/rlp/stream/integer.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "input": - [ - { - "operation": "append", - "value": "0x0400" - } - ], - "output": "0x820400" -} diff --git a/util/json-tests/json/rlp/stream/list_of_empty_data.json b/util/json-tests/json/rlp/stream/list_of_empty_data.json deleted file mode 100644 index c5b898f25..000000000 --- a/util/json-tests/json/rlp/stream/list_of_empty_data.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "input": - [ - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "" - }, - { - "operation": "append", - "value": "" - }, - { - "operation": "append", - "value": "" - } - ], - "output": "0xc3808080" -} diff --git a/util/json-tests/json/rlp/stream/list_of_empty_data2.json b/util/json-tests/json/rlp/stream/list_of_empty_data2.json deleted file mode 100644 index 76043af91..000000000 --- a/util/json-tests/json/rlp/stream/list_of_empty_data2.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "input": - [ - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append_empty" - }, - { - "operation": "append_empty" - }, - { - "operation": "append_empty" - } - ], - "output": "0xc3808080" -} diff --git a/util/json-tests/json/rlp/stream/longlist.json b/util/json-tests/json/rlp/stream/longlist.json deleted file mode 100644 index 4054d9c26..000000000 --- a/util/json-tests/json/rlp/stream/longlist.json +++ /dev/null @@ -1,521 +0,0 @@ -{ - "input": [ - { - "operation": "append_list", - "len": 32 - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }, - { - "operation": "append_list", - "len": 3 - }, - { - "operation": "append", - "value": "asdf" - }, - { - "operation": "append", - "value": "qwer" - }, - { - "operation": "append", - "value": "zxcv" - }], - "output": "0xf90200cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376" - -} \ No newline at end of file diff --git a/util/json-tests/json/rlp/stream/longstring.json b/util/json-tests/json/rlp/stream/longstring.json deleted file mode 100644 index 42b9aa2e0..000000000 --- a/util/json-tests/json/rlp/stream/longstring.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "input": - [ - { - "operation": "append", - "value": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mauris magna, suscipit sed vehicula non, iaculis faucibus tortor. Proin suscipit ultricies malesuada. Duis tortor elit, dictum quis tristique eu, ultrices at risus. Morbi a est imperdiet mi ullamcorper aliquet suscipit nec lorem. Aenean quis leo mollis, vulputate elit varius, consequat enim. Nulla ultrices turpis justo, et posuere urna consectetur nec. Proin non convallis metus. Donec tempor ipsum in mauris congue sollicitudin. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse convallis sem vel massa faucibus, eget lacinia lacus tempor. Nulla quis ultricies purus. Proin auctor rhoncus nibh condimentum mollis. Aliquam consequat enim at metus luctus, a eleifend purus egestas. Curabitur at nibh metus. Nam bibendum, neque at auctor tristique, lorem libero aliquet arcu, non interdum tellus lectus sit amet eros. Cras rhoncus, metus ac ornare cursus, dolor justo ultrices metus, at ullamcorper volutpat" - } - ], - "output": "0xb904004c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20437572616269747572206d6175726973206d61676e612c20737573636970697420736564207665686963756c61206e6f6e2c20696163756c697320666175636962757320746f72746f722e2050726f696e20737573636970697420756c74726963696573206d616c6573756164612e204475697320746f72746f7220656c69742c2064696374756d2071756973207472697374697175652065752c20756c7472696365732061742072697375732e204d6f72626920612065737420696d70657264696574206d6920756c6c616d636f7270657220616c6971756574207375736369706974206e6563206c6f72656d2e2041656e65616e2071756973206c656f206d6f6c6c69732c2076756c70757461746520656c6974207661726975732c20636f6e73657175617420656e696d2e204e756c6c6120756c74726963657320747572706973206a7573746f2c20657420706f73756572652075726e6120636f6e7365637465747572206e65632e2050726f696e206e6f6e20636f6e76616c6c6973206d657475732e20446f6e65632074656d706f7220697073756d20696e206d617572697320636f6e67756520736f6c6c696369747564696e2e20566573746962756c756d20616e746520697073756d207072696d697320696e206661756369627573206f726369206c756374757320657420756c74726963657320706f737565726520637562696c69612043757261653b2053757370656e646973736520636f6e76616c6c69732073656d2076656c206d617373612066617563696275732c2065676574206c6163696e6961206c616375732074656d706f722e204e756c6c61207175697320756c747269636965732070757275732e2050726f696e20617563746f722072686f6e637573206e69626820636f6e64696d656e74756d206d6f6c6c69732e20416c697175616d20636f6e73657175617420656e696d206174206d65747573206c75637475732c206120656c656966656e6420707572757320656765737461732e20437572616269747572206174206e696268206d657475732e204e616d20626962656e64756d2c206e6571756520617420617563746f72207472697374697175652c206c6f72656d206c696265726f20616c697175657420617263752c206e6f6e20696e74657264756d2074656c6c7573206c65637475732073697420616d65742065726f732e20437261732072686f6e6375732c206d65747573206163206f726e617265206375727375732c20646f6c6f72206a7573746f20756c747269636573206d657475732c20617420756c6c616d636f7270657220766f6c7574706174" -} diff --git a/util/json-tests/json/trie/README.md b/util/json-tests/json/trie/README.md deleted file mode 100644 index 6d7f479a3..000000000 --- a/util/json-tests/json/trie/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Trie tests guideline - -Trie test input is an array of operations. Each operation must have 2 fields: - -- `operation` - string, either `insert` or `remove` -- `key` - string, or hex value prefixed with `0x` - -And optional field: - -- `value`- which is used by `insert` operation - -### Example - -```json -{ - "input": - [ - { - "operation": "insert", - "key": "world", - "value": "hello" - }, - { - "operation": "insert", - "key": "0x1234", - "value": "ooooops" - }, - { - "operation": "remove", - "key": "0x1234" - } - ], - "output": "0x5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84" -} -``` diff --git a/util/json-tests/json/trie/basic.json b/util/json-tests/json/trie/basic.json deleted file mode 100644 index f737ef337..000000000 --- a/util/json-tests/json/trie/basic.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "input": - [ - { - "operation": "insert", - "key": "A", - "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - } - ], - "output": "0xd23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab" -} diff --git a/util/json-tests/json/trie/branching.json b/util/json-tests/json/trie/branching.json deleted file mode 100644 index 7bbb13f03..000000000 --- a/util/json-tests/json/trie/branching.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "input": [ - { - "operation": "insert", - "key": "0x04110d816c380812a427968ece99b1c963dfbce6", - "value": "something" - }, - { - "operation": "insert", - "key": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87", - "value": "something" - }, - { - "operation": "insert", - "key": "0x0a517d755cebbf66312b30fff713666a9cb917e0", - "value": "something" - }, - { - "operation": "insert", - "key": "0x24dd378f51adc67a50e339e8031fe9bd4aafab36", - "value": "something" - }, - { - "operation": "insert", - "key": "0x293f982d000532a7861ab122bdc4bbfd26bf9030", - "value": "something" - }, - { - "operation": "insert", - "key": "0x2cf5732f017b0cf1b1f13a1478e10239716bf6b5", - "value": "something" - }, - { - "operation": "insert", - "key": "0x31c640b92c21a1f1465c91070b4b3b4d6854195f", - "value": "something" - }, - { - "operation": "insert", - "key": "0x37f998764813b136ddf5a754f34063fd03065e36", - "value": "something" - }, - { - "operation": "insert", - "key": "0x37fa399a749c121f8a15ce77e3d9f9bec8020d7a", - "value": "something" - }, - { - "operation": "insert", - "key": "0x4f36659fa632310b6ec438dea4085b522a2dd077", - "value": "something" - }, - { - "operation": "insert", - "key": "0x62c01474f089b07dae603491675dc5b5748f7049", - "value": "something" - }, - { - "operation": "insert", - "key": "0x729af7294be595a0efd7d891c9e51f89c07950c7", - "value": "something" - }, - { - "operation": "insert", - "key": "0x83e3e5a16d3b696a0314b30b2534804dd5e11197", - "value": "something" - }, - { - "operation": "insert", - "key": "0x8703df2417e0d7c59d063caa9583cb10a4d20532", - "value": "something" - }, - { - "operation": "insert", - "key": "0x8dffcd74e5b5923512916c6a64b502689cfa65e1", - "value": "something" - }, - { - "operation": "insert", - "key": "0x95a4d7cccb5204733874fa87285a176fe1e9e240", - "value": "something" - }, - { - "operation": "insert", - "key": "0x99b2fcba8120bedd048fe79f5262a6690ed38c39", - "value": "something" - }, - { - "operation": "insert", - "key": "0xa4202b8b8afd5354e3e40a219bdc17f6001bf2cf", - "value": "something" - }, - { - "operation": "insert", - "key": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "value": "something" - }, - { - "operation": "insert", - "key": "0xa9647f4a0a14042d91dc33c0328030a7157c93ae", - "value": "something" - }, - { - "operation": "insert", - "key": "0xaa6cffe5185732689c18f37a7f86170cb7304c2a", - "value": "something" - }, - { - "operation": "insert", - "key": "0xaae4a2e3c51c04606dcb3723456e58f3ed214f45", - "value": "something" - }, - { - "operation": "insert", - "key": "0xc37a43e940dfb5baf581a0b82b351d48305fc885", - "value": "something" - }, - { - "operation": "insert", - "key": "0xd2571607e241ecf590ed94b12d87c94babe36db6", - "value": "something" - }, - { - "operation": "insert", - "key": "0xf735071cbee190d76b704ce68384fc21e389fbe7", - "value": "something" - }, - { - "operation": "remove", - "key": "0x04110d816c380812a427968ece99b1c963dfbce6" - }, - { - "operation": "remove", - "key": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" - }, - { - "operation": "remove", - "key": "0x0a517d755cebbf66312b30fff713666a9cb917e0" - }, - { - "operation": "remove", - "key": "0x24dd378f51adc67a50e339e8031fe9bd4aafab36" - }, - { - "operation": "remove", - "key": "0x293f982d000532a7861ab122bdc4bbfd26bf9030" - }, - { - "operation": "remove", - "key": "0x2cf5732f017b0cf1b1f13a1478e10239716bf6b5" - }, - { - "operation": "remove", - "key": "0x31c640b92c21a1f1465c91070b4b3b4d6854195f" - }, - { - "operation": "remove", - "key": "0x37f998764813b136ddf5a754f34063fd03065e36" - }, - { - "operation": "remove", - "key": "0x37fa399a749c121f8a15ce77e3d9f9bec8020d7a" - }, - { - "operation": "remove", - "key": "0x4f36659fa632310b6ec438dea4085b522a2dd077" - }, - { - "operation": "remove", - "key": "0x62c01474f089b07dae603491675dc5b5748f7049" - }, - { - "operation": "remove", - "key": "0x729af7294be595a0efd7d891c9e51f89c07950c7" - }, - { - "operation": "remove", - "key": "0x83e3e5a16d3b696a0314b30b2534804dd5e11197" - }, - { - "operation": "remove", - "key": "0x8703df2417e0d7c59d063caa9583cb10a4d20532" - }, - { - "operation": "remove", - "key": "0x8dffcd74e5b5923512916c6a64b502689cfa65e1" - }, - { - "operation": "remove", - "key": "0x95a4d7cccb5204733874fa87285a176fe1e9e240" - }, - { - "operation": "remove", - "key": "0x99b2fcba8120bedd048fe79f5262a6690ed38c39" - }, - { - "operation": "remove", - "key": "0xa4202b8b8afd5354e3e40a219bdc17f6001bf2cf" - }, - { - "operation": "remove", - "key": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" - }, - { - "operation": "remove", - "key": "0xa9647f4a0a14042d91dc33c0328030a7157c93ae" - }, - { - "operation": "remove", - "key": "0xaa6cffe5185732689c18f37a7f86170cb7304c2a" - }, - { - "operation": "remove", - "key": "0xaae4a2e3c51c04606dcb3723456e58f3ed214f45" - }, - { - "operation": "remove", - "key": "0xc37a43e940dfb5baf581a0b82b351d48305fc885" - }, - { - "operation": "remove", - "key": "0xd2571607e241ecf590ed94b12d87c94babe36db6" - }, - { - "operation": "remove", - "key": "0xf735071cbee190d76b704ce68384fc21e389fbe7" - }], - "output": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" -} \ No newline at end of file diff --git a/util/json-tests/json/trie/dogs.json b/util/json-tests/json/trie/dogs.json deleted file mode 100644 index a6ff7c891..000000000 --- a/util/json-tests/json/trie/dogs.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "input": - [ - { - "operation": "insert", - "key": "doe", - "value": "reindeer" - }, - { - "operation": "insert", - "key": "dogglesworth", - "value": "cat" - }, - { - "operation": "insert", - "key": "dog", - "value": "puppy" - } - ], - "output": "0x8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3" -} diff --git a/util/json-tests/json/trie/empty.json b/util/json-tests/json/trie/empty.json deleted file mode 100644 index ca146df54..000000000 --- a/util/json-tests/json/trie/empty.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "input": [], - "output": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" -} diff --git a/util/json-tests/json/trie/empty_values.json b/util/json-tests/json/trie/empty_values.json deleted file mode 100644 index ac367f913..000000000 --- a/util/json-tests/json/trie/empty_values.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "input": - [ - { - "operation": "insert", - "key": "do", - "value": "verb" - }, - { - "operation": "insert", - "key": "ether", - "value": "wookiedoo" - }, - { - "operation": "insert", - "key": "horse", - "value": "stallion" - }, - { - "operation": "insert", - "key": "shaman", - "value": "horse" - }, - { - "operation": "insert", - "key": "doge", - "value": "coin" - }, - { - "operation": "remove", - "key": "ether" - }, - { - "operation": "insert", - "key": "dog", - "value": "puppy" - }, - { - "operation": "remove", - "key": "shaman" - } - ], - "output": "0x5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84" -} diff --git a/util/json-tests/json/trie/foo.json b/util/json-tests/json/trie/foo.json deleted file mode 100644 index 4b8c0a87f..000000000 --- a/util/json-tests/json/trie/foo.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "input": - [ - { - "operation": "insert", - "key": "foo", - "value": "bar" - }, - { - "operation": "insert", - "key": "food", - "value": "bass" - } - ], - "output": "0x17beaa1648bafa633cda809c90c04af50fc8aed3cb40d16efbddee6fdf63c4c3" -} diff --git a/util/json-tests/json/trie/jeff.json b/util/json-tests/json/trie/jeff.json deleted file mode 100644 index 1f3093fad..000000000 --- a/util/json-tests/json/trie/jeff.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "input": [ - { - "operation": "insert", - "key": "0x0000000000000000000000000000000000000000000000000000000000000045", - "value": "0x22b224a1420a802ab51d326e29fa98e34c4f24ea" - }, - { - "operation": "insert", - "key": "0x0000000000000000000000000000000000000000000000000000000000000046", - "value": "0x67706c2076330000000000000000000000000000000000000000000000000000" - }, - { - "operation": "insert", - "key": "0x0000000000000000000000000000000000000000000000000000001234567890", - "value": "0x697c7b8c961b56f675d570498424ac8de1a918f6" - }, - { - "operation": "insert", - "key": "0x000000000000000000000000697c7b8c961b56f675d570498424ac8de1a918f6", - "value": "0x1234567890" - }, - { - "operation": "insert", - "key": "0x0000000000000000000000007ef9e639e2733cb34e4dfc576d4b23f72db776b2", - "value": "0x4655474156000000000000000000000000000000000000000000000000000000" - }, - { - "operation": "insert", - "key": "0x000000000000000000000000ec4f34c97e43fbb2816cfd95e388353c7181dab1", - "value": "0x4e616d6552656700000000000000000000000000000000000000000000000000" - }, - { - "operation": "insert", - "key": "0x4655474156000000000000000000000000000000000000000000000000000000", - "value": "0x7ef9e639e2733cb34e4dfc576d4b23f72db776b2" - }, - { - "operation": "insert", - "key": "0x4e616d6552656700000000000000000000000000000000000000000000000000", - "value": "0xec4f34c97e43fbb2816cfd95e388353c7181dab1" - }, - { - "operation": "remove", - "key": "0x0000000000000000000000000000000000000000000000000000001234567890" - }, - { - "operation": "insert", - "key": "0x000000000000000000000000697c7b8c961b56f675d570498424ac8de1a918f6", - "value": "0x6f6f6f6820736f2067726561742c207265616c6c6c793f000000000000000000" - }, - { - "operation": "insert", - "key": "0x6f6f6f6820736f2067726561742c207265616c6c6c793f000000000000000000", - "value": "0x697c7b8c961b56f675d570498424ac8de1a918f6" - }], - "output": "0x9f6221ebb8efe7cff60a716ecb886e67dd042014be444669f0159d8e68b42100" -} \ No newline at end of file diff --git a/util/json-tests/src/lib.rs b/util/json-tests/src/lib.rs deleted file mode 100644 index e250d5dfe..000000000 --- a/util/json-tests/src/lib.rs +++ /dev/null @@ -1,82 +0,0 @@ -// 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 . - -extern crate rustc_serialize; -extern crate glob; - -use std::str::from_utf8; -use std::path::*; -use std::io::prelude::*; -use std::fs::File; -use glob::glob; -use rustc_serialize::*; - -mod util; -pub mod trie; -pub mod rlp; - -pub trait JsonTest: Sized { - type Input; - type Output; - - fn new(data: &[u8]) -> Self; - fn input(&self) -> Self::Input; - fn output(&self) -> Self::Output; -} - -pub struct JsonLoader { - json: json::Json -} - -impl JsonTest for JsonLoader { - type Input = json::Json; - type Output = json::Json; - - fn new(data: &[u8]) -> Self { - JsonLoader { - json: json::Json::from_str(from_utf8(data).unwrap()).unwrap() - } - } - fn input(&self) -> Self::Input { - self.json.as_object().unwrap()["input"].clone() - } - - fn output(&self) -> Self::Output { - self.json.as_object().unwrap()["output"].clone() - } -} - -pub fn execute_test(data: &[u8], f: &mut F) where T: JsonTest, F: FnMut(T::Input, T::Output) { - let test = T::new(data); - f(test.input(), test.output()) -} - -pub fn execute_test_from_file(path: &Path, f: &mut F) where T: JsonTest, F: FnMut(T::Input, T::Output) { - let mut file = File::open(path).unwrap(); - let mut buffer = vec![]; - let _ = file.read_to_end(&mut buffer); - let test = T::new(&buffer); - f(test.input(), test.output()) -} - -pub fn execute_tests_from_directory(pattern: &str, f: &mut F) where T: JsonTest, F: FnMut(String, T::Input, T::Output) { - for path in glob(pattern).unwrap().filter_map(Result::ok) { - execute_test_from_file::(&path, &mut | input, output | { - f(path.to_str().unwrap().to_string(), input, output); - }); - } -} - diff --git a/util/json-tests/src/rlp.rs b/util/json-tests/src/rlp.rs deleted file mode 100644 index 0b8e4c904..000000000 --- a/util/json-tests/src/rlp.rs +++ /dev/null @@ -1,68 +0,0 @@ -// 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 . - -//! json rlp tests -use rustc_serialize::*; -use super::{JsonTest, JsonLoader}; -use util::*; - -pub enum Operation { - Append(Vec), - AppendList(usize), - AppendRaw(Vec, usize), - AppendEmpty -} - -impl Into for json::Json { - fn into(self) -> Operation { - let obj = self.as_object().unwrap(); - match obj["operation"].as_string().unwrap().as_ref() { - "append" => Operation::Append(hex_or_string(obj["value"].as_string().unwrap())), - "append_list" => Operation::AppendList(obj["len"].as_u64().unwrap() as usize), - "append_raw" => Operation::AppendRaw(hex_or_string(obj["value"].as_string().unwrap()), obj["len"].as_u64().unwrap() as usize), - "append_empty" => Operation::AppendEmpty, - other => { panic!("Unsupported opertation: {}", other); } - } - } -} - -pub struct RlpStreamTest { - loader: JsonLoader -} - -impl JsonTest for RlpStreamTest { - type Input = Vec; - type Output = Vec; - - fn new(data: &[u8]) -> Self { - RlpStreamTest { - loader: JsonLoader::new(data) - } - } - - fn input(&self) -> Self::Input { - self.loader.input().as_array().unwrap() - .iter() - .cloned() - .map(|i| i.into()) - .collect() - } - - fn output(&self) -> Self::Output { - hex_or_string(self.loader.output().as_string().unwrap()) - } -} - diff --git a/util/json-tests/src/trie.rs b/util/json-tests/src/trie.rs deleted file mode 100644 index a060167af..000000000 --- a/util/json-tests/src/trie.rs +++ /dev/null @@ -1,105 +0,0 @@ -// 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 . - -//! json trie tests -use std::collections::HashMap; -use rustc_serialize::*; -use super::{JsonTest, JsonLoader}; -use util::*; - -#[derive(RustcDecodable)] -struct RawOperation { - operation: String, - key: String, - value: Option -} - -pub enum Operation { - Insert(Vec, Vec), - Remove(Vec) -} - -impl Into for RawOperation { - fn into(self) -> Operation { - match self.operation.as_ref() { - "insert" => Operation::Insert(hex_or_string(&self.key), hex_or_string(&self.value.unwrap())), - "remove" => Operation::Remove(hex_or_string(&self.key)), - other => panic!("invalid operation type: {}", other) - } - } -} - -pub struct TrieTest { - loader: JsonLoader -} - -impl JsonTest for TrieTest { - type Input = Vec; - type Output = Vec; - - fn new(data: &[u8]) -> Self { - TrieTest { - loader: JsonLoader::new(data) - } - } - - fn input(&self) -> Self::Input { - let mut decoder = json::Decoder::new(self.loader.input()); - let raw: Vec = Decodable::decode(&mut decoder).unwrap(); - raw.into_iter() - .map(|i| i.into()) - .collect() - } - - fn output(&self) -> Self::Output { - hex_or_string(self.loader.output().as_string().unwrap()) - } -} - -pub struct TriehashTest { - trietest: TrieTest -} - -impl JsonTest for TriehashTest { - type Input = Vec<(Vec, Vec)>; - type Output = Vec; - - fn new(data: &[u8]) -> Self { - TriehashTest { - trietest: TrieTest::new(data) - } - } - - fn input(&self) -> Self::Input { - self.trietest.input() - .into_iter() - .fold(HashMap::new(), | mut map, o | { - match o { - Operation::Insert(k, v) => map.insert(k, v), - Operation::Remove(k) => map.remove(&k) - }; - map - }) - .into_iter() - .map(|p| { p }) - .collect() - } - - fn output(&self) -> Self::Output { - self.trietest.output() - } -} - diff --git a/util/src/bytes.rs b/util/src/bytes.rs index 5ef2044ee..4e3d73da8 100644 --- a/util/src/bytes.rs +++ b/util/src/bytes.rs @@ -24,7 +24,7 @@ //! use util::bytes::BytesConvertable; //! //! let arr = [0; 5]; -//! let slice: &[u8] = arr.bytes(); +//! let slice: &[u8] = arr.as_slice(); //! } //! //! fn main() { @@ -120,12 +120,12 @@ impl<'a> ToPretty for &'a [u8] { impl<'a> ToPretty for &'a Bytes { fn pretty(&self) -> PrettySlice { - PrettySlice(self.bytes()) + PrettySlice(self.as_slice()) } } impl ToPretty for Bytes { fn pretty(&self) -> PrettySlice { - PrettySlice(self.bytes()) + PrettySlice(self.as_slice()) } } @@ -162,23 +162,19 @@ pub type Bytes = Vec; /// Slice of bytes to underlying memory pub trait BytesConvertable { - // TODO: rename to as_slice /// Get the underlying byte-wise representation of the value. - /// Deprecated - use `as_slice` instead. - fn bytes(&self) -> &[u8]; - /// Get the underlying byte-wise representation of the value. - fn as_slice(&self) -> &[u8] { self.bytes() } + fn as_slice(&self) -> &[u8]; /// Get a copy of the underlying byte-wise representation. fn to_bytes(&self) -> Bytes { self.as_slice().to_vec() } } impl BytesConvertable for T where T: AsRef<[u8]> { - fn bytes(&self) -> &[u8] { self.as_ref() } + fn as_slice(&self) -> &[u8] { self.as_ref() } } #[test] fn bytes_convertable() { - assert_eq!(vec![0x12u8, 0x34].bytes(), &[0x12u8, 0x34]); + assert_eq!(vec![0x12u8, 0x34].as_slice(), &[0x12u8, 0x34]); assert!([0u8; 0].as_slice().is_empty()); } diff --git a/util/src/error.rs b/util/src/error.rs index 5e9f8e7a8..c5f40d717 100644 --- a/util/src/error.rs +++ b/util/src/error.rs @@ -101,7 +101,7 @@ impl fmt::Display for Mismatch { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] /// Error indicating value found is outside of a valid range. pub struct OutOfBounds { /// Minimum allowed value. diff --git a/util/src/hash.rs b/util/src/hash.rs index 16f0bde9e..95e8f8c8c 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -16,16 +16,18 @@ //! General hash types, a fixed-size raw-data type used as the output of hash functions. -use standard::*; +use rustc_serialize::hex::FromHex; +use std::{ops, fmt, cmp}; +use std::cmp::*; +use std::ops::*; +use std::hash::{Hash, Hasher}; +use std::str::FromStr; use math::log2; use error::UtilError; use rand::Rng; use rand::os::OsRng; use bytes::{BytesConvertable,Populatable}; -use from_json::*; use bigint::uint::{Uint, U256}; -use rustc_serialize::hex::ToHex; -use serde; /// Trait for a fixed-size byte array to be used as the output of hash functions. /// @@ -228,55 +230,6 @@ macro_rules! impl_hash { } } - impl serde::Serialize for $from { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: serde::Serializer { - let mut hex = "0x".to_owned(); - hex.push_str(self.to_hex().as_ref()); - serializer.serialize_str(hex.as_ref()) - } - } - - impl serde::Deserialize for $from { - fn deserialize(deserializer: &mut D) -> Result<$from, D::Error> - where D: serde::Deserializer { - struct HashVisitor; - - impl serde::de::Visitor for HashVisitor { - type Value = $from; - - fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { - // 0x + len - if value.len() != 2 + $size * 2 { - return Err(serde::Error::custom("Invalid length.")); - } - - value[2..].from_hex().map(|ref v| $from::from_slice(v)).map_err(|_| serde::Error::custom("Invalid hex value.")) - } - - fn visit_string(&mut self, value: String) -> Result where E: serde::Error { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize(HashVisitor) - } - } - - impl FromJson for $from { - fn from_json(json: &Json) -> Self { - match *json { - Json::String(ref s) => { - match s.len() % 2 { - 0 => FromStr::from_str(clean_0x(s)).unwrap(), - _ => FromStr::from_str(&("0".to_owned() + &(clean_0x(s).to_owned()))[..]).unwrap() - } - }, - _ => Default::default(), - } - } - } - impl fmt::Debug for $from { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for i in &self.0[..] { @@ -285,6 +238,7 @@ macro_rules! impl_hash { Ok(()) } } + impl fmt::Display for $from { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for i in &self.0[0..2] { @@ -506,13 +460,13 @@ impl<'a> From<&'a U256> for H256 { impl From for U256 { fn from(value: H256) -> U256 { - U256::from(value.bytes()) + U256::from(value.as_slice()) } } impl<'a> From<&'a H256> for U256 { fn from(value: &'a H256) -> U256 { - U256::from(value.bytes()) + U256::from(value.as_slice()) } } @@ -532,17 +486,6 @@ impl From for H64 { } } -/* -impl<'a> From<&'a H256> for Address { - fn from(value: &'a H256) -> Address { - let mut ret = Address::new(); - ret.0.copy_from_slice(&value[12..32]); - ret - } - } -} -*/ - impl From
for H256 { fn from(value: Address) -> H256 { let mut ret = H256::new(); @@ -596,11 +539,6 @@ impl_hash!(H520, 65); impl_hash!(H1024, 128); impl_hash!(H2048, 256); -/// Constant address for point 0. Often used as a default. -pub static ZERO_ADDRESS: Address = Address([0x00; 20]); -/// Constant 256-bit datum for 0. Often used as a default. -pub static ZERO_H256: H256 = H256([0x00; 32]); - #[cfg(test)] mod tests { use hash::*; diff --git a/util/src/journaldb/archivedb.rs b/util/src/journaldb/archivedb.rs index 40172cccd..ca436d7ea 100644 --- a/util/src/journaldb/archivedb.rs +++ b/util/src/journaldb/archivedb.rs @@ -79,7 +79,7 @@ impl ArchiveDB { } fn payload(&self, key: &H256) -> Option { - self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) + self.backing.get(key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) } } @@ -177,7 +177,7 @@ impl JournalDB for ArchiveDB { let (key, (value, rc)) = i; if rc > 0 { assert!(rc == 1); - batch.put(&key.bytes(), &value).expect("Low-level database error. Some issue with your hard disk?"); + batch.put(&key, &value).expect("Low-level database error. Some issue with your hard disk?"); inserts += 1; } if rc < 0 { @@ -202,7 +202,7 @@ impl JournalDB for ArchiveDB { fn latest_era(&self) -> Option { self.latest_era } fn state(&self, id: &H256) -> Option { - self.backing.get_by_prefix(&id.bytes()[0..12]).and_then(|b| Some(b.to_vec())) + self.backing.get_by_prefix(&id[0..12]).and_then(|b| Some(b.to_vec())) } fn is_pruned(&self) -> bool { false } diff --git a/util/src/journaldb/earlymergedb.rs b/util/src/journaldb/earlymergedb.rs index 2b87f8345..b860ae598 100644 --- a/util/src/journaldb/earlymergedb.rs +++ b/util/src/journaldb/earlymergedb.rs @@ -106,7 +106,7 @@ impl EarlyMergeDB { } fn morph_key(key: &H256, index: u8) -> Bytes { - let mut ret = key.bytes().to_owned(); + let mut ret = key.to_bytes(); ret.push(index); ret } @@ -130,7 +130,7 @@ impl EarlyMergeDB { } // this is the first entry for this node in the journal. - if backing.get(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?").is_some() { + if backing.get(h).expect("Low-level database error. Some issue with your hard disk?").is_some() { // already in the backing DB. start counting, and remember it was already in. Self::set_already_in(batch, &h); refs.insert(h.clone(), RefInfo{queue_refs: 1, in_archive: true}); @@ -143,7 +143,7 @@ impl EarlyMergeDB { // Gets removed when a key leaves the journal, so should never be set when we're placing a new key. //Self::reset_already_in(&h); assert!(!Self::is_already_in(backing, &h)); - batch.put(&h.bytes(), d).expect("Low-level database error. Some issue with your hard disk?"); + batch.put(h, d).expect("Low-level database error. Some issue with your hard disk?"); refs.insert(h.clone(), RefInfo{queue_refs: 1, in_archive: false}); if trace { trace!(target: "jdb.fine", " insert({}): New to queue, not in DB: Inserting into queue and DB", h); @@ -204,7 +204,7 @@ impl EarlyMergeDB { } Some(RefInfo{queue_refs: 1, in_archive: false}) => { refs.remove(h); - batch.delete(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?"); + batch.delete(h).expect("Low-level database error. Some issue with your hard disk?"); if trace { trace!(target: "jdb.fine", " remove({}): Not in archive, only 1 ref in queue: Removing from queue and DB", h); } @@ -212,7 +212,7 @@ impl EarlyMergeDB { None => { // Gets removed when moving from 1 to 0 additional refs. Should never be here at 0 additional refs. //assert!(!Self::is_already_in(db, &h)); - batch.delete(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?"); + batch.delete(h).expect("Low-level database error. Some issue with your hard disk?"); if trace { trace!(target: "jdb.fine", " remove({}): Not in queue - MUST BE IN ARCHIVE: Removing from DB", h); } @@ -237,7 +237,7 @@ impl EarlyMergeDB { } fn payload(&self, key: &H256) -> Option { - self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) + self.backing.get(key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) } fn read_refs(db: &Database) -> (Option, HashMap) { diff --git a/util/src/journaldb/overlayrecentdb.rs b/util/src/journaldb/overlayrecentdb.rs index 0ffea308b..7afe7e5ac 100644 --- a/util/src/journaldb/overlayrecentdb.rs +++ b/util/src/journaldb/overlayrecentdb.rs @@ -141,7 +141,7 @@ impl OverlayRecentDB { } fn payload(&self, key: &H256) -> Option { - self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) + self.backing.get(key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) } fn read_overlay(db: &Database) -> JournalOverlay { diff --git a/util/src/json_aid.rs b/util/src/json_aid.rs deleted file mode 100644 index 7bf940b99..000000000 --- a/util/src/json_aid.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use common::*; - -/// Remove the `"0x"`, if present, from the left of `s`, returning the remaining slice. -pub fn clean(s: &str) -> &str { - if s.len() >= 2 && &s[0..2] == "0x" { - &s[2..] - } else { - s - } -} - -fn u256_from_str(s: &str) -> U256 { - if s.len() >= 2 && &s[0..2] == "0x" { - U256::from_str(&s[2..]).unwrap_or_else(|_| U256::zero()) - } else { - U256::from_dec_str(s).unwrap_or_else(|_| U256::zero()) - } -} - -impl FromJson for Bytes { - fn from_json(json: &Json) -> Self { - match *json { - Json::String(ref s) => match s.len() % 2 { - 0 => FromHex::from_hex(clean(s)).unwrap_or_else(|_| vec![]), - _ => FromHex::from_hex(&("0".to_owned() + &(clean(s).to_owned()))[..]).unwrap_or_else(|_| vec![]), - }, - _ => vec![], - } - } -} - -impl FromJson for BTreeMap { - fn from_json(json: &Json) -> Self { - match *json { - Json::Object(ref o) => o.iter().map(|(key, value)| (u256_from_str(key).into(), U256::from_json(value).into())).collect(), - _ => BTreeMap::new(), - } - } -} - -impl FromJson for Vec where T: FromJson { - fn from_json(json: &Json) -> Self { - match *json { - Json::Array(ref o) => o.iter().map(|x|T::from_json(x)).collect(), - _ => Vec::new(), - } - } -} - -impl FromJson for Option where T: FromJson { - fn from_json(json: &Json) -> Self { - match *json { - Json::String(ref o) if o.is_empty() => None, - Json::Null => None, - _ => Some(FromJson::from_json(json)), - } - } -} - -impl FromJson for u64 { - fn from_json(json: &Json) -> Self { - U256::from_json(json).low_u64() - } -} - -impl FromJson for u32 { - fn from_json(json: &Json) -> Self { - U256::from_json(json).low_u64() as u32 - } -} - -impl FromJson for u16 { - fn from_json(json: &Json) -> Self { - U256::from_json(json).low_u64() as u16 - } -} - -#[test] -fn u256_from_json() { - let j = Json::from_str("{ \"dec\": \"10\", \"hex\": \"0x0a\", \"int\": 10 }").unwrap(); - - let v: U256 = xjson!(&j["dec"]); - assert_eq!(U256::from(10), v); - let v: U256 = xjson!(&j["hex"]); - assert_eq!(U256::from(10), v); - let v: U256 = xjson!(&j["int"]); - assert_eq!(U256::from(10), v); -} - -#[test] -fn h256_from_json() { - let j = Json::from_str("{ \"with\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\", \"without\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\" }").unwrap(); - - let v: H256 = xjson!(&j["with"]); - assert_eq!(H256::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(), v); - let v: H256 = xjson!(&j["without"]); - assert_eq!(H256::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(), v); -} - -#[test] -fn vec_u256_from_json() { - let j = Json::from_str("{ \"array\": [ \"10\", \"0x0a\", 10] }").unwrap(); - - let v: Vec = xjson!(&j["array"]); - assert_eq!(vec![U256::from(10); 3], v); -} - -#[test] -fn vec_h256_from_json() { - let j = Json::from_str("{ \"array\": [ \"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\", \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"] }").unwrap(); - - let v: Vec = xjson!(&j["array"]); - assert_eq!(vec![H256::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(); 2], v); -} - -#[test] -fn simple_types() { - let j = Json::from_str("{ \"null\": null, \"empty\": \"\", \"int\": 42, \"dec\": \"42\", \"hex\": \"0x2a\" }").unwrap(); - let v: u16 = xjson!(&j["int"]); - assert_eq!(42u16, v); - let v: u32 = xjson!(&j["dec"]); - assert_eq!(42u32, v); - let v: u64 = xjson!(&j["hex"]); - assert_eq!(42u64, v); -} - -#[test] -fn option_types() { - let j = Json::from_str("{ \"null\": null, \"empty\": \"\", \"int\": 42, \"dec\": \"42\", \"hex\": \"0x2a\" }").unwrap(); - let v: Option = xjson!(&j["int"]); - assert_eq!(Some(42u16), v); - let v: Option = xjson!(&j["dec"]); - assert_eq!(Some(42u16), v); - let v: Option = xjson!(&j["null"]); - assert_eq!(None, v); - let v: Option = xjson!(&j["empty"]); - assert_eq!(None, v); -} diff --git a/util/src/lib.rs b/util/src/lib.rs index 31e072dc7..08480d85f 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -108,7 +108,6 @@ extern crate secp256k1; extern crate arrayvec; extern crate elastic_array; extern crate crossbeam; -extern crate serde; #[macro_use] extern crate log as rlog; extern crate igd; @@ -117,6 +116,8 @@ extern crate libc; extern crate target_info; extern crate bigint; extern crate chrono; +pub extern crate using_queue; +pub extern crate table; extern crate ansi_term; pub mod standard; @@ -130,8 +131,6 @@ pub mod hash; pub mod bytes; pub mod rlp; pub mod misc; -pub mod using_queue; -mod json_aid; pub mod vector; pub mod sha3; pub mod hashdb; @@ -145,22 +144,19 @@ pub mod crypto; pub mod triehash; pub mod trie; pub mod nibbleslice; +pub mod nibblevec; mod heapsizeof; -pub mod squeeze; pub mod semantic_version; pub mod io; pub mod network; pub mod log; pub mod panics; -pub mod table; pub mod network_settings; pub mod path; mod timer; pub use common::*; pub use misc::*; -pub use using_queue::*; -pub use json_aid::*; pub use rlp::*; pub use hashdb::*; pub use memorydb::*; @@ -171,7 +167,6 @@ pub use crypto::*; pub use triehash::*; pub use trie::*; pub use nibbleslice::*; -pub use squeeze::*; pub use semantic_version::*; pub use network::*; pub use io::*; diff --git a/util/src/memorydb.rs b/util/src/memorydb.rs index f63dfd992..73435e40a 100644 --- a/util/src/memorydb.rs +++ b/util/src/memorydb.rs @@ -198,14 +198,14 @@ impl HashDB for MemoryDB { let key = value.sha3(); if match self.data.get_mut(&key) { Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => { - *old_value = From::from(value.bytes()); + *old_value = From::from(value); *rc += 1; false }, Some(&mut (_, ref mut x)) => { *x += 1; false } , None => true, }{ // ... None falls through into... - self.data.insert(key.clone(), (From::from(value.bytes()), 1)); + self.data.insert(key.clone(), (From::from(value), 1)); } key } @@ -262,8 +262,8 @@ fn memorydb_denote() { for _ in 0..1000 { let r = H256::random(); let k = r.sha3(); - let &(ref v, ref rc) = m.denote(&k, r.bytes().to_vec()); - assert_eq!(v, &r.bytes()); + let &(ref v, ref rc) = m.denote(&k, r.to_bytes()); + assert_eq!(v.as_slice(), r.as_slice()); assert_eq!(*rc, 0); } diff --git a/util/src/nibblevec.rs b/util/src/nibblevec.rs new file mode 100644 index 000000000..dac022929 --- /dev/null +++ b/util/src/nibblevec.rs @@ -0,0 +1,130 @@ +//! An owning, nibble-oriented byte vector. + +use ::NibbleSlice; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] +/// Owning, nibble-oriented byte vector. Counterpart to NibbleSlice. +pub struct NibbleVec { + inner: Vec, + len: usize, +} + +impl NibbleVec { + /// Make a new NibbleVec + pub fn new() -> Self { + NibbleVec { + inner: Vec::new(), + len: 0 + } + } + + /// Make a NibbleVec with capacity for `n` nibbles. + pub fn with_capacity(n: usize) -> Self { + NibbleVec { + inner: Vec::with_capacity((n / 2) + (n % 2)), + len: 0 + } + } + + /// Length of the NibbleVec + pub fn len(&self) -> usize { self.len } + + /// Capacity of the NibbleVec. + pub fn capacity(&self) -> usize { self.inner.capacity() * 2 } + + /// Try to get the nibble at the given offset. + pub fn at(&self, idx: usize) -> u8 { + if idx % 2 == 0 { + self.inner[idx / 2] >> 4 + } else { + self.inner[idx / 2] & 0x0F + } + } + + /// Push a nibble onto the NibbleVec. Ignores the high 4 bits. + pub fn push(&mut self, nibble: u8) { + let nibble = nibble & 0x0F; + + if self.len % 2 == 0 { + self.inner.push(nibble << 4); + } else { + *self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; + } + + self.len += 1; + } + + /// Try to pop a nibble off the NibbleVec. Fails if len == 0. + pub fn pop(&mut self) -> Option { + if self.len == 0 { + return None; + } + + let byte = self.inner.pop().expect("len != 0; inner has last elem; qed"); + let nibble = if self.len % 2 == 0 { + self.inner.push(byte & 0xF0); + byte & 0x0F + } else { + byte >> 4 + }; + + self.len -= 1; + Some(nibble) + } + + /// Try to treat this NibbleVec as a NibbleSlice. Works only if len is even. + pub fn as_nibbleslice(&self) -> Option { + if self.len % 2 == 0 { + Some(NibbleSlice::new(self.inner())) + } else { + None + } + } + + /// Get the underlying byte slice. + pub fn inner(&self) -> &[u8] { + &self.inner[..] + } +} + +impl<'a> From> for NibbleVec { + fn from(s: NibbleSlice<'a>) -> Self { + let mut v = NibbleVec::with_capacity(s.len()); + for i in 0..s.len() { + v.push(s.at(i)); + } + v + } +} + +#[cfg(test)] +mod tests { + use super::NibbleVec; + + #[test] + fn push_pop() { + let mut v = NibbleVec::new(); + + for i in 0..16 { + v.push(i); + assert_eq!(v.len() - 1, i as usize); + assert_eq!(v.at(i as usize), i); + } + + for i in (0..16).rev() { + assert_eq!(v.pop(), Some(i)); + assert_eq!(v.len(), i as usize); + } + } + + #[test] + fn nibbleslice_conv() { + let mut v = NibbleVec::new(); + for i in 0..10 { + v.push(i); + } + + let v2: NibbleVec = v.as_nibbleslice().unwrap().into(); + assert_eq!(v, v2); + } +} \ No newline at end of file diff --git a/util/src/overlaydb.rs b/util/src/overlaydb.rs index b1ea44dac..63ec3dd50 100644 --- a/util/src/overlaydb.rs +++ b/util/src/overlaydb.rs @@ -172,10 +172,10 @@ impl OverlayDB { /// Get the refs and value of the given key. fn payload(&self, key: &H256) -> Option<(Bytes, u32)> { - self.backing.get(&key.bytes()) + self.backing.get(key) .expect("Low-level database error. Some issue with your hard disk?") .map(|d| { - let r = Rlp::new(d.deref()); + let r = Rlp::new(&d); (r.at(1).as_val(), r.at(0).as_val()) }) } @@ -186,10 +186,10 @@ impl OverlayDB { let mut s = RlpStream::new_list(2); s.append(&payload.1); s.append(&payload.0); - batch.put(&key.bytes(), s.as_raw()).expect("Low-level database error. Some issue with your hard disk?"); + batch.put(key, s.as_raw()).expect("Low-level database error. Some issue with your hard disk?"); false } else { - batch.delete(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?"); + batch.delete(key).expect("Low-level database error. Some issue with your hard disk?"); true } } @@ -200,10 +200,10 @@ impl OverlayDB { let mut s = RlpStream::new_list(2); s.append(&payload.1); s.append(&payload.0); - self.backing.put(&key.bytes(), s.as_raw()).expect("Low-level database error. Some issue with your hard disk?"); + self.backing.put(key, s.as_raw()).expect("Low-level database error. Some issue with your hard disk?"); false } else { - self.backing.delete(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?"); + self.backing.delete(key).expect("Low-level database error. Some issue with your hard disk?"); true } } diff --git a/util/src/rlp/bytes.rs b/util/src/rlp/bytes.rs index d252af828..479fc7261 100644 --- a/util/src/rlp/bytes.rs +++ b/util/src/rlp/bytes.rs @@ -148,9 +148,9 @@ impl_uint_to_bytes!(U128); impl ToBytes for T where T: FixedHash { fn to_bytes>(&self, out: &mut V) { - out.vec_extend(self.bytes()); + out.vec_extend(self.as_slice()); } - fn to_bytes_len(&self) -> usize { self.bytes().len() } + fn to_bytes_len(&self) -> usize { self.as_slice().len() } } /// Error returned when `FromBytes` conversation goes wrong diff --git a/util/src/rlp/tests.rs b/util/src/rlp/tests.rs index 3df2f2d97..3d9ed40f1 100644 --- a/util/src/rlp/tests.rs +++ b/util/src/rlp/tests.rs @@ -14,9 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -extern crate json_tests; -use self::json_tests::execute_tests_from_directory; -use self::json_tests::rlp as rlptest; use std::{fmt, cmp}; use std::str::FromStr; use rlp; @@ -340,26 +337,6 @@ fn decode_untrusted_vector_of_vectors_str() { run_decode_tests(tests); } -#[test] -fn test_rlp_json() { - println!("Json rlp test: "); - execute_tests_from_directory::("json-tests/json/rlp/stream/*.json", &mut | file, input, output | { - println!("file: {}", file); - - let mut stream = RlpStream::new(); - for operation in input.into_iter() { - match operation { - rlptest::Operation::Append(ref v) => stream.append(v), - rlptest::Operation::AppendList(len) => stream.begin_list(len), - rlptest::Operation::AppendRaw(ref raw, len) => stream.append_raw(raw, len), - rlptest::Operation::AppendEmpty => stream.append_empty_data() - }; - } - - assert_eq!(stream.out(), output); - }); -} - #[test] fn test_decoding_array() { let v = vec![5u16, 2u16]; diff --git a/util/src/sha3.rs b/util/src/sha3.rs index 7e8382250..d2a071759 100644 --- a/util/src/sha3.rs +++ b/util/src/sha3.rs @@ -58,7 +58,7 @@ impl Hashable for T where T: BytesConvertable { } fn sha3_into(&self, dest: &mut [u8]) { unsafe { - let input: &[u8] = self.bytes(); + let input: &[u8] = self.as_slice(); sha3_256(dest.as_mut_ptr(), dest.len(), input.as_ptr(), input.len()); } } diff --git a/util/src/squeeze.rs b/util/src/squeeze.rs deleted file mode 100644 index 69aab9728..000000000 --- a/util/src/squeeze.rs +++ /dev/null @@ -1,84 +0,0 @@ -// 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 . - -//! Helper module that should be used to randomly squeeze -//! caches to a given size in bytes -//! -//! ``` -//! extern crate heapsize; -//! extern crate ethcore_util as util; -//! use std::collections::HashMap; -//! use std::mem::size_of; -//! use heapsize::HeapSizeOf; -//! use util::squeeze::Squeeze; -//! -//! fn main() { -//! let initial_size = 60; -//! let mut map: HashMap = HashMap::with_capacity(initial_size); -//! assert!(map.capacity() >= initial_size); -//! for i in 0..initial_size { -//! map.insert(i as u8, i as u8); -//! } -//! -//! assert_eq!(map.heap_size_of_children(), map.capacity() * 2 * size_of::()); -//! assert_eq!(map.len(), initial_size); -//! let initial_heap_size = map.heap_size_of_children(); -//! -//! // squeeze it to size of key and value -//! map.squeeze(2 * size_of::()); -//! assert_eq!(map.len(), 1); -//! -//! // its likely that heap size was reduced, but we can't be 100% sure -//! assert!(initial_heap_size >= map.heap_size_of_children()); -//! } -//! ``` - -use std::collections::HashMap; -use std::hash::Hash; -use heapsize::HeapSizeOf; - -/// Should be used to squeeze collections to certain size in bytes -pub trait Squeeze { - /// Try to reduce collection size to `size` bytes - fn squeeze(&mut self, size: usize); -} - -impl Squeeze for HashMap where K: Eq + Hash + Clone + HeapSizeOf, T: HeapSizeOf { - fn squeeze(&mut self, size: usize) { - if self.is_empty() { - return - } - - let size_of_entry = self.heap_size_of_children() / self.capacity(); - let all_entries = size_of_entry * self.len(); - let mut shrinked_size = all_entries; - - while !self.is_empty() && shrinked_size > size { - // could be optimized - let key = self.keys().next().unwrap().clone(); - self.remove(&key); - shrinked_size -= size_of_entry; - } - - self.shrink_to_fit(); - - // if we squeezed something, but not enough, squeeze again - if all_entries != shrinked_size && self.heap_size_of_children() > size { - self.squeeze(size); - } - } -} - diff --git a/util/src/trie/standardmap.rs b/util/src/trie/standardmap.rs index b7f3a9500..216d29ad0 100644 --- a/util/src/trie/standardmap.rs +++ b/util/src/trie/standardmap.rs @@ -64,16 +64,16 @@ impl StandardMap { fn random_bytes(min_count: usize, journal_count: usize, seed: &mut H256) -> Vec { assert!(min_count + journal_count <= 32); *seed = seed.sha3(); - let r = min_count + (seed.bytes()[31] as usize % (journal_count + 1)); - seed.bytes()[0..r].to_vec() + let r = min_count + (seed[31] as usize % (journal_count + 1)); + seed[0..r].to_vec() } /// Get a random value. Equal chance of being 1 byte as of 32. `seed` is mutated pseudoramdonly and used. fn random_value(seed: &mut H256) -> Bytes { *seed = seed.sha3(); - match seed.bytes()[0] % 2 { - 1 => vec![seed.bytes()[31];1], - _ => seed.bytes().to_vec(), + match seed[0] % 2 { + 1 => vec![seed[31];1], + _ => seed.to_vec(), } } @@ -82,10 +82,10 @@ impl StandardMap { fn random_word(alphabet: &[u8], min_count: usize, journal_count: usize, seed: &mut H256) -> Vec { assert!(min_count + journal_count <= 32); *seed = seed.sha3(); - let r = min_count + (seed.bytes()[31] as usize % (journal_count + 1)); + let r = min_count + (seed[31] as usize % (journal_count + 1)); let mut ret: Vec = Vec::with_capacity(r); for i in 0..r { - ret.push(alphabet[seed.bytes()[i] as usize % alphabet.len()]); + ret.push(alphabet[seed[i] as usize % alphabet.len()]); } ret } diff --git a/util/src/trie/triedbmut.rs b/util/src/trie/triedbmut.rs index 4d46814c0..859bc52e1 100644 --- a/util/src/trie/triedbmut.rs +++ b/util/src/trie/triedbmut.rs @@ -676,8 +676,6 @@ impl<'db> fmt::Debug for TrieDBMut<'db> { #[cfg(test)] mod tests { - extern crate json_tests; - use self::json_tests::{trie, execute_tests_from_directory}; use triehash::*; use hash::*; use hashdb::*; @@ -858,6 +856,21 @@ mod tests { ])); } + #[test] + fn insert_out_of_order() { + let mut memdb = MemoryDB::new(); + let mut root = H256::new(); + let mut t = TrieDBMut::new(&mut memdb, &mut root); + t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]); + t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]); + t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]); + assert_eq!(*t.root(), trie_root(vec![ + (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), + (vec![0x81u8, 0x23], vec![0x81u8, 0x23]), + (vec![0xf1u8, 0x23], vec![0xf1u8, 0x23]), + ])); + } + #[test] fn insert_value_into_branch_root() { let mut memdb = MemoryDB::new(); @@ -1063,23 +1076,64 @@ mod tests { } #[test] - fn test_trie_json() { - println!("Json trie test: "); - execute_tests_from_directory::("json-tests/json/trie/*.json", &mut | file, input, output | { - println!("file: {}", file); + fn branching_test() { + use std::str::FromStr; + use rustc_serialize::hex::FromHex; - let mut memdb = MemoryDB::new(); - let mut root = H256::new(); - let mut t = TrieDBMut::new(&mut memdb, &mut root); - for operation in input.into_iter() { - match operation { - trie::Operation::Insert(key, value) => t.insert(&key, &value), - trie::Operation::Remove(key) => t.remove(&key) - } - } - - assert_eq!(*t.root(), H256::from_slice(&output)); - }); + let mut memdb = MemoryDB::new(); + let mut root = H256::new(); + let mut t = TrieDBMut::new(&mut memdb, &mut root); + t.insert(&"04110d816c380812a427968ece99b1c963dfbce6".from_hex().unwrap(), b"something"); + t.insert(&"095e7baea6a6c7c4c2dfeb977efac326af552d87".from_hex().unwrap(), b"something"); + t.insert(&"0a517d755cebbf66312b30fff713666a9cb917e0".from_hex().unwrap(), b"something"); + t.insert(&"24dd378f51adc67a50e339e8031fe9bd4aafab36".from_hex().unwrap(), b"something"); + t.insert(&"293f982d000532a7861ab122bdc4bbfd26bf9030".from_hex().unwrap(), b"something"); + t.insert(&"2cf5732f017b0cf1b1f13a1478e10239716bf6b5".from_hex().unwrap(), b"something"); + t.insert(&"31c640b92c21a1f1465c91070b4b3b4d6854195f".from_hex().unwrap(), b"something"); + t.insert(&"37f998764813b136ddf5a754f34063fd03065e36".from_hex().unwrap(), b"something"); + t.insert(&"37fa399a749c121f8a15ce77e3d9f9bec8020d7a".from_hex().unwrap(), b"something"); + t.insert(&"4f36659fa632310b6ec438dea4085b522a2dd077".from_hex().unwrap(), b"something"); + t.insert(&"62c01474f089b07dae603491675dc5b5748f7049".from_hex().unwrap(), b"something"); + t.insert(&"729af7294be595a0efd7d891c9e51f89c07950c7".from_hex().unwrap(), b"something"); + t.insert(&"83e3e5a16d3b696a0314b30b2534804dd5e11197".from_hex().unwrap(), b"something"); + t.insert(&"8703df2417e0d7c59d063caa9583cb10a4d20532".from_hex().unwrap(), b"something"); + t.insert(&"8dffcd74e5b5923512916c6a64b502689cfa65e1".from_hex().unwrap(), b"something"); + t.insert(&"95a4d7cccb5204733874fa87285a176fe1e9e240".from_hex().unwrap(), b"something"); + t.insert(&"99b2fcba8120bedd048fe79f5262a6690ed38c39".from_hex().unwrap(), b"something"); + t.insert(&"a4202b8b8afd5354e3e40a219bdc17f6001bf2cf".from_hex().unwrap(), b"something"); + t.insert(&"a94f5374fce5edbc8e2a8697c15331677e6ebf0b".from_hex().unwrap(), b"something"); + t.insert(&"a9647f4a0a14042d91dc33c0328030a7157c93ae".from_hex().unwrap(), b"something"); + t.insert(&"aa6cffe5185732689c18f37a7f86170cb7304c2a".from_hex().unwrap(), b"something"); + t.insert(&"aae4a2e3c51c04606dcb3723456e58f3ed214f45".from_hex().unwrap(), b"something"); + t.insert(&"c37a43e940dfb5baf581a0b82b351d48305fc885".from_hex().unwrap(), b"something"); + t.insert(&"d2571607e241ecf590ed94b12d87c94babe36db6".from_hex().unwrap(), b"something"); + t.insert(&"f735071cbee190d76b704ce68384fc21e389fbe7".from_hex().unwrap(), b"something"); + t.insert(&"04110d816c380812a427968ece99b1c963dfbce6".from_hex().unwrap(), &[]); + t.insert(&"095e7baea6a6c7c4c2dfeb977efac326af552d87".from_hex().unwrap(), &[]); + t.insert(&"0a517d755cebbf66312b30fff713666a9cb917e0".from_hex().unwrap(), &[]); + t.insert(&"24dd378f51adc67a50e339e8031fe9bd4aafab36".from_hex().unwrap(), &[]); + t.insert(&"293f982d000532a7861ab122bdc4bbfd26bf9030".from_hex().unwrap(), &[]); + t.insert(&"2cf5732f017b0cf1b1f13a1478e10239716bf6b5".from_hex().unwrap(), &[]); + t.insert(&"31c640b92c21a1f1465c91070b4b3b4d6854195f".from_hex().unwrap(), &[]); + t.insert(&"37f998764813b136ddf5a754f34063fd03065e36".from_hex().unwrap(), &[]); + t.insert(&"37fa399a749c121f8a15ce77e3d9f9bec8020d7a".from_hex().unwrap(), &[]); + t.insert(&"4f36659fa632310b6ec438dea4085b522a2dd077".from_hex().unwrap(), &[]); + t.insert(&"62c01474f089b07dae603491675dc5b5748f7049".from_hex().unwrap(), &[]); + t.insert(&"729af7294be595a0efd7d891c9e51f89c07950c7".from_hex().unwrap(), &[]); + t.insert(&"83e3e5a16d3b696a0314b30b2534804dd5e11197".from_hex().unwrap(), &[]); + t.insert(&"8703df2417e0d7c59d063caa9583cb10a4d20532".from_hex().unwrap(), &[]); + t.insert(&"8dffcd74e5b5923512916c6a64b502689cfa65e1".from_hex().unwrap(), &[]); + t.insert(&"95a4d7cccb5204733874fa87285a176fe1e9e240".from_hex().unwrap(), &[]); + t.insert(&"99b2fcba8120bedd048fe79f5262a6690ed38c39".from_hex().unwrap(), &[]); + t.insert(&"a4202b8b8afd5354e3e40a219bdc17f6001bf2cf".from_hex().unwrap(), &[]); + t.insert(&"a94f5374fce5edbc8e2a8697c15331677e6ebf0b".from_hex().unwrap(), &[]); + t.insert(&"a9647f4a0a14042d91dc33c0328030a7157c93ae".from_hex().unwrap(), &[]); + t.insert(&"aa6cffe5185732689c18f37a7f86170cb7304c2a".from_hex().unwrap(), &[]); + t.insert(&"aae4a2e3c51c04606dcb3723456e58f3ed214f45".from_hex().unwrap(), &[]); + t.insert(&"c37a43e940dfb5baf581a0b82b351d48305fc885".from_hex().unwrap(), &[]); + t.insert(&"d2571607e241ecf590ed94b12d87c94babe36db6".from_hex().unwrap(), &[]); + t.insert(&"f735071cbee190d76b704ce68384fc21e389fbe7".from_hex().unwrap(), &[]); + assert_eq!(*t.root(), H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap()); } #[test] diff --git a/util/src/triehash.rs b/util/src/triehash.rs index 54ea6f702..f5f4e2123 100644 --- a/util/src/triehash.rs +++ b/util/src/triehash.rs @@ -46,7 +46,8 @@ pub fn ordered_trie_root(input: Vec>) -> H256 { // optimize it later .into_iter() .enumerate() - .fold(BTreeMap::new(), | mut acc, (i, vec) | { acc.insert(rlp::encode(&i).to_vec(), vec); acc }) + .map(|(i, vec)| (rlp::encode(&i).to_vec(), vec)) + .collect::>() // then move them to a vector .into_iter() .map(|(k, v)| (as_nibbles(&k), v) ) @@ -78,10 +79,7 @@ pub fn trie_root(input: Vec<(Vec, Vec)>) -> H256 { let gen_input = input // first put elements into btree to sort them and to remove duplicates .into_iter() - .fold(BTreeMap::new(), | mut acc, (k, v) | { - acc.insert(k, v); - acc - }) + .collect::>() // then move them to a vector .into_iter() .map(|(k, v)| (as_nibbles(&k), v) ) @@ -97,7 +95,7 @@ pub fn trie_root(input: Vec<(Vec, Vec)>) -> H256 { /// use std::str::FromStr; /// use util::triehash::*; /// use util::hash::*; -/// +/// /// fn main() { /// let v = vec![ /// (From::from("doe"), From::from("reindeer")), @@ -113,10 +111,8 @@ pub fn sec_trie_root(input: Vec<(Vec, Vec)>) -> H256 { let gen_input = input // first put elements into btree to sort them and to remove duplicates .into_iter() - .fold(BTreeMap::new(), | mut acc, (k, v) | { - acc.insert(k.sha3().to_vec(), v); - acc - }) + .map(|(k, v)| (k.sha3().to_vec(), v)) + .collect::>() // then move them to a vector .into_iter() .map(|(k, v)| (as_nibbles(&k), v) ) @@ -324,10 +320,16 @@ fn test_hex_prefix_encode() { #[cfg(test)] mod tests { - extern crate json_tests; - use self::json_tests::*; - use hash::*; - use triehash::*; + use std::str::FromStr; + use hash::H256; + use super::trie_root; + + #[test] + fn simple_test() { + assert_eq!(trie_root(vec![ + (b"A".to_vec(), b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_vec()) + ]), H256::from_str("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab").unwrap()); + } #[test] fn test_triehash_out_of_order() { @@ -343,11 +345,4 @@ mod tests { ])); } - #[test] - fn test_triehash_json() { - execute_tests_from_directory::("json-tests/json/trie/*.json", &mut | file, input, output | { - println!("file: {}, output: {:?}", file, output); - assert_eq!(trie_root(input), H256::from_slice(&output)); - }); - } } diff --git a/util/json-tests/Cargo.toml b/util/table/Cargo.toml similarity index 62% rename from util/json-tests/Cargo.toml rename to util/table/Cargo.toml index 3185e2e59..57d47a8e9 100644 --- a/util/json-tests/Cargo.toml +++ b/util/table/Cargo.toml @@ -1,8 +1,6 @@ [package] -name = "json-tests" +name = "table" version = "0.1.0" authors = ["debris "] [dependencies] -rustc-serialize = "0.3" -glob = "*" diff --git a/util/src/table.rs b/util/table/src/lib.rs similarity index 100% rename from util/src/table.rs rename to util/table/src/lib.rs diff --git a/util/using_queue/Cargo.toml b/util/using_queue/Cargo.toml new file mode 100644 index 000000000..0708cbd00 --- /dev/null +++ b/util/using_queue/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "using_queue" +version = "0.1.0" +authors = ["debris "] + +[dependencies] diff --git a/util/src/using_queue.rs b/util/using_queue/src/lib.rs similarity index 99% rename from util/src/using_queue.rs rename to util/using_queue/src/lib.rs index e5e1a5a58..0daa66f59 100644 --- a/util/src/using_queue.rs +++ b/util/using_queue/src/lib.rs @@ -13,6 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . + //! Queue-like datastructure including notion of usage. /// Special queue-like datastructure that includes the notion of @@ -50,7 +51,7 @@ impl UsingQueue where T: Clone { pub fn peek_last_ref(&self) -> Option<&T> { self.pending.as_ref().or(self.in_use.last()) } - + /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); /// this constitutes using the item and will remain in the queue for at least another /// `max_size` invocations of `push()`.