diff --git a/Cargo.lock b/Cargo.lock index e2b4da1eb..7e1aa0d2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,7 +125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bigint" -version = "3.0.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -301,6 +301,7 @@ dependencies = [ "ethcore-util 1.8.0", "ethjson 0.1.0", "rlp 0.2.0", + "rlp_derive 0.1.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -526,6 +527,7 @@ dependencies = [ "price-info 1.7.0", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.0", + "rlp_derive 0.1.0", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -542,7 +544,7 @@ dependencies = [ name = "ethcore-bigint" version = "0.1.3" dependencies = [ - "bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bigint 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -652,6 +654,7 @@ dependencies = [ "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.0", + "rlp_derive 0.1.0", "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -773,7 +776,6 @@ dependencies = [ "ethcore-devtools 1.8.0", "ethcore-logger 1.8.0", "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1217,7 +1219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#4d3ec22c7aba426988a678b489b2791e95283699" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed" dependencies = [ "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1229,7 +1231,7 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#4d3ec22c7aba426988a678b489b2791e95283699" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed" dependencies = [ "hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1242,7 +1244,7 @@ dependencies = [ [[package]] name = "jsonrpc-ipc-server" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#4d3ec22c7aba426988a678b489b2791e95283699" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed" dependencies = [ "bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1255,7 +1257,7 @@ dependencies = [ [[package]] name = "jsonrpc-macros" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#4d3ec22c7aba426988a678b489b2791e95283699" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1265,7 +1267,7 @@ dependencies = [ [[package]] name = "jsonrpc-minihttp-server" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#4d3ec22c7aba426988a678b489b2791e95283699" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed" dependencies = [ "bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1280,7 +1282,7 @@ dependencies = [ [[package]] name = "jsonrpc-pubsub" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#4d3ec22c7aba426988a678b489b2791e95283699" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1290,7 +1292,7 @@ dependencies = [ [[package]] name = "jsonrpc-server-utils" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#4d3ec22c7aba426988a678b489b2791e95283699" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed" dependencies = [ "bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1303,7 +1305,7 @@ dependencies = [ [[package]] name = "jsonrpc-tcp-server" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#4d3ec22c7aba426988a678b489b2791e95283699" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed" dependencies = [ "bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1317,7 +1319,7 @@ dependencies = [ [[package]] name = "jsonrpc-ws-server" version = "7.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#4d3ec22c7aba426988a678b489b2791e95283699" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -1860,6 +1862,7 @@ dependencies = [ "fetch 0.1.0", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1971,6 +1974,7 @@ dependencies = [ "fetch 0.1.0", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", @@ -2053,7 +2057,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/paritytech/js-precompiled.git#dd9b92d9d8c244678e15163347f9adb2e2560959" +source = "git+https://github.com/paritytech/js-precompiled.git#416ced84c23b1a776d53ee4a3023eb4eb4736cf8" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2305,7 +2309,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.3.10" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2396,6 +2400,15 @@ dependencies = [ "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rlp_derive" +version = "0.1.0" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.2.0", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rocksdb" version = "0.4.5" @@ -2453,7 +2466,7 @@ dependencies = [ name = "rpc-cli" version = "1.4.0" dependencies = [ - "bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bigint 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-util 1.8.0", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "parity-rpc 1.8.0", @@ -2588,7 +2601,7 @@ name = "serde_derive" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2730,7 +2743,7 @@ name = "syn" version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3231,7 +3244,7 @@ dependencies = [ "checksum backtrace-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0d842ea781ce92be2bf78a9b38883948542749640b8378b3b2f03d1fd9f1ff" "checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1" "checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c" -"checksum bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0673c930652d3d4d6dcd5c45b5db4fa5f8f33994d7323618c43c083b223e8c" +"checksum bigint 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b45beaa0727835a98df09295d4250afc52c3f7d375d560997ae942c95b98ceb" "checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e" "checksum bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6e1e6fb1c9e3d6fcdec57216a74eaa03e41f52a22f13a16438251d8e88b89da" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" @@ -3384,7 +3397,7 @@ dependencies = [ "checksum quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29cec87bc2816766d7e4168302d505dd06b0a825aed41b00633d296e922e02dd" "checksum quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0aad603e8d7fb67da22dbdf1f4b826ce8829e406124109e73cf1b2454b93a71c" "checksum quine-mc_cluskey 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6683b0e23d80813b1a535841f0048c1537d3f86d63c999e8373b39a9b0eb74a" -"checksum quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6732e32663c9c271bfc7c1823486b471f18c47a2dbf87c066897b7b51afc83be" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5" "checksum rayon 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c83adcb08e5b922e804fe1918142b422602ef11f2fd670b0b52218cb5984a20" "checksum rayon-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "767d91bacddf07d442fe39257bf04fd95897d1c47c545d009f6beb03efd038f8" diff --git a/README.md b/README.md index 16758bc27..24178edbe 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Parity's current release is 1.7. You can download it at https://github.com/parit ## Build dependencies -**Parity requires Rust version 1.18.0 to build** +**Parity requires Rust version 1.19.0 to build** We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this: diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 75e15e396..143bbe30f 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -27,6 +27,7 @@ time = "0.1.35" unicase = "1.3" url = "1.0" zip = { version = "0.1", default-features = false } +itertools = "0.5" jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } diff --git a/dapps/src/apps/mod.rs b/dapps/src/apps/mod.rs index 376b8a36f..c38c6784a 100644 --- a/dapps/src/apps/mod.rs +++ b/dapps/src/apps/mod.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::BTreeMap; use std::path::PathBuf; use std::sync::Arc; @@ -30,8 +29,8 @@ use {WebProxyTokens, ParentFrameSettings}; mod app; mod cache; -mod fs; mod ui; +pub mod fs; pub mod fetcher; pub mod manifest; @@ -64,9 +63,10 @@ pub fn all_endpoints( web_proxy_tokens: Arc, remote: Remote, fetch: F, -) -> Endpoints { +) -> (Vec, Endpoints) { // fetch fs dapps at first to avoid overwriting builtins - let mut pages = fs::local_endpoints(dapps_path, embeddable.clone()); + let mut pages = fs::local_endpoints(dapps_path.clone(), embeddable.clone()); + let local_endpoints: Vec = pages.keys().cloned().collect(); for path in extra_dapps { if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone()) { pages.insert(id, endpoint); @@ -80,10 +80,10 @@ pub fn all_endpoints( pages.insert("proxy".into(), ProxyPac::boxed(embeddable.clone(), dapps_domain.to_owned())); pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone())); - Arc::new(pages) + (local_endpoints, pages) } -fn insert(pages: &mut BTreeMap>, id: &str, embed_at: Embeddable) { +fn insert(pages: &mut Endpoints, id: &str, embed_at: Embeddable) { pages.insert(id.to_owned(), Box::new(match embed_at { Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address), Embeddable::No => PageEndpoint::new(T::default()), diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs index ea8fd0a38..ea5825b74 100644 --- a/dapps/src/endpoint.rs +++ b/dapps/src/endpoint.rs @@ -16,7 +16,6 @@ //! URL Endpoint traits -use std::sync::Arc; use std::collections::BTreeMap; use hyper::{self, server, net}; @@ -39,7 +38,7 @@ pub struct EndpointInfo { pub icon_url: String, } -pub type Endpoints = Arc>>; +pub type Endpoints = BTreeMap>; pub type Handler = server::Handler + Send; pub trait Endpoint : Send + Sync { diff --git a/dapps/src/handlers/mod.rs b/dapps/src/handlers/mod.rs index 7937ce667..a8beabe84 100644 --- a/dapps/src/handlers/mod.rs +++ b/dapps/src/handlers/mod.rs @@ -31,8 +31,7 @@ pub use self::redirect::Redirection; pub use self::streaming::StreamingHandler; use std::iter; -use util::Itertools; - +use itertools::Itertools; use url::Url; use hyper::{server, header, net, uri}; use {apps, address, Embeddable}; @@ -67,10 +66,20 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd // Allow fonts from data: and HTTPS. b"font-src 'self' data: https:;".to_vec(), // Allow inline scripts and scripts eval (webpack/jsconsole) - b"script-src 'self' 'unsafe-inline' 'unsafe-eval';".to_vec(), - // Same restrictions as script-src (fallback) with additional + { + let script_src = embeddable_on.as_ref() + .map(|e| e.extra_script_src.iter() + .map(|&(ref host, port)| address(host, port)) + .join(" ") + ).unwrap_or_default(); + format!( + "script-src 'self' 'unsafe-inline' 'unsafe-eval' {};", + script_src + ).into_bytes() + }, + // Same restrictions as script-src with additional // blob: that is required for camera access (worker) - b"worker-src 'self' 'unsafe-inline' 'unsafe-eval' blob: ;".to_vec(), + b"worker-src 'self' 'unsafe-inline' 'unsafe-eval' https: blob:;".to_vec(), // Restrict everything else to the same origin. b"default-src 'self';".to_vec(), // Run in sandbox mode (although it's not fully safe since we allow same-origin and script) @@ -90,7 +99,7 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd .into_iter() .chain(embed.extra_embed_on .iter() - .map(|&(ref host, port)| format!("{}:{}", host, port)) + .map(|&(ref host, port)| address(host, port)) ); let ancestors = if embed.host == "127.0.0.1" { diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index f34c24cae..135f0bb36 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -22,6 +22,7 @@ extern crate base32; extern crate futures; extern crate futures_cpupool; +extern crate itertools; extern crate linked_hash_map; extern crate mime_guess; extern crate ntp; @@ -69,9 +70,11 @@ mod web; #[cfg(test)] mod tests; +use std::collections::HashMap; +use std::mem; use std::path::PathBuf; use std::sync::Arc; -use std::collections::HashMap; +use util::RwLock; use jsonrpc_http_server::{self as http, hyper, Origin}; @@ -101,31 +104,54 @@ impl WebProxyTokens for F where F: Fn(String) -> Option + Send + Sync } /// Current supported endpoints. +#[derive(Default, Clone)] pub struct Endpoints { - endpoints: endpoint::Endpoints, + local_endpoints: Arc>>, + endpoints: Arc>, + dapps_path: PathBuf, + embeddable: Option, } impl Endpoints { /// Returns a current list of app endpoints. pub fn list(&self) -> Vec { - self.endpoints.iter().filter_map(|(ref k, ref e)| { + self.endpoints.read().iter().filter_map(|(ref k, ref e)| { e.info().map(|ref info| apps::App::from_info(k, info)) }).collect() } + + /// Check for any changes in the local dapps folder and update. + pub fn refresh_local_dapps(&self) { + let new_local = apps::fs::local_endpoints(&self.dapps_path, self.embeddable.clone()); + let old_local = mem::replace(&mut *self.local_endpoints.write(), new_local.keys().cloned().collect()); + let (_, to_remove): (_, Vec<_>) = old_local + .into_iter() + .partition(|k| new_local.contains_key(&k.clone())); + + let mut endpoints = self.endpoints.write(); + // remove the dead dapps + for k in to_remove { + endpoints.remove(&k); + } + // new dapps to be added + for (k, v) in new_local { + if !endpoints.contains_key(&k) { + endpoints.insert(k, v); + } + } + } } /// Dapps server as `jsonrpc-http-server` request middleware. pub struct Middleware { + endpoints: Endpoints, router: router::Router, - endpoints: endpoint::Endpoints, } impl Middleware { /// Get local endpoints handle. - pub fn endpoints(&self) -> Endpoints { - Endpoints { - endpoints: self.endpoints.clone(), - } + pub fn endpoints(&self) -> &Endpoints { + &self.endpoints } /// Creates new middleware for UI server. @@ -164,8 +190,8 @@ impl Middleware { ); Middleware { - router: router, endpoints: Default::default(), + router: router, } } @@ -176,6 +202,7 @@ impl Middleware { remote: Remote, ui_address: Option<(String, u16)>, extra_embed_on: Vec<(String, u16)>, + extra_script_src: Vec<(String, u16)>, dapps_path: PathBuf, extra_dapps: Vec, dapps_domain: &str, @@ -184,15 +211,15 @@ impl Middleware { web_proxy_tokens: Arc, fetch: F, ) -> Self { - let embeddable = as_embeddable(ui_address, extra_embed_on, dapps_domain); + let embeddable = as_embeddable(ui_address, extra_embed_on, extra_script_src, dapps_domain); let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new( hash_fetch::urlhint::URLHintContract::new(registrar), sync_status.clone(), remote.clone(), fetch.clone(), ).embeddable_on(embeddable.clone()).allow_dapps(true)); - let endpoints = apps::all_endpoints( - dapps_path, + let (local_endpoints, endpoints) = apps::all_endpoints( + dapps_path.clone(), extra_dapps, dapps_domain, embeddable.clone(), @@ -200,6 +227,12 @@ impl Middleware { remote.clone(), fetch.clone(), ); + let endpoints = Endpoints { + endpoints: Arc::new(RwLock::new(endpoints)), + dapps_path, + local_endpoints: Arc::new(RwLock::new(local_endpoints)), + embeddable: embeddable.clone(), + }; let special = { let mut special = special_endpoints( @@ -225,8 +258,8 @@ impl Middleware { ); Middleware { - router: router, - endpoints: endpoints, + endpoints, + router, } } } @@ -263,12 +296,14 @@ fn address(host: &str, port: u16) -> String { fn as_embeddable( ui_address: Option<(String, u16)>, extra_embed_on: Vec<(String, u16)>, + extra_script_src: Vec<(String, u16)>, dapps_domain: &str, ) -> Option { ui_address.map(|(host, port)| ParentFrameSettings { host, port, extra_embed_on, + extra_script_src, dapps_domain: dapps_domain.to_owned(), }) } @@ -289,8 +324,10 @@ pub struct ParentFrameSettings { pub host: String, /// Port pub port: u16, - /// Additional pages the pages can be embedded on. + /// Additional URLs the dapps can be embedded on. pub extra_embed_on: Vec<(String, u16)>, + /// Additional URLs the dapp scripts can be loaded from. + pub extra_script_src: Vec<(String, u16)>, /// Dapps Domain (web3.site) pub dapps_domain: String, } diff --git a/dapps/src/router.rs b/dapps/src/router.rs index 5cf92ff7e..2b74d51df 100644 --- a/dapps/src/router.rs +++ b/dapps/src/router.rs @@ -28,7 +28,8 @@ use jsonrpc_http_server as http; use apps; use apps::fetcher::Fetcher; -use endpoint::{Endpoint, Endpoints, EndpointPath, Handler}; +use endpoint::{Endpoint, EndpointPath, Handler}; +use Endpoints; use handlers; use Embeddable; @@ -50,26 +51,27 @@ pub struct Router { dapps_domain: String, } -impl http::RequestMiddleware for Router { - fn on_request(&self, req: &server::Request, control: &Control) -> http::RequestMiddlewareAction { +impl Router { + fn resolve_request(&self, req: &server::Request, control: Control, refresh_dapps: bool) -> (bool, Option>) { // Choose proper handler depending on path / domain let url = handlers::extract_url(req); let endpoint = extract_endpoint(&url, &self.dapps_domain); let referer = extract_referer_endpoint(req, &self.dapps_domain); let is_utils = endpoint.1 == SpecialEndpoint::Utils; - let is_origin_set = req.headers().get::().is_some(); let is_get_request = *req.method() == hyper::Method::Get; let is_head_request = *req.method() == hyper::Method::Head; + let has_dapp = |dapp: &str| self.endpoints + .as_ref() + .map_or(false, |endpoints| endpoints.endpoints.read().contains_key(dapp)); trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req); - - let control = control.clone(); debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint); - let handler: Option> = match (endpoint.0, endpoint.1, referer) { + + (is_utils, match (endpoint.0, endpoint.1, referer) { // Handle invalid web requests that we can recover from (ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url))) if referer.app_id == apps::WEB_PATH - && self.endpoints.as_ref().map(|ep| ep.contains_key(apps::WEB_PATH)).unwrap_or(false) + && has_dapp(apps::WEB_PATH) && !is_web_endpoint(path) => { @@ -88,11 +90,13 @@ impl http::RequestMiddleware for Router { .map(|special| special.to_async_handler(path.clone().unwrap_or_default(), control)) }, // Then delegate to dapp - (Some(ref path), _, _) if self.endpoints.as_ref().map(|ep| ep.contains_key(&path.app_id)).unwrap_or(false) => { + (Some(ref path), _, _) if has_dapp(&path.app_id) => { trace!(target: "dapps", "Resolving to local/builtin dapp."); Some(self.endpoints .as_ref() .expect("endpoints known to be set; qed") + .endpoints + .read() .get(&path.app_id) .expect("endpoints known to contain key; qed") .to_async_handler(path.clone(), control)) @@ -110,13 +114,19 @@ impl http::RequestMiddleware for Router { => { trace!(target: "dapps", "Resolving to 404."); - Some(Box::new(handlers::ContentHandler::error( - hyper::StatusCode::NotFound, - "404 Not Found", - "Requested content was not found.", - None, - self.embeddable_on.clone(), - ))) + if refresh_dapps { + debug!(target: "dapps", "Refreshing dapps and re-trying."); + self.endpoints.as_ref().map(|endpoints| endpoints.refresh_local_dapps()); + return self.resolve_request(req, control, false) + } else { + Some(Box::new(handlers::ContentHandler::error( + hyper::StatusCode::NotFound, + "404 Not Found", + "Requested content was not found.", + None, + self.embeddable_on.clone(), + ))) + } }, // Any other GET|HEAD requests to home page. _ if (is_get_request || is_head_request) && self.special.contains_key(&SpecialEndpoint::Home) => { @@ -130,8 +140,15 @@ impl http::RequestMiddleware for Router { trace!(target: "dapps", "Resolving to RPC call."); None } - }; + }) + } +} +impl http::RequestMiddleware for Router { + fn on_request(&self, req: &server::Request, control: &Control) -> http::RequestMiddlewareAction { + let control = control.clone(); + let is_origin_set = req.headers().get::().is_some(); + let (is_utils, handler) = self.resolve_request(req, control, self.endpoints.is_some()); match handler { Some(handler) => http::RequestMiddlewareAction::Respond { should_validate_hosts: !is_utils, diff --git a/dapps/src/tests/fetch.rs b/dapps/src/tests/fetch.rs index 8abc86196..f12323155 100644 --- a/dapps/src/tests/fetch.rs +++ b/dapps/src/tests/fetch.rs @@ -39,7 +39,7 @@ fn should_resolve_dapp() { // then response.assert_status("HTTP/1.1 404 Not Found"); - assert_eq!(registrar.calls.lock().len(), 2); + assert_eq!(registrar.calls.lock().len(), 4); assert_security_headers_for_embed(&response.headers); } diff --git a/dapps/src/tests/helpers/mod.rs b/dapps/src/tests/helpers/mod.rs index 38dd82de6..30d3ba8f9 100644 --- a/dapps/src/tests/helpers/mod.rs +++ b/dapps/src/tests/helpers/mod.rs @@ -260,6 +260,7 @@ impl Server { remote, signer_address, vec![], + vec![], dapps_path, extra_dapps, DAPPS_DOMAIN.into(), diff --git a/dapps/src/tests/redirection.rs b/dapps/src/tests/redirection.rs index 1e9b039e2..81d3ec76c 100644 --- a/dapps/src/tests/redirection.rs +++ b/dapps/src/tests/redirection.rs @@ -204,4 +204,3 @@ fn should_serve_utils() { assert_eq!(response.body.contains("function(){"), true); assert_security_headers(&response.headers); } - diff --git a/dapps/src/web.rs b/dapps/src/web.rs index 637a5287e..5222f51b5 100644 --- a/dapps/src/web.rs +++ b/dapps/src/web.rs @@ -241,5 +241,3 @@ impl server::Handler for WebHandler { } } } - - diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 0b4abc1bb..c49e31898 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -47,6 +47,7 @@ num_cpus = "1.2" price-info = { path = "../price-info" } rand = "0.3" rlp = { path = "../util/rlp" } +rlp_derive = { path = "../util/rlp_derive" } rust-crypto = "0.2.34" rustc-hex = "1.0" semver = "0.6" diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index b652050d2..9fda78f47 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -21,6 +21,7 @@ ethcore-devtools = { path = "../../devtools" } evm = { path = "../evm" } vm = { path = "../vm" } rlp = { path = "../../util/rlp" } +rlp_derive = { path = "../../util/rlp_derive" } time = "0.1" smallvec = "0.4" futures = "0.1" diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 3f0e50584..d1cdfe9aa 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -100,8 +100,8 @@ pub trait LightChainClient: Send + Sync { /// Get an iterator over a block and its ancestry. fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box + 'a>; - /// Get the signing network ID. - fn signing_network_id(&self) -> Option; + /// Get the signing chain ID. + fn signing_chain_id(&self) -> Option; /// Get environment info for execution at a given block. /// Fails if that block's header is not stored. @@ -260,9 +260,9 @@ impl Client { self.chain.ancestry_iter(start) } - /// Get the signing network id. - pub fn signing_network_id(&self) -> Option { - self.engine.signing_network_id(&self.latest_env_info()) + /// Get the signing chain id. + pub fn signing_chain_id(&self) -> Option { + self.engine.signing_chain_id(&self.latest_env_info()) } /// Flush the header queue. @@ -448,8 +448,8 @@ impl LightChainClient for Client { Box::new(Client::ancestry_iter(self, start)) } - fn signing_network_id(&self) -> Option { - Client::signing_network_id(self) + fn signing_chain_id(&self) -> Option { + Client::signing_chain_id(self) } fn env_info(&self, id: BlockId) -> Option { diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index b172134cf..c2ab483d2 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -76,6 +76,8 @@ extern crate futures; extern crate itertools; extern crate rand; extern crate rlp; +#[macro_use] +extern crate rlp_derive; extern crate serde; extern crate smallvec; extern crate stats; diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs index ff4a32535..51f916b15 100644 --- a/ethcore/light/src/types/request/mod.rs +++ b/ethcore/light/src/types/request/mod.rs @@ -650,7 +650,7 @@ pub mod header { use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; /// Potentially incomplete headers request. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Incomplete { /// Start block. pub start: Field, @@ -662,27 +662,6 @@ pub mod header { pub reverse: bool, } - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - start: rlp.val_at(0)?, - skip: rlp.val_at(1)?, - max: rlp.val_at(2)?, - reverse: rlp.val_at(3)? - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4) - .append(&self.start) - .append(&self.skip) - .append(&self.max) - .append(&self.reverse); - } - } - impl super::IncompleteRequest for Incomplete { type Complete = Complete; type Response = Response; @@ -784,26 +763,12 @@ pub mod header_proof { use util::{Bytes, U256, H256}; /// Potentially incomplete header proof request. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Incomplete { /// Block number. pub num: Field, } - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - num: rlp.val_at(0)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(1).append(&self.num); - } - } - impl super::IncompleteRequest for Incomplete { type Complete = Complete; type Response = Response; @@ -889,30 +854,15 @@ pub mod header_proof { /// Request and response for transaction index. pub mod transaction_index { use super::{Field, NoSuchOutput, OutputKind, Output}; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; use util::H256; /// Potentially incomplete transaction index request. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Incomplete { /// Transaction hash to get index for. pub hash: Field, } - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - hash: rlp.val_at(0)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(1).append(&self.hash); - } - } - impl super::IncompleteRequest for Incomplete { type Complete = Complete; type Response = Response; @@ -959,7 +909,7 @@ pub mod transaction_index { } /// The output of a request for transaction index. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Response { /// Block number. pub num: u64, @@ -976,55 +926,21 @@ pub mod transaction_index { f(1, Output::Hash(self.hash)); } } - - impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Response { - num: rlp.val_at(0)?, - hash: rlp.val_at(1)?, - index: rlp.val_at(2)?, - }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3) - .append(&self.num) - .append(&self.hash) - .append(&self.index); - } - } } /// Request and response for block receipts pub mod block_receipts { use super::{Field, NoSuchOutput, OutputKind, Output}; use ethcore::receipt::Receipt; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; use util::H256; /// Potentially incomplete block receipts request. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Incomplete { /// Block hash to get receipts for. pub hash: Field, } - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - hash: rlp.val_at(0)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(1).append(&self.hash); - } - } - impl super::IncompleteRequest for Incomplete { type Complete = Complete; type Response = Response; @@ -1068,7 +984,7 @@ pub mod block_receipts { } /// The output of a request for block receipts. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper)] pub struct Response { /// The block receipts. pub receipts: Vec @@ -1078,20 +994,6 @@ pub mod block_receipts { /// Fill reusable outputs by providing them to the function. fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} } - - impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Response { - receipts: rlp.as_list()?, - }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.append_list(&self.receipts); - } - } } /// Request and response for a block body @@ -1102,26 +1004,12 @@ pub mod block_body { use util::H256; /// Potentially incomplete block body request. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Incomplete { /// Block hash to get receipts for. pub hash: Field, } - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - hash: rlp.val_at(0)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(1).append(&self.hash); - } - } - impl super::IncompleteRequest for Incomplete { type Complete = Complete; type Response = Response; @@ -1201,11 +1089,10 @@ pub mod block_body { /// A request for an account proof. pub mod account { use super::{Field, NoSuchOutput, OutputKind, Output}; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; use util::{Bytes, U256, H256}; /// Potentially incomplete request for an account proof. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Incomplete { /// Block hash to request state proof for. pub block_hash: Field, @@ -1213,23 +1100,6 @@ pub mod account { pub address_hash: Field, } - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - block_hash: rlp.val_at(0)?, - address_hash: rlp.val_at(1)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2) - .append(&self.block_hash) - .append(&self.address_hash); - } - } - impl super::IncompleteRequest for Incomplete { type Complete = Complete; type Response = Response; @@ -1292,7 +1162,7 @@ pub mod account { } /// The output of a request for an account state proof. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Response { /// Inclusion/exclusion proof pub proof: Vec, @@ -1313,39 +1183,15 @@ pub mod account { f(1, Output::Hash(self.storage_root)); } } - - impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Response { - proof: rlp.list_at(0)?, - nonce: rlp.val_at(1)?, - balance: rlp.val_at(2)?, - code_hash: rlp.val_at(3)?, - storage_root: rlp.val_at(4)? - }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(5) - .append_list::,_>(&self.proof[..]) - .append(&self.nonce) - .append(&self.balance) - .append(&self.code_hash) - .append(&self.storage_root); - } - } } /// A request for a storage proof. pub mod storage { use super::{Field, NoSuchOutput, OutputKind, Output}; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; use util::{Bytes, H256}; /// Potentially incomplete request for an storage proof. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Incomplete { /// Block hash to request state proof for. pub block_hash: Field, @@ -1355,25 +1201,6 @@ pub mod storage { pub key_hash: Field, } - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - block_hash: rlp.val_at(0)?, - address_hash: rlp.val_at(1)?, - key_hash: rlp.val_at(2)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3) - .append(&self.block_hash) - .append(&self.address_hash) - .append(&self.key_hash); - } - } - impl super::IncompleteRequest for Incomplete { type Complete = Complete; type Response = Response; @@ -1450,7 +1277,7 @@ pub mod storage { } /// The output of a request for an account state proof. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Response { /// Inclusion/exclusion proof pub proof: Vec, @@ -1464,33 +1291,15 @@ pub mod storage { f(0, Output::Hash(self.value)); } } - - impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Response { - proof: rlp.list_at(0)?, - value: rlp.val_at(1)?, - }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2) - .append_list::,_>(&self.proof[..]) - .append(&self.value); - } - } } /// A request for contract code. pub mod contract_code { use super::{Field, NoSuchOutput, OutputKind, Output}; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; use util::{Bytes, H256}; /// Potentially incomplete contract code request. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Incomplete { /// The block hash to request the state for. pub block_hash: Field, @@ -1498,23 +1307,6 @@ pub mod contract_code { pub code_hash: Field, } - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - block_hash: rlp.val_at(0)?, - code_hash: rlp.val_at(1)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2) - .append(&self.block_hash) - .append(&self.code_hash); - } - } - impl super::IncompleteRequest for Incomplete { type Complete = Complete; type Response = Response; @@ -1573,7 +1365,7 @@ pub mod contract_code { } /// The output of a request for - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper)] pub struct Response { /// The requested code. pub code: Bytes, @@ -1583,21 +1375,6 @@ pub mod contract_code { /// Fill reusable outputs by providing them to the function. fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} } - - impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { - - Ok(Response { - code: rlp.as_val()?, - }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.append(&self.code); - } - } } /// A request for proof of execution. @@ -1608,7 +1385,7 @@ pub mod execution { use util::{Bytes, Address, U256, H256, DBValue}; /// Potentially incomplete execution proof request. - #[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Incomplete { /// The block hash to request the state for. pub block_hash: Field, @@ -1626,38 +1403,6 @@ pub mod execution { pub data: Bytes, } - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - block_hash: rlp.val_at(0)?, - from: rlp.val_at(1)?, - action: rlp.val_at(2)?, - gas: rlp.val_at(3)?, - gas_price: rlp.val_at(4)?, - value: rlp.val_at(5)?, - data: rlp.val_at(6)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(7) - .append(&self.block_hash) - .append(&self.from); - - match self.action { - Action::Create => s.append_empty_data(), - Action::Call(ref addr) => s.append(addr), - }; - - s.append(&self.gas) - .append(&self.gas_price) - .append(&self.value) - .append(&self.data); - } - } - impl super::IncompleteRequest for Incomplete { type Complete = Complete; type Response = Response; diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index 249ca40af..752cec964 100755 --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -519,6 +519,11 @@ impl AccountProvider { } } + /// Returns account public key. + pub fn account_public(&self, address: Address, password: &str) -> Result { + self.sstore.public(&self.sstore.account_ref(&address)?, password) + } + /// Returns each account along with name and meta. pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> { self.sstore.set_name(&self.sstore.account_ref(&address)?, name)?; @@ -697,6 +702,13 @@ impl AccountProvider { Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?) } + /// Agree on shared key. + pub fn agree(&self, address: Address, password: Option, other_public: &Public) -> Result { + let account = self.sstore.account_ref(&address)?; + let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; + Ok(self.sstore.agree(&account, &password, other_public)?) + } + /// Returns the underlying `SecretStore` reference if one exists. pub fn list_geth_accounts(&self, testnet: bool) -> Vec
{ self.sstore.list_geth_accounts(testnet).into_iter().map(|a| Address::from(a).into()).collect() diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index bd0defa47..001df861c 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -19,6 +19,7 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::mem; +use itertools::Itertools; use bloomchain as bc; use util::*; use rlp::*; diff --git a/ethcore/src/blockchain/extras.rs b/ethcore/src/blockchain/extras.rs index ed3a5009b..0e2396f8c 100644 --- a/ethcore/src/blockchain/extras.rs +++ b/ethcore/src/blockchain/extras.rs @@ -25,11 +25,9 @@ use engines::epoch::{Transition as EpochTransition}; use header::BlockNumber; use receipt::Receipt; -use rlp::*; -use util::*; +use util::{HeapSizeOf, H256, H264, U256}; use util::kvdb::PREFIX_LEN as DB_PREFIX_LEN; - /// Represents index of extra data in database #[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)] pub enum ExtrasIndex { @@ -184,7 +182,7 @@ impl Key for u64 { } /// Familial details concerning a block -#[derive(Debug, Clone)] +#[derive(Debug, Clone, RlpEncodable, RlpDecodable)] pub struct BlockDetails { /// Block number pub number: BlockNumber, @@ -202,30 +200,8 @@ impl HeapSizeOf for BlockDetails { } } -impl Decodable for BlockDetails { - fn decode(rlp: &UntrustedRlp) -> Result { - let details = BlockDetails { - number: rlp.val_at(0)?, - total_difficulty: rlp.val_at(1)?, - parent: rlp.val_at(2)?, - children: rlp.list_at(3)?, - }; - Ok(details) - } -} - -impl Encodable for BlockDetails { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4); - s.append(&self.number); - s.append(&self.total_difficulty); - s.append(&self.parent); - s.append_list(&self.children); - } -} - /// Represents address of certain transaction within block -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, RlpEncodable, RlpDecodable)] pub struct TransactionAddress { /// Block hash pub block_hash: H256, @@ -237,27 +213,8 @@ impl HeapSizeOf for TransactionAddress { fn heap_size_of_children(&self) -> usize { 0 } } -impl Decodable for TransactionAddress { - fn decode(rlp: &UntrustedRlp) -> Result { - let tx_address = TransactionAddress { - block_hash: rlp.val_at(0)?, - index: rlp.val_at(1)?, - }; - - Ok(tx_address) - } -} - -impl Encodable for TransactionAddress { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2); - s.append(&self.block_hash); - s.append(&self.index); - } -} - /// Contains all block receipts. -#[derive(Clone)] +#[derive(Clone, RlpEncodableWrapper, RlpDecodableWrapper)] pub struct BlockReceipts { pub receipts: Vec, } @@ -270,20 +227,6 @@ impl BlockReceipts { } } -impl Decodable for BlockReceipts { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(BlockReceipts { - receipts: rlp.as_list()?, - }) - } -} - -impl Encodable for BlockReceipts { - fn rlp_append(&self, s: &mut RlpStream) { - s.append_list(&self.receipts); - } -} - impl HeapSizeOf for BlockReceipts { fn heap_size_of_children(&self) -> usize { self.receipts.heap_size_of_children() @@ -291,27 +234,12 @@ impl HeapSizeOf for BlockReceipts { } /// Candidate transitions to an epoch with specific number. -#[derive(Clone)] +#[derive(Clone, RlpEncodable, RlpDecodable)] pub struct EpochTransitions { pub number: u64, pub candidates: Vec, } -impl Encodable for EpochTransitions { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2).append(&self.number).append_list(&self.candidates); - } -} - -impl Decodable for EpochTransitions { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(EpochTransitions { - number: rlp.val_at(0)?, - candidates: rlp.list_at(1)?, - }) - } -} - #[cfg(test)] mod tests { use rlp::*; diff --git a/ethcore/src/blooms/bloom.rs b/ethcore/src/blooms/bloom.rs index cac9ff448..c79091cb4 100644 --- a/ethcore/src/blooms/bloom.rs +++ b/ethcore/src/blooms/bloom.rs @@ -15,12 +15,11 @@ // along with Parity. If not, see . use bloomchain as bc; -use rlp::*; use util::HeapSizeOf; use basic_types::LogBloom; /// Helper structure representing bloom of the trace. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, RlpEncodableWrapper, RlpDecodableWrapper)] pub struct Bloom(LogBloom); impl From for Bloom { @@ -43,18 +42,6 @@ impl Into for Bloom { } } -impl Decodable for Bloom { - fn decode(rlp: &UntrustedRlp) -> Result { - LogBloom::decode(rlp).map(Bloom) - } -} - -impl Encodable for Bloom { - fn rlp_append(&self, s: &mut RlpStream) { - Encodable::rlp_append(&self.0, s) - } -} - impl HeapSizeOf for Bloom { fn heap_size_of_children(&self) -> usize { 0 diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 7141c7bdb..ff09a7b2b 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -20,9 +20,10 @@ use std::sync::{Arc, Weak}; use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::time::{Instant}; use time::precise_time_ns; +use itertools::Itertools; // util -use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock, MutexGuard, Hashable}; +use util::{Bytes, PerfTimer, Mutex, RwLock, MutexGuard, Hashable}; use util::{journaldb, DBValue, TrieFactory, Trie}; use util::{U256, H256, Address, H2048}; use util::trie::TrieSpec; @@ -1112,20 +1113,9 @@ impl Client { }.fake_sign(from) } - fn do_call(&self, env_info: &EnvInfo, state: &mut State, increase_balance: bool, t: &SignedTransaction, analytics: CallAnalytics) -> Result { + fn do_call(&self, env_info: &EnvInfo, state: &mut State, t: &SignedTransaction, analytics: CallAnalytics) -> Result { let original_state = if analytics.state_diffing { Some(state.clone()) } else { None }; - // give the sender a sufficient balance (if calling in pending block) - if increase_balance { - let sender = t.sender(); - let balance = state.balance(&sender).map_err(ExecutionError::from)?; - let needed_balance = t.value + t.gas * t.gas_price; - if balance < needed_balance { - state.add_balance(&sender, &(needed_balance - balance), state::CleanupMode::NoEmpty) - .map_err(ExecutionError::from)?; - } - } - let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; let mut ret = Executive::new(state, env_info, &*self.engine).transact_virtual(t, options)?; @@ -1167,7 +1157,7 @@ impl BlockChainClient for Client { // that's just a copy of the state. let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; - self.do_call(&env_info, &mut state, block == BlockId::Pending, transaction, analytics) + self.do_call(&env_info, &mut state, transaction, analytics) } fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result, CallError> { @@ -1179,7 +1169,7 @@ impl BlockChainClient for Client { let mut results = Vec::with_capacity(transactions.len()); for &(ref t, analytics) in transactions { - let ret = self.do_call(&env_info, &mut state, block == BlockId::Pending, t, analytics)?; + let ret = self.do_call(&env_info, &mut state, t, analytics)?; env_info.gas_used = ret.cumulative_gas_used; results.push(ret); } @@ -1730,8 +1720,8 @@ impl BlockChainClient for Client { } } - fn signing_network_id(&self) -> Option { - self.engine.signing_network_id(&self.latest_env_info()) + fn signing_chain_id(&self) -> Option { + self.engine.signing_chain_id(&self.latest_env_info()) } fn block_extra_info(&self, id: BlockId) -> Option> { @@ -1770,9 +1760,9 @@ impl BlockChainClient for Client { value: U256::zero(), data: data, }; - let network_id = self.engine.signing_network_id(&self.latest_env_info()); - let signature = self.engine.sign(transaction.hash(network_id))?; - let signed = SignedTransaction::new(transaction.with_signature(signature, network_id))?; + let chain_id = self.engine.signing_chain_id(&self.latest_env_info()); + let signature = self.engine.sign(transaction.hash(chain_id))?; + let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; self.miner.import_own_transaction(self, signed.into()) } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 2a205868d..0e7e53e43 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -20,6 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder}; use std::sync::Arc; use std::collections::{HashMap, BTreeMap}; use std::mem; +use itertools::Itertools; use rustc_hex::FromHex; use util::*; use rlp::*; @@ -733,7 +734,7 @@ impl BlockChainClient for TestBlockChainClient { self.miner.ready_transactions(info.best_block_number, info.best_block_timestamp) } - fn signing_network_id(&self) -> Option { None } + fn signing_chain_id(&self) -> Option { None } fn mode(&self) -> Mode { Mode::Active } @@ -764,9 +765,9 @@ impl BlockChainClient for TestBlockChainClient { value: U256::default(), data: data, }; - let network_id = Some(self.spec.params().network_id); - let sig = self.spec.engine.sign(transaction.hash(network_id)).unwrap(); - let signed = SignedTransaction::new(transaction.with_signature(sig, network_id)).unwrap(); + let chain_id = Some(self.spec.chain_id()); + let sig = self.spec.engine.sign(transaction.hash(chain_id)).unwrap(); + let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap(); self.miner.import_own_transaction(self, signed.into()) } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 8e1bd8b18..d7b7bba44 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . use std::collections::BTreeMap; +use itertools::Itertools; use block::{OpenBlock, SealedBlock, ClosedBlock}; use blockchain::TreeRoute; @@ -33,7 +34,7 @@ use trace::LocalizedTrace; use transaction::{LocalizedTransaction, PendingTransaction, SignedTransaction}; use verification::queue::QueueInfo as BlockQueueInfo; -use util::{U256, Address, H256, H2048, Bytes, Itertools}; +use util::{U256, Address, H256, H2048, Bytes}; use util::hashdb::DBValue; use types::ids::*; @@ -239,8 +240,8 @@ pub trait BlockChainClient : Sync + Send { corpus.into() } - /// Get the preferred network ID to sign on - fn signing_network_id(&self) -> Option; + /// Get the preferred chain ID to sign on + fn signing_chain_id(&self) -> Option; /// Get the mode. fn mode(&self) -> Mode; diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index e7284bfbe..194fdd8fe 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -804,9 +804,9 @@ impl Engine for AuthorityRound { fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> { t.check_low_s()?; - if let Some(n) = t.network_id() { + if let Some(n) = t.chain_id() { if header.number() >= self.params().eip155_transition && n != self.params().chain_id { - return Err(TransactionError::InvalidNetworkId.into()); + return Err(TransactionError::InvalidChainId.into()); } } diff --git a/ethcore/src/engines/epoch.rs b/ethcore/src/engines/epoch.rs index f738113cf..586059e83 100644 --- a/ethcore/src/engines/epoch.rs +++ b/ethcore/src/engines/epoch.rs @@ -16,14 +16,12 @@ //! Epoch verifiers and transitions. +use util::H256; use error::Error; use header::Header; -use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; -use util::H256; - /// A full epoch transition. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, RlpEncodable, RlpDecodable)] pub struct Transition { /// Block hash at which the transition occurred. pub block_hash: H256, @@ -33,46 +31,14 @@ pub struct Transition { pub proof: Vec, } -impl Encodable for Transition { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3) - .append(&self.block_hash) - .append(&self.block_number) - .append(&self.proof); - } -} - -impl Decodable for Transition { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Transition { - block_hash: rlp.val_at(0)?, - block_number: rlp.val_at(1)?, - proof: rlp.val_at(2)?, - }) - } -} - /// An epoch transition pending a finality proof. /// Not all transitions need one. +#[derive(RlpEncodableWrapper, RlpDecodableWrapper)] pub struct PendingTransition { /// "transition/epoch" proof from the engine. pub proof: Vec, } -impl Encodable for PendingTransition { - fn rlp_append(&self, s: &mut RlpStream) { - s.append(&self.proof); - } -} - -impl Decodable for PendingTransition { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(PendingTransition { - proof: rlp.as_val()?, - }) - } -} - /// Verifier for all blocks within an epoch with self-contained state. /// /// See docs on `Engine` relating to proving functions for more details. diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index a293963d7..00f322318 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -263,7 +263,7 @@ pub trait Engine : Sync + Send { // TODO: Add flags for which bits of the transaction to check. // TODO: consider including State in the params. fn verify_transaction_basic(&self, t: &UnverifiedTransaction, _header: &Header) -> Result<(), Error> { - t.verify_basic(true, Some(self.params().network_id), true)?; + t.verify_basic(true, Some(self.params().chain_id), true)?; Ok(()) } @@ -273,7 +273,7 @@ pub trait Engine : Sync + Send { } /// The network ID that transactions should be signed with. - fn signing_network_id(&self, _env_info: &EnvInfo) -> Option { + fn signing_chain_id(&self, _env_info: &EnvInfo) -> Option { Some(self.params().chain_id) } diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 3bdef480c..552154580 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -62,6 +62,6 @@ impl Engine for NullEngine { } fn snapshot_components(&self) -> Option> { - Some(Box::new(::snapshot::PowSnapshot(10000))) + Some(Box::new(::snapshot::PowSnapshot::new(10000, 10000))) } } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 96790dca7..1abcf2c8f 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -452,7 +452,7 @@ mod tests { let s0: Secret = "1".sha3().into(); let v0 = tap.insert_account(s0.clone(), "").unwrap(); let v1 = tap.insert_account("0".sha3().into(), "").unwrap(); - let network_id = Spec::new_validator_safe_contract().network_id(); + let chain_id = Spec::new_validator_safe_contract().chain_id(); let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, Some(tap)); client.engine().register_client(Arc::downgrade(&client)); let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); @@ -466,7 +466,7 @@ mod tests { action: Action::Call(validator_contract), value: 0.into(), data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), - }.sign(&s0, Some(network_id)); + }.sign(&s0, Some(chain_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); assert_eq!(client.chain_info().best_block_number, 1); @@ -478,7 +478,7 @@ mod tests { action: Action::Call(validator_contract), value: 0.into(), data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), - }.sign(&s0, Some(network_id)); + }.sign(&s0, Some(chain_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); // The transaction is not yet included so still unable to seal. @@ -497,7 +497,7 @@ mod tests { action: Action::Call(Address::default()), value: 0.into(), data: Vec::new(), - }.sign(&s0, Some(network_id)); + }.sign(&s0, Some(chain_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); // Able to seal again. diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 194cbcb38..172323c0d 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -78,8 +78,8 @@ pub enum TransactionError { RecipientBanned, /// Contract creation code is banned. CodeBanned, - /// Invalid network ID given. - InvalidNetworkId, + /// Invalid chain ID given. + InvalidChainId, } impl fmt::Display for TransactionError { @@ -103,7 +103,7 @@ impl fmt::Display for TransactionError { SenderBanned => "Sender is temporarily banned.".into(), RecipientBanned => "Recipient is temporarily banned.".into(), CodeBanned => "Contract code is temporarily banned.".into(), - InvalidNetworkId => "Transaction of this network ID is not allowed on this chain.".into(), + InvalidChainId => "Transaction of this chain ID is not allowed on this chain.".into(), }; f.write_fmt(format_args!("Transaction error ({})", msg)) diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 7e275cf98..6235255e4 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -39,7 +39,10 @@ pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); /// Number of blocks in an ethash snapshot. // make dependent on difficulty incrment divisor? -const SNAPSHOT_BLOCKS: u64 = 30000; +const SNAPSHOT_BLOCKS: u64 = 5000; +/// Maximum number of blocks allowed in an ethash snapshot. +const MAX_SNAPSHOT_BLOCKS: u64 = 30000; + /// Ethash params. #[derive(Debug, PartialEq)] @@ -206,7 +209,7 @@ impl Engine for Arc { } } - fn signing_network_id(&self, env_info: &EnvInfo) -> Option { + fn signing_chain_id(&self, env_info: &EnvInfo) -> Option { if env_info.number >= self.params().eip155_transition { Some(self.params().chain_id) } else { @@ -397,8 +400,8 @@ impl Engine for Arc { } let check_low_s = header.number() >= self.ethash_params.homestead_transition; - let network_id = if header.number() >= self.params().eip155_transition { Some(self.params().chain_id) } else { None }; - t.verify_basic(check_low_s, network_id, false)?; + let chain_id = if header.number() >= self.params().eip155_transition { Some(self.params().chain_id) } else { None }; + t.verify_basic(check_low_s, chain_id, false)?; Ok(()) } @@ -407,7 +410,7 @@ impl Engine for Arc { } fn snapshot_components(&self) -> Option> { - Some(Box::new(::snapshot::PowSnapshot(SNAPSHOT_BLOCKS))) + Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS))) } } diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index bd7cf28c1..8f2fb06f2 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -157,7 +157,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { pub fn transact_virtual(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result { let sender = t.sender(); let balance = self.state.balance(&sender)?; - let needed_balance = t.value + t.gas * t.gas_price; + let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price)); if balance < needed_balance { // give the sender a sufficient balance self.state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty)?; diff --git a/ethcore/src/json_tests/transaction.rs b/ethcore/src/json_tests/transaction.rs index a3c3c889d..3d71a5faf 100644 --- a/ethcore/src/json_tests/transaction.rs +++ b/ethcore/src/json_tests/transaction.rs @@ -36,25 +36,25 @@ fn do_json_test(json_data: &[u8]) -> Vec { Some(x) if x < 3_000_000 => &homestead_schedule, Some(_) => &metropolis_schedule }; - let allow_network_id_of_one = number.map_or(false, |n| n >= 2_675_000); + let allow_chain_id_of_one = number.map_or(false, |n| n >= 2_675_000); let allow_unsigned = number.map_or(false, |n| n >= 3_000_000); let rlp: Vec = test.rlp.into(); let res = UntrustedRlp::new(&rlp) .as_val() .map_err(From::from) - .and_then(|t: UnverifiedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_network_id_of_one, allow_unsigned)); + .and_then(|t: UnverifiedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_chain_id_of_one, allow_unsigned)); fail_unless(test.transaction.is_none() == res.is_err(), "Validity different"); if let (Some(tx), Some(sender)) = (test.transaction, test.sender) { let t = res.unwrap(); fail_unless(SignedTransaction::new(t.clone()).unwrap().sender() == sender.into(), "sender mismatch"); - let is_acceptable_network_id = match t.network_id() { + let is_acceptable_chain_id = match t.chain_id() { None => true, - Some(1) if allow_network_id_of_one => true, + Some(1) if allow_chain_id_of_one => true, _ => false, }; - fail_unless(is_acceptable_network_id, "Network ID unacceptable"); + fail_unless(is_acceptable_chain_id, "Network ID unacceptable"); let data: Vec = tx.data.into(); fail_unless(t.data == data, "data mismatch"); fail_unless(t.gas_price == tx.gas_price.into(), "gas_price mismatch"); diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 6c862ecda..15d44626b 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -101,6 +101,9 @@ extern crate num; extern crate price_info; extern crate rand; extern crate rlp; + +#[macro_use] +extern crate rlp_derive; extern crate rustc_hex; extern crate semver; extern crate stats; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 02463d929..0639c645f 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1306,10 +1306,10 @@ mod tests { } fn transaction() -> SignedTransaction { - transaction_with_network_id(2) + transaction_with_chain_id(2) } - fn transaction_with_network_id(id: u64) -> SignedTransaction { + fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction { let keypair = Random.generate().unwrap(); Transaction { action: Action::Create, @@ -1318,7 +1318,7 @@ mod tests { gas: U256::from(100_000), gas_price: U256::zero(), nonce: U256::zero(), - }.sign(keypair.secret(), Some(id)) + }.sign(keypair.secret(), Some(chain_id)) } #[test] @@ -1399,14 +1399,14 @@ mod tests { let client = generate_dummy_client(2); - assert_eq!(miner.import_external_transactions(&*client, vec![transaction_with_network_id(spec.network_id()).into()]).pop().unwrap().unwrap(), TransactionImportResult::Current); + assert_eq!(miner.import_external_transactions(&*client, vec![transaction_with_chain_id(spec.chain_id()).into()]).pop().unwrap().unwrap(), TransactionImportResult::Current); miner.update_sealing(&*client); client.flush_queue(); assert!(miner.pending_block(0).is_none()); assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber); - assert_eq!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_network_id(spec.network_id()).into(), None)).unwrap(), TransactionImportResult::Current); + assert_eq!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None)).unwrap(), TransactionImportResult::Current); miner.update_sealing(&*client); client.flush_queue(); diff --git a/ethcore/src/miner/stratum.rs b/ethcore/src/miner/stratum.rs index e419247f0..0031bb715 100644 --- a/ethcore/src/miner/stratum.rs +++ b/ethcore/src/miner/stratum.rs @@ -32,7 +32,6 @@ use util::Mutex; use miner::{self, Miner, MinerService}; use client::Client; use block::IsBlock; -use std::str::FromStr; use rlp::encode; /// Configures stratum server options. @@ -60,7 +59,7 @@ impl SubmitPayload { return Err(PayloadError::ArgumentsAmountUnexpected(payload.len())); } - let nonce = match H64::from_str(clean_0x(&payload[0])) { + let nonce = match clean_0x(&payload[0]).parse::() { Ok(nonce) => nonce, Err(e) => { warn!(target: "stratum", "submit_work ({}): invalid nonce ({:?})", &payload[0], e); @@ -68,7 +67,7 @@ impl SubmitPayload { } }; - let pow_hash = match H256::from_str(clean_0x(&payload[1])) { + let pow_hash = match clean_0x(&payload[1]).parse::() { Ok(pow_hash) => pow_hash, Err(e) => { warn!(target: "stratum", "submit_work ({}): invalid hash ({:?})", &payload[1], e); @@ -76,7 +75,7 @@ impl SubmitPayload { } }; - let mix_hash = match H256::from_str(clean_0x(&payload[2])) { + let mix_hash = match clean_0x(&payload[2]).parse::() { Ok(mix_hash) => mix_hash, Err(e) => { warn!(target: "stratum", "submit_work ({}): invalid mix-hash ({:?})", &payload[2], e); @@ -133,7 +132,7 @@ impl JobDispatcher for StratumJobDispatcher { fn submit(&self, payload: Vec) -> Result<(), StratumServiceError> { let payload = SubmitPayload::from_args(payload).map_err(|e| - StratumServiceError::Dispatch(format!("{}", e)) + StratumServiceError::Dispatch(e.to_string()) )?; trace!( @@ -144,14 +143,16 @@ impl JobDispatcher for StratumJobDispatcher { payload.mix_hash, ); - self.with_core_void(|client, miner| { + self.with_core_result(|client, miner| { let seal = vec![encode(&payload.mix_hash).into_vec(), encode(&payload.nonce).into_vec()]; - if let Err(e) = miner.submit_seal(&*client, payload.pow_hash, seal) { - warn!(target: "stratum", "submit_seal error: {:?}", e); - }; - }); - - Ok(()) + match miner.submit_seal(&*client, payload.pow_hash, seal) { + Ok(_) => Ok(()), + Err(e) => { + warn!(target: "stratum", "submit_seal error: {:?}", e); + Err(StratumServiceError::Dispatch(e.to_string())) + } + } + }) } } @@ -181,8 +182,11 @@ impl StratumJobDispatcher { self.client.upgrade().and_then(|client| self.miner.upgrade().and_then(|miner| (f)(client, miner))) } - fn with_core_void(&self, f: F) where F: Fn(Arc, Arc) { - self.client.upgrade().map(|client| self.miner.upgrade().map(|miner| (f)(client, miner))); + fn with_core_result(&self, f: F) -> Result<(), StratumServiceError> where F: Fn(Arc, Arc) -> Result<(), StratumServiceError> { + match (self.client.upgrade(), self.miner.upgrade()) { + (Some(client), Some(miner)) => f(client, miner), + _ => Ok(()), + } } } @@ -230,7 +234,7 @@ impl Stratum { let dispatcher = Arc::new(StratumJobDispatcher::new(miner, client)); let stratum_svc = StratumService::start( - &SocketAddr::new(IpAddr::from_str(&options.listen_addr)?, options.port), + &SocketAddr::new(options.listen_addr.parse::()?, options.port), dispatcher.clone(), options.secret.clone(), )?; diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index bbd2fffa5..4204b591d 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -16,6 +16,7 @@ use std::fmt; use std::collections::BTreeMap; +use itertools::Itertools; use util::*; use state::Account; use ethjson; diff --git a/ethcore/src/pod_state.rs b/ethcore/src/pod_state.rs index 3e8d0bbfe..5a0265dc1 100644 --- a/ethcore/src/pod_state.rs +++ b/ethcore/src/pod_state.rs @@ -18,6 +18,7 @@ use std::fmt; use std::collections::BTreeMap; +use itertools::Itertools; use util::*; use pod_account::{self, PodAccount}; use types::state_diff::StateDiff; diff --git a/ethcore/src/snapshot/consensus/work.rs b/ethcore/src/snapshot/consensus/work.rs index 4975203b7..2bf154fc4 100644 --- a/ethcore/src/snapshot/consensus/work.rs +++ b/ethcore/src/snapshot/consensus/work.rs @@ -37,11 +37,24 @@ use rand::OsRng; /// Snapshot creation and restoration for PoW chains. /// This includes blocks from the head of the chain as a /// loose assurance that the chain is valid. -/// -/// The field is the number of blocks from the head of the chain -/// to include in the snapshot. #[derive(Clone, Copy, PartialEq)] -pub struct PowSnapshot(pub u64); +pub struct PowSnapshot { + /// Number of blocks from the head of the chain + /// to include in the snapshot. + pub blocks: u64, + /// Number of to allow in the snapshot when restoring. + pub max_restore_blocks: u64, +} + +impl PowSnapshot { + /// Create a new instance. + pub fn new(blocks: u64, max_restore_blocks: u64) -> PowSnapshot { + PowSnapshot { + blocks: blocks, + max_restore_blocks: max_restore_blocks, + } + } +} impl SnapshotComponents for PowSnapshot { fn chunk_all( @@ -57,7 +70,7 @@ impl SnapshotComponents for PowSnapshot { current_hash: block_at, writer: chunk_sink, preferred_size: preferred_size, - }.chunk_all(self.0) + }.chunk_all(self.blocks) } fn rebuilder( @@ -66,7 +79,7 @@ impl SnapshotComponents for PowSnapshot { db: Arc, manifest: &ManifestData, ) -> Result, ::error::Error> { - PowRebuilder::new(chain, db, manifest, self.0).map(|r| Box::new(r) as Box<_>) + PowRebuilder::new(chain, db, manifest, self.max_restore_blocks).map(|r| Box::new(r) as Box<_>) } fn min_supported_version(&self) -> u64 { ::snapshot::MIN_SUPPORTED_STATE_CHUNK_VERSION } @@ -218,7 +231,7 @@ impl Rebuilder for PowRebuilder { trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 3); if self.fed_blocks + num_blocks > self.snapshot_blocks { - return Err(Error::TooManyBlocks(self.snapshot_blocks, self.fed_blocks).into()) + return Err(Error::TooManyBlocks(self.snapshot_blocks, self.fed_blocks + num_blocks).into()) } // todo: assert here that these values are consistent with chunks being in order. diff --git a/ethcore/src/snapshot/io.rs b/ethcore/src/snapshot/io.rs index 8cb778117..f28adcf7f 100644 --- a/ethcore/src/snapshot/io.rs +++ b/ethcore/src/snapshot/io.rs @@ -27,7 +27,7 @@ use std::path::{Path, PathBuf}; use util::Bytes; use util::hash::H256; -use rlp::{self, Encodable, RlpStream, UntrustedRlp}; +use rlp::{RlpStream, UntrustedRlp}; use super::ManifestData; @@ -49,24 +49,9 @@ pub trait SnapshotWriter { } // (hash, len, offset) +#[derive(RlpEncodable, RlpDecodable)] struct ChunkInfo(H256, u64, u64); -impl Encodable for ChunkInfo { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3); - s.append(&self.0).append(&self.1).append(&self.2); - } -} - -impl rlp::Decodable for ChunkInfo { - fn decode(rlp: &UntrustedRlp) -> Result { - let hash = rlp.val_at(0)?; - let len = rlp.val_at(1)?; - let off = rlp.val_at(2)?; - Ok(ChunkInfo(hash, len, off)) - } -} - /// A packed snapshot writer. This writes snapshots to a single concatenated file. /// /// The file format is very simple and consists of three parts: diff --git a/ethcore/src/snapshot/tests/proof_of_authority.rs b/ethcore/src/snapshot/tests/proof_of_authority.rs index a62dbaf10..7c44ec20a 100644 --- a/ethcore/src/snapshot/tests/proof_of_authority.rs +++ b/ethcore/src/snapshot/tests/proof_of_authority.rs @@ -130,7 +130,7 @@ fn make_chain(accounts: Arc, blocks_beyond: usize, transitions: action: Action::Call(Address::new()), value: 1.into(), data: Vec::new(), - }.sign(&*RICH_SECRET, client.signing_network_id()); + }.sign(&*RICH_SECRET, client.signing_chain_id()); *nonce = *nonce + 1.into(); vec![transaction] @@ -176,7 +176,7 @@ fn make_chain(accounts: Arc, blocks_beyond: usize, transitions: action: Action::Call(addr), value: 0.into(), data: data, - }.sign(&*RICH_SECRET, client.signing_network_id()); + }.sign(&*RICH_SECRET, client.signing_chain_id()); pending.push(transaction); diff --git a/ethcore/src/snapshot/tests/proof_of_work.rs b/ethcore/src/snapshot/tests/proof_of_work.rs index e7d0a0964..a6c0166f5 100644 --- a/ethcore/src/snapshot/tests/proof_of_work.rs +++ b/ethcore/src/snapshot/tests/proof_of_work.rs @@ -30,7 +30,7 @@ use util::kvdb::{self, KeyValueDB, DBTransaction}; use std::sync::Arc; use std::sync::atomic::AtomicBool; -const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot(30000); +const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot { blocks: 30000, max_restore_blocks: 30000 }; fn chunk_and_restore(amount: u64) { let mut canon_chain = ChainGenerator::default(); diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 395f8ef9c..ba57a7f27 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -380,6 +380,9 @@ impl Spec { /// Get the configured Network ID. pub fn network_id(&self) -> u64 { self.params().network_id } + /// Get the chain ID used for signing. + pub fn chain_id(&self) -> u64 { self.params().chain_id } + /// Get the configured subprotocol name. pub fn subprotocol_name(&self) -> String { self.params().subprotocol_name.clone() } diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 5d05e66f5..d8ba8313e 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -211,7 +211,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, ac action: Action::Create, data: vec![], value: U256::zero(), - }.sign(kp.secret(), Some(test_spec.network_id())), None).unwrap(); + }.sign(kp.secret(), Some(test_spec.chain_id())), None).unwrap(); n += 1; } diff --git a/ethcore/src/trace/bloom.rs b/ethcore/src/trace/bloom.rs index 561a83719..ed34d6505 100644 --- a/ethcore/src/trace/bloom.rs +++ b/ethcore/src/trace/bloom.rs @@ -1,10 +1,9 @@ use bloomchain::Bloom; use bloomchain::group::{BloomGroup, GroupPosition}; -use rlp::*; use basic_types::LogBloom; /// Helper structure representing bloom of the trace. -#[derive(Clone)] +#[derive(Clone, RlpEncodableWrapper, RlpDecodableWrapper)] pub struct BlockTracesBloom(LogBloom); impl From for BlockTracesBloom { @@ -28,7 +27,7 @@ impl Into for BlockTracesBloom { } /// Represents group of X consecutive blooms. -#[derive(Clone)] +#[derive(Clone, RlpEncodableWrapper, RlpDecodableWrapper)] pub struct BlockTracesBloomGroup { blooms: Vec, } @@ -59,34 +58,6 @@ impl Into for BlockTracesBloomGroup { } } -impl Decodable for BlockTracesBloom { - fn decode(rlp: &UntrustedRlp) -> Result { - LogBloom::decode(rlp).map(BlockTracesBloom) - } -} - -impl Encodable for BlockTracesBloom { - fn rlp_append(&self, s: &mut RlpStream) { - Encodable::rlp_append(&self.0, s) - } -} - -impl Decodable for BlockTracesBloomGroup { - fn decode(rlp: &UntrustedRlp) -> Result { - let blooms = rlp.as_list()?; - let group = BlockTracesBloomGroup { - blooms: blooms - }; - Ok(group) - } -} - -impl Encodable for BlockTracesBloomGroup { - fn rlp_append(&self, s: &mut RlpStream) { - s.append_list(&self.blooms); - } -} - /// Represents `BloomGroup` position in database. #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct TraceGroupPosition { diff --git a/ethcore/src/trace/types/flat.rs b/ethcore/src/trace/types/flat.rs index da304694d..8b65f1f4c 100644 --- a/ethcore/src/trace/types/flat.rs +++ b/ethcore/src/trace/types/flat.rs @@ -77,7 +77,7 @@ impl Decodable for FlatTrace { } /// Represents all traces produced by a single transaction. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, RlpEncodableWrapper, RlpDecodableWrapper)] pub struct FlatTransactionTraces(Vec); impl From> for FlatTransactionTraces { @@ -99,18 +99,6 @@ impl FlatTransactionTraces { } } -impl Encodable for FlatTransactionTraces { - fn rlp_append(&self, s: &mut RlpStream) { - s.append_list(&self.0); - } -} - -impl Decodable for FlatTransactionTraces { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(FlatTransactionTraces(rlp.as_list()?)) - } -} - impl Into> for FlatTransactionTraces { fn into(self) -> Vec { self.0 @@ -118,7 +106,7 @@ impl Into> for FlatTransactionTraces { } /// Represents all traces produced by transactions in a single block. -#[derive(Debug, PartialEq, Clone, Default)] +#[derive(Debug, PartialEq, Clone, Default, RlpEncodableWrapper, RlpDecodableWrapper)] pub struct FlatBlockTraces(Vec); impl HeapSizeOf for FlatBlockTraces { @@ -140,18 +128,6 @@ impl FlatBlockTraces { } } -impl Encodable for FlatBlockTraces { - fn rlp_append(&self, s: &mut RlpStream) { - s.append_list(&self.0); - } -} - -impl Decodable for FlatBlockTraces { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(FlatBlockTraces(rlp.as_list()?)) - } -} - impl Into> for FlatBlockTraces { fn into(self) -> Vec { self.0 diff --git a/ethcore/src/trace/types/trace.rs b/ethcore/src/trace/types/trace.rs index 3863a935e..5fa0260c6 100644 --- a/ethcore/src/trace/types/trace.rs +++ b/ethcore/src/trace/types/trace.rs @@ -27,7 +27,7 @@ use evm::CallType; use super::error::Error; /// `Call` result. -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] pub struct CallResult { /// Gas used by call. @@ -36,27 +36,8 @@ pub struct CallResult { pub output: Bytes, } -impl Encodable for CallResult { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2); - s.append(&self.gas_used); - s.append(&self.output); - } -} - -impl Decodable for CallResult { - fn decode(rlp: &UntrustedRlp) -> Result { - let res = CallResult { - gas_used: rlp.val_at(0)?, - output: rlp.val_at(1)?, - }; - - Ok(res) - } -} - /// `Create` result. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] pub struct CreateResult { /// Gas used by create. @@ -67,27 +48,6 @@ pub struct CreateResult { pub address: Address, } -impl Encodable for CreateResult { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3); - s.append(&self.gas_used); - s.append(&self.code); - s.append(&self.address); - } -} - -impl Decodable for CreateResult { - fn decode(rlp: &UntrustedRlp) -> Result { - let res = CreateResult { - gas_used: rlp.val_at(0)?, - code: rlp.val_at(1)?, - address: rlp.val_at(2)?, - }; - - Ok(res) - } -} - impl CreateResult { /// Returns bloom. pub fn bloom(&self) -> LogBloom { @@ -96,7 +56,7 @@ impl CreateResult { } /// Description of a _call_ action, either a `CALL` operation or a message transction. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] pub struct Call { /// The sending account. @@ -126,33 +86,6 @@ impl From for Call { } } -impl Encodable for Call { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(6); - s.append(&self.from); - s.append(&self.to); - s.append(&self.value); - s.append(&self.gas); - s.append(&self.input); - s.append(&self.call_type); - } -} - -impl Decodable for Call { - fn decode(rlp: &UntrustedRlp) -> Result { - let res = Call { - from: rlp.val_at(0)?, - to: rlp.val_at(1)?, - value: rlp.val_at(2)?, - gas: rlp.val_at(3)?, - input: rlp.val_at(4)?, - call_type: rlp.val_at(5)?, - }; - - Ok(res) - } -} - impl Call { /// Returns call action bloom. /// The bloom contains from and to addresses. @@ -163,7 +96,7 @@ impl Call { } /// Description of a _create_ action, either a `CREATE` operation or a create transction. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] pub struct Create { /// The address of the creator. @@ -187,29 +120,6 @@ impl From for Create { } } -impl Encodable for Create { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4); - s.append(&self.from); - s.append(&self.value); - s.append(&self.gas); - s.append(&self.init); - } -} - -impl Decodable for Create { - fn decode(rlp: &UntrustedRlp) -> Result { - let res = Create { - from: rlp.val_at(0)?, - value: rlp.val_at(1)?, - gas: rlp.val_at(2)?, - init: rlp.val_at(3)?, - }; - - Ok(res) - } -} - impl Create { /// Returns bloom create action bloom. /// The bloom contains only from address. @@ -219,7 +129,7 @@ impl Create { } /// Suicide action. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] pub struct Suicide { /// Suicided address. @@ -238,28 +148,6 @@ impl Suicide { } } -impl Encodable for Suicide { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3); - s.append(&self.address); - s.append(&self.refund_address); - s.append(&self.balance); - } -} - -impl Decodable for Suicide { - fn decode(rlp: &UntrustedRlp) -> Result { - let res = Suicide { - address: rlp.val_at(0)?, - refund_address: rlp.val_at(1)?, - balance: rlp.val_at(2)?, - }; - - Ok(res) - } -} - - /// Description of an action that we trace; will be either a call or a create. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "ipc", binary)] @@ -394,7 +282,7 @@ impl Res { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] /// A diff of some chunk of memory. pub struct MemoryDiff { @@ -404,24 +292,7 @@ pub struct MemoryDiff { pub data: Bytes, } -impl Encodable for MemoryDiff { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2); - s.append(&self.offset); - s.append(&self.data); - } -} - -impl Decodable for MemoryDiff { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(MemoryDiff { - offset: rlp.val_at(0)?, - data: rlp.val_at(1)?, - }) - } -} - -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] /// A diff of some storage value. pub struct StorageDiff { @@ -431,24 +302,7 @@ pub struct StorageDiff { pub value: U256, } -impl Encodable for StorageDiff { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(2); - s.append(&self.location); - s.append(&self.value); - } -} - -impl Decodable for StorageDiff { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(StorageDiff { - location: rlp.val_at(0)?, - value: rlp.val_at(1)?, - }) - } -} - -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] /// A record of an executed VM operation. pub struct VMExecutedOperation { @@ -462,28 +316,7 @@ pub struct VMExecutedOperation { pub store_diff: Option, } -impl Encodable for VMExecutedOperation { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4); - s.append(&self.gas_used); - s.append_list(&self.stack_push); - s.append(&self.mem_diff); - s.append(&self.store_diff); - } -} - -impl Decodable for VMExecutedOperation { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(VMExecutedOperation { - gas_used: rlp.val_at(0)?, - stack_push: rlp.list_at(1)?, - mem_diff: rlp.val_at(2)?, - store_diff: rlp.val_at(3)?, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] /// A record of the execution of a single VM operation. pub struct VMOperation { @@ -497,30 +330,7 @@ pub struct VMOperation { pub executed: Option, } -impl Encodable for VMOperation { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4); - s.append(&self.pc); - s.append(&self.instruction); - s.append(&self.gas_cost); - s.append(&self.executed); - } -} - -impl Decodable for VMOperation { - fn decode(rlp: &UntrustedRlp) -> Result { - let res = VMOperation { - pc: rlp.val_at(0)?, - instruction: rlp.val_at(1)?, - gas_cost: rlp.val_at(2)?, - executed: rlp.val_at(3)?, - }; - - Ok(res) - } -} - -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)] #[cfg_attr(feature = "ipc", binary)] /// A record of a full VM trace for a CALL/CREATE. pub struct VMTrace { @@ -534,26 +344,3 @@ pub struct VMTrace { /// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction. pub subs: Vec, } - -impl Encodable for VMTrace { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4); - s.append(&self.parent_step); - s.append(&self.code); - s.append_list(&self.operations); - s.append_list(&self.subs); - } -} - -impl Decodable for VMTrace { - fn decode(rlp: &UntrustedRlp) -> Result { - let res = VMTrace { - parent_step: rlp.val_at(0)?, - code: rlp.val_at(1)?, - operations: rlp.list_at(2)?, - subs: rlp.list_at(3)?, - }; - - Ok(res) - } -} diff --git a/ethcore/src/transaction.rs b/ethcore/src/transaction.rs index 6f5470028..0bd61dceb 100644 --- a/ethcore/src/transaction.rs +++ b/ethcore/src/transaction.rs @@ -56,6 +56,15 @@ impl Decodable for Action { } } +impl Encodable for Action { + fn rlp_append(&self, s: &mut RlpStream) { + match *self { + Action::Create => s.append_internal(&""), + Action::Call(ref addr) => s.append_internal(addr), + }; + } +} + /// Transaction activation condition. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Condition { @@ -85,18 +94,15 @@ pub struct Transaction { impl Transaction { /// Append object with a without signature into RLP stream - pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, network_id: Option) { - s.begin_list(if network_id.is_none() { 6 } else { 9 }); + pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, chain_id: Option) { + s.begin_list(if chain_id.is_none() { 6 } else { 9 }); s.append(&self.nonce); s.append(&self.gas_price); s.append(&self.gas); - match self.action { - Action::Create => s.append_empty_data(), - Action::Call(ref to) => s.append(to) - }; + s.append(&self.action); s.append(&self.value); s.append(&self.data); - if let Some(n) = network_id { + if let Some(n) = chain_id { s.append(&n); s.append(&0u8); s.append(&0u8); @@ -157,27 +163,27 @@ impl From for UnverifiedTransaction { impl Transaction { /// The message hash of the transaction. - pub fn hash(&self, network_id: Option) -> H256 { + pub fn hash(&self, chain_id: Option) -> H256 { let mut stream = RlpStream::new(); - self.rlp_append_unsigned_transaction(&mut stream, network_id); + self.rlp_append_unsigned_transaction(&mut stream, chain_id); stream.as_raw().sha3() } /// Signs the transaction as coming from `sender`. - pub fn sign(self, secret: &Secret, network_id: Option) -> SignedTransaction { - let sig = ::ethkey::sign(secret, &self.hash(network_id)) + pub fn sign(self, secret: &Secret, chain_id: Option) -> SignedTransaction { + let sig = ::ethkey::sign(secret, &self.hash(chain_id)) .expect("data is valid and context has signing capabilities; qed"); - SignedTransaction::new(self.with_signature(sig, network_id)) + SignedTransaction::new(self.with_signature(sig, chain_id)) .expect("secret is valid so it's recoverable") } /// Signs the transaction with signature. - pub fn with_signature(self, sig: Signature, network_id: Option) -> UnverifiedTransaction { + pub fn with_signature(self, sig: Signature, chain_id: Option) -> UnverifiedTransaction { UnverifiedTransaction { unsigned: self, r: sig.r().into(), s: sig.s().into(), - v: sig.v() as u64 + if let Some(n) = network_id { 35 + n * 2 } else { 27 }, + v: sig.v() as u64 + if let Some(n) = chain_id { 35 + n * 2 } else { 27 }, hash: 0.into(), }.compute_hash() } @@ -210,13 +216,13 @@ impl Transaction { } /// Add EIP-86 compatible empty signature. - pub fn null_sign(self, network_id: u64) -> SignedTransaction { + pub fn null_sign(self, chain_id: u64) -> SignedTransaction { SignedTransaction { transaction: UnverifiedTransaction { unsigned: self, r: U256::zero(), s: U256::zero(), - v: network_id, + v: chain_id, hash: 0.into(), }.compute_hash(), sender: UNSIGNED_SENDER, @@ -244,7 +250,7 @@ pub struct UnverifiedTransaction { /// Plain Transaction. unsigned: Transaction, /// The V field of the signature; the LS bit described which half of the curve our point falls - /// in. The MS bits describe which network this transaction is for. If 27/28, its for all networks. + /// in. The MS bits describe which chain this transaction is for. If 27/28, its for all chains. v: u64, /// The R field of the signature; helps describe the point on the curve. r: U256, @@ -308,10 +314,7 @@ impl UnverifiedTransaction { s.append(&self.nonce); s.append(&self.gas_price); s.append(&self.gas); - match self.action { - Action::Create => s.append_empty_data(), - Action::Call(ref to) => s.append(to) - }; + s.append(&self.action); s.append(&self.value); s.append(&self.data); s.append(&self.v); @@ -330,8 +333,8 @@ impl UnverifiedTransaction { /// The `v` value that appears in the RLP. pub fn original_v(&self) -> u64 { self.v } - /// The network ID, or `None` if this is a global transaction. - pub fn network_id(&self) -> Option { + /// The chain ID, or `None` if this is a global transaction. + pub fn chain_id(&self) -> Option { match self.v { v if self.is_unsigned() => Some(v), v if v > 36 => Some((v - 35) / 2), @@ -360,15 +363,15 @@ impl UnverifiedTransaction { /// Recovers the public key of the sender. pub fn recover_public(&self) -> Result { - Ok(recover(&self.signature(), &self.unsigned.hash(self.network_id()))?) + Ok(recover(&self.signature(), &self.unsigned.hash(self.chain_id()))?) } /// Do basic validation, checking for valid signature and minimum gas, // TODO: consider use in block validation. #[cfg(test)] #[cfg(feature = "json-tests")] - pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool, allow_empty_signature: bool) -> Result { - let chain_id = if allow_network_id_of_one { Some(1) } else { None }; + pub fn validate(self, schedule: &Schedule, require_low: bool, allow_chain_id_of_one: bool, allow_empty_signature: bool) -> Result { + let chain_id = if allow_chain_id_of_one { Some(1) } else { None }; self.verify_basic(require_low, chain_id, allow_empty_signature)?; if !allow_empty_signature || !self.is_unsigned() { self.recover_public()?; @@ -388,10 +391,10 @@ impl UnverifiedTransaction { if allow_empty_signature && self.is_unsigned() && !(self.gas_price.is_zero() && self.value.is_zero() && self.nonce.is_zero()) { return Err(EthkeyError::InvalidSignature.into()) } - match (self.network_id(), chain_id) { + match (self.chain_id(), chain_id) { (None, _) => {}, (Some(n), Some(m)) if n == m => {}, - _ => return Err(TransactionError::InvalidNetworkId.into()), + _ => return Err(TransactionError::InvalidChainId.into()), }; Ok(()) } @@ -555,7 +558,7 @@ mod tests { } else { panic!(); } assert_eq!(t.value, U256::from(0x0au64)); assert_eq!(public_to_address(&t.recover_public().unwrap()), "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".into()); - assert_eq!(t.network_id(), None); + assert_eq!(t.chain_id(), None); } #[test] @@ -572,7 +575,7 @@ mod tests { data: b"Hello!".to_vec() }.sign(&key.secret(), None); assert_eq!(Address::from(key.public().sha3()), t.sender()); - assert_eq!(t.network_id(), None); + assert_eq!(t.chain_id(), None); } #[test] @@ -586,15 +589,15 @@ mod tests { data: b"Hello!".to_vec() }.fake_sign(Address::from(0x69)); assert_eq!(Address::from(0x69), t.sender()); - assert_eq!(t.network_id(), None); + assert_eq!(t.chain_id(), None); let t = t.clone(); assert_eq!(Address::from(0x69), t.sender()); - assert_eq!(t.network_id(), None); + assert_eq!(t.chain_id(), None); } #[test] - fn should_recover_from_network_specific_signing() { + fn should_recover_from_chain_specific_signing() { use ethkey::{Random, Generator}; let key = Random.generate().unwrap(); let t = Transaction { @@ -606,7 +609,7 @@ mod tests { data: b"Hello!".to_vec() }.sign(&key.secret(), Some(69)); assert_eq!(Address::from(key.public().sha3()), t.sender()); - assert_eq!(t.network_id(), Some(69)); + assert_eq!(t.chain_id(), Some(69)); } #[test] @@ -617,7 +620,7 @@ mod tests { let signed = decode(&FromHex::from_hex(tx_data).unwrap()); let signed = SignedTransaction::new(signed).unwrap(); assert_eq!(signed.sender(), address.into()); - flushln!("networkid: {:?}", signed.network_id()); + flushln!("chainid: {:?}", signed.chain_id()); }; test_vector("f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce"); diff --git a/ethcore/types/Cargo.toml b/ethcore/types/Cargo.toml index 77f392bf6..82963f960 100644 --- a/ethcore/types/Cargo.toml +++ b/ethcore/types/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Parity Technologies "] [dependencies] rlp = { path = "../../util/rlp" } +rlp_derive = { path = "../../util/rlp_derive" } ethcore-util = { path = "../../util" } ethjson = { path = "../../json" } bloomable = { path = "../../util/bloomable" } diff --git a/ethcore/types/src/basic_account.rs b/ethcore/types/src/basic_account.rs index c071040cf..f30872f6b 100644 --- a/ethcore/types/src/basic_account.rs +++ b/ethcore/types/src/basic_account.rs @@ -16,11 +16,10 @@ //! Basic account type -- the decoded RLP from the state trie. -use rlp::*; use util::{U256, H256}; /// Basic account type. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct BasicAccount { /// Nonce of the account. pub nonce: U256, @@ -31,24 +30,3 @@ pub struct BasicAccount { /// Code hash of the account. pub code_hash: H256, } - -impl Encodable for BasicAccount { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4) - .append(&self.nonce) - .append(&self.balance) - .append(&self.storage_root) - .append(&self.code_hash); - } -} - -impl Decodable for BasicAccount { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(BasicAccount { - nonce: rlp.val_at(0)?, - balance: rlp.val_at(1)?, - storage_root: rlp.val_at(2)?, - code_hash: rlp.val_at(3)?, - }) - } -} diff --git a/ethcore/types/src/lib.rs b/ethcore/types/src/lib.rs index 7650cf651..10a4ac71e 100644 --- a/ethcore/types/src/lib.rs +++ b/ethcore/types/src/lib.rs @@ -19,6 +19,8 @@ extern crate ethcore_util as util; extern crate ethjson; extern crate rlp; +#[macro_use] +extern crate rlp_derive; extern crate bloomable; #[cfg(test)] diff --git a/ethcore/types/src/log_entry.rs b/ethcore/types/src/log_entry.rs index f917a4dab..152b48a0e 100644 --- a/ethcore/types/src/log_entry.rs +++ b/ethcore/types/src/log_entry.rs @@ -19,7 +19,6 @@ use std::ops::Deref; use util::{H256, Address, Bytes, HeapSizeOf, Hashable}; use bloomable::Bloomable; -use rlp::*; use {BlockNumber}; use ethjson; @@ -27,7 +26,7 @@ use ethjson; pub type LogBloom = ::util::H2048; /// A record of execution for a `LOG` operation. -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct LogEntry { /// The address of the contract executing at the point of the `LOG` operation. pub address: Address, @@ -37,26 +36,6 @@ pub struct LogEntry { pub data: Bytes, } -impl Encodable for LogEntry { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3); - s.append(&self.address); - s.append_list(&self.topics); - s.append(&self.data); - } -} - -impl Decodable for LogEntry { - fn decode(rlp: &UntrustedRlp) -> Result { - let entry = LogEntry { - address: rlp.val_at(0)?, - topics: rlp.list_at(1)?, - data: rlp.val_at(2)?, - }; - Ok(entry) - } -} - impl HeapSizeOf for LogEntry { fn heap_size_of_children(&self) -> usize { self.topics.heap_size_of_children() + self.data.heap_size_of_children() diff --git a/ethcore/wasm/src/env.rs b/ethcore/wasm/src/env.rs index cb7462046..777016a1b 100644 --- a/ethcore/wasm/src/env.rs +++ b/ethcore/wasm/src/env.rs @@ -17,8 +17,9 @@ //! Wasm env module bindings use parity_wasm::elements::ValueType::*; -use parity_wasm::interpreter::UserFunctionDescriptor; +use parity_wasm::interpreter::{self, UserFunctionDescriptor}; use parity_wasm::interpreter::UserFunctionDescriptor::*; +use super::runtime::Runtime; pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[ Static( @@ -81,63 +82,29 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[ &[I32], None, ), - Static( - "_abort", - &[], - None, - ), - Static( - "abortOnCannotGrowMemory", - &[I32; 0], - Some(I32) - ), - - /* - THIS IS EXPERIMENTAL RUST-ONLY RUNTIME EXTERNS, THEY ARE SUBJECT TO CHANGE - - AVOID YOUR WASM CONTAINS ANY OF THESE OTHERWISE - EITHER FACE THE NEED OF HARDFORK - OR YOU CAN STUCK ON SPECIFIC RUST VERSION FOR WASM COMPILATION - */ - - Static( - "_rust_begin_unwind", - &[I32; 4], - None, - ), Static( "_emscripten_memcpy_big", &[I32; 3], Some(I32), ), - Static( - "___syscall6", - &[I32; 2], - Some(I32), - ), - Static( - "___syscall140", - &[I32; 2], - Some(I32) - ), - Static( - "___syscall146", - &[I32; 2], - Some(I32) - ), - Static( - "___syscall54", - &[I32; 2], - Some(I32) - ), + + // TODO: Get rid of it also somehow? Static( "_llvm_trap", &[I32; 0], None ), + Static( - "___setErrNo", - &[I32; 1], - None + "_llvm_bswap_i64", + &[I32; 2], + Some(I32) ), ]; + +pub fn native_bindings<'a>(runtime: &'a mut Runtime) -> interpreter::UserFunctions<'a> { + interpreter::UserFunctions { + executor: runtime, + functions: ::std::borrow::Cow::from(SIGNATURES), + } +} \ No newline at end of file diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index ec6e67405..3fec0f781 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -32,8 +32,6 @@ mod result; mod tests; mod env; -use std::sync::Arc; - const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024; use parity_wasm::{interpreter, elements}; @@ -89,6 +87,7 @@ impl vm::Vm for WasmInterpreter { DEFAULT_STACK_SPACE, params.gas.low_u64(), RuntimeContext::new(params.address, params.sender), + &self.program, ); let mut cursor = ::std::io::Cursor::new(&*code); @@ -112,16 +111,8 @@ impl vm::Vm for WasmInterpreter { )?; { - let execution_params = interpreter::ExecutionParams::with_external( - "env".into(), - Arc::new( - interpreter::env_native_module(env_instance, native_bindings(&mut runtime)) - .map_err(|err| { - // todo: prefer explicit panic here also? - vm::Error::Wasm(format!("Error instantiating native bindings: {:?}", err)) - })? - ) - ).add_argument(interpreter::RuntimeValue::I32(d_ptr.as_raw() as i32)); + let execution_params = runtime.execution_params() + .add_argument(interpreter::RuntimeValue::I32(d_ptr.as_raw() as i32)); let module_instance = self.program.add_module("contract", contract_module, Some(&execution_params.externals)) .map_err(|err| { @@ -158,13 +149,6 @@ impl vm::Vm for WasmInterpreter { } } -fn native_bindings<'a>(runtime: &'a mut Runtime) -> interpreter::UserFunctions<'a> { - interpreter::UserFunctions { - executor: runtime, - functions: ::std::borrow::Cow::from(env::SIGNATURES), - } -} - impl From for vm::Error { fn from(err: runtime::Error) -> vm::Error { vm::Error::Wasm(format!("WASM runtime-error: {:?}", err)) diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index a983c954f..e1def857e 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -72,24 +72,26 @@ impl RuntimeContext { } /// Runtime enviroment data for wasm contract execution -pub struct Runtime<'a> { +pub struct Runtime<'a, 'b> { gas_counter: u64, gas_limit: u64, dynamic_top: u32, ext: &'a mut vm::Ext, memory: Arc, context: RuntimeContext, + instance: &'b interpreter::ProgramInstance, } -impl<'a> Runtime<'a> { +impl<'a, 'b> Runtime<'a, 'b> { /// New runtime for wasm contract with specified params - pub fn with_params<'b>( - ext: &'b mut vm::Ext, + pub fn with_params<'c, 'd>( + ext: &'c mut vm::Ext, memory: Arc, stack_space: u32, gas_limit: u64, context: RuntimeContext, - ) -> Runtime<'b> { + program_instance: &'d interpreter::ProgramInstance, + ) -> Runtime<'c, 'd> { Runtime { gas_counter: 0, gas_limit: gas_limit, @@ -97,6 +99,7 @@ impl<'a> Runtime<'a> { memory: memory, ext: ext, context: context, + instance: program_instance, } } @@ -449,9 +452,58 @@ impl<'a> Runtime<'a> { Ok(Some(0i32.into())) } + + fn bswap_32(x: u32) -> u32 { + x >> 24 | x >> 8 & 0xff00 | x << 8 & 0xff0000 | x << 24 + } + + fn bitswap_i64(&mut self, context: interpreter::CallerContext) + -> Result, interpreter::Error> + { + let x1 = context.value_stack.pop_as::()?; + let x2 = context.value_stack.pop_as::()?; + + let result = ((Runtime::bswap_32(x2 as u32) as u64) << 32 + | Runtime::bswap_32(x1 as u32) as u64) as i64; + + self.return_i64(result) + } + + fn return_i64(&mut self, val: i64) -> Result, interpreter::Error> { + let uval = val as u64; + let hi = (uval >> 32) as i32; + let lo = (uval << 32 >> 32) as i32; + + let target = self.instance.module("contract") + .ok_or(interpreter::Error::Trap("Error locating main execution entry".to_owned()))?; + target.execute_export( + "setTempRet0", + self.execution_params().add_argument( + interpreter::RuntimeValue::I32(hi).into() + ), + )?; + Ok(Some( + (lo).into() + )) + } + + pub fn execution_params(&mut self) -> interpreter::ExecutionParams { + use super::env; + + let env_instance = self.instance.module("env") + .expect("Env module always exists; qed"); + + interpreter::ExecutionParams::with_external( + "env".into(), + Arc::new( + interpreter::env_native_module(env_instance, env::native_bindings(self)) + .expect("Env module always exists; qed") + ) + ) + } } -impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> { +impl<'a, 'b> interpreter::UserFunctionExecutor for Runtime<'a, 'b> { fn execute(&mut self, name: &str, context: interpreter::CallerContext) -> Result, interpreter::Error> { @@ -494,6 +546,9 @@ impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> { "_emscripten_memcpy_big" => { self.mem_copy(context) }, + "_llvm_bswap_i64" => { + self.bitswap_i64(context) + }, _ => { trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name); self.user_trap(context) diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index c12dc8935..df01b8df6 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -57,7 +57,7 @@ fn empty() { test_finalize(interpreter.exec(params, &mut ext)).unwrap() }; - assert_eq!(gas_left, U256::from(99_996)); + assert_eq!(gas_left, U256::from(99_992)); } // This test checks if the contract deserializes payload header properly. @@ -85,7 +85,7 @@ fn logger() { }; println!("ext.store: {:?}", ext.store); - assert_eq!(gas_left, U256::from(99590)); + assert_eq!(gas_left, U256::from(99327)); let address_val: H256 = address.into(); assert_eq!( ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"), @@ -136,7 +136,7 @@ fn identity() { } }; - assert_eq!(gas_left, U256::from(99_687)); + assert_eq!(gas_left, U256::from(99_672)); assert_eq!( Address::from_slice(&result), @@ -170,7 +170,7 @@ fn dispersion() { } }; - assert_eq!(gas_left, U256::from(99_423)); + assert_eq!(gas_left, U256::from(99_270)); assert_eq!( result, @@ -199,7 +199,7 @@ fn suicide_not() { } }; - assert_eq!(gas_left, U256::from(99_656)); + assert_eq!(gas_left, U256::from(99_578)); assert_eq!( result, @@ -233,7 +233,7 @@ fn suicide() { } }; - assert_eq!(gas_left, U256::from(99_740)); + assert_eq!(gas_left, U256::from(99_621)); assert!(ext.suicides.contains(&refund)); } @@ -264,7 +264,7 @@ fn create() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Create, - gas: U256::from(99_767), + gas: U256::from(99_674), sender_address: None, receive_address: None, value: Some(1_000_000_000.into()), @@ -272,7 +272,7 @@ fn create() { code_address: None, } )); - assert_eq!(gas_left, U256::from(99_759)); + assert_eq!(gas_left, U256::from(99_596)); } @@ -306,7 +306,7 @@ fn call_code() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Call, - gas: U256::from(99_061), + gas: U256::from(99_069), sender_address: Some(sender), receive_address: Some(receiver), value: None, @@ -314,7 +314,7 @@ fn call_code() { code_address: Some("0d13710000000000000000000000000000000000".parse().unwrap()), } )); - assert_eq!(gas_left, U256::from(94196)); + assert_eq!(gas_left, U256::from(94144)); // siphash result let res = LittleEndian::read_u32(&result[..]); @@ -351,7 +351,7 @@ fn call_static() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Call, - gas: U256::from(99_061), + gas: U256::from(99_069), sender_address: Some(sender), receive_address: Some(receiver), value: None, @@ -359,7 +359,7 @@ fn call_static() { code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()), } )); - assert_eq!(gas_left, U256::from(94196)); + assert_eq!(gas_left, U256::from(94144)); // siphash result let res = LittleEndian::read_u32(&result[..]); @@ -378,13 +378,73 @@ fn realloc() { let mut ext = FakeExt::new(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(); - let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("Realloc should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } + let mut interpreter = wasm_interpreter(); + let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { panic!("Realloc should return payload"); }, + GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), + } }; - assert_eq!(gas_left, U256::from(98326)); + assert_eq!(gas_left, U256::from(99432)); assert_eq!(result, vec![0u8; 2]); } + +// Tests that contract's ability to read from a storage +// Test prepopulates address into storage, than executes a contract which read that address from storage and write this address into result +#[test] +fn storage_read() { + let code = load_sample!("storage_read.wasm"); + let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + let mut ext = FakeExt::new(); + ext.store.insert("0100000000000000000000000000000000000000000000000000000000000000".into(), address.into()); + + let (gas_left, result) = { + let mut interpreter = wasm_interpreter(); + let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { panic!("storage_read should return payload"); }, + GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), + } + }; + + assert_eq!(gas_left, U256::from(99682)); + assert_eq!(Address::from(&result[12..32]), address); +} + + +// Tests that contract's ability to read from a storage +// Test prepopulates address into storage, than executes a contract which read that address from storage and write this address into result +#[test] +fn math_add() { + ::ethcore_logger::init_log(); + let code = load_sample!("math.wasm"); + + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + + let mut args = [0u8; 64]; + let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap(); + let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap(); + arg_a.to_big_endian(&mut args[0..32]); + arg_b.to_big_endian(&mut args[32..64]); + params.data = Some(args.to_vec()); + + let (gas_left, result) = { + let mut interpreter = wasm_interpreter(); + let result = interpreter.exec(params, &mut FakeExt::new()).expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { panic!("storage_read should return payload"); }, + GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), + } + }; + + let sum: U256 = (&result[..]).into(); + + assert_eq!(gas_left, U256::from(96284)); + assert_eq!(sum, U256::from_dec_str("1888888888888888888888888888887").unwrap()); +} diff --git a/ethstore/src/account/safe_account.rs b/ethstore/src/account/safe_account.rs index e0512fe8d..478b796e6 100755 --- a/ethstore/src/account/safe_account.rs +++ b/ethstore/src/account/safe_account.rs @@ -14,7 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use ethkey::{KeyPair, sign, Address, Signature, Message, Public}; +use ethkey::{KeyPair, sign, Address, Signature, Message, Public, Secret}; +use crypto::ecdh::agree; use {json, Error, crypto}; use account::Version; use super::crypto::Crypto; @@ -135,6 +136,12 @@ impl SafeAccount { crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) } + /// Agree on shared key. + pub fn agree(&self, password: &str, other: &Public) -> Result { + let secret = self.crypto.secret(password)?; + agree(&secret, other).map_err(From::from) + } + /// Derive public key. pub fn public(&self, password: &str) -> Result { let secret = self.crypto.secret(password)?; diff --git a/ethstore/src/ethstore.rs b/ethstore/src/ethstore.rs index 246671990..f3bb24071 100755 --- a/ethstore/src/ethstore.rs +++ b/ethstore/src/ethstore.rs @@ -97,6 +97,10 @@ impl SimpleSecretStore for EthStore { self.store.sign_derived(account_ref, password, derivation, message) } + fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result { + self.store.agree(account, password, other) + } + fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error> { let account = self.get(account)?; account.decrypt(password, shared_mac, message) @@ -495,18 +499,26 @@ impl SimpleSecretStore for EthMultiStore { fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result { let accounts = self.get_matching(account, password)?; - for account in accounts { - return account.sign(password, message); + match accounts.first() { + Some(ref account) => account.sign(password, message), + None => Err(Error::InvalidPassword), } - Err(Error::InvalidPassword) } fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error> { let accounts = self.get_matching(account, password)?; - for account in accounts { - return account.decrypt(password, shared_mac, message); + match accounts.first() { + Some(ref account) => account.decrypt(password, shared_mac, message), + None => Err(Error::InvalidPassword), + } + } + + fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result { + let accounts = self.get_matching(account, password)?; + match accounts.first() { + Some(ref account) => account.agree(password, other), + None => Err(Error::InvalidPassword), } - Err(Error::InvalidPassword) } fn create_vault(&self, name: &str, password: &str) -> Result<(), Error> { diff --git a/ethstore/src/secret_store.rs b/ethstore/src/secret_store.rs index 2deae023e..e364245b7 100755 --- a/ethstore/src/secret_store.rs +++ b/ethstore/src/secret_store.rs @@ -60,6 +60,8 @@ pub trait SimpleSecretStore: Send + Sync { fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message) -> Result; /// Decrypt a messages with given account. fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error>; + /// Agree on shared key. + fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result; /// Returns all accounts in this secret store. fn accounts(&self) -> Result, Error>; diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index f2621121e..e259eec7a 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -116,6 +116,9 @@ impl trace::VMTracer for Informant { self.stack.extend_from_slice(stack_push); if let Some((pos, data)) = mem_diff { + if self.memory.len() < (pos + data.len()) { + self.memory.resize(pos + data.len(), 0); + } self.memory[pos..pos + data.len()].copy_from_slice(data); } diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index 232642015..bad0ef1f0 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -35,7 +35,7 @@ use docopt::Docopt; use rustc_hex::FromHex; use util::{U256, Bytes, Address}; use ethcore::spec; -use vm::ActionParams; +use vm::{ActionParams, CallType}; mod info; mod display; @@ -47,15 +47,17 @@ EVM implementation for Parity. Copyright 2016, 2017 Parity Technologies (UK) Ltd Usage: - evmbin stats [options] - evmbin [options] - evmbin [-h | --help] + parity-evm stats [options] + parity-evm [options] + parity-evm [-h | --help] Transaction options: --code CODE Contract code as hex (without 0x). + --to ADDRESS Recipient address (without 0x). --from ADDRESS Sender address (without 0x). --input DATA Input data as hex (without 0x). --gas GAS Supplied gas as hex (without 0x). + --gas-price WEI Supplied gas price as hex (without 0x). General options: --json Display verbose results in JSON. @@ -78,16 +80,26 @@ fn main() { fn run(args: Args, mut informant: T) { let from = arg(args.from(), "--from"); + let to = arg(args.to(), "--to"); let code = arg(args.code(), "--code"); let spec = arg(args.spec(), "--chain"); let gas = arg(args.gas(), "--gas"); + let gas_price = arg(args.gas(), "--gas-price"); let data = arg(args.data(), "--input"); + if code.is_none() && to == Address::default() { + die("Either --code or --to is required."); + } + let mut params = ActionParams::default(); + params.call_type = if code.is_none() { CallType::Call } else { CallType::None }; + params.code_address = to; + params.address = to; params.sender = from; params.origin = from; params.gas = gas; - params.code = Some(Arc::new(code)); + params.gas_price = gas_price; + params.code = code.map(Arc::new); params.data = data; informant.set_gas(gas); @@ -99,10 +111,12 @@ fn run(args: Args, mut informant: T) { struct Args { cmd_stats: bool, flag_from: Option, + flag_to: Option, flag_code: Option, flag_gas: Option, + flag_gas_price: Option, flag_input: Option, - flag_spec: Option, + flag_chain: Option, flag_json: bool, } @@ -114,6 +128,13 @@ impl Args { } } + pub fn gas_price(&self) -> Result { + match self.flag_gas_price { + Some(ref gas_price) => gas_price.parse().map_err(to_string), + None => Ok(U256::zero()), + } + } + pub fn from(&self) -> Result { match self.flag_from { Some(ref from) => from.parse().map_err(to_string), @@ -121,10 +142,17 @@ impl Args { } } - pub fn code(&self) -> Result { + pub fn to(&self) -> Result { + match self.flag_to { + Some(ref to) => to.parse().map_err(to_string), + None => Ok(Address::default()), + } + } + + pub fn code(&self) -> Result, String> { match self.flag_code { - Some(ref code) => code.from_hex().map_err(to_string), - None => Err("Code is required!".into()), + Some(ref code) => code.from_hex().map(Some).map_err(to_string), + None => Ok(None), } } @@ -136,7 +164,7 @@ impl Args { } pub fn spec(&self) -> Result { - Ok(match self.flag_spec { + Ok(match self.flag_chain { Some(ref filename) => { let file = fs::File::open(filename).map_err(|e| format!("{}", e))?; spec::Spec::load(::std::env::temp_dir(), file)? @@ -160,3 +188,37 @@ fn die(msg: T) -> ! { println!("{}", msg); ::std::process::exit(-1) } + +#[cfg(test)] +mod tests { + use docopt::Docopt; + use super::{Args, USAGE}; + + fn run>(args: &[T]) -> Args { + Docopt::new(USAGE).and_then(|d| d.argv(args.into_iter()).deserialize()).unwrap() + } + + #[test] + fn should_parse_all_the_options() { + let args = run(&[ + "parity-evm", + "--json", + "--gas", "1", + "--gas-price", "2", + "--from", "0000000000000000000000000000000000000003", + "--to", "0000000000000000000000000000000000000004", + "--code", "05", + "--input", "06", + "--chain", "./testfile", + ]); + + assert_eq!(args.flag_json, true); + assert_eq!(args.gas(), Ok(1.into())); + assert_eq!(args.gas_price(), Ok(2.into())); + assert_eq!(args.from(), Ok(3.into())); + assert_eq!(args.to(), Ok(4.into())); + assert_eq!(args.code(), Ok(Some(vec![05]))); + assert_eq!(args.data(), Ok(Some(vec![06]))); + assert_eq!(args.flag_chain, Some("./testfile".to_owned())); + } +} diff --git a/js/package-lock.json b/js/package-lock.json index 198028e30..5234a5bb7 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.8.11", + "version": "1.8.16", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -8955,7 +8955,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "requires": { "brace-expansion": "1.1.8" } @@ -11417,7 +11417,7 @@ "react-qr-reader": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/react-qr-reader/-/react-qr-reader-1.1.3.tgz", - "integrity": "sha512-ruBF8KaSwUW9nbzjO4rA7/HOCGYZuNUz9od7uBRy8SRBi24nwxWWmwa2z8R6vPGDRglA0y2Qk1aVBuC1olTnHw==", + "integrity": "sha1-dDmnZvyZPLj17u/HLCnblh1AswI=", "requires": { "jsqr": "git+https://github.com/JodusNodus/jsQR.git#5ba1acefa1cbb9b2bc92b49f503f2674e2ec212b", "prop-types": "15.5.10", diff --git a/js/package.json b/js/package.json index 828fb672d..98f91bd31 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.8.11", + "version": "1.8.16", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", diff --git a/js/src/Application/store.js b/js/src/Application/store.js index 8d2093d45..9008997b8 100644 --- a/js/src/Application/store.js +++ b/js/src/Application/store.js @@ -28,11 +28,18 @@ export default class Store { this._migrateStore(); this._api = api; - // Show the first run if it hasn't been shown before - // (thus an undefined value) - this.firstrunVisible = store.get(LS_FIRST_RUN_KEY) === undefined; - this._checkAccounts(); + // Show the first run the storage doesn't hold `false` value + const firstrunVisible = store.get(LS_FIRST_RUN_KEY) !== false; + + // Only check accounts if we might show the first run + if (firstrunVisible) { + api.transport.once('open', () => { + this._checkAccounts(); + }); + } else { + this.firstrunVisible = false; + } } @action closeFirstrun = () => { @@ -50,7 +57,7 @@ export default class Store { } /** - * Migrate the old LocalStorage ket format + * Migrate the old LocalStorage key format * to the new one */ _migrateStore () { @@ -70,12 +77,16 @@ export default class Store { this._api.parity.allAccountsInfo() ]) .then(([ vaults, info ]) => { - const accounts = Object.keys(info).filter((address) => info[address].uuid); + const accounts = Object.keys(info) + .filter((address) => info[address].uuid) + // In DEV mode, the empty phrase account is already added + .filter((address) => address.toLowerCase() !== '0x00a329c0648769a73afac7f9381e08fb43dbea72'); + // Has accounts if any vaults or accounts const hasAccounts = (accounts && accounts.length > 0) || (vaults && vaults.length > 0); // Show First Run if no accounts and no vaults - this.toggleFirstrun(this.firstrunVisible || !hasAccounts); + this.toggleFirstrun(!hasAccounts); }) .catch((error) => { console.error('checkAccounts', error); diff --git a/js/src/embed.js b/js/src/embed.js index d9fe7182e..a1a2573d1 100644 --- a/js/src/embed.js +++ b/js/src/embed.js @@ -59,14 +59,19 @@ class FakeTransport { class FrameSecureApi extends SecureApi { constructor (transport) { - super(transport.uiUrl, null, () => { - return transport; - }); + super( + transport.uiUrl, + null, + () => transport, + () => 'http:' + ); } connect () { // Do nothing - this API does not need connecting this.emit('connecting'); + // Fetch settings + this._fetchSettings(); // Fire connected event with some delay. setTimeout(() => { this.emit('connected'); diff --git a/js/src/secureApi.js b/js/src/secureApi.js index 5523f1d76..f20859d83 100644 --- a/js/src/secureApi.js +++ b/js/src/secureApi.js @@ -325,7 +325,8 @@ export default class SecureApi extends Api { _fetchSettings () { return Promise .all([ - this._uiApi.parity.dappsUrl(), + // ignore dapps disabled errors + this._uiApi.parity.dappsUrl().catch(() => null), this._uiApi.parity.wsUrl() ]) .then(([dappsUrl, wsUrl]) => { diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index a98d62493..a49717085 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -76,8 +76,10 @@ path = "$HOME/.parity/dapps" user = "test_user" pass = "test_pass" -[secretstore] +[secretstore] disable = false +disable_http = false +disable_acl_check = false nodes = [] http_interface = "local" http_port = 8082 diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index b978918ce..b5b949cc7 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -195,7 +195,7 @@ usage! { or |c: &Config| otry!(c.websockets).interface.clone(), flag_ws_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore,shh,shh_pubsub", or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")), - flag_ws_origins: String = "chrome-extension://*", + flag_ws_origins: String = "chrome-extension://*,moz-extension://*", or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")), flag_ws_hosts: String = "none", or |c: &Config| otry!(c.websockets).hosts.as_ref().map(|vec| vec.join(",")), @@ -217,6 +217,10 @@ usage! { // Secret Store flag_no_secretstore: bool = false, or |c: &Config| otry!(c.secretstore).disable.clone(), + flag_no_secretstore_http: bool = false, + or |c: &Config| otry!(c.secretstore).disable_http.clone(), + flag_no_secretstore_acl_check: bool = false, + or |c: &Config| otry!(c.secretstore).disable_acl_check.clone(), flag_secretstore_secret: Option = None, or |c: &Config| otry!(c.secretstore).self_secret.clone().map(Some), flag_secretstore_nodes: String = "", @@ -520,6 +524,8 @@ struct Dapps { #[derive(Default, Debug, PartialEq, Deserialize)] struct SecretStore { disable: Option, + disable_http: Option, + disable_acl_check: Option, self_secret: Option, nodes: Option>, interface: Option, @@ -796,6 +802,8 @@ mod tests { flag_no_dapps: false, flag_no_secretstore: false, + flag_no_secretstore_http: false, + flag_no_secretstore_acl_check: false, flag_secretstore_secret: None, flag_secretstore_nodes: "".into(), flag_secretstore_interface: "local".into(), @@ -1031,6 +1039,8 @@ mod tests { }), secretstore: Some(SecretStore { disable: None, + disable_http: None, + disable_acl_check: None, self_secret: None, nodes: None, interface: None, diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index cb090dced..dc1205ecf 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -234,6 +234,8 @@ API and Console Options: Secret Store Options: --no-secretstore Disable Secret Store functionality. (default: {flag_no_secretstore}) + --no-secretstore-http Disable Secret Store HTTP API. (default: {flag_no_secretstore_http}) + --no-acl-check Disable ACL check (useful for test environments). (default: {flag_no_secretstore_acl_check}) --secretstore-secret SECRET Hex-encoded secret key of this node. (required, default: {flag_secretstore_secret:?}). --secretstore-nodes NODES Comma-separated list of other secret store cluster nodes in form diff --git a/parity/configuration.rs b/parity/configuration.rs index b28d2608e..35a2a7f47 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -20,6 +20,7 @@ use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::collections::BTreeMap; use std::cmp::max; +use std::str::FromStr; use cli::{Args, ArgsError}; use util::{Hashable, H256, U256, Bytes, version_data, Address}; use util::journaldb::Algorithm; @@ -41,7 +42,7 @@ use ethcore_logger::Config as LogConfig; use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path}; use dapps::Configuration as DappsConfiguration; use ipfs::Configuration as IpfsConfiguration; -use secretstore::Configuration as SecretStoreConfiguration; +use secretstore::{Configuration as SecretStoreConfiguration, NodeSecretKey}; use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack}; use run::RunCmd; use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat}; @@ -551,6 +552,10 @@ impl Configuration { Ok(options) } + fn ui_port(&self) -> u16 { + self.args.flag_ports_shift + self.args.flag_ui_port + } + fn ntp_servers(&self) -> Vec { self.args.flag_ntp_servers.split(",").map(str::to_owned).collect() } @@ -560,12 +565,15 @@ impl Configuration { enabled: self.ui_enabled(), ntp_servers: self.ntp_servers(), interface: self.ui_interface(), - port: self.args.flag_ports_shift + self.args.flag_ui_port, + port: self.ui_port(), hosts: self.ui_hosts(), } } fn dapps_config(&self) -> DappsConfiguration { + let dev_ui = if self.args.flag_ui_no_validation { vec![("localhost".to_owned(), 3000)] } else { vec![] }; + let ui_port = self.ui_port(); + DappsConfiguration { enabled: self.dapps_enabled(), ntp_servers: self.ntp_servers(), @@ -575,17 +583,34 @@ impl Configuration { } else { vec![] }, - extra_embed_on: if self.args.flag_ui_no_validation { - vec![("localhost".to_owned(), 3000)] - } else { - vec![] + extra_embed_on: { + let mut extra_embed = dev_ui.clone(); + match self.ui_hosts() { + // In case host validation is disabled allow all frame ancestors + None => extra_embed.push(("*".to_owned(), ui_port)), + Some(hosts) => extra_embed.extend(hosts.into_iter().filter_map(|host| { + let mut it = host.split(":"); + let host = it.next(); + let port = it.next().and_then(|v| u16::from_str(v).ok()); + + match (host, port) { + (Some(host), Some(port)) => Some((host.into(), port)), + (Some(host), None) => Some((host.into(), ui_port)), + _ => None, + } + })), + } + extra_embed }, + extra_script_src: dev_ui, } } fn secretstore_config(&self) -> Result { Ok(SecretStoreConfiguration { enabled: self.secretstore_enabled(), + http_enabled: self.secretstore_http_enabled(), + acl_check_enabled: self.secretstore_acl_check_enabled(), self_secret: self.secretstore_self_secret()?, nodes: self.secretstore_nodes()?, interface: self.secretstore_interface(), @@ -993,10 +1018,13 @@ impl Configuration { self.interface(&self.args.flag_secretstore_http_interface) } - fn secretstore_self_secret(&self) -> Result, String> { + fn secretstore_self_secret(&self) -> Result, String> { match self.args.flag_secretstore_secret { - Some(ref s) => Ok(Some(s.parse() - .map_err(|e| format!("Invalid secret store secret: {}. Error: {:?}", s, e))?)), + Some(ref s) if s.len() == 64 => Ok(Some(NodeSecretKey::Plain(s.parse() + .map_err(|e| format!("Invalid secret store secret: {}. Error: {:?}", s, e))?))), + Some(ref s) if s.len() == 40 => Ok(Some(NodeSecretKey::KeyStore(s.parse() + .map_err(|e| format!("Invalid secret store secret address: {}. Error: {:?}", s, e))?))), + Some(_) => Err(format!("Invalid secret store secret. Must be either existing account address, or hex-encoded private key")), None => Ok(None), } } @@ -1045,6 +1073,14 @@ impl Configuration { !self.args.flag_no_secretstore && cfg!(feature = "secretstore") } + fn secretstore_http_enabled(&self) -> bool { + !self.args.flag_no_secretstore_http && cfg!(feature = "secretstore") + } + + fn secretstore_acl_check_enabled(&self) -> bool { + !self.args.flag_no_secretstore_acl_check + } + fn ui_enabled(&self) -> bool { if self.args.flag_force_ui { return true; @@ -1275,7 +1311,7 @@ mod tests { interface: "127.0.0.1".into(), port: 8546, apis: ApiSet::UnsafeContext, - origins: Some(vec!["chrome-extension://*".into()]), + origins: Some(vec!["chrome-extension://*".into(), "moz-extension://*".into()]), hosts: Some(vec![]), signer_path: expected.into(), ui_address: Some(("127.0.0.1".to_owned(), 8180)), @@ -1350,6 +1386,7 @@ mod tests { whisper: Default::default(), }; expected.secretstore_conf.enabled = cfg!(feature = "secretstore"); + expected.secretstore_conf.http_enabled = cfg!(feature = "secretstore"); assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(expected)); } diff --git a/parity/dapps.rs b/parity/dapps.rs index a67b3fe3a..fddd050f5 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -40,6 +40,7 @@ pub struct Configuration { pub dapps_path: PathBuf, pub extra_dapps: Vec, pub extra_embed_on: Vec<(String, u16)>, + pub extra_script_src: Vec<(String, u16)>, } impl Default for Configuration { @@ -56,6 +57,7 @@ impl Default for Configuration { dapps_path: replace_home(&data_dir, "$BASE/dapps").into(), extra_dapps: vec![], extra_embed_on: vec![], + extra_script_src: vec![], } } } @@ -168,6 +170,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result, _dapps_domain: &str, _extra_embed_on: Vec<(String, u16)>, + _extra_script_src: Vec<(String, u16)>, ) -> Result { Err("Your Parity version has been compiled without WebApps support.".into()) } @@ -251,6 +255,7 @@ mod server { extra_dapps: Vec, dapps_domain: &str, extra_embed_on: Vec<(String, u16)>, + extra_script_src: Vec<(String, u16)>, ) -> Result { let signer = deps.signer; let parity_remote = parity_reactor::Remote::new(deps.remote.clone()); @@ -262,6 +267,7 @@ mod server { parity_remote, deps.ui_address, extra_embed_on, + extra_script_src, dapps_path, extra_dapps, dapps_domain, @@ -291,7 +297,7 @@ mod server { pub fn service(middleware: &Option) -> Option> { middleware.as_ref().map(|m| Arc::new(DappsServiceWrapper { - endpoints: m.endpoints() + endpoints: m.endpoints().clone(), }) as Arc) } @@ -313,5 +319,10 @@ mod server { }) .collect() } + + fn refresh_local_dapps(&self) -> bool { + self.endpoints.refresh_local_dapps(); + true + } } } diff --git a/parity/rpc.rs b/parity/rpc.rs index 9173e7b2f..1e148dad2 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -94,7 +94,7 @@ impl From for HttpConfiguration { enabled: conf.enabled, interface: conf.interface, port: conf.port, - apis: rpc_apis::ApiSet::SafeContext, + apis: rpc_apis::ApiSet::UnsafeContext, cors: None, hosts: conf.hosts, server_threads: None, @@ -163,7 +163,7 @@ impl Default for WsConfiguration { interface: "127.0.0.1".into(), port: 8546, apis: ApiSet::UnsafeContext, - origins: Some(vec!["chrome-extension://*".into()]), + origins: Some(vec!["chrome-extension://*".into(), "moz-extension://*".into()]), hosts: Some(Vec::new()), signer_path: replace_home(&data_dir, "$BASE/signer").into(), support_token_api: true, diff --git a/parity/run.rs b/parity/run.rs index 0c5f11bbb..49624480c 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -56,7 +56,7 @@ use signer; use url; // how often to take periodic snapshots. -const SNAPSHOT_PERIOD: u64 = 10000; +const SNAPSHOT_PERIOD: u64 = 5000; // how many blocks to wait before starting a periodic snapshot. const SNAPSHOT_HISTORY: u64 = 100; @@ -507,7 +507,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R } // Attempt to sign in the engine signer. - if !passwords.into_iter().any(|p| miner.set_engine_signer(engine_signer, p).is_ok()) { + if !passwords.iter().any(|p| miner.set_engine_signer(engine_signer, (*p).clone()).is_ok()) { return Err(format!("No valid password for the consensus signer {}. {}", engine_signer, VERIFY_PASSWORD_HINT)); } } @@ -734,6 +734,8 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R // secret store key server let secretstore_deps = secretstore::Dependencies { client: client.clone(), + account_provider: account_provider, + accounts_passwords: &passwords, }; let secretstore_key_server = secretstore::start(cmd.secretstore_conf.clone(), secretstore_deps)?; diff --git a/parity/secretstore.rs b/parity/secretstore.rs index f215c937c..eb5922540 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -17,17 +17,32 @@ use std::collections::BTreeMap; use std::sync::Arc; use dir::default_data_path; +use ethcore::account_provider::AccountProvider; use ethcore::client::Client; use ethkey::{Secret, Public}; use helpers::replace_home; +use util::Address; + +#[derive(Debug, PartialEq, Clone)] +/// This node secret key. +pub enum NodeSecretKey { + /// Stored as plain text in configuration file. + Plain(Secret), + /// Stored as account in key store. + KeyStore(Address), +} #[derive(Debug, PartialEq, Clone)] /// Secret store configuration pub struct Configuration { /// Is secret store functionality enabled? pub enabled: bool, + /// Is HTTP API enabled? + pub http_enabled: bool, + /// Is ACL check enabled. + pub acl_check_enabled: bool, /// This node secret. - pub self_secret: Option, + pub self_secret: Option, /// Other nodes IDs + addresses. pub nodes: BTreeMap, /// Interface to listen to @@ -43,9 +58,13 @@ pub struct Configuration { } /// Secret store dependencies -pub struct Dependencies { +pub struct Dependencies<'a> { /// Blockchain client. pub client: Arc, + /// Account provider. + pub account_provider: Arc, + /// Passed accounts passwords. + pub accounts_passwords: &'a [String], } #[cfg(not(feature = "secretstore"))] @@ -65,9 +84,11 @@ mod server { #[cfg(feature="secretstore")] mod server { + use std::sync::Arc; use ethcore_secretstore; use ethkey::KeyPair; - use super::{Configuration, Dependencies}; + use ansi_term::Colour::Red; + use super::{Configuration, Dependencies, NodeSecretKey}; /// Key server pub struct KeyServer { @@ -76,17 +97,45 @@ mod server { impl KeyServer { /// Create new key server - pub fn new(conf: Configuration, deps: Dependencies) -> Result { - let self_secret = conf.self_secret.ok_or("self secret is required when using secretstore")?; - let mut conf = ethcore_secretstore::ServiceConfiguration { - listener_address: ethcore_secretstore::NodeAddress { + pub fn new(mut conf: Configuration, deps: Dependencies) -> Result { + if !conf.acl_check_enabled { + warn!("Running SecretStore with disabled ACL check: {}", Red.bold().paint("everyone has access to stored keys")); + } + + let self_secret: Arc = match conf.self_secret.take() { + Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( + KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), + Some(NodeSecretKey::KeyStore(account)) => { + // Check if account exists + if !deps.account_provider.has_account(account.clone()).unwrap_or(false) { + return Err(format!("Account {} passed as secret store node key is not found", account)); + } + + // Check if any passwords have been read from the password file(s) + if deps.accounts_passwords.is_empty() { + return Err(format!("No password found for the secret store node account {}", account)); + } + + // Attempt to sign in the engine signer. + let password = deps.accounts_passwords.iter() + .find(|p| deps.account_provider.sign(account.clone(), Some((*p).clone()), Default::default()).is_ok()) + .ok_or(format!("No valid password for the secret store node account {}", account))?; + Arc::new(ethcore_secretstore::KeyStoreNodeKeyPair::new(deps.account_provider, account, password.clone()) + .map_err(|e| format!("{}", e))?) + }, + None => return Err("self secret is required when using secretstore".into()), + }; + + let key_server_name = format!("{}:{}", conf.interface, conf.port); + let mut cconf = ethcore_secretstore::ServiceConfiguration { + listener_address: if conf.http_enabled { Some(ethcore_secretstore::NodeAddress { address: conf.http_interface.clone(), port: conf.http_port, - }, + }) } else { None }, data_path: conf.data_path.clone(), + acl_check_enabled: conf.acl_check_enabled, cluster_config: ethcore_secretstore::ClusterConfiguration { threads: 4, - self_private: (**self_secret).into(), listener_address: ethcore_secretstore::NodeAddress { address: conf.interface.clone(), port: conf.port, @@ -99,12 +148,10 @@ mod server { }, }; - let self_key_pair = KeyPair::from_secret(self_secret.clone()) - .map_err(|e| format!("valid secret is required when using secretstore. Error: {}", e))?; - conf.cluster_config.nodes.insert(self_key_pair.public().clone(), conf.cluster_config.listener_address.clone()); + cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone()); - let key_server = ethcore_secretstore::start(deps.client, conf) - .map_err(Into::::into)?; + let key_server = ethcore_secretstore::start(deps.client, self_secret, cconf) + .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; Ok(KeyServer { _key_server: key_server, @@ -120,6 +167,8 @@ impl Default for Configuration { let data_dir = default_data_path(); Configuration { enabled: true, + http_enabled: true, + acl_check_enabled: true, self_secret: None, nodes: BTreeMap::new(), interface: "127.0.0.1".to_owned(), diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 2619c754f..73230c24e 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -24,6 +24,7 @@ serde_json = "1.0" time = "0.1" tokio-timer = "0.1" transient-hashmap = "0.4" +itertools = "0.5" jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } diff --git a/rpc/src/authcodes.rs b/rpc/src/authcodes.rs index 57de437ab..4427eda78 100644 --- a/rpc/src/authcodes.rs +++ b/rpc/src/authcodes.rs @@ -18,9 +18,10 @@ use std::io::{self, Read, Write}; use std::path::Path; use std::{fs, time, mem}; +use itertools::Itertools; use rand::Rng; use rand::os::OsRng; -use util::{H256, Hashable, Itertools}; +use util::{H256, Hashable}; /// Providing current time in seconds pub trait TimeProvider { diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index fec19f78a..e75d187de 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -24,6 +24,7 @@ extern crate cid; extern crate crypto as rust_crypto; extern crate futures; extern crate futures_cpupool; +extern crate itertools; extern crate multihash; extern crate order_stat; extern crate rand; diff --git a/rpc/src/v1/helpers/dapps.rs b/rpc/src/v1/helpers/dapps.rs index 34f4fe1b5..391a12c82 100644 --- a/rpc/src/v1/helpers/dapps.rs +++ b/rpc/src/v1/helpers/dapps.rs @@ -22,12 +22,6 @@ use v1::types::LocalDapp; pub trait DappsService: Send + Sync + 'static { /// List available local dapps. fn list_dapps(&self) -> Vec; -} - -impl DappsService for F where - F: Fn() -> Vec + Send + Sync + 'static -{ - fn list_dapps(&self) -> Vec { - (*self)() - } + /// Refresh local dapps list + fn refresh_local_dapps(&self) -> bool; } diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index ddd238fd8..b2602d6a1 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -133,7 +133,7 @@ impl Dispatcher for FullDispatcher BoxFuture, Error> { let (client, miner) = (self.client.clone(), self.miner.clone()); - let network_id = client.signing_network_id(); + let chain_id = client.signing_chain_id(); let address = filled.from; future::done({ let t = Transaction { @@ -146,12 +146,12 @@ impl Dispatcher for FullDispatcher, filled: FilledTransactionRequest, password: SignWith) -> BoxFuture, Error> { - let network_id = self.client.signing_network_id(); + let chain_id = self.client.signing_chain_id(); let address = filled.from; let with_nonce = move |filled: FilledTransactionRequest, nonce| { @@ -372,14 +372,14 @@ impl Dispatcher for LightDispatcher { }; if accounts.is_hardware_address(address) { - return hardware_signature(&*accounts, address, t, network_id).map(WithToken::No) + return hardware_signature(&*accounts, address, t, chain_id).map(WithToken::No) } - let hash = t.hash(network_id); + let hash = t.hash(chain_id); let signature = signature(&*accounts, address, hash, password)?; Ok(signature.map(|sig| { - SignedTransaction::new(t.with_signature(sig, network_id)) + SignedTransaction::new(t.with_signature(sig, chain_id)) .expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed") })) }; @@ -512,6 +512,10 @@ pub fn execute( ).boxed() }, ConfirmationPayload::EthSignMessage(address, data) => { + if accounts.is_hardware_address(address) { + return future::err(errors::unsupported("Signing via hardware wallets is not supported.", None)).boxed(); + } + let hash = eth_data_hash(data); let res = signature(&accounts, address, hash, pass) .map(|result| result @@ -522,6 +526,10 @@ pub fn execute( future::done(res).boxed() }, ConfirmationPayload::Decrypt(address, data) => { + if accounts.is_hardware_address(address) { + return future::err(errors::unsupported("Decrypting via hardware wallets is not supported.", None)).boxed(); + } + let res = decrypt(&accounts, address, data, pass) .map(|result| result .map(RpcBytes) @@ -544,20 +552,20 @@ fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: } // obtain a hardware signature from the given account. -fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transaction, network_id: Option) +fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transaction, chain_id: Option) -> Result { debug_assert!(accounts.is_hardware_address(address)); let mut stream = rlp::RlpStream::new(); - t.rlp_append_unsigned_transaction(&mut stream, network_id); + t.rlp_append_unsigned_transaction(&mut stream, chain_id); let signature = accounts.sign_with_hardware(address, &stream.as_raw()) .map_err(|e| { debug!(target: "miner", "Error signing transaction with hardware wallet: {}", e); errors::account("Error signing transaction with hardware wallet", e) })?; - SignedTransaction::new(t.with_signature(signature, network_id)) + SignedTransaction::new(t.with_signature(signature, chain_id)) .map_err(|e| { debug!(target: "miner", "Hardware wallet has produced invalid signature: {}", e); errors::account("Invalid signature generated", e) diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 9be54991f..559f3a6b9 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -71,6 +71,14 @@ pub fn public_unsupported(details: Option) -> Error { } } +pub fn unsupported>(msg: T, details: Option) -> Error { + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: msg.into(), + data: details.map(Into::into).map(Value::String), + } +} + pub fn request_not_found() -> Error { Error { code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND), @@ -302,7 +310,7 @@ pub fn transaction_message(error: TransactionError) -> String { GasLimitExceeded { limit, got } => { format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got) }, - InvalidNetworkId => "Invalid network id.".into(), + InvalidChainId => "Invalid chain id.".into(), InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(), SenderBanned => "Sender is banned in local queue.".into(), RecipientBanned => "Recipient is banned in local queue.".into(), diff --git a/rpc/src/v1/helpers/fake_sign.rs b/rpc/src/v1/helpers/fake_sign.rs index 21cb0cfe1..c5b95f6a4 100644 --- a/rpc/src/v1/helpers/fake_sign.rs +++ b/rpc/src/v1/helpers/fake_sign.rs @@ -18,6 +18,7 @@ use std::sync::Arc; use ethcore::client::MiningBlockChainClient; use ethcore::miner::MinerService; use ethcore::transaction::{Transaction, SignedTransaction, Action}; +use util::U256; use jsonrpc_core::Error; use v1::helpers::CallRequest; @@ -27,13 +28,22 @@ pub fn sign_call( client: &Arc, miner: &Arc, request: CallRequest, + gas_cap: bool, ) -> Result { let from = request.from.unwrap_or(0.into()); + let mut gas = request.gas.unwrap_or(U256::max_value()); + if gas_cap { + let max_gas = 50_000_000.into(); + if gas > max_gas { + warn!("Gas limit capped to {} (from {})", max_gas, gas); + gas = max_gas + } + } Ok(Transaction { nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)), action: request.to.map_or(Action::Create, Action::Call), - gas: request.gas.unwrap_or(50_000_000.into()), + gas, gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(&**client, &**miner)), value: request.value.unwrap_or(0.into()), data: request.data.unwrap_or_default(), diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 7c656c752..0cf70bf23 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -612,9 +612,9 @@ impl Eth for EthClient where self.send_raw_transaction(raw) } - fn call(&self, request: CallRequest, num: Trailing) -> BoxFuture { + fn call(&self, meta: Self::Metadata, request: CallRequest, num: Trailing) -> BoxFuture { let request = CallRequest::into(request); - let signed = match fake_sign::sign_call(&self.client, &self.miner, request) { + let signed = match fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp()) { Ok(signed) => signed, Err(e) => return future::err(e).boxed(), }; @@ -628,9 +628,9 @@ impl Eth for EthClient where ).boxed() } - fn estimate_gas(&self, request: CallRequest, num: Trailing) -> BoxFuture { + fn estimate_gas(&self, meta: Self::Metadata, request: CallRequest, num: Trailing) -> BoxFuture { let request = CallRequest::into(request); - let signed = match fake_sign::sign_call(&self.client, &self.miner, request) { + let signed = match fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp()) { Ok(signed) => signed, Err(e) => return future::err(e).boxed(), }; diff --git a/rpc/src/v1/impls/eth_pubsub.rs b/rpc/src/v1/impls/eth_pubsub.rs index 6ba88be21..f918a44ea 100644 --- a/rpc/src/v1/impls/eth_pubsub.rs +++ b/rpc/src/v1/impls/eth_pubsub.rs @@ -245,17 +245,27 @@ impl EthPubSub for EthPubSubClient { kind: pubsub::Kind, params: Trailing, ) { - match (kind, params.into()) { + let error = match (kind, params.into()) { (pubsub::Kind::NewHeads, None) => { - self.heads_subscribers.write().push(subscriber) + self.heads_subscribers.write().push(subscriber); + return; }, (pubsub::Kind::Logs, Some(pubsub::Params::Logs(filter))) => { self.logs_subscribers.write().push(subscriber, filter.into()); + return; + }, + (pubsub::Kind::NewHeads, _) => { + errors::invalid_params("newHeads", "Expected no parameters.") + }, + (pubsub::Kind::Logs, _) => { + errors::invalid_params("logs", "Expected a filter object.") }, _ => { - let _ = subscriber.reject(errors::unimplemented(None)); + errors::unimplemented(None) }, - } + }; + + let _ = subscriber.reject(error); } fn unsubscribe(&self, id: SubscriptionId) -> BoxFuture { diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index fd03f1226..d33b5a06f 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -383,7 +383,7 @@ impl Eth for EthClient { self.send_raw_transaction(raw) } - fn call(&self, req: CallRequest, num: Trailing) -> BoxFuture { + fn call(&self, _meta: Self::Metadata, req: CallRequest, num: Trailing) -> BoxFuture { self.fetcher().proved_execution(req, num).and_then(|res| { match res { Ok(exec) => Ok(exec.output.into()), @@ -392,7 +392,7 @@ impl Eth for EthClient { }).boxed() } - fn estimate_gas(&self, req: CallRequest, num: Trailing) -> BoxFuture { + fn estimate_gas(&self, _meta: Self::Metadata, req: CallRequest, num: Trailing) -> BoxFuture { // TODO: binary chop for more accurate estimates. self.fetcher().proved_execution(req, num).and_then(|res| { match res { diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 62a7721fc..676e39b11 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -390,7 +390,7 @@ impl Parity for ParityClient { ipfs::cid(content) } - fn call(&self, _requests: Vec, _block: Trailing) -> BoxFuture, Error> { + fn call(&self, _meta: Self::Metadata, _requests: Vec, _block: Trailing) -> BoxFuture, Error> { future::err(errors::light_unimplemented(None)).boxed() } } diff --git a/rpc/src/v1/impls/light/parity_set.rs b/rpc/src/v1/impls/light/parity_set.rs index fdeee693f..b97327168 100644 --- a/rpc/src/v1/impls/light/parity_set.rs +++ b/rpc/src/v1/impls/light/parity_set.rs @@ -135,6 +135,10 @@ impl ParitySet for ParitySetClient { })) } + fn dapps_refresh(&self) -> Result { + self.dapps.as_ref().map(|dapps| dapps.refresh_local_dapps()).ok_or_else(errors::dapps_disabled) + } + fn dapps_list(&self) -> Result, Error> { self.dapps.as_ref().map(|dapps| dapps.list_dapps()).ok_or_else(errors::dapps_disabled) } diff --git a/rpc/src/v1/impls/light/trace.rs b/rpc/src/v1/impls/light/trace.rs index 81ed592c5..22360183d 100644 --- a/rpc/src/v1/impls/light/trace.rs +++ b/rpc/src/v1/impls/light/trace.rs @@ -17,7 +17,9 @@ //! Traces api implementation. use jsonrpc_core::Error; +use jsonrpc_core::futures::{future, Future, BoxFuture}; use jsonrpc_macros::Trailing; +use v1::Metadata; use v1::traits::Traces; use v1::helpers::errors; use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, TraceOptions, H256}; @@ -27,6 +29,8 @@ use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, By pub struct TracesClient; impl Traces for TracesClient { + type Metadata = Metadata; + fn filter(&self, _filter: TraceFilter) -> Result>, Error> { Err(errors::light_unimplemented(None)) } @@ -43,12 +47,12 @@ impl Traces for TracesClient { Err(errors::light_unimplemented(None)) } - fn call(&self, _request: CallRequest, _flags: TraceOptions, _block: Trailing) -> Result { - Err(errors::light_unimplemented(None)) + fn call(&self, _meta: Self::Metadata, _request: CallRequest, _flags: TraceOptions, _block: Trailing) -> BoxFuture { + future::err(errors::light_unimplemented(None)).boxed() } - fn call_many(&self, _request: Vec<(CallRequest, TraceOptions)>, _block: Trailing) -> Result, Error> { - Err(errors::light_unimplemented(None)) + fn call_many(&self, _meta: Self::Metadata, _request: Vec<(CallRequest, TraceOptions)>, _block: Trailing) -> BoxFuture, Error> { + future::err(errors::light_unimplemented(None)).boxed() } fn raw_transaction(&self, _raw_transaction: Bytes, _flags: TraceOptions, _block: Trailing) -> Result { diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index d855d7e17..fb82c5205 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -411,11 +411,11 @@ impl Parity for ParityClient where ipfs::cid(content) } - fn call(&self, requests: Vec, block: Trailing) -> BoxFuture, Error> { + fn call(&self, meta: Self::Metadata, requests: Vec, block: Trailing) -> BoxFuture, Error> { let requests: Result, Error> = requests .into_iter() .map(|request| Ok(( - fake_sign::sign_call(&self.client, &self.miner, request.into())?, + fake_sign::sign_call(&self.client, &self.miner, request.into(), meta.is_dapp())?, Default::default() ))) .collect(); diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index dc771c4f8..540964e63 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -176,6 +176,10 @@ impl ParitySet for ParitySetClient where })) } + fn dapps_refresh(&self) -> Result { + self.dapps.as_ref().map(|dapps| dapps.refresh_local_dapps()).ok_or_else(errors::dapps_disabled) + } + fn dapps_list(&self) -> Result, Error> { self.dapps.as_ref().map(|dapps| dapps.list_dapps()).ok_or_else(errors::dapps_disabled) } diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 8028168fd..302190fd4 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -124,9 +124,9 @@ impl Personal for PersonalClient { .map(move |tx| (tx, dispatcher)) }) .and_then(|(pending_tx, dispatcher)| { - let network_id = pending_tx.network_id(); - trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", - ::rlp::encode(&*pending_tx).into_vec().pretty(), network_id); + let chain_id = pending_tx.chain_id(); + trace!(target: "miner", "send_transaction: dispatching tx: {} for chain ID {:?}", + ::rlp::encode(&*pending_tx).into_vec().pretty(), chain_id); dispatcher.dispatch_transaction(pending_tx).map(Into::into) }) diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 31a0e889e..9ed2c39a4 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -18,13 +18,15 @@ use std::sync::Arc; -use rlp::UntrustedRlp; use ethcore::client::{MiningBlockChainClient, CallAnalytics, TransactionId, TraceId}; use ethcore::miner::MinerService; use ethcore::transaction::SignedTransaction; +use rlp::UntrustedRlp; use jsonrpc_core::Error; +use jsonrpc_core::futures::{self, Future, BoxFuture}; use jsonrpc_macros::Trailing; +use v1::Metadata; use v1::traits::Traces; use v1::helpers::{errors, fake_sign}; use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, TraceOptions, H256}; @@ -54,6 +56,8 @@ impl TracesClient { } impl Traces for TracesClient where C: MiningBlockChainClient + 'static, M: MinerService + 'static { + type Metadata = Metadata; + fn filter(&self, filter: TraceFilter) -> Result>, Error> { Ok(self.client.filter_traces(filter.into()) .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) @@ -79,31 +83,35 @@ impl Traces for TracesClient where C: MiningBlockChainClient + 'stat .map(LocalizedTrace::from)) } - fn call(&self, request: CallRequest, flags: TraceOptions, block: Trailing) -> Result { + fn call(&self, meta: Self::Metadata, request: CallRequest, flags: TraceOptions, block: Trailing) -> BoxFuture { let block = block.unwrap_or_default(); let request = CallRequest::into(request); - let signed = fake_sign::sign_call(&self.client, &self.miner, request)?; + let signed = try_bf!(fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp())); - self.client.call(&signed, to_call_analytics(flags), block.into()) + let res = self.client.call(&signed, to_call_analytics(flags), block.into()) .map(TraceResults::from) - .map_err(errors::call) + .map_err(errors::call); + + futures::done(res).boxed() } - fn call_many(&self, requests: Vec<(CallRequest, TraceOptions)>, block: Trailing) -> Result, Error> { + fn call_many(&self, meta: Self::Metadata, requests: Vec<(CallRequest, TraceOptions)>, block: Trailing) -> BoxFuture, Error> { let block = block.unwrap_or_default(); - let requests = requests.into_iter() + let requests = try_bf!(requests.into_iter() .map(|(request, flags)| { let request = CallRequest::into(request); - let signed = fake_sign::sign_call(&self.client, &self.miner, request)?; + let signed = fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp())?; Ok((signed, to_call_analytics(flags))) }) - .collect::, _>>()?; + .collect::, Error>>()); - self.client.call_many(&requests, block.into()) + let res = self.client.call_many(&requests, block.into()) .map(|results| results.into_iter().map(TraceResults::from).collect()) - .map_err(errors::call) + .map_err(errors::call); + + futures::done(res).boxed() } fn raw_transaction(&self, raw_transaction: Bytes, flags: TraceOptions, block: Trailing) -> Result { diff --git a/rpc/src/v1/metadata.rs b/rpc/src/v1/metadata.rs index b65003ad8..f0644d455 100644 --- a/rpc/src/v1/metadata.rs +++ b/rpc/src/v1/metadata.rs @@ -42,6 +42,15 @@ impl Metadata { _ => DappId::default(), } } + + /// Returns true if the request originates from a Dapp. + pub fn is_dapp(&self) -> bool { + if let Origin::Dapps(_) = self.origin { + true + } else { + false + } + } } impl jsonrpc_core::Metadata for Metadata {} diff --git a/rpc/src/v1/tests/helpers/dapps.rs b/rpc/src/v1/tests/helpers/dapps.rs index 44148ab0f..6a7182dd0 100644 --- a/rpc/src/v1/tests/helpers/dapps.rs +++ b/rpc/src/v1/tests/helpers/dapps.rs @@ -34,4 +34,8 @@ impl DappsService for TestDappsService { icon_url: "title.png".into(), }] } + + fn refresh_local_dapps(&self) -> bool { + true + } } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index f935b93e2..d1835948f 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -544,7 +544,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", @@ -860,12 +860,13 @@ fn rpc_eth_sign_transaction() { let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + r#""raw":"0x"# + &rlp.to_hex() + r#"","# + r#""tx":{"# + - r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# + + r#""blockHash":null,"blockNumber":null,"# + + &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + + r#""condition":null,"creates":null,"# + &format!("\"from\":\"0x{:?}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + r#""input":"0x","# + - &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.recover_public().unwrap()) + &format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) + diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 443379b4f..22e0388e2 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -233,7 +233,7 @@ fn rpc_parity_remove_transaction() { let hash = signed.hash(); let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""#.to_owned() + &format!("0x{:?}", hash) + r#""], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","networkId":null,"nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; miner.pending_transactions.lock().insert(hash, signed); assert_eq!(io.handle_request_sync(&request), Some(response.to_owned())); diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index f92486d16..6827334de 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -454,12 +454,13 @@ fn should_confirm_sign_transaction_with_rlp() { let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + r#""raw":"0x"# + &rlp.to_hex() + r#"","# + r#""tx":{"# + - r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# + + r#""blockHash":null,"blockNumber":null,"# + + &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + + r#""condition":null,"creates":null,"# + &format!("\"from\":\"0x{:?}\",", &address) + r#""gas":"0x989680","gasPrice":"0x1000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + r#""input":"0x","# + - &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x0","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) + diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 91d792253..4c6951f1a 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -297,12 +297,13 @@ fn should_add_sign_transaction_to_the_queue() { let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + r#""raw":"0x"# + &rlp.to_hex() + r#"","# + r#""tx":{"# + - r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# + + r#""blockHash":null,"blockNumber":null,"# + + &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + + r#""condition":null,"creates":null,"# + &format!("\"from\":\"0x{:?}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + r#""input":"0x","# + - &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) + diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index 50890648c..cd3ae3666 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -25,12 +25,12 @@ use vm::CallType; use jsonrpc_core::IoHandler; use v1::tests::helpers::{TestMinerService}; -use v1::{Traces, TracesClient}; +use v1::{Metadata, Traces, TracesClient}; struct Tester { client: Arc, _miner: Arc, - io: IoHandler, + io: IoHandler, } fn io() -> Tester { @@ -67,7 +67,7 @@ fn io() -> Tester { })); let miner = Arc::new(TestMinerService::default()); let traces = TracesClient::new(&client, &miner); - let mut io = IoHandler::new(); + let mut io = IoHandler::default(); io.extend_with(traces.to_delegate()); Tester { diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 941263335..9e7ed9715 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -110,12 +110,12 @@ build_rpc_trait! { fn submit_transaction(&self, Bytes) -> Result; /// Call contract, returning the output data. - #[rpc(async, name = "eth_call")] - fn call(&self, CallRequest, Trailing) -> BoxFuture; + #[rpc(meta, name = "eth_call")] + fn call(&self, Self::Metadata, CallRequest, Trailing) -> BoxFuture; /// Estimate gas needed for execution of given contract. - #[rpc(async, name = "eth_estimateGas")] - fn estimate_gas(&self, CallRequest, Trailing) -> BoxFuture; + #[rpc(meta, name = "eth_estimateGas")] + fn estimate_gas(&self, Self::Metadata, CallRequest, Trailing) -> BoxFuture; /// Get transaction by its hash. #[rpc(name = "eth_getTransactionByHash")] diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index a0b1a6e99..c9ca522c1 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -205,7 +205,7 @@ build_rpc_trait! { fn ipfs_cid(&self, Bytes) -> Result; /// Call contract, returning the output data. - #[rpc(async, name = "parity_call")] - fn call(&self, Vec, Trailing) -> BoxFuture, Error>; + #[rpc(meta, name = "parity_call")] + fn call(&self, Self::Metadata, Vec, Trailing) -> BoxFuture, Error>; } } diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs index 1feb39718..15f35b671 100644 --- a/rpc/src/v1/traits/parity_set.rs +++ b/rpc/src/v1/traits/parity_set.rs @@ -96,6 +96,10 @@ build_rpc_trait! { #[rpc(async, name = "parity_hashContent")] fn hash_content(&self, String) -> BoxFuture; + /// Returns true if refresh successful, error if unsuccessful or server is disabled. + #[rpc(name = "parity_dappsRefresh")] + fn dapps_refresh(&self) -> Result; + /// Returns a list of local dapps #[rpc(name = "parity_dappsList")] fn dapps_list(&self) -> Result, Error>; diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs index 568788af2..33bcb0afd 100644 --- a/rpc/src/v1/traits/traces.rs +++ b/rpc/src/v1/traits/traces.rs @@ -17,12 +17,15 @@ //! Traces specific rpc interface. use jsonrpc_core::Error; +use jsonrpc_core::futures::BoxFuture; use jsonrpc_macros::Trailing; use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256, TraceOptions}; build_rpc_trait! { /// Traces specific rpc interface. pub trait Traces { + type Metadata; + /// Returns traces matching given filter. #[rpc(name = "trace_filter")] fn filter(&self, TraceFilter) -> Result>, Error>; @@ -40,12 +43,12 @@ build_rpc_trait! { fn block_traces(&self, BlockNumber) -> Result>, Error>; /// Executes the given call and returns a number of possible traces for it. - #[rpc(name = "trace_call")] - fn call(&self, CallRequest, TraceOptions, Trailing) -> Result; + #[rpc(meta, name = "trace_call")] + fn call(&self, Self::Metadata, CallRequest, TraceOptions, Trailing) -> BoxFuture; /// Executes all given calls and returns a number of possible traces for each of it. - #[rpc(name = "trace_callMany")] - fn call_many(&self, Vec<(CallRequest, TraceOptions)>, Trailing) -> Result, Error>; + #[rpc(meta, name = "trace_callMany")] + fn call_many(&self, Self::Metadata, Vec<(CallRequest, TraceOptions)>, Trailing) -> BoxFuture, Error>; /// Executes the given raw transaction and returns a number of possible traces for it. #[rpc(name = "trace_rawTransaction")] diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 525d2c090..10f7a2476 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -214,7 +214,7 @@ impl Serialize for Rich { // and serialize value.serialize(serializer) } else { - Err(S::Error::custom("Unserializable structures.")) + Err(S::Error::custom("Unserializable structures: expected objects")) } } } @@ -230,7 +230,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}]"#); let t = BlockTransactions::Hashes(vec![H256::default().into()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs index 8f3f09f51..334bf360d 100644 --- a/rpc/src/v1/types/block_number.rs +++ b/rpc/src/v1/types/block_number.rs @@ -79,8 +79,12 @@ impl<'a> Visitor<'a> for BlockNumberVisitor { "latest" => Ok(BlockNumber::Latest), "earliest" => Ok(BlockNumber::Earliest), "pending" => Ok(BlockNumber::Pending), - _ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16).map(BlockNumber::Num).map_err(|_| Error::custom("invalid block number")), - _ => value.parse::().map(BlockNumber::Num).map_err(|_| Error::custom("invalid block number")) + _ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16).map(BlockNumber::Num).map_err(|e| { + Error::custom(format!("Invalid block number: {}", e)) + }), + _ => value.parse::().map(BlockNumber::Num).map_err(|e| { + Error::custom(format!("Invalid block number: {}", e)) + }), } } diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index deaa234fc..4bd10af44 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -81,9 +81,9 @@ impl<'a> Visitor<'a> for BytesVisitor { ); Ok(Bytes::new(Vec::new())) } else if value.len() >= 2 && &value[0..2] == "0x" && value.len() & 1 == 0 { - Ok(Bytes::new(FromHex::from_hex(&value[2..]).map_err(|_| Error::custom("invalid hex"))?)) + Ok(Bytes::new(FromHex::from_hex(&value[2..]).map_err(|e| Error::custom(format!("Invalid hex: {}", e)))?)) } else { - Err(Error::custom("invalid format")) + Err(Error::custom("Invalid bytes format. Expected a 0x-prefixed hex string with even length")) } } diff --git a/rpc/src/v1/types/derivation.rs b/rpc/src/v1/types/derivation.rs index cf0f03c85..76becbaeb 100644 --- a/rpc/src/v1/types/derivation.rs +++ b/rpc/src/v1/types/derivation.rs @@ -121,7 +121,7 @@ impl<'a> Visitor<'a> for DerivationTypeVisitor { match value { "soft" => Ok(DerivationType::Soft), "hard" => Ok(DerivationType::Hard), - _ => Err(Error::custom("invalid derivation type")), + v => Err(Error::custom(format!("invalid derivation type: {:?}", v))), } } diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index 8a1181eb8..058d14d7e 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -43,7 +43,7 @@ impl<'a, T> Deserialize<'a> for VariadicValue where T: DeserializeOwned { from_value(v.clone()).map(VariadicValue::Single) .or_else(|_| from_value(v).map(VariadicValue::Multiple)) - .map_err(|_| D::Error::custom("Invalid type.")) + .map_err(|err| D::Error::custom(format!("Invalid variadic value type: {}", err))) } } diff --git a/rpc/src/v1/types/index.rs b/rpc/src/v1/types/index.rs index 3c9d56f6e..4e44ce49c 100644 --- a/rpc/src/v1/types/index.rs +++ b/rpc/src/v1/types/index.rs @@ -47,8 +47,12 @@ impl<'a> Visitor<'a> for IndexVisitor { fn visit_str(self, value: &str) -> Result where E: Error { match value { - _ if value.starts_with("0x") => usize::from_str_radix(&value[2..], 16).map(Index).map_err(|_| Error::custom("invalid index")), - _ => value.parse::().map(Index).map_err(|_| Error::custom("invalid index")), + _ if value.starts_with("0x") => usize::from_str_radix(&value[2..], 16).map(Index).map_err(|e| { + Error::custom(format!("Invalid index: {}", e)) + }), + _ => value.parse::().map(Index).map_err(|e| { + Error::custom(format!("Invalid index: {}", e)) + }), } } diff --git a/rpc/src/v1/types/pubsub.rs b/rpc/src/v1/types/pubsub.rs index ce9d6d38d..e7deeba4f 100644 --- a/rpc/src/v1/types/pubsub.rs +++ b/rpc/src/v1/types/pubsub.rs @@ -84,7 +84,7 @@ impl<'a> Deserialize<'a> for Params { } from_value(v.clone()).map(Params::Logs) - .map_err(|_| D::Error::custom("Invalid type.")) + .map_err(|e| D::Error::custom(format!("Invalid Pub-Sub parameters: {}", e))) } } diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index ee0203d65..d5eb63b44 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -59,8 +59,8 @@ pub struct Transaction { #[serde(rename="publicKey")] pub public_key: Option, /// The network id of the transaction, if any. - #[serde(rename="networkId")] - pub network_id: Option, + #[serde(rename="chainId")] + pub chain_id: Option, /// The standardised V field of the signature (0 or 1). #[serde(rename="standardV")] pub standard_v: U256, @@ -196,7 +196,7 @@ impl Transaction { }, raw: ::rlp::encode(&t.signed).into_vec().into(), public_key: t.recover_public().ok().map(Into::into), - network_id: t.network_id(), + chain_id: t.chain_id(), standard_v: t.standard_v().into(), v: t.original_v().into(), r: signature.r().into(), @@ -230,7 +230,7 @@ impl Transaction { }, raw: ::rlp::encode(&t).into_vec().into(), public_key: t.public_key().map(Into::into), - network_id: t.network_id(), + chain_id: t.chain_id(), standard_v: t.standard_v().into(), v: t.original_v().into(), r: signature.r().into(), @@ -273,7 +273,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}"#); } #[test] diff --git a/rpc_cli/Cargo.toml b/rpc_cli/Cargo.toml index 52fc58fe4..5e407cb74 100644 --- a/rpc_cli/Cargo.toml +++ b/rpc_cli/Cargo.toml @@ -9,7 +9,7 @@ version = "1.4.0" [dependencies] futures = "0.1" rpassword = "0.3.0" -bigint = "3.0" +bigint = "4.0" parity-rpc = { path = "../rpc" } parity-rpc-client = { path = "../rpc_client" } ethcore-util = { path = "../util" } diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 000000000..2bf5ad044 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +stable diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index 37d5bcd25..5b8284c6f 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -15,8 +15,9 @@ // along with Parity. If not, see . use std::sync::{Arc, Weak}; +use std::collections::{HashMap, HashSet}; use futures::{future, Future}; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use ethkey::public_to_address; use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; use native_contracts::SecretStoreAclStorage; @@ -47,6 +48,12 @@ struct CachedContract { contract: Option, } +/// Dummy ACL storage implementation (check always passed). +#[derive(Default, Debug)] +pub struct DummyAclStorage { + prohibited: RwLock>>, +} + impl OnChainAclStorage { pub fn new(client: &Arc) -> Arc { let acl_storage = Arc::new(OnChainAclStorage { @@ -113,36 +120,22 @@ impl CachedContract { } } -#[cfg(test)] -pub mod tests { - use std::collections::{HashMap, HashSet}; - use parking_lot::RwLock; - use types::all::{Error, ServerKeyId, Public}; - use super::AclStorage; - - #[derive(Default, Debug)] - /// Dummy ACL storage implementation - pub struct DummyAclStorage { - prohibited: RwLock>>, - } - - impl DummyAclStorage { - #[cfg(test)] - /// Prohibit given requestor access to given document - pub fn prohibit(&self, public: Public, document: ServerKeyId) { - self.prohibited.write() - .entry(public) - .or_insert_with(Default::default) - .insert(document); - } - } - - impl AclStorage for DummyAclStorage { - fn check(&self, public: &Public, document: &ServerKeyId) -> Result { - Ok(self.prohibited.read() - .get(public) - .map(|docs| !docs.contains(document)) - .unwrap_or(true)) - } +impl DummyAclStorage { + /// Prohibit given requestor access to given documents + #[cfg(test)] + pub fn prohibit(&self, public: Public, document: ServerKeyId) { + self.prohibited.write() + .entry(public) + .or_insert_with(Default::default) + .insert(document); + } +} + +impl AclStorage for DummyAclStorage { + fn check(&self, public: &Public, document: &ServerKeyId) -> Result { + Ok(self.prohibited.read() + .get(public) + .map(|docs| !docs.contains(document)) + .unwrap_or(true)) } } diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index 1f7f14ede..883389365 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -39,7 +39,7 @@ use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, NodeAddr /// To sign message with server key: GET /{server_key_id}/{signature}/{message_hash} pub struct KeyServerHttpListener { - _http_server: HttpListening, + http_server: Option, handler: Arc>, } @@ -74,19 +74,20 @@ struct KeyServerSharedHttpHandler { impl KeyServerHttpListener where T: KeyServer + 'static { /// Start KeyServer http listener - pub fn start(listener_address: &NodeAddress, key_server: T) -> Result { + pub fn start(listener_address: Option, key_server: T) -> Result { let shared_handler = Arc::new(KeyServerSharedHttpHandler { key_server: key_server, }); - let handler = KeyServerHttpHandler { - handler: shared_handler.clone(), - }; - let listener_addr: &str = &format!("{}:{}", listener_address.address, listener_address.port); - let http_server = HttpServer::http(&listener_addr).expect("cannot start HttpServer"); - let http_server = http_server.handle(handler).expect("cannot start HttpServer"); + let http_server = listener_address + .map(|listener_address| format!("{}:{}", listener_address.address, listener_address.port)) + .map(|listener_address| HttpServer::http(&listener_address).expect("cannot start HttpServer")) + .map(|http_server| http_server.handle(KeyServerHttpHandler { + handler: shared_handler.clone(), + }).expect("cannot start HttpServer")); + let listener = KeyServerHttpListener { - _http_server: http_server, + http_server: http_server, handler: shared_handler, }; Ok(listener) @@ -128,7 +129,7 @@ impl MessageSigner for KeyServerHttpListener where T: KeyServer + 'static impl Drop for KeyServerHttpListener where T: KeyServer + 'static { fn drop(&mut self) { // ignore error as we are dropping anyway - let _ = self._http_server.close(); + self.http_server.take().map(|mut s| { let _ = s.close(); }); } } @@ -318,7 +319,7 @@ mod tests { fn http_listener_successfully_drops() { let key_server = DummyKeyServer; let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 }; - let listener = KeyServerHttpListener::start(&address, key_server).unwrap(); + let listener = KeyServerHttpListener::start(Some(address), key_server).unwrap(); drop(listener); } diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index c83e460f3..f9fad19df 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -26,7 +26,7 @@ use super::acl_storage::AclStorage; use super::key_storage::KeyStorage; use super::key_server_set::KeyServerSet; use key_server_cluster::{math, ClusterCore}; -use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; +use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer, NodeKeyPair}; use types::all::{Error, Public, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, ClusterConfiguration, MessageHash, EncryptedMessageSignature}; use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration}; @@ -45,9 +45,9 @@ pub struct KeyServerCore { impl KeyServerImpl { /// Create new key server instance - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, acl_storage: Arc, key_storage: Arc) -> Result { + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, acl_storage: Arc, key_storage: Arc) -> Result { Ok(KeyServerImpl { - data: Arc::new(Mutex::new(KeyServerCore::new(config, key_server_set, acl_storage, key_storage)?)), + data: Arc::new(Mutex::new(KeyServerCore::new(config, key_server_set, self_key_pair, acl_storage, key_storage)?)), }) } @@ -144,10 +144,10 @@ impl MessageSigner for KeyServerImpl { } impl KeyServerCore { - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, acl_storage: Arc, key_storage: Arc) -> Result { + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, acl_storage: Arc, key_storage: Arc) -> Result { let config = NetClusterConfiguration { threads: config.threads, - self_key_pair: ethkey::KeyPair::from_secret_slice(&config.self_private)?, + self_key_pair: self_key_pair, listen_address: (config.listener_address.address.clone(), config.listener_address.port), key_server_set: key_server_set, allow_connecting_to_higher_nodes: config.allow_connecting_to_higher_nodes, @@ -196,8 +196,9 @@ pub mod tests { use std::collections::BTreeMap; use ethcrypto; use ethkey::{self, Secret, Random, Generator}; - use acl_storage::tests::DummyAclStorage; + use acl_storage::DummyAclStorage; use key_storage::tests::DummyKeyStorage; + use node_key_pair::PlainNodeKeyPair; use key_server_set::tests::MapKeyServerSet; use key_server_cluster::math; use util::H256; @@ -244,7 +245,6 @@ pub mod tests { let key_pairs: Vec<_> = (0..num_nodes).map(|_| Random.generate().unwrap()).collect(); let configs: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { threads: 1, - self_private: (***key_pairs[i].secret()).into(), listener_address: NodeAddress { address: "127.0.0.1".into(), port: start_port + (i as u16), @@ -259,8 +259,11 @@ pub mod tests { let key_servers_set: BTreeMap = configs[0].nodes.iter() .map(|(k, a)| (k.clone(), format!("{}:{}", a.address, a.port).parse().unwrap())) .collect(); - let key_servers: Vec<_> = configs.into_iter().map(|cfg| - KeyServerImpl::new(&cfg, Arc::new(MapKeyServerSet::new(key_servers_set.clone())), Arc::new(DummyAclStorage::default()), Arc::new(DummyKeyStorage::default())).unwrap() + let key_servers: Vec<_> = configs.into_iter().enumerate().map(|(i, cfg)| + KeyServerImpl::new(&cfg, Arc::new(MapKeyServerSet::new(key_servers_set.clone())), + Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), + Arc::new(DummyAclStorage::default()), + Arc::new(DummyKeyStorage::default())).unwrap() ).collect(); // wait until connections are established. It is fast => do not bother with events here diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index d77a82431..c267d1259 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -28,7 +28,7 @@ use tokio_core::reactor::{Handle, Remote, Interval}; use tokio_core::net::{TcpListener, TcpStream}; use ethkey::{Public, KeyPair, Signature, Random, Generator}; use util::H256; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, KeyServerSet}; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, KeyServerSet, NodeKeyPair}; use key_server_cluster::cluster_sessions::{ClusterSession, ClusterSessions, GenerationSessionWrapper, EncryptionSessionWrapper, DecryptionSessionWrapper, SigningSessionWrapper}; use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, @@ -99,7 +99,7 @@ pub struct ClusterConfiguration { /// Allow connecting to 'higher' nodes. pub allow_connecting_to_higher_nodes: bool, /// KeyPair this node holds. - pub self_key_pair: KeyPair, + pub self_key_pair: Arc, /// Interface to listen to. pub listen_address: (String, u16), /// Cluster nodes set. @@ -146,7 +146,7 @@ pub struct ClusterData { /// Handle to the cpu thread pool. pool: CpuPool, /// KeyPair this node holds. - self_key_pair: KeyPair, + self_key_pair: Arc, /// Connections data. connections: ClusterConnections, /// Active sessions data. @@ -262,7 +262,7 @@ impl ClusterCore { fn connect_future(handle: &Handle, data: Arc, node_address: SocketAddr) -> BoxedEmptyFuture { let disconnected_nodes = data.connections.disconnected_nodes().keys().cloned().collect(); net_connect(&node_address, handle, data.self_key_pair.clone(), disconnected_nodes) - .then(move |result| ClusterCore::process_connection_result(data, false, result)) + .then(move |result| ClusterCore::process_connection_result(data, Some(node_address), result)) .then(|_| finished(())) .boxed() } @@ -290,7 +290,7 @@ impl ClusterCore { /// Accept connection future. fn accept_connection_future(handle: &Handle, data: Arc, stream: TcpStream, node_address: SocketAddr) -> BoxedEmptyFuture { net_accept_connection(node_address, stream, handle, data.self_key_pair.clone()) - .then(move |result| ClusterCore::process_connection_result(data, true, result)) + .then(move |result| ClusterCore::process_connection_result(data, None, result)) .then(|_| finished(())) .boxed() } @@ -370,10 +370,10 @@ impl ClusterCore { } /// Process connection future result. - fn process_connection_result(data: Arc, is_inbound: bool, result: Result>, io::Error>) -> IoFuture> { + fn process_connection_result(data: Arc, outbound_addr: Option, result: Result>, io::Error>) -> IoFuture> { match result { Ok(DeadlineStatus::Meet(Ok(connection))) => { - let connection = Connection::new(is_inbound, connection); + let connection = Connection::new(outbound_addr.is_none(), connection); if data.connections.insert(connection.clone()) { ClusterCore::process_connection_messages(data.clone(), connection) } else { @@ -381,15 +381,21 @@ impl ClusterCore { } }, Ok(DeadlineStatus::Meet(Err(err))) => { - warn!(target: "secretstore_net", "{}: protocol error {} when establishind connection", data.self_key_pair.public(), err); + warn!(target: "secretstore_net", "{}: protocol error {} when establishing {} connection{}", + data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" }, + outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); finished(Ok(())).boxed() }, Ok(DeadlineStatus::Timeout) => { - warn!(target: "secretstore_net", "{}: timeout when establishind connection", data.self_key_pair.public()); + warn!(target: "secretstore_net", "{}: timeout when establishing {} connection{}", + data.self_key_pair.public(), if outbound_addr.is_some() { "outbound" } else { "inbound" }, + outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); finished(Ok(())).boxed() }, Err(err) => { - warn!(target: "secretstore_net", "{}: network error {} when establishind connection", data.self_key_pair.public(), err); + warn!(target: "secretstore_net", "{}: network error {} when establishing {} connection{}", + data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" }, + outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); finished(Ok(())).boxed() }, } @@ -989,7 +995,7 @@ pub mod tests { use parking_lot::Mutex; use tokio_core::reactor::Core; use ethkey::{Random, Generator, Public}; - use key_server_cluster::{NodeId, SessionId, Error, DummyAclStorage, DummyKeyStorage, MapKeyServerSet}; + use key_server_cluster::{NodeId, SessionId, Error, DummyAclStorage, DummyKeyStorage, MapKeyServerSet, PlainNodeKeyPair}; use key_server_cluster::message::Message; use key_server_cluster::cluster::{Cluster, ClusterCore, ClusterConfiguration}; use key_server_cluster::generation_session::{Session as GenerationSession, SessionState as GenerationSessionState}; @@ -1068,7 +1074,7 @@ pub mod tests { let key_pairs: Vec<_> = (0..num_nodes).map(|_| Random.generate().unwrap()).collect(); let cluster_params: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { threads: 1, - self_key_pair: key_pairs[i].clone(), + self_key_pair: Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), listen_address: ("127.0.0.1".to_owned(), ports_begin + i as u16), key_server_set: Arc::new(MapKeyServerSet::new(key_pairs.iter().enumerate() .map(|(j, kp)| (kp.public().clone(), format!("127.0.0.1:{}", ports_begin + j as u16).parse().unwrap())) diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 6a806bb92..bc3c6aad0 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -467,7 +467,7 @@ impl Ord for DecryptionSessionId { mod tests { use std::sync::Arc; use std::collections::BTreeMap; - use super::super::super::acl_storage::tests::DummyAclStorage; + use acl_storage::DummyAclStorage; use ethkey::{self, KeyPair, Random, Generator, Public, Secret}; use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::tests::DummyCluster; diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index e94d5bd35..ade78bc57 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -399,7 +399,7 @@ impl SessionImpl { // check state if data.state != SessionState::WaitingForKeysDissemination { match data.state { - SessionState::WaitingForInitializationComplete => return Err(Error::TooEarlyForRequest), + SessionState::WaitingForInitializationComplete | SessionState::WaitingForInitializationConfirm(_) => return Err(Error::TooEarlyForRequest), _ => return Err(Error::InvalidStateForRequest), } } @@ -1104,7 +1104,7 @@ pub mod tests { secret1: math::generate_random_scalar().unwrap().into(), secret2: math::generate_random_scalar().unwrap().into(), publics: vec![math::generate_random_point().unwrap().into()], - }).unwrap_err(), Error::InvalidStateForRequest); + }).unwrap_err(), Error::TooEarlyForRequest); } #[test] diff --git a/secret_store/src/key_server_cluster/io/handshake.rs b/secret_store/src/key_server_cluster/io/handshake.rs index df8f6cbf7..bf52ab798 100644 --- a/secret_store/src/key_server_cluster/io/handshake.rs +++ b/secret_store/src/key_server_cluster/io/handshake.rs @@ -15,24 +15,25 @@ // along with Parity. If not, see . use std::io; +use std::sync::Arc; use std::collections::BTreeSet; use futures::{Future, Poll, Async}; use tokio_io::{AsyncRead, AsyncWrite}; -use ethkey::{Random, Generator, KeyPair, Secret, sign, verify_public}; +use ethkey::{Random, Generator, KeyPair, verify_public}; use util::H256; -use key_server_cluster::{NodeId, Error}; +use key_server_cluster::{NodeId, Error, NodeKeyPair}; use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; use key_server_cluster::io::{write_message, write_encrypted_message, WriteMessage, ReadMessage, - read_message, read_encrypted_message, compute_shared_key}; + read_message, read_encrypted_message, fix_shared_key}; /// Start handshake procedure with another node from the cluster. -pub fn handshake(a: A, self_key_pair: KeyPair, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn handshake(a: A, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); handshake_with_plain_confirmation(a, self_confirmation_plain, self_key_pair, trusted_nodes) } /// Start handshake procedure with another node from the cluster and given plain confirmation. -pub fn handshake_with_plain_confirmation(a: A, self_confirmation_plain: Result, self_key_pair: KeyPair, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn handshake_with_plain_confirmation(a: A, self_confirmation_plain: Result, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { let (error, state) = match self_confirmation_plain.clone() .and_then(|c| Handshake::::make_public_key_message(self_key_pair.public().clone(), c)) { Ok(message) => (None, HandshakeState::SendPublicKey(write_message(a, message))), @@ -53,7 +54,7 @@ pub fn handshake_with_plain_confirmation(a: A, self_confirmation_plain: Resul } /// Wait for handshake procedure to be started by another node from the cluster. -pub fn accept_handshake(a: A, self_key_pair: KeyPair) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn accept_handshake(a: A, self_key_pair: Arc) -> Handshake where A: AsyncWrite + AsyncRead { let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); let (error, state) = match self_confirmation_plain.clone() { Ok(_) => (None, HandshakeState::ReceivePublicKey(read_message(a))), @@ -87,7 +88,7 @@ pub struct Handshake { is_active: bool, error: Option<(A, Result)>, state: HandshakeState, - self_key_pair: KeyPair, + self_key_pair: Arc, self_confirmation_plain: H256, trusted_nodes: Option>, other_node_id: Option, @@ -117,9 +118,9 @@ impl Handshake where A: AsyncRead + AsyncWrite { }))) } - fn make_private_key_signature_message(secret: &Secret, confirmation_plain: &H256) -> Result { + fn make_private_key_signature_message(self_key_pair: &NodeKeyPair, confirmation_plain: &H256) -> Result { Ok(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature { - confirmation_signed: sign(secret, confirmation_plain)?.into(), + confirmation_signed: self_key_pair.sign(confirmation_plain)?.into(), }))) } } @@ -142,15 +143,15 @@ impl Future for Handshake where A: AsyncRead + AsyncWrite { read_message(stream) ), Async::NotReady) } else { - self.shared_key = match compute_shared_key(self.self_key_pair.secret(), + self.shared_key = match self.self_key_pair.compute_shared_key( self.other_node_id.as_ref().expect("we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; other_node_id is filled in ReceivePublicKey; qed") - ) { + ).map_err(Into::into).and_then(|sk| fix_shared_key(sk.secret())) { Ok(shared_key) => Some(shared_key), - Err(err) => return Ok((stream, Err(err)).into()), + Err(err) => return Ok((stream, Err(err.into())).into()), }; let message = match Handshake::::make_private_key_signature_message( - self.self_key_pair.secret(), + &*self.self_key_pair, self.other_confirmation_plain.as_ref().expect("we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; other_confirmation_plain is filled in ReceivePublicKey; qed") ) { Ok(message) => message, @@ -179,15 +180,15 @@ impl Future for Handshake where A: AsyncRead + AsyncWrite { self.other_node_id = Some(message.node_id.into()); self.other_confirmation_plain = Some(message.confirmation_plain.into()); if self.is_active { - self.shared_key = match compute_shared_key(self.self_key_pair.secret(), + self.shared_key = match self.self_key_pair.compute_shared_key( self.other_node_id.as_ref().expect("filled couple of lines above; qed") - ) { + ).map_err(Into::into).and_then(|sk| fix_shared_key(sk.secret())) { Ok(shared_key) => Some(shared_key), - Err(err) => return Ok((stream, Err(err)).into()), + Err(err) => return Ok((stream, Err(err.into())).into()), }; let message = match Handshake::::make_private_key_signature_message( - self.self_key_pair.secret(), + &*self.self_key_pair, self.other_confirmation_plain.as_ref().expect("filled couple of lines above; qed") ) { Ok(message) => message, @@ -248,11 +249,14 @@ impl Future for Handshake where A: AsyncRead + AsyncWrite { #[cfg(test)] mod tests { + use std::sync::Arc; use std::collections::BTreeSet; use futures::Future; use ethkey::{Random, Generator, sign}; + use ethcrypto::ecdh::agree; use util::H256; - use key_server_cluster::io::message::compute_shared_key; + use key_server_cluster::PlainNodeKeyPair; + use key_server_cluster::io::message::fix_shared_key; use key_server_cluster::io::message::tests::TestIo; use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; use super::{handshake_with_plain_confirmation, accept_handshake, HandshakeResult}; @@ -283,9 +287,9 @@ mod tests { let (self_confirmation_plain, io) = prepare_test_io(); let self_key_pair = io.self_key_pair().clone(); let trusted_nodes: BTreeSet<_> = vec![io.peer_public().clone()].into_iter().collect(); - let shared_key = compute_shared_key(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap(); + let shared_key = fix_shared_key(&agree(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap()).unwrap(); - let handshake = handshake_with_plain_confirmation(io, Ok(self_confirmation_plain), self_key_pair, trusted_nodes); + let handshake = handshake_with_plain_confirmation(io, Ok(self_confirmation_plain), Arc::new(PlainNodeKeyPair::new(self_key_pair)), trusted_nodes); let handshake_result = handshake.wait().unwrap(); assert_eq!(handshake_result.1, Ok(HandshakeResult { node_id: handshake_result.0.peer_public().clone(), @@ -298,9 +302,9 @@ mod tests { let (self_confirmation_plain, io) = prepare_test_io(); let self_key_pair = io.self_key_pair().clone(); let trusted_nodes: BTreeSet<_> = vec![io.peer_public().clone()].into_iter().collect(); - let shared_key = compute_shared_key(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap(); + let shared_key = fix_shared_key(&agree(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap()).unwrap(); - let mut handshake = accept_handshake(io, self_key_pair); + let mut handshake = accept_handshake(io, Arc::new(PlainNodeKeyPair::new(self_key_pair))); handshake.set_self_confirmation_plain(self_confirmation_plain); let handshake_result = handshake.wait().unwrap(); diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index 49b71e39d..5a6b50a3e 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -19,9 +19,8 @@ use std::u16; use std::ops::Deref; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use serde_json; -use ethcrypto::ecdh::agree; use ethcrypto::ecies::{encrypt_single_message, decrypt_single_message}; -use ethkey::{Public, Secret, KeyPair}; +use ethkey::{Secret, KeyPair}; use ethkey::math::curve_order; use util::{H256, U256}; use key_server_cluster::Error; @@ -154,12 +153,11 @@ pub fn decrypt_message(key: &KeyPair, payload: Vec) -> Result, Error Ok(decrypt_single_message(key.secret(), &payload)?) } -/// Compute shared encryption key. -pub fn compute_shared_key(self_secret: &Secret, other_public: &Public) -> Result { +/// Fix shared encryption key. +pub fn fix_shared_key(shared_secret: &Secret) -> Result { // secret key created in agree function is invalid, as it is not calculated mod EC.field.n // => let's do it manually - let shared_secret = agree(self_secret, other_public)?; - let shared_secret: H256 = (*shared_secret).into(); + let shared_secret: H256 = (**shared_secret).into(); let shared_secret: U256 = shared_secret.into(); let shared_secret: H256 = (shared_secret % curve_order()).into(); let shared_key_pair = KeyPair::from_secret_slice(&*shared_secret)?; @@ -204,8 +202,9 @@ pub mod tests { use futures::Poll; use tokio_io::{AsyncRead, AsyncWrite}; use ethkey::{KeyPair, Public}; + use ethcrypto::ecdh::agree; use key_server_cluster::message::Message; - use super::{MESSAGE_HEADER_SIZE, MessageHeader, compute_shared_key, encrypt_message, serialize_message, + use super::{MESSAGE_HEADER_SIZE, MessageHeader, fix_shared_key, encrypt_message, serialize_message, serialize_header, deserialize_header}; pub struct TestIo { @@ -217,7 +216,7 @@ pub mod tests { impl TestIo { pub fn new(self_key_pair: KeyPair, peer_public: Public) -> Self { - let shared_key_pair = compute_shared_key(self_key_pair.secret(), &peer_public).unwrap(); + let shared_key_pair = fix_shared_key(&agree(self_key_pair.secret(), &peer_public).unwrap()).unwrap(); TestIo { self_key_pair: self_key_pair, peer_public: peer_public, diff --git a/secret_store/src/key_server_cluster/io/mod.rs b/secret_store/src/key_server_cluster/io/mod.rs index 57071038e..dfea33683 100644 --- a/secret_store/src/key_server_cluster/io/mod.rs +++ b/secret_store/src/key_server_cluster/io/mod.rs @@ -26,7 +26,7 @@ mod write_message; pub use self::deadline::{deadline, Deadline, DeadlineStatus}; pub use self::handshake::{handshake, accept_handshake, Handshake, HandshakeResult}; pub use self::message::{MessageHeader, SerializedMessage, serialize_message, deserialize_message, - encrypt_message, compute_shared_key}; + encrypt_message, fix_shared_key}; pub use self::read_header::{read_header, ReadHeader}; pub use self::read_payload::{read_payload, read_encrypted_payload, ReadPayload}; pub use self::read_message::{read_message, read_encrypted_message, ReadMessage}; diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 8f6ae4add..4fcda1539 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -20,6 +20,7 @@ use ethkey; use ethcrypto; use super::types::all::ServerKeyId; +pub use super::traits::NodeKeyPair; pub use super::types::all::{NodeId, EncryptedDocumentKeyShadow}; pub use super::acl_storage::AclStorage; pub use super::key_storage::{KeyStorage, DocumentKeyShare}; @@ -30,10 +31,12 @@ pub use self::generation_session::Session as GenerationSession; pub use self::encryption_session::Session as EncryptionSession; pub use self::decryption_session::Session as DecryptionSession; +#[cfg(test)] +pub use super::node_key_pair::PlainNodeKeyPair; #[cfg(test)] pub use super::key_storage::tests::DummyKeyStorage; #[cfg(test)] -pub use super::acl_storage::tests::DummyAclStorage; +pub use super::acl_storage::DummyAclStorage; #[cfg(test)] pub use super::key_server_set::tests::MapKeyServerSet; diff --git a/secret_store/src/key_server_cluster/net/accept_connection.rs b/secret_store/src/key_server_cluster/net/accept_connection.rs index 339625f3f..d85e492dd 100644 --- a/secret_store/src/key_server_cluster/net/accept_connection.rs +++ b/secret_store/src/key_server_cluster/net/accept_connection.rs @@ -15,18 +15,18 @@ // along with Parity. If not, see . use std::io; +use std::sync::Arc; use std::net::SocketAddr; use std::time::Duration; use futures::{Future, Poll}; use tokio_core::reactor::Handle; use tokio_core::net::TcpStream; -use ethkey::KeyPair; -use key_server_cluster::Error; +use key_server_cluster::{Error, NodeKeyPair}; use key_server_cluster::io::{accept_handshake, Handshake, Deadline, deadline}; use key_server_cluster::net::Connection; /// Create future for accepting incoming connection. -pub fn accept_connection(address: SocketAddr, stream: TcpStream, handle: &Handle, self_key_pair: KeyPair) -> Deadline { +pub fn accept_connection(address: SocketAddr, stream: TcpStream, handle: &Handle, self_key_pair: Arc) -> Deadline { let accept = AcceptConnection { handshake: accept_handshake(stream, self_key_pair), address: address, diff --git a/secret_store/src/key_server_cluster/net/connect.rs b/secret_store/src/key_server_cluster/net/connect.rs index 449168ab2..7515494e4 100644 --- a/secret_store/src/key_server_cluster/net/connect.rs +++ b/secret_store/src/key_server_cluster/net/connect.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::sync::Arc; use std::collections::BTreeSet; use std::io; use std::time::Duration; @@ -21,13 +22,12 @@ use std::net::SocketAddr; use futures::{Future, Poll, Async}; use tokio_core::reactor::Handle; use tokio_core::net::{TcpStream, TcpStreamNew}; -use ethkey::KeyPair; -use key_server_cluster::{Error, NodeId}; +use key_server_cluster::{Error, NodeId, NodeKeyPair}; use key_server_cluster::io::{handshake, Handshake, Deadline, deadline}; use key_server_cluster::net::Connection; /// Create future for connecting to other node. -pub fn connect(address: &SocketAddr, handle: &Handle, self_key_pair: KeyPair, trusted_nodes: BTreeSet) -> Deadline { +pub fn connect(address: &SocketAddr, handle: &Handle, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Deadline { let connect = Connect { state: ConnectState::TcpConnect(TcpStream::connect(address, handle)), address: address.clone(), @@ -48,7 +48,7 @@ enum ConnectState { pub struct Connect { state: ConnectState, address: SocketAddr, - self_key_pair: KeyPair, + self_key_pair: Arc, trusted_nodes: BTreeSet, } diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 00246ae64..e56306142 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -572,7 +572,7 @@ mod tests { use std::collections::{BTreeMap, VecDeque}; use ethkey::{self, Random, Generator, Public}; use util::H256; - use super::super::super::acl_storage::tests::DummyAclStorage; + use acl_storage::DummyAclStorage; use key_server_cluster::{NodeId, SessionId, SessionMeta, Error, KeyStorage}; use key_server_cluster::cluster::tests::DummyCluster; use key_server_cluster::generation_session::{Session as GenerationSession}; diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index d5af7a5fa..fdbb5fa40 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -200,7 +200,7 @@ pub mod tests { use devtools::RandomTempPath; use ethkey::{Random, Generator, Public, Secret}; use util::Database; - use super::super::types::all::{Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, ServerKeyId}; + use types::all::{Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, ServerKeyId}; use super::{DB_META_KEY_VERSION, KeyStorage, PersistentKeyStorage, DocumentKeyShare, SerializableDocumentKeyShareV0, SerializableDocumentKeyShareV1, upgrade_db}; @@ -234,14 +234,11 @@ pub mod tests { fn persistent_key_storage() { let path = RandomTempPath::create_dir(); let config = ServiceConfiguration { - listener_address: NodeAddress { - address: "0.0.0.0".to_owned(), - port: 8082, - }, + listener_address: None, + acl_check_enabled: true, data_path: path.as_str().to_owned(), cluster_config: ClusterConfiguration { threads: 1, - self_private: (**Random.generate().unwrap().secret().clone()).into(), listener_address: NodeAddress { address: "0.0.0.0".to_owned(), port: 8083, diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index 9750f7223..d7f35a55a 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -59,22 +59,28 @@ mod key_server; mod key_storage; mod serialization; mod key_server_set; +mod node_key_pair; use std::sync::Arc; use ethcore::client::Client; pub use types::all::{ServerKeyId, EncryptedDocumentKey, RequestSignature, Public, Error, NodeAddress, ServiceConfiguration, ClusterConfiguration}; -pub use traits::{KeyServer}; +pub use traits::{NodeKeyPair, KeyServer}; +pub use self::node_key_pair::{PlainNodeKeyPair, KeyStoreNodeKeyPair}; /// Start new key server instance -pub fn start(client: Arc, config: ServiceConfiguration) -> Result, Error> { +pub fn start(client: Arc, self_key_pair: Arc, config: ServiceConfiguration) -> Result, Error> { use std::sync::Arc; - let acl_storage = acl_storage::OnChainAclStorage::new(&client); + let acl_storage: Arc = if config.acl_check_enabled { + acl_storage::OnChainAclStorage::new(&client) + } else { + Arc::new(acl_storage::DummyAclStorage::default()) + }; let key_server_set = key_server_set::OnChainKeyServerSet::new(&client, config.cluster_config.nodes.clone())?; let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?); - let key_server = key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, acl_storage, key_storage)?; - let listener = http_listener::KeyServerHttpListener::start(&config.listener_address, key_server)?; + let key_server = key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, self_key_pair, acl_storage, key_storage)?; + let listener = http_listener::KeyServerHttpListener::start(config.listener_address, key_server)?; Ok(Box::new(listener)) } diff --git a/secret_store/src/node_key_pair.rs b/secret_store/src/node_key_pair.rs new file mode 100644 index 000000000..ce6c88a07 --- /dev/null +++ b/secret_store/src/node_key_pair.rs @@ -0,0 +1,84 @@ +// Copyright 2015-2017 Parity Technologies (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::sync::Arc; +use ethcrypto::ecdh::agree; +use ethkey::{KeyPair, Public, Signature, Error as EthKeyError, sign}; +use ethcore::account_provider::AccountProvider; +use util::{Address, H256}; +use traits::NodeKeyPair; + +pub struct PlainNodeKeyPair { + key_pair: KeyPair, +} + +pub struct KeyStoreNodeKeyPair { + account_provider: Arc, + address: Address, + public: Public, + password: String, +} + +impl PlainNodeKeyPair { + pub fn new(key_pair: KeyPair) -> Self { + PlainNodeKeyPair { + key_pair: key_pair, + } + } +} + +impl NodeKeyPair for PlainNodeKeyPair { + fn public(&self) -> &Public { + self.key_pair.public() + } + + fn sign(&self, data: &H256) -> Result { + sign(self.key_pair.secret(), data) + } + + fn compute_shared_key(&self, peer_public: &Public) -> Result { + agree(self.key_pair.secret(), peer_public).map_err(|e| EthKeyError::Custom(e.into())) + .and_then(KeyPair::from_secret) + } +} + +impl KeyStoreNodeKeyPair { + pub fn new(account_provider: Arc, address: Address, password: String) -> Result { + let public = account_provider.account_public(address.clone(), &password).map_err(|e| EthKeyError::Custom(format!("{}", e)))?; + Ok(KeyStoreNodeKeyPair { + account_provider: account_provider, + address: address, + public: public, + password: password, + }) + } +} + +impl NodeKeyPair for KeyStoreNodeKeyPair { + fn public(&self) -> &Public { + &self.public + } + + fn sign(&self, data: &H256) -> Result { + self.account_provider.sign(self.address.clone(), Some(self.password.clone()), data.clone()) + .map_err(|e| EthKeyError::Custom(format!("{}", e))) + } + + fn compute_shared_key(&self, peer_public: &Public) -> Result { + KeyPair::from_secret(self.account_provider.agree(self.address.clone(), Some(self.password.clone()), peer_public) + .map_err(|e| EthKeyError::Custom(format!("{}", e)))?) + } +} diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index 33a4eff3c..31da748e0 100644 --- a/secret_store/src/traits.rs +++ b/secret_store/src/traits.rs @@ -14,9 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use ethkey::{KeyPair, Signature, Error as EthKeyError}; +use util::H256; use types::all::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, EncryptedDocumentKey, EncryptedDocumentKeyShadow}; +/// Node key pair. +pub trait NodeKeyPair: Send + Sync { + /// Public portion of key. + fn public(&self) -> &Public; + /// Sign data with node key. + fn sign(&self, data: &H256) -> Result; + /// Compute shared key to encrypt channel between two nodes. + fn compute_shared_key(&self, peer_public: &Public) -> Result; +} + /// Server key (SK) generator. pub trait ServerKeyGenerator { /// Generate new SK. diff --git a/secret_store/src/types/all.rs b/secret_store/src/types/all.rs index 54fc8acae..6867c82f3 100644 --- a/secret_store/src/types/all.rs +++ b/secret_store/src/types/all.rs @@ -69,8 +69,10 @@ pub struct NodeAddress { #[binary] /// Secret store configuration pub struct ServiceConfiguration { - /// HTTP listener address. - pub listener_address: NodeAddress, + /// HTTP listener address. If None, HTTP API is disabled. + pub listener_address: Option, + /// Is ACL check enabled. If false, everyone has access to all keys. Useful for tests only. + pub acl_check_enabled: bool, /// Data directory path for secret store pub data_path: String, /// Cluster configuration. @@ -83,8 +85,6 @@ pub struct ServiceConfiguration { pub struct ClusterConfiguration { /// Number of threads reserved by cluster. pub threads: usize, - /// Private key this node holds. - pub self_private: Vec, // holds ethkey::Secret /// This node address. pub listener_address: NodeAddress, /// All cluster nodes addresses. diff --git a/sync/src/chain.rs b/sync/src/chain.rs index d6937381c..3a472c88f 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -133,7 +133,7 @@ const MAX_TRANSACTION_PACKET_SIZE: usize = 8 * 1024 * 1024; // Maximal number of transactions in sent in single packet. const MAX_TRANSACTIONS_TO_PROPAGATE: usize = 64; // Min number of blocks to be behind for a snapshot sync -const SNAPSHOT_RESTORE_THRESHOLD: BlockNumber = 100000; +const SNAPSHOT_RESTORE_THRESHOLD: BlockNumber = 10000; const SNAPSHOT_MIN_PEERS: usize = 3; const STATUS_PACKET: u8 = 0x00; diff --git a/sync/src/tests/consensus.rs b/sync/src/tests/consensus.rs index 499b7de17..9b01156b7 100644 --- a/sync/src/tests/consensus.rs +++ b/sync/src/tests/consensus.rs @@ -42,7 +42,7 @@ impl IoHandler for TestIoHandler { } } -fn new_tx(secret: &Secret, nonce: U256, network_id: u64) -> PendingTransaction { +fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction { let signed = Transaction { nonce: nonce.into(), gas_price: 0.into(), @@ -50,7 +50,7 @@ fn new_tx(secret: &Secret, nonce: U256, network_id: u64) -> PendingTransaction { action: Action::Call(Address::default()), value: 0.into(), data: Vec::new(), - }.sign(secret, Some(network_id)); + }.sign(secret, Some(chain_id)); PendingTransaction::new(signed, None) } @@ -62,7 +62,7 @@ fn authority_round() { ap.insert_account(s0.secret().clone(), "").unwrap(); ap.insert_account(s1.secret().clone(), "").unwrap(); - let network_id = Spec::new_test_round().network_id(); + let chain_id = Spec::new_test_round().chain_id(); let mut net = TestNet::with_spec_and_accounts(2, SyncConfig::default(), Spec::new_test_round, Some(ap)); let io_handler0: Arc> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() }); let io_handler1: Arc> = Arc::new(TestIoHandler { client: net.peer(1).chain.clone() }); @@ -76,15 +76,15 @@ fn authority_round() { // exchange statuses net.sync(); // Trigger block proposal - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), network_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), network_id)).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id)).unwrap(); // Sync a block net.sync(); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), network_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), network_id)).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id)).unwrap(); // Move to next proposer step. net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); @@ -93,8 +93,8 @@ fn authority_round() { assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); // Fork the network with equal height. - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), network_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), network_id)).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id)).unwrap(); // Let both nodes build one block. net.peer(0).chain.engine().step(); let early_hash = net.peer(0).chain.chain_info().best_block_hash; @@ -116,8 +116,8 @@ fn authority_round() { assert_eq!(ci1.best_block_hash, early_hash); // Selfish miner - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), network_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), network_id)).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), chain_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), chain_id)).unwrap(); // Node 0 is an earlier primary. net.peer(0).chain.engine().step(); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4); @@ -128,7 +128,7 @@ fn authority_round() { // Node 1 makes 2 blocks, but is a later primary on the first one. net.peer(1).chain.engine().step(); net.peer(1).chain.engine().step(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), chain_id)).unwrap(); net.peer(1).chain.engine().step(); net.peer(1).chain.engine().step(); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5); @@ -149,7 +149,7 @@ fn tendermint() { ap.insert_account(s0.secret().clone(), "").unwrap(); ap.insert_account(s1.secret().clone(), "").unwrap(); - let network_id = Spec::new_test_tendermint().network_id(); + let chain_id = Spec::new_test_tendermint().chain_id(); let mut net = TestNet::with_spec_and_accounts(2, SyncConfig::default(), Spec::new_test_tendermint, Some(ap)); let io_handler0: Arc> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() }); let io_handler1: Arc> = Arc::new(TestIoHandler { client: net.peer(1).chain.clone() }); @@ -165,7 +165,7 @@ fn tendermint() { // Exhange statuses net.sync(); // Propose - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), network_id)).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id)).unwrap(); net.sync(); // Propose timeout, synchronous for now net.peer(0).chain.engine().step(); @@ -176,7 +176,7 @@ fn tendermint() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id)).unwrap(); // Commit timeout net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); @@ -190,8 +190,8 @@ fn tendermint() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), network_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), network_id)).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id)).unwrap(); // Peers get disconnected. // Commit net.peer(0).chain.engine().step(); @@ -199,8 +199,8 @@ fn tendermint() { // Propose net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), network_id)).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), network_id)).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id)).unwrap(); // Send different prevotes net.sync(); // Prevote timeout diff --git a/util/Cargo.toml b/util/Cargo.toml index 5306e3c57..0e485431e 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -19,7 +19,6 @@ rust-crypto = "0.2.34" elastic-array = "0.9" rlp = { path = "rlp" } heapsize = "0.4" -itertools = "0.5" sha3 = { path = "sha3" } clippy = { version = "0.0.103", optional = true} ethcore-devtools = { path = "../devtools" } diff --git a/util/benches/trie.rs b/util/benches/trie.rs index d526b97ac..e02077ad4 100644 --- a/util/benches/trie.rs +++ b/util/benches/trie.rs @@ -45,14 +45,14 @@ fn random_bytes(min_count: usize, diff_count: usize, seed: &mut H256) -> Vec assert!(min_count + diff_count <= 32); *seed = seed.sha3(); let r = min_count + (seed[31] as usize % (diff_count + 1)); - seed[0..r].into_vec() + seed[0..r].to_vec() } fn random_value(seed: &mut H256) -> Bytes { *seed = seed.sha3(); match seed[0] % 2 { 1 => vec![seed[31];1], - _ => seed.into_vec(), + _ => seed.to_vec(), } } diff --git a/util/bigint/Cargo.toml b/util/bigint/Cargo.toml index 477925334..36a272f8c 100644 --- a/util/bigint/Cargo.toml +++ b/util/bigint/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.3" authors = ["Parity Technologies "] [dependencies] -bigint = "3.0" +bigint = "4.0" rustc-hex = "1.0" rand = "0.3.12" libc = "0.2" diff --git a/util/rlp/benches/rlp.rs b/util/rlp/benches/rlp.rs index 927b48583..6aeabaf5d 100644 --- a/util/rlp/benches/rlp.rs +++ b/util/rlp/benches/rlp.rs @@ -15,7 +15,7 @@ #![feature(test)] extern crate test; -extern crate bigint; +extern crate ethcore_bigint as bigint; extern crate rlp; use test::Bencher; diff --git a/util/rlp_derive/Cargo.toml b/util/rlp_derive/Cargo.toml new file mode 100644 index 000000000..81e50cd44 --- /dev/null +++ b/util/rlp_derive/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rlp_derive" +version = "0.1.0" +authors = ["debris "] + +[lib] +name = "rlp_derive" +proc-macro = true + +[dependencies] +syn = "0.11.11" +quote = "0.3.15" + +[dev-dependencies] +rlp = { path = "../rlp" } diff --git a/util/rlp_derive/src/de.rs b/util/rlp_derive/src/de.rs new file mode 100644 index 000000000..a3e32739a --- /dev/null +++ b/util/rlp_derive/src/de.rs @@ -0,0 +1,139 @@ +use {syn, quote}; + +struct ParseQuotes { + single: quote::Tokens, + list: quote::Tokens, + takes_index: bool, +} + +fn decodable_parse_quotes() -> ParseQuotes { + ParseQuotes { + single: quote! { rlp.val_at }, + list: quote! { rlp.list_at }, + takes_index: true, + } +} + +fn decodable_wrapper_parse_quotes() -> ParseQuotes { + ParseQuotes { + single: quote! { rlp.as_val }, + list: quote! { rlp.as_list }, + takes_index: false, + } +} + +pub fn impl_decodable(ast: &syn::DeriveInput) -> quote::Tokens { + let body = match ast.body { + syn::Body::Struct(ref s) => s, + _ => panic!("#[derive(RlpDecodable)] is only defined for structs."), + }; + + let stmts: Vec<_> = match *body { + syn::VariantData::Struct(ref fields) | syn::VariantData::Tuple(ref fields) => + fields.iter().enumerate().map(decodable_field_map).collect(), + syn::VariantData::Unit => panic!("#[derive(RlpDecodable)] is not defined for Unit structs."), + }; + + let name = &ast.ident; + + let dummy_const = syn::Ident::new(format!("_IMPL_RLP_DECODABLE_FOR_{}", name)); + let impl_block = quote! { + impl rlp::Decodable for #name { + fn decode(rlp: &rlp::UntrustedRlp) -> Result { + let result = #name { + #(#stmts)* + }; + + Ok(result) + } + } + }; + + quote! { + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const #dummy_const: () = { + extern crate rlp; + #impl_block + }; + } +} + +pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> quote::Tokens { + let body = match ast.body { + syn::Body::Struct(ref s) => s, + _ => panic!("#[derive(RlpDecodableWrapper)] is only defined for structs."), + }; + + let stmt = match *body { + syn::VariantData::Struct(ref fields) | syn::VariantData::Tuple(ref fields) => { + if fields.len() == 1 { + let field = fields.first().expect("fields.len() == 1; qed"); + decodable_field(0, field, decodable_wrapper_parse_quotes()) + } else { + panic!("#[derive(RlpDecodableWrapper)] is only defined for structs with one field.") + } + }, + syn::VariantData::Unit => panic!("#[derive(RlpDecodableWrapper)] is not defined for Unit structs."), + }; + + let name = &ast.ident; + + let dummy_const = syn::Ident::new(format!("_IMPL_RLP_DECODABLE_FOR_{}", name)); + let impl_block = quote! { + impl rlp::Decodable for #name { + fn decode(rlp: &rlp::UntrustedRlp) -> Result { + let result = #name { + #stmt + }; + + Ok(result) + } + } + }; + + quote! { + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const #dummy_const: () = { + extern crate rlp; + #impl_block + }; + } +} + +fn decodable_field_map(tuple: (usize, &syn::Field)) -> quote::Tokens { + decodable_field(tuple.0, tuple.1, decodable_parse_quotes()) +} + +fn decodable_field(index: usize, field: &syn::Field, quotes: ParseQuotes) -> quote::Tokens { + let ident = match field.ident { + Some(ref ident) => ident.to_string(), + None => index.to_string(), + }; + + let id = syn::Ident::new(ident); + let index = syn::Ident::new(index.to_string()); + + let single = quotes.single; + let list = quotes.list; + + match field.ty { + syn::Ty::Path(_, ref path) => { + let ident = &path.segments.first().expect("there must be at least 1 segment").ident; + if &ident.to_string() == "Vec" { + if quotes.takes_index { + quote! { #id: #list(#index)?, } + } else { + quote! { #id: #list()?, } + } + } else { + if quotes.takes_index { + quote! { #id: #single(#index)?, } + } else { + quote! { #id: #single()?, } + } + } + }, + _ => panic!("rlp_derive not supported"), + } +} + diff --git a/util/rlp_derive/src/en.rs b/util/rlp_derive/src/en.rs new file mode 100644 index 000000000..77f361200 --- /dev/null +++ b/util/rlp_derive/src/en.rs @@ -0,0 +1,110 @@ +use {syn, quote}; + +pub fn impl_encodable(ast: &syn::DeriveInput) -> quote::Tokens { + let body = match ast.body { + syn::Body::Struct(ref s) => s, + _ => panic!("#[derive(RlpEncodable)] is only defined for structs."), + }; + + let stmts: Vec<_> = match *body { + syn::VariantData::Struct(ref fields) | syn::VariantData::Tuple(ref fields) => + fields.iter().enumerate().map(encodable_field_map).collect(), + syn::VariantData::Unit => panic!("#[derive(RlpEncodable)] is not defined for Unit structs."), + }; + + let name = &ast.ident; + + let stmts_len = syn::Ident::new(stmts.len().to_string()); + let dummy_const = syn::Ident::new(format!("_IMPL_RLP_ENCODABLE_FOR_{}", name)); + let impl_block = quote! { + impl rlp::Encodable for #name { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.begin_list(#stmts_len); + #(#stmts)* + } + } + }; + + quote! { + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const #dummy_const: () = { + extern crate rlp; + #impl_block + }; + } +} + +pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> quote::Tokens { + let body = match ast.body { + syn::Body::Struct(ref s) => s, + _ => panic!("#[derive(RlpEncodableWrapper)] is only defined for structs."), + }; + + let stmt = match *body { + syn::VariantData::Struct(ref fields) | syn::VariantData::Tuple(ref fields) => { + if fields.len() == 1 { + let field = fields.first().expect("fields.len() == 1; qed"); + encodable_field(0, field) + } else { + panic!("#[derive(RlpEncodableWrapper)] is only defined for structs with one field.") + } + }, + syn::VariantData::Unit => panic!("#[derive(RlpEncodableWrapper)] is not defined for Unit structs."), + }; + + let name = &ast.ident; + + let dummy_const = syn::Ident::new(format!("_IMPL_RLP_ENCODABLE_FOR_{}", name)); + let impl_block = quote! { + impl rlp::Encodable for #name { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + #stmt + } + } + }; + + quote! { + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const #dummy_const: () = { + extern crate rlp; + #impl_block + }; + } +} + +fn encodable_field_map(tuple: (usize, &syn::Field)) -> quote::Tokens { + encodable_field(tuple.0, tuple.1) +} + +fn encodable_field(index: usize, field: &syn::Field) -> quote::Tokens { + let ident = match field.ident { + Some(ref ident) => ident.to_string(), + None => index.to_string(), + }; + + let id = syn::Ident::new(format!("self.{}", ident)); + + match field.ty { + syn::Ty::Path(_, ref path) => { + let top_segment = path.segments.first().expect("there must be at least 1 segment"); + let ident = &top_segment.ident; + if &ident.to_string() == "Vec" { + let inner_ident = match top_segment.parameters { + syn::PathParameters::AngleBracketed(ref angle) => { + let ty = angle.types.first().expect("Vec has only one angle bracketed type; qed"); + match *ty { + syn::Ty::Path(_, ref path) => &path.segments.first().expect("there must be at least 1 segment").ident, + _ => panic!("rlp_derive not supported"), + } + }, + _ => unreachable!("Vec has only one angle bracketed type; qed"), + }; + quote! { stream.append_list::<#inner_ident, _>(&#id); } + } else { + quote! { stream.append(&#id); } + } + }, + _ => panic!("rlp_derive not supported"), + } +} + diff --git a/util/rlp_derive/src/lib.rs b/util/rlp_derive/src/lib.rs new file mode 100644 index 000000000..9c598d08e --- /dev/null +++ b/util/rlp_derive/src/lib.rs @@ -0,0 +1,43 @@ +extern crate proc_macro; +extern crate syn; +#[macro_use] +extern crate quote; + +mod en; +mod de; + +use proc_macro::TokenStream; +use en::{impl_encodable, impl_encodable_wrapper}; +use de::{impl_decodable, impl_decodable_wrapper}; + +#[proc_macro_derive(RlpEncodable)] +pub fn encodable(input: TokenStream) -> TokenStream { + let s = input.to_string(); + let ast = syn::parse_derive_input(&s).unwrap(); + let gen = impl_encodable(&ast); + gen.parse().unwrap() +} + +#[proc_macro_derive(RlpEncodableWrapper)] +pub fn encodable_wrapper(input: TokenStream) -> TokenStream { + let s = input.to_string(); + let ast = syn::parse_derive_input(&s).unwrap(); + let gen = impl_encodable_wrapper(&ast); + gen.parse().unwrap() +} + +#[proc_macro_derive(RlpDecodable)] +pub fn decodable(input: TokenStream) -> TokenStream { + let s = input.to_string(); + let ast = syn::parse_derive_input(&s).unwrap(); + let gen = impl_decodable(&ast); + gen.parse().unwrap() +} + +#[proc_macro_derive(RlpDecodableWrapper)] +pub fn decodable_wrapper(input: TokenStream) -> TokenStream { + let s = input.to_string(); + let ast = syn::parse_derive_input(&s).unwrap(); + let gen = impl_decodable_wrapper(&ast); + gen.parse().unwrap() +} diff --git a/util/rlp_derive/tests/rlp.rs b/util/rlp_derive/tests/rlp.rs new file mode 100644 index 000000000..c87380524 --- /dev/null +++ b/util/rlp_derive/tests/rlp.rs @@ -0,0 +1,44 @@ +extern crate rlp; +#[macro_use] +extern crate rlp_derive; + +use rlp::{encode, decode}; + +#[derive(Debug, PartialEq, RlpEncodable, RlpDecodable)] +struct Foo { + a: String, +} + +#[derive(Debug, PartialEq, RlpEncodableWrapper, RlpDecodableWrapper)] +struct FooWrapper { + a: String, +} + +#[test] +fn test_encode_foo() { + let foo = Foo { + a: "cat".into(), + }; + + let expected = vec![0xc4, 0x83, b'c', b'a', b't']; + let out = encode(&foo).into_vec(); + assert_eq!(out, expected); + + let decoded = decode(&expected); + assert_eq!(foo, decoded); +} + +#[test] +fn test_encode_foo_wrapper() { + let foo = FooWrapper { + a: "cat".into(), + }; + + let expected = vec![0x83, b'c', b'a', b't']; + let out = encode(&foo).into_vec(); + assert_eq!(out, expected); + + let decoded = decode(&expected); + assert_eq!(foo, decoded); +} + diff --git a/util/src/journaldb/earlymergedb.rs b/util/src/journaldb/earlymergedb.rs index 5f409d327..b7b57d537 100644 --- a/util/src/journaldb/earlymergedb.rs +++ b/util/src/journaldb/earlymergedb.rs @@ -21,7 +21,6 @@ use std::collections::HashMap; use std::sync::Arc; use parking_lot::RwLock; use heapsize::HeapSizeOf; -use itertools::Itertools; use rlp::*; use hashdb::*; use memorydb::*; @@ -432,7 +431,9 @@ impl JournalDB for EarlyMergeDB { // - we write the key into our journal for this block; r.begin_list(inserts.len()); - inserts.iter().foreach(|&(k, _)| {r.append(&k);}); + for &(k, _) in &inserts { + r.append(&k); + } r.append_list(&removes); Self::insert_keys(&inserts, &*self.backing, self.column, &mut refs, batch, trace); diff --git a/util/src/kvdb.rs b/util/src/kvdb.rs index e6568d68c..44321c531 100644 --- a/util/src/kvdb.rs +++ b/util/src/kvdb.rs @@ -493,6 +493,7 @@ impl Database { } opts.set_parsed_options(&format!("max_total_wal_size={}", 64 * 1024 * 1024))?; opts.set_parsed_options("verify_checksums_in_compaction=0")?; + opts.set_parsed_options("keep_log_file_num=1")?; opts.set_max_open_files(config.max_open_files); opts.create_if_missing(true); opts.set_use_fsync(false); diff --git a/util/src/lib.rs b/util/src/lib.rs index 544f60d39..46730fe9c 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -106,7 +106,6 @@ extern crate rlp; extern crate regex; extern crate lru_cache; extern crate heapsize; -extern crate itertools; extern crate ethcore_logger; #[macro_use] @@ -153,7 +152,6 @@ pub use bigint::hash; pub use ansi_term::{Colour, Style}; pub use heapsize::HeapSizeOf; -pub use itertools::Itertools; pub use parking_lot::{Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; /// 160-bit integer representing account address diff --git a/util/src/trie/triedb.rs b/util/src/trie/triedb.rs index 262a874ea..e9f4bc671 100644 --- a/util/src/trie/triedb.rs +++ b/util/src/trie/triedb.rs @@ -15,7 +15,6 @@ // along with Parity. If not, see . use std::fmt; -use itertools::Itertools; use hashdb::*; use nibbleslice::*; use rlp::*; @@ -276,9 +275,15 @@ impl<'a> TrieDBIterator<'a> { /// Descend into a payload. fn descend(&mut self, d: &[u8]) -> super::Result<()> { + let node = Node::decoded(&self.db.get_raw_or_lookup(d)?).into(); + Ok(self.descend_into_node(node)) + } + + /// Descend into a payload. + fn descend_into_node(&mut self, node: OwnedNode) { self.trail.push(Crumb { status: Status::Entering, - node: Node::decoded(&self.db.get_raw_or_lookup(d)?).into(), + node: node, }); match &self.trail.last().expect("just pushed item; qed").node { &OwnedNode::Leaf(ref n, _) | &OwnedNode::Extension(ref n, _) => { @@ -286,14 +291,20 @@ impl<'a> TrieDBIterator<'a> { }, _ => {} } - - Ok(()) } /// The present key. fn key(&self) -> Bytes { // collapse the key_nibbles down to bytes. - self.key_nibbles.iter().step(2).zip(self.key_nibbles.iter().skip(1).step(2)).map(|(h, l)| h * 16 + l).collect() + let nibbles = &self.key_nibbles; + let mut i = 1; + let mut result = Bytes::with_capacity(nibbles.len() / 2); + let len = nibbles.len(); + while i < len { + result.push(nibbles[i - 1] * 16 + nibbles[i]); + i += 2; + } + result } } @@ -311,52 +322,67 @@ impl<'a> Iterator for TrieDBIterator<'a> { type Item = TrieItem<'a>; fn next(&mut self) -> Option { + enum IterStep { + Continue, + PopTrail, + Descend(super::Result), + } + loop { - let b = match self.trail.last_mut() { - Some(mut b) => { b.increment(); b.clone() }, - None => return None, + let iter_step = { + match self.trail.last_mut() { + Some(b) => { b.increment(); }, + None => return None, + } + + let b = self.trail.last().expect("trail.last_mut().is_some(); qed"); + + match (b.status.clone(), &b.node) { + (Status::Exiting, n) => { + match *n { + OwnedNode::Leaf(ref n, _) | OwnedNode::Extension(ref n, _) => { + let l = self.key_nibbles.len(); + self.key_nibbles.truncate(l - n.len()); + }, + OwnedNode::Branch(_, _) => { self.key_nibbles.pop(); }, + _ => {} + } + IterStep::PopTrail + }, + (Status::At, &OwnedNode::Leaf(_, ref v)) | (Status::At, &OwnedNode::Branch(_, Some(ref v))) => { + return Some(Ok((self.key(), v.clone()))); + }, + (Status::At, &OwnedNode::Extension(_, ref d)) => IterStep::Descend(self.db.get_raw_or_lookup(&*d)), + (Status::At, &OwnedNode::Branch(_, _)) => IterStep::Continue, + (Status::AtChild(i), &OwnedNode::Branch(ref children, _)) if children[i].len() > 0 => { + match i { + 0 => self.key_nibbles.push(0), + i => *self.key_nibbles.last_mut() + .expect("pushed as 0; moves sequentially; removed afterwards; qed") = i as u8, + } + IterStep::Descend(self.db.get_raw_or_lookup(&*children[i])) + }, + (Status::AtChild(i), &OwnedNode::Branch(_, _)) => { + if i == 0 { + self.key_nibbles.push(0); + } + IterStep::Continue + }, + _ => panic!() // Should never see Entering or AtChild without a Branch here. + } }; - match (b.status, b.node) { - (Status::Exiting, n) => { - match n { - OwnedNode::Leaf(n, _) | OwnedNode::Extension(n, _) => { - let l = self.key_nibbles.len(); - self.key_nibbles.truncate(l - n.len()); - }, - OwnedNode::Branch(_, _) => { self.key_nibbles.pop(); }, - _ => {} - } + + match iter_step { + IterStep::PopTrail => { self.trail.pop(); - // continue }, - (Status::At, OwnedNode::Leaf(_, v)) | (Status::At, OwnedNode::Branch(_, Some(v))) => { - return Some(Ok((self.key(), v))); + IterStep::Descend(Ok(d)) => { + self.descend_into_node(Node::decoded(&d).into()) }, - (Status::At, OwnedNode::Extension(_, d)) => { - if let Err(e) = self.descend(&*d) { - return Some(Err(e)); - } - // continue - }, - (Status::At, OwnedNode::Branch(_, _)) => {}, - (Status::AtChild(i), OwnedNode::Branch(ref children, _)) if children[i].len() > 0 => { - match i { - 0 => self.key_nibbles.push(0), - i => *self.key_nibbles.last_mut() - .expect("pushed as 0; moves sequentially; removed afterwards; qed") = i as u8, - } - if let Err(e) = self.descend(&*children[i]) { - return Some(Err(e)); - } - // continue - }, - (Status::AtChild(i), OwnedNode::Branch(_, _)) => { - if i == 0 { - self.key_nibbles.push(0); - } - // continue - }, - _ => panic!() // Should never see Entering or AtChild without a Branch here. + IterStep::Descend(Err(e)) => { + return Some(Err(e)) + } + IterStep::Continue => {}, } } }